Skip to content

Commit

Permalink
feat: add first screen values and identifier config for sign-in optio…
Browse files Browse the repository at this point in the history
…ns (#786)
  • Loading branch information
xiaoyijun authored Aug 28, 2024
1 parent cb57a5e commit 5b46f9c
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 3 deletions.
19 changes: 19 additions & 0 deletions .changeset/blue-phones-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"@logto/client": minor
"@logto/js": minor
---

add support for identifier-based and single sign-on first screens and configurable identifiers

You can now set `identifier:sign_in`, `identifier:register`, `single_sign_on` or `reset_password` as the first screen in the sign-in process. Additionally, you can specify which identifiers (`email`, `phone`, `username`) are allowed for `identifier:sign_in`, `identifier:register` and `reset_password` flows.

Note the the original `interactionMode` is now deprecated, please use the new `firstScreen` option instead.

Example (React):
```typescript
signIn({
redirectUri,
firstScreen: 'identifier:sign_in',
identifiers: ['email', 'phone'],
});
```
5 changes: 4 additions & 1 deletion packages/client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export type SignInOptions = {
prompt?: SignInUriParameters['prompt'];
} & Pick<
SignInUriParameters,
'interactionMode' | 'firstScreen' | 'loginHint' | 'directSignIn' | 'extraParams'
'interactionMode' | 'firstScreen' | 'identifiers' | 'loginHint' | 'directSignIn' | 'extraParams'
>;

/**
Expand Down Expand Up @@ -292,6 +292,7 @@ export class StandardLogtoClient {
redirectUri: redirectUriUrl,
postRedirectUri: postRedirectUriUrl,
firstScreen,
identifiers,
interactionMode,
loginHint,
directSignIn,
Expand All @@ -302,6 +303,7 @@ export class StandardLogtoClient {
redirectUri: options,
postRedirectUri: undefined,
firstScreen: undefined,
identifiers: undefined,
interactionMode: mode,
loginHint: hint,
directSignIn: undefined,
Expand Down Expand Up @@ -329,6 +331,7 @@ export class StandardLogtoClient {
resources,
prompt: prompt ?? promptViaConfig,
firstScreen,
identifiers,
interactionMode,
loginHint,
directSignIn,
Expand Down
1 change: 1 addition & 0 deletions packages/js/src/consts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export enum QueryKey {
/** The query key for specifying the organization ID. */
OrganizationId = 'organization_id',
FirstScreen = 'first_screen',
Identifier = 'identifier',
DirectSignIn = 'direct_sign_in',
}

Expand Down
16 changes: 16 additions & 0 deletions packages/js/src/core/sign-in.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ describe('generateSignInUri', () => {
);
});

test('with identifier', () => {
const signInUri = generateSignInUri({
authorizationEndpoint,
clientId,
redirectUri,
codeChallenge,
state,
firstScreen: 'identifier:sign_in',
identifiers: ['email', 'phone'],
});

expect(signInUri).toEqual(
'https://logto.dev/oidc/sign-in?client_id=clientId&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback&code_challenge=codeChallenge&code_challenge_method=S256&state=state&response_type=code&prompt=consent&scope=openid+offline_access+profile&first_screen=identifier%3Asign_in&identifier=email+phone'
);
});

test('with directSignIn', () => {
const signInUri = generateSignInUri({
authorizationEndpoint,
Expand Down
26 changes: 25 additions & 1 deletion packages/js/src/core/sign-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export type DirectSignInOptions = {
target: string;
};

type Identifier = 'email' | 'phone' | 'username';

export type SignInUriParameters = {
authorizationEndpoint: string;
clientId: string;
Expand All @@ -31,7 +33,23 @@ export type SignInUriParameters = {
* The first screen to be shown in the sign-in experience.
*/
firstScreen?: FirstScreen;
/** The first screen to be shown in the sign-in experience. */
/**
* Specifies identifiers used in the identifier sign-in or identifier register page.
*
* Available values: `email`, `phone`, `username`.
*
* This parameter is applicable only when the `firstScreen` is set to either `identifierSignIn` or `identifierRegister`.
*
* If the provided identifier is not supported in the Logto sign-in experience configuration, it will be ignored,
* and if no one of them is supported, it will fallback to the sign-in / sign-up method value set in the sign-in experience configuration.
*
*/
identifiers?: Identifier[];
/**
* The first screen to be shown in the sign-in experience.
*
* @deprecated Use `firstScreen` instead.
*/
interactionMode?: InteractionMode;
/**
* Login hint indicates the current user (usually an email address or a phone number).
Expand Down Expand Up @@ -66,6 +84,7 @@ const buildPrompt = (prompt?: Prompt | Prompt[]) => {
return prompt ?? Prompt.Consent;
};

// eslint-disable-next-line complexity
export const generateSignInUri = ({
authorizationEndpoint,
clientId,
Expand All @@ -76,6 +95,7 @@ export const generateSignInUri = ({
resources,
prompt,
firstScreen,
identifiers: identifier,
interactionMode,
loginHint,
directSignIn,
Expand Down Expand Up @@ -121,6 +141,10 @@ export const generateSignInUri = ({
urlSearchParameters.append(QueryKey.InteractionMode, interactionMode);
}

if (identifier && identifier.length > 0) {
urlSearchParameters.append(QueryKey.Identifier, identifier.join(' '));
}

if (extraParams) {
for (const [key, value] of Object.entries(extraParams)) {
urlSearchParameters.append(key, value);
Expand Down
13 changes: 12 additions & 1 deletion packages/js/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,24 @@ export type Requester = <T>(...args: Parameters<typeof fetch>) => Promise<T>;
*
* - `signIn`: The authorization request will be initiated with a sign-in page.
* - `signUp`: The authorization request will be initiated with a sign-up page.
*
* @deprecated Use `FirstScreen` instead.
*/
export type InteractionMode = 'signIn' | 'signUp';

/**
* The first screen to be shown in the sign-in experience. Note it's not a part of the OIDC
* standard, but a Logto-specific extension.
*
* Note: `signIn` is deprecated, use `sign_in` instead
*
* @experimental Don't use this type as it's under development.
*/
export type FirstScreen = 'signIn' | 'register';
export type FirstScreen =
| 'signIn'
| 'sign_in'
| 'register'
| 'reset_password'
| 'identifier:sign_in'
| 'identifier:register'
| 'single_sign_on';

0 comments on commit 5b46f9c

Please sign in to comment.