Skip to content

Commit f8ff1de

Browse files
fix(fe): address sea-snake nit review comments on SSO sign-in
- Rename "Sign in with SSO" → "Continue with SSO" (auth + add-method) - Simplify mapSubmitError: log raw errors to console, show user-facing messages for the key cases (domain not configured, OAuth errors, canary allowlist, rate limit), generic fallback for everything else - Disable SSO continue button in add-access-method flow when the discovery result reveals the same (iss, aud) is already linked - Add optional `name` to ii-openid-configuration schema and propagate orgName through SsoDiscoveryResult → ssoDomainStorage → openIdName so the access-methods UI can render e.g. "DFINITY" instead of just the domain - Update locale catalogs via lingui extract
1 parent 8e29a57 commit f8ff1de

20 files changed

Lines changed: 210 additions & 336 deletions

File tree

src/frontend/src/lib/components/wizards/addAccessMethod/AddAccessMethodWizard.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
<SignInWithSso
101101
continueWithSso={handleContinueWithSso}
102102
goBack={addAccessMethodFlow.chooseMethod}
103+
existingCredentials={openIdCredentials}
103104
/>
104105
{/if}
105106

src/frontend/src/lib/components/wizards/addAccessMethod/views/AddAccessMethod.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125
disabled={authenticatingProviderId !== undefined}
126126
size="xl"
127127
class="flex-1"
128-
aria-label={$t`Sign in with SSO`}
128+
aria-label={$t`Continue with SSO`}
129129
>
130130
<SsoIcon class="size-6" />
131131
</Button>

src/frontend/src/lib/components/wizards/auth/views/PickAuthenticationMethod.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
disabled={authenticatingProviderId !== undefined}
9999
size="xl"
100100
class="flex-1"
101-
aria-label={$t`Sign in with SSO`}
101+
aria-label={$t`Continue with SSO`}
102102
>
103103
<SsoIcon class="size-6" />
104104
</Button>

src/frontend/src/lib/components/wizards/auth/views/SignInWithSso.svelte

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@
1313
} from "$lib/utils/ssoDiscovery";
1414
import type { SsoDiscoveryResult } from "$lib/utils/ssoDiscovery";
1515
import { OAuthProviderError } from "$lib/utils/openID";
16+
import type { OpenIdCredential } from "$lib/generated/internet_identity_types";
1617
import { t } from "$lib/stores/locale.store";
1718
1819
interface Props {
1920
continueWithSso: (
2021
result: SsoDiscoveryResult,
2122
) => Promise<void | "cancelled">;
2223
goBack: () => void;
24+
existingCredentials?: OpenIdCredential[];
2325
}
2426
25-
const { continueWithSso, goBack }: Props = $props();
27+
const { continueWithSso, goBack, existingCredentials }: Props = $props();
2628
2729
/**
2830
* Debounce delay before kicking off the (network-heavy) two-hop lookup.
@@ -47,62 +49,29 @@
4749
let debounceTimer: ReturnType<typeof setTimeout> | undefined;
4850
4951
const mapSubmitError = (e: unknown, domainInput: string): string => {
52+
console.error("SSO sign-in error:", e);
5053
if (e instanceof DomainNotConfiguredError) {
51-
if (e.reason === "http-error" && e.httpStatus !== undefined) {
52-
return $t`${domainInput} didn't serve /.well-known/ii-openid-configuration (HTTP ${String(e.httpStatus)}). The domain owner needs to publish it for II to sign you in.`;
53-
}
5454
if (e.reason === "network") {
5555
return $t`Couldn't reach ${domainInput}. Check the spelling and your network, then try again.`;
5656
}
57-
if (e.detail !== undefined && e.detail.length > 0) {
58-
return $t`${domainInput}'s /.well-known/ii-openid-configuration is malformed: ${e.detail}`;
59-
}
60-
return $t`${domainInput}'s /.well-known/ii-openid-configuration is malformed.`;
57+
return $t`${domainInput} isn't set up for SSO with Internet Identity. The domain owner needs to publish /.well-known/ii-openid-configuration.`;
6158
}
6259
if (e instanceof OAuthProviderError) {
63-
// `unsupported_response_type` is the signature of an SSO app that
64-
// only allows the plain authorization-code flow. II needs the
65-
// hybrid flow (id_token + code) because it verifies JWTs canister-
66-
// side with no token-endpoint exchange. Spell out the fix so the
67-
// SSO admin can act on it directly.
6860
if (e.error === "unsupported_response_type") {
69-
return $t`${domainInput}'s SSO app doesn't allow the hybrid OAuth flow II requires. Ask the SSO admin to enable response_type "id_token code" (e.g. in Okta, set the app to Single-Page Application with "Implicit (hybrid)" grant enabled).`;
61+
return $t`${domainInput}'s SSO app doesn't allow the hybrid OAuth flow II requires. Ask the SSO admin to enable response_type "id_token code".`;
7062
}
7163
if (e.error === "access_denied") {
72-
return $t`${domainInput}'s SSO denied the sign-in. Try again, and check with your SSO admin if the problem persists.`;
73-
}
74-
if (e.errorDescription !== undefined && e.errorDescription.length > 0) {
75-
return $t`${domainInput}'s SSO returned "${e.error}": ${e.errorDescription}`;
64+
return $t`${domainInput}'s SSO denied the sign-in. Try again, or check with your SSO admin.`;
7665
}
7766
return $t`${domainInput}'s SSO returned error "${e.error}".`;
7867
}
7968
if (e instanceof Error) {
80-
const msg = e.message;
81-
if (msg.toLowerCase().includes("canary allowlist")) {
82-
return $t`SSO is not available for "${domainInput}" yet. Ask an II admin to register this domain.`;
83-
}
84-
if (msg.includes("Provider issuer hostname mismatch")) {
85-
return $t`SSO provider misconfigured: issuer doesn't match the configured hostname. (${msg})`;
86-
}
87-
if (msg.includes("Provider authorization endpoint hostname mismatch")) {
88-
return $t`SSO provider misconfigured: authorization endpoint points to a different host than the issuer. (${msg})`;
69+
if (e.message.toLowerCase().includes("canary allowlist")) {
70+
return $t`SSO is not available for "${domainInput}" yet.`;
8971
}
90-
if (msg.includes("Provider issuer must use HTTPS")) {
91-
return $t`SSO provider misconfigured: issuer URL is not HTTPS. (${msg})`;
92-
}
93-
if (msg.includes("Provider authorization endpoint must use HTTPS")) {
94-
return $t`SSO provider misconfigured: authorization endpoint is not HTTPS. (${msg})`;
95-
}
96-
if (msg.startsWith("Provider discovery:")) {
97-
return $t`SSO provider's discovery document is malformed: ${msg}`;
98-
}
99-
if (msg.startsWith("Rate limited:")) {
72+
if (e.message.startsWith("Rate limited:")) {
10073
return $t`Too many recent attempts for ${domainInput}. Wait a few minutes and try again.`;
10174
}
102-
if (msg === "Too many concurrent SSO discovery requests") {
103-
return $t`Several SSO sign-ins are in flight already. Wait a moment and try again.`;
104-
}
105-
return msg;
10675
}
10776
return $t`SSO sign-in failed. Please try again.`;
10877
};
@@ -156,7 +125,17 @@
156125
});
157126
const result = await discoverSsoConfig(trimmed);
158127
if (matchesCurrent()) {
159-
preparedResult = result;
128+
if (
129+
existingCredentials?.some(
130+
(c) =>
131+
c.iss === result.discovery.issuer &&
132+
c.aud === result.clientId,
133+
)
134+
) {
135+
error = $t`This SSO domain is already linked to your identity.`;
136+
} else {
137+
preparedResult = result;
138+
}
160139
}
161140
} catch (e) {
162141
if (matchesCurrent()) {

src/frontend/src/lib/flows/addAccessMethodFlow.svelte.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ export class AddAccessMethodFlow {
155155
linkSsoAccount = async (
156156
result: SsoDiscoveryResult,
157157
): Promise<OpenIdCredential> => {
158-
const { domain, clientId, discovery } = result;
158+
const { domain, orgName, clientId, discovery } = result;
159159
const syntheticConfig: OpenIdConfig = {
160160
auth_uri: discovery.authorization_endpoint,
161161
jwks_uri: "",
@@ -171,6 +171,7 @@ export class AddAccessMethodFlow {
171171
rememberSsoDomainForCredential(
172172
{ iss: credential.iss, sub: credential.sub, aud: credential.aud },
173173
domain,
174+
orgName,
174175
);
175176
return credential;
176177
};

src/frontend/src/lib/flows/authFlow.svelte.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export class AuthFlow {
108108
type: "signUp";
109109
}
110110
> => {
111-
const { domain, clientId, discovery } = ssoResult;
111+
const { domain, orgName, clientId, discovery } = ssoResult;
112112

113113
// Build a synthetic OpenIdConfig from SSO discovery result
114114
const syntheticConfig: OpenIdConfig = {
@@ -151,7 +151,7 @@ export class AuthFlow {
151151
authenticationV2Funnel.trigger(AuthenticationV2Events.ContinueWithOpenID);
152152
}
153153
const { iss, sub, aud } = decodeJWT(jwt);
154-
rememberSsoDomainForCredential({ iss, sub, aud }, domain);
154+
rememberSsoDomainForCredential({ iss, sub, aud }, domain, orgName);
155155

156156
return await this.continueWithOpenId(syntheticConfig, jwt);
157157
};

src/frontend/src/lib/locales/de.po

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@ msgstr "{application} ist zur neuen Internet Identity umgezogen"
2525
msgid "{displayName} account"
2626
msgstr ""
2727

28-
msgid "{domainInput} didn't serve /.well-known/ii-openid-configuration (HTTP {0}). The domain owner needs to publish it for II to sign you in."
28+
msgid "{domainInput} isn't set up for SSO with Internet Identity. The domain owner needs to publish /.well-known/ii-openid-configuration."
2929
msgstr ""
3030

31-
msgid "{domainInput}'s /.well-known/ii-openid-configuration is malformed: {0}"
31+
msgid "{domainInput}'s SSO app doesn't allow the hybrid OAuth flow II requires. Ask the SSO admin to enable response_type \"id_token code\"."
3232
msgstr ""
3333

34-
msgid "{domainInput}'s /.well-known/ii-openid-configuration is malformed."
34+
msgid "{domainInput}'s SSO denied the sign-in. Try again, or check with your SSO admin."
35+
msgstr ""
36+
37+
msgid "{domainInput}'s SSO returned error \"{0}\"."
3538
msgstr ""
3639

3740
msgid "{identityName} has been removed from this device."
@@ -241,6 +244,9 @@ msgstr "Mit {name} fortfahren"
241244
msgid "Continue with passkey"
242245
msgstr "Mit Passkey fortfahren"
243246

247+
msgid "Continue with SSO"
248+
msgstr ""
249+
244250
msgid "Couldn't reach {domainInput}. Check the spelling and your network, then try again."
245251
msgstr ""
246252

@@ -830,9 +836,6 @@ msgstr "Sitzung abgelaufen"
830836
msgid "Set as default sign-in"
831837
msgstr "Als Standardanmeldung festlegen"
832838

833-
msgid "Several SSO sign-ins are in flight already. Wait a moment and try again."
834-
msgstr ""
835-
836839
msgid "Show all"
837840
msgstr "Alle anzeigen"
838841

@@ -851,9 +854,6 @@ msgstr "Anmelden mit bekannten Methoden"
851854
msgid "Sign in with another identity"
852855
msgstr "Mit einer anderen Identität anmelden"
853856

854-
msgid "Sign in with SSO"
855-
msgstr ""
856-
857857
msgid "Sign In With SSO"
858858
msgstr ""
859859

@@ -903,22 +903,7 @@ msgstr "Quellcode"
903903
msgid "SSO"
904904
msgstr ""
905905

906-
msgid "SSO is not available for \"{domainInput}\" yet. Ask an II admin to register this domain."
907-
msgstr ""
908-
909-
msgid "SSO provider misconfigured: authorization endpoint is not HTTPS. ({msg})"
910-
msgstr ""
911-
912-
msgid "SSO provider misconfigured: authorization endpoint points to a different host than the issuer. ({msg})"
913-
msgstr ""
914-
915-
msgid "SSO provider misconfigured: issuer doesn't match the configured hostname. ({msg})"
916-
msgstr ""
917-
918-
msgid "SSO provider misconfigured: issuer URL is not HTTPS. ({msg})"
919-
msgstr ""
920-
921-
msgid "SSO provider's discovery document is malformed: {msg}"
906+
msgid "SSO is not available for \"{domainInput}\" yet."
922907
msgstr ""
923908

924909
msgid "SSO sign-in failed. Please try again."
@@ -1017,6 +1002,9 @@ msgstr "Dies kann einige Sekunden dauern"
10171002
msgid "This passkey is no longer associated with any identity."
10181003
msgstr "Dieser Passkey ist mit keiner Identität mehr verknüpft."
10191004

1005+
msgid "This SSO domain is already linked to your identity."
1006+
msgstr ""
1007+
10201008
msgid "This YubiKey has older firmware (5.1) that's incompatible."
10211009
msgstr "Dieser YubiKey hat eine ältere Firmware (5.1), die inkompatibel ist."
10221010

src/frontend/src/lib/locales/en.po

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@ msgstr "{application} has moved to the new Internet Identity"
2525
msgid "{displayName} account"
2626
msgstr "{displayName} account"
2727

28-
msgid "{domainInput} didn't serve /.well-known/ii-openid-configuration (HTTP {0}). The domain owner needs to publish it for II to sign you in."
29-
msgstr "{domainInput} didn't serve /.well-known/ii-openid-configuration (HTTP {0}). The domain owner needs to publish it for II to sign you in."
28+
msgid "{domainInput} isn't set up for SSO with Internet Identity. The domain owner needs to publish /.well-known/ii-openid-configuration."
29+
msgstr "{domainInput} isn't set up for SSO with Internet Identity. The domain owner needs to publish /.well-known/ii-openid-configuration."
3030

31-
msgid "{domainInput}'s /.well-known/ii-openid-configuration is malformed: {0}"
32-
msgstr "{domainInput}'s /.well-known/ii-openid-configuration is malformed: {0}"
31+
msgid "{domainInput}'s SSO app doesn't allow the hybrid OAuth flow II requires. Ask the SSO admin to enable response_type \"id_token code\"."
32+
msgstr "{domainInput}'s SSO app doesn't allow the hybrid OAuth flow II requires. Ask the SSO admin to enable response_type \"id_token code\"."
3333

34-
msgid "{domainInput}'s /.well-known/ii-openid-configuration is malformed."
35-
msgstr "{domainInput}'s /.well-known/ii-openid-configuration is malformed."
34+
msgid "{domainInput}'s SSO denied the sign-in. Try again, or check with your SSO admin."
35+
msgstr "{domainInput}'s SSO denied the sign-in. Try again, or check with your SSO admin."
36+
37+
msgid "{domainInput}'s SSO returned error \"{0}\"."
38+
msgstr "{domainInput}'s SSO returned error \"{0}\"."
3639

3740
msgid "{identityName} has been removed from this device."
3841
msgstr "{identityName} has been removed from this device."
@@ -241,6 +244,9 @@ msgstr "Continue with {name}"
241244
msgid "Continue with passkey"
242245
msgstr "Continue with passkey"
243246

247+
msgid "Continue with SSO"
248+
msgstr "Continue with SSO"
249+
244250
msgid "Couldn't reach {domainInput}. Check the spelling and your network, then try again."
245251
msgstr "Couldn't reach {domainInput}. Check the spelling and your network, then try again."
246252

@@ -830,9 +836,6 @@ msgstr "Session timed out"
830836
msgid "Set as default sign-in"
831837
msgstr "Set as default sign-in"
832838

833-
msgid "Several SSO sign-ins are in flight already. Wait a moment and try again."
834-
msgstr "Several SSO sign-ins are in flight already. Wait a moment and try again."
835-
836839
msgid "Show all"
837840
msgstr "Show all"
838841

@@ -851,9 +854,6 @@ msgstr "Sign in using familiar methods"
851854
msgid "Sign in with another identity"
852855
msgstr "Sign in with another identity"
853856

854-
msgid "Sign in with SSO"
855-
msgstr "Sign in with SSO"
856-
857857
msgid "Sign In With SSO"
858858
msgstr "Sign In With SSO"
859859

@@ -903,23 +903,8 @@ msgstr "Source code"
903903
msgid "SSO"
904904
msgstr "SSO"
905905

906-
msgid "SSO is not available for \"{domainInput}\" yet. Ask an II admin to register this domain."
907-
msgstr "SSO is not available for \"{domainInput}\" yet. Ask an II admin to register this domain."
908-
909-
msgid "SSO provider misconfigured: authorization endpoint is not HTTPS. ({msg})"
910-
msgstr "SSO provider misconfigured: authorization endpoint is not HTTPS. ({msg})"
911-
912-
msgid "SSO provider misconfigured: authorization endpoint points to a different host than the issuer. ({msg})"
913-
msgstr "SSO provider misconfigured: authorization endpoint points to a different host than the issuer. ({msg})"
914-
915-
msgid "SSO provider misconfigured: issuer doesn't match the configured hostname. ({msg})"
916-
msgstr "SSO provider misconfigured: issuer doesn't match the configured hostname. ({msg})"
917-
918-
msgid "SSO provider misconfigured: issuer URL is not HTTPS. ({msg})"
919-
msgstr "SSO provider misconfigured: issuer URL is not HTTPS. ({msg})"
920-
921-
msgid "SSO provider's discovery document is malformed: {msg}"
922-
msgstr "SSO provider's discovery document is malformed: {msg}"
906+
msgid "SSO is not available for \"{domainInput}\" yet."
907+
msgstr "SSO is not available for \"{domainInput}\" yet."
923908

924909
msgid "SSO sign-in failed. Please try again."
925910
msgstr "SSO sign-in failed. Please try again."
@@ -1017,6 +1002,9 @@ msgstr "This may take a few seconds"
10171002
msgid "This passkey is no longer associated with any identity."
10181003
msgstr "This passkey is no longer associated with any identity."
10191004

1005+
msgid "This SSO domain is already linked to your identity."
1006+
msgstr "This SSO domain is already linked to your identity."
1007+
10201008
msgid "This YubiKey has older firmware (5.1) that's incompatible."
10211009
msgstr "This YubiKey has older firmware (5.1) that's incompatible."
10221010

0 commit comments

Comments
 (0)