@ofidj/generator-fidj 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -409,9 +409,44 @@ function render() {
|
|
|
409
409
|
config.ownCredentials ? credentialFields() : "",
|
|
410
410
|
isFidjItself,
|
|
411
411
|
);
|
|
412
|
+
// On Fidj itself there is nobody to hand the person to, so the credential
|
|
413
|
+
// screen is fetched straight away: what they see is the form, or their name.
|
|
414
|
+
// Once per document, and never once the provider has already answered — a
|
|
415
|
+
// refusal or a cancellation must not bounce them round again.
|
|
416
|
+
if (
|
|
417
|
+
oidc &&
|
|
418
|
+
isFidjItself &&
|
|
419
|
+
// Only from the sign-in screen. Without this it fired on every render where
|
|
420
|
+
// nobody was signed in — including the recovery screens, so a person opening
|
|
421
|
+
// a password-reset link was handed a sign-in form instead of the reset they
|
|
422
|
+
// had asked for, and could never finish.
|
|
423
|
+
element("signin") &&
|
|
424
|
+
!askedProvider &&
|
|
425
|
+
!interactionId &&
|
|
426
|
+
!signedIn &&
|
|
427
|
+
// Nor when the shell has something to say. A password reset ends on this
|
|
428
|
+
// screen with "your password has been changed"; leaving for the provider
|
|
429
|
+
// would swallow the one confirmation the person was waiting for.
|
|
430
|
+
!message
|
|
431
|
+
) {
|
|
432
|
+
askedProvider = true;
|
|
433
|
+
void (async () => {
|
|
434
|
+
if (!(await providerRendersHere())) return;
|
|
435
|
+
window.location.assign(await oidc.beginLogin());
|
|
436
|
+
})();
|
|
437
|
+
}
|
|
412
438
|
|
|
439
|
+
// Forgetting the address it remembered and handing the person back to a
|
|
440
|
+
// session it never ended would recognise them again: the offer has to reach
|
|
441
|
+
// the provider, not just this browser's memory.
|
|
413
442
|
element("forget-hint")?.addEventListener("click", () => {
|
|
414
443
|
forgetSignIn(config.appId);
|
|
444
|
+
if (oidc) {
|
|
445
|
+
void (async () => {
|
|
446
|
+
window.location.assign(await oidc.beginLogin({prompt: "login"}));
|
|
447
|
+
})();
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
415
450
|
render();
|
|
416
451
|
});
|
|
417
452
|
void bindAgreement(element<HTMLFormElement>("signin"), config.title, config.apiEndpoint, config.appId, signInAgreementAccepted);
|
|
@@ -594,6 +629,25 @@ const refusals: Record<string, string> = {
|
|
|
594
629
|
// dashboard it points people to is itself. "Sign in with Fidj" is the right
|
|
595
630
|
// label on an app that is not Fidj; here it names a provider the person is
|
|
596
631
|
// standing in, and hides the form behind a click that only fetches it.
|
|
632
|
+
let askedProvider = false;
|
|
633
|
+
// Whether the provider renders its sign-in on this front end. A deployment
|
|
634
|
+
// decides that, so the shell asks rather than assumes: hopping to a provider
|
|
635
|
+
// that answers with its own page would land the person on the API's origin,
|
|
636
|
+
// which is the thing this is meant to avoid.
|
|
637
|
+
let signinOnThisUi: boolean | null = null;
|
|
638
|
+
async function providerRendersHere() {
|
|
639
|
+
if (signinOnThisUi !== null) return signinOnThisUi;
|
|
640
|
+
try {
|
|
641
|
+
const response = await fetch(
|
|
642
|
+
new URL("status", config.apiEndpoint.replace(/\/?$/, "/")).href,
|
|
643
|
+
{signal: AbortSignal.timeout(5000)},
|
|
644
|
+
);
|
|
645
|
+
signinOnThisUi = response.ok && (await response.json()).signin === "fidj-ui";
|
|
646
|
+
} catch {
|
|
647
|
+
signinOnThisUi = false;
|
|
648
|
+
}
|
|
649
|
+
return signinOnThisUi;
|
|
650
|
+
}
|
|
597
651
|
const isFidjItself = (() => {
|
|
598
652
|
try {
|
|
599
653
|
return new URL(config.dashboardUrl).origin === window.location.origin;
|
|
@@ -649,10 +703,14 @@ function interactionScreen() {
|
|
|
649
703
|
try {
|
|
650
704
|
typed = sessionStorage.getItem("fidj.interaction.email") || "";
|
|
651
705
|
} catch {}
|
|
706
|
+
// Fidj signing into Fidj: saying "the account behind fidj" and "fidj never
|
|
707
|
+
// sees your password" about itself is nonsense in the same family as offering
|
|
708
|
+
// to sign in with Fidj on Fidj.
|
|
709
|
+
const itself = details.app.id === config.appId;
|
|
652
710
|
const body =
|
|
653
711
|
details.prompt === "login"
|
|
654
|
-
? `<h2
|
|
655
|
-
<p class="signin-lead"
|
|
712
|
+
? `<h2>${itself ? "Sign in to Fidj" : "Sign in to continue to " + asking}</h2>
|
|
713
|
+
<p class="signin-lead">${itself ? "One account across every app that uses Fidj, and a separate set of choices for each one." : `This is Fidj, the account behind ${asking}. One account, and separate choices for every app that uses it — ${asking} never sees your password.`}</p>
|
|
656
714
|
${notice}
|
|
657
715
|
<form method="post" action="${escape(action)}" id="interaction">
|
|
658
716
|
<input type="hidden" name="csrf" value="${escape(details.csrf)}">
|
|
@@ -663,8 +721,8 @@ function interactionScreen() {
|
|
|
663
721
|
<button class="secondary" type="submit" name="action" value="signup">Create a Fidj account</button>
|
|
664
722
|
<button class="quiet" type="submit" name="action" value="cancel" formnovalidate>Cancel and go back</button>
|
|
665
723
|
</form>`
|
|
666
|
-
: `<h2
|
|
667
|
-
<p class="signin-lead">${asking} is asking for the information below. Optional privacy choices stay separate, and you can change them in Fidj at any time
|
|
724
|
+
: `<h2>${itself ? "Continue to Fidj" : "Continue to " + asking}</h2>
|
|
725
|
+
<p class="signin-lead">${itself ? "Fidj is asking for the information below. Optional privacy choices stay separate for every app, including this one." : `${asking} is asking for the information below. Optional privacy choices stay separate, and you can change them in Fidj at any time.`}</p>
|
|
668
726
|
${notice}
|
|
669
727
|
<ul class="scope-list">${details.scopes
|
|
670
728
|
.filter((scope) => scopeMeaning[scope])
|
|
@@ -675,6 +733,7 @@ function interactionScreen() {
|
|
|
675
733
|
<label class="agreement-choice"><input type="checkbox" name="terms" value="true" required><span>I accept ${asking}'s service agreement.</span></label>
|
|
676
734
|
${details.termsUri ? `<p class="fineprint"><a href="${escape(details.termsUri)}" target="_blank" rel="noopener noreferrer">Service agreement</a>${details.privacyUri ? ` · <a href="${escape(details.privacyUri)}" target="_blank" rel="noopener noreferrer">Privacy notice</a>` : ""}</p>` : ""}
|
|
677
735
|
<button class="primary" type="submit" name="action" value="continue">Allow and continue</button>
|
|
736
|
+
<button type="button" id="not-me" class="quiet">Not you? Sign in with another account</button>
|
|
678
737
|
<button class="quiet" type="submit" name="action" value="cancel" formnovalidate>Cancel and go back</button>
|
|
679
738
|
</form>`;
|
|
680
739
|
|
|
@@ -693,6 +752,19 @@ function interactionScreen() {
|
|
|
693
752
|
field.type = hidden ? "text" : "password";
|
|
694
753
|
button.textContent = hidden ? "Hide" : "Show";
|
|
695
754
|
});
|
|
755
|
+
// Being recognised is the point, and a dead end when the person is not who
|
|
756
|
+
// Fidj thinks — a shared computer, a second account, somebody else's tab. So
|
|
757
|
+
// the screen that recognises them asks again on request, which is what
|
|
758
|
+
// prompt=login means, rather than merely forgetting a remembered address.
|
|
759
|
+
element("not-me")?.addEventListener("click", () => {
|
|
760
|
+
forgetSignIn(config.appId);
|
|
761
|
+
try {
|
|
762
|
+
sessionStorage.removeItem("fidj.interaction.email");
|
|
763
|
+
} catch {}
|
|
764
|
+
void (async () => {
|
|
765
|
+
window.location.assign(await oidc!.beginLogin({prompt: "login"}));
|
|
766
|
+
})();
|
|
767
|
+
});
|
|
696
768
|
element("interaction")?.addEventListener("submit", () => {
|
|
697
769
|
const address = element<HTMLInputElement>("email")?.value || "";
|
|
698
770
|
try {
|