@logto/client 2.4.0 → 2.5.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.
@@ -39,16 +39,17 @@ export type InferStorageKey<S> = S extends Storage<infer Key> ? Key : never;
39
39
  * @param url The URL to navigate to.
40
40
  * @param parameters The parameters for the navigation.
41
41
  * @param parameters.redirectUri The redirect URI that the user will be redirected to after the
42
- * flow is completed. That is, the "redirect URI" for sign-in and "post-logout redirect URI" for
43
- * sign-out.
44
- * @param parameters.for The purpose of the navigation. It can be either "sign-in" or "sign-out".
42
+ * flow is completed. That is, the "redirect URI" for "sign-in" and "post-logout redirect URI" for
43
+ * "sign-out". For the "post-sign-in" navigation, it should be ignored.
44
+ * @param parameters.for The purpose of the navigation. It can be either "sign-in", "sign-out", or
45
+ * "post-sign-in".
45
46
  * @remarks Usually, the `redirectUri` parameter can be ignored unless the client needs to pass the
46
47
  * redirect scheme or other parameters to the native app, such as `ASWebAuthenticationSession` in
47
48
  * iOS.
48
49
  */
49
50
  export type Navigate = (url: string, parameters: {
50
51
  redirectUri?: string;
51
- for: 'sign-in' | 'sign-out';
52
+ for: 'sign-in' | 'sign-out' | 'post-sign-in';
52
53
  }) => void | Promise<void>;
53
54
  export type JwtVerifier = {
54
55
  verifyIdToken(idToken: string): Promise<void>;
package/lib/client.cjs CHANGED
@@ -9,6 +9,7 @@ var memoize = require('./utils/memoize.cjs');
9
9
  var once = require('./utils/once.cjs');
10
10
  var types = require('./adapter/types.cjs');
11
11
 
12
+ /* eslint-disable max-lines */
12
13
  /**
13
14
  * The Logto base client class that provides the essential methods for
14
15
  * interacting with the Logto server.
@@ -139,23 +140,12 @@ class StandardLogtoClient {
139
140
  }
140
141
  return js.fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);
141
142
  }
142
- /**
143
- * Start the sign-in flow with the specified redirect URI. The URI must be
144
- * registered in the Logto Console.
145
- *
146
- * The user will be redirected to that URI after the sign-in flow is completed,
147
- * and the client will be able to get the authorization code from the URI.
148
- * To fetch the tokens from the authorization code, use {@link handleSignInCallback}
149
- * after the user is redirected in the callback URI.
150
- *
151
- * @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
152
- * @param interactionMode The interaction mode to be used for the authorization request. Note it's not
153
- * a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
154
- *
155
- * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
156
- * @see {@link InteractionMode}
157
- */
158
- async signIn(redirectUri, interactionMode) {
143
+ async signIn(options, mode) {
144
+ const { redirectUri: redirectUriUrl, postRedirectUri: postRedirectUriUrl, interactionMode, } = typeof options === 'string' || options instanceof URL
145
+ ? { redirectUri: options, postRedirectUri: undefined, interactionMode: mode }
146
+ : options;
147
+ const redirectUri = redirectUriUrl.toString();
148
+ const postRedirectUri = postRedirectUriUrl?.toString();
159
149
  const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;
160
150
  const { authorizationEndpoint } = await this.getOidcConfig();
161
151
  const [codeVerifier, state] = await Promise.all([
@@ -166,7 +156,7 @@ class StandardLogtoClient {
166
156
  const signInUri = js.generateSignInUri({
167
157
  authorizationEndpoint,
168
158
  clientId,
169
- redirectUri,
159
+ redirectUri: redirectUri.toString(),
170
160
  codeChallenge,
171
161
  state,
172
162
  scopes,
@@ -175,7 +165,7 @@ class StandardLogtoClient {
175
165
  interactionMode,
176
166
  });
177
167
  await Promise.all([
178
- this.setSignInSession({ redirectUri, codeVerifier, state }),
168
+ this.setSignInSession({ redirectUri, postRedirectUri, codeVerifier, state }),
179
169
  this.setRefreshToken(null),
180
170
  this.setIdToken(null),
181
171
  ]);
@@ -349,7 +339,7 @@ class StandardLogtoClient {
349
339
  if (!signInSession) {
350
340
  throw new errors.LogtoClientError('sign_in_session.not_found');
351
341
  }
352
- const { redirectUri, state, codeVerifier } = signInSession;
342
+ const { redirectUri, postRedirectUri, state, codeVerifier } = signInSession;
353
343
  const code = js.verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);
354
344
  // NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)
355
345
  const accessTokenKey = index$2.buildAccessTokenKey();
@@ -377,7 +367,11 @@ class StandardLogtoClient {
377
367
  });
378
368
  await this.saveAccessTokenMap();
379
369
  await this.setSignInSession(null);
370
+ if (postRedirectUri) {
371
+ await this.adapter.navigate(postRedirectUri, { for: 'post-sign-in' });
372
+ }
380
373
  }
381
374
  }
375
+ /* eslint-enable max-lines */
382
376
 
383
377
  exports.StandardLogtoClient = StandardLogtoClient;
package/lib/client.d.ts CHANGED
@@ -2,6 +2,27 @@ import { type IdTokenClaims, type UserInfoResponse, type InteractionMode, type A
2
2
  import { type Nullable } from '@silverhand/essentials';
3
3
  import { ClientAdapterInstance, type ClientAdapter, type JwtVerifier } from './adapter/index.js';
4
4
  import type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types/index.js';
5
+ export type SignInOptions = {
6
+ /**
7
+ * The redirect URI that the user will be redirected to after the sign-in flow is completed.
8
+ */
9
+ redirectUri: string | URL;
10
+ /**
11
+ * The URI that the user will be redirected to after `redirectUri` successfully handled the
12
+ * sign-in callback. If not specified, the user will stay on the `redirectUri` page.
13
+ */
14
+ postRedirectUri?: string | URL;
15
+ /**
16
+ * The interaction mode to be used for the authorization request. It determines the first page
17
+ * that the user will see in the sign-in flow.
18
+ *
19
+ * Note it's not a part of the OIDC standard, but a Logto-specific extension.
20
+ *
21
+ * @default InteractionMode.SignIn
22
+ * @see {@link InteractionMode}
23
+ */
24
+ interactionMode?: InteractionMode;
25
+ };
5
26
  /**
6
27
  * The Logto base client class that provides the essential methods for
7
28
  * interacting with the Logto server.
@@ -106,6 +127,7 @@ export declare class StandardLogtoClient {
106
127
  * @throws LogtoClientError if the user is not authenticated.
107
128
  */
108
129
  fetchUserInfo(): Promise<UserInfoResponse>;
130
+ signIn(options: SignInOptions): Promise<void>;
109
131
  /**
110
132
  * Start the sign-in flow with the specified redirect URI. The URI must be
111
133
  * registered in the Logto Console.
@@ -115,14 +137,10 @@ export declare class StandardLogtoClient {
115
137
  * To fetch the tokens from the authorization code, use {@link handleSignInCallback}
116
138
  * after the user is redirected in the callback URI.
117
139
  *
118
- * @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
119
- * @param interactionMode The interaction mode to be used for the authorization request. Note it's not
120
- * a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
121
- *
122
- * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
123
- * @see {@link InteractionMode}
140
+ * @param redirectUri See {@link SignInOptions.redirectUri}.
141
+ * @param interactionMode See {@link SignInOptions.interactionMode}.
124
142
  */
125
- signIn(redirectUri: string, interactionMode?: InteractionMode): Promise<void>;
143
+ signIn(redirectUri: SignInOptions['redirectUri'], interactionMode?: SignInOptions['interactionMode']): Promise<void>;
126
144
  /**
127
145
  * Check if the user is redirected from the sign-in page by checking if the
128
146
  * current URL matches the redirect URI in the sign-in session.
package/lib/client.js CHANGED
@@ -7,6 +7,7 @@ import { memoize } from './utils/memoize.js';
7
7
  import { once } from './utils/once.js';
8
8
  import { PersistKey, CacheKey } from './adapter/types.js';
9
9
 
10
+ /* eslint-disable max-lines */
10
11
  /**
11
12
  * The Logto base client class that provides the essential methods for
12
13
  * interacting with the Logto server.
@@ -137,23 +138,12 @@ class StandardLogtoClient {
137
138
  }
138
139
  return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);
139
140
  }
140
- /**
141
- * Start the sign-in flow with the specified redirect URI. The URI must be
142
- * registered in the Logto Console.
143
- *
144
- * The user will be redirected to that URI after the sign-in flow is completed,
145
- * and the client will be able to get the authorization code from the URI.
146
- * To fetch the tokens from the authorization code, use {@link handleSignInCallback}
147
- * after the user is redirected in the callback URI.
148
- *
149
- * @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
150
- * @param interactionMode The interaction mode to be used for the authorization request. Note it's not
151
- * a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
152
- *
153
- * @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
154
- * @see {@link InteractionMode}
155
- */
156
- async signIn(redirectUri, interactionMode) {
141
+ async signIn(options, mode) {
142
+ const { redirectUri: redirectUriUrl, postRedirectUri: postRedirectUriUrl, interactionMode, } = typeof options === 'string' || options instanceof URL
143
+ ? { redirectUri: options, postRedirectUri: undefined, interactionMode: mode }
144
+ : options;
145
+ const redirectUri = redirectUriUrl.toString();
146
+ const postRedirectUri = postRedirectUriUrl?.toString();
157
147
  const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;
158
148
  const { authorizationEndpoint } = await this.getOidcConfig();
159
149
  const [codeVerifier, state] = await Promise.all([
@@ -164,7 +154,7 @@ class StandardLogtoClient {
164
154
  const signInUri = generateSignInUri({
165
155
  authorizationEndpoint,
166
156
  clientId,
167
- redirectUri,
157
+ redirectUri: redirectUri.toString(),
168
158
  codeChallenge,
169
159
  state,
170
160
  scopes,
@@ -173,7 +163,7 @@ class StandardLogtoClient {
173
163
  interactionMode,
174
164
  });
175
165
  await Promise.all([
176
- this.setSignInSession({ redirectUri, codeVerifier, state }),
166
+ this.setSignInSession({ redirectUri, postRedirectUri, codeVerifier, state }),
177
167
  this.setRefreshToken(null),
178
168
  this.setIdToken(null),
179
169
  ]);
@@ -347,7 +337,7 @@ class StandardLogtoClient {
347
337
  if (!signInSession) {
348
338
  throw new LogtoClientError('sign_in_session.not_found');
349
339
  }
350
- const { redirectUri, state, codeVerifier } = signInSession;
340
+ const { redirectUri, postRedirectUri, state, codeVerifier } = signInSession;
351
341
  const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);
352
342
  // NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)
353
343
  const accessTokenKey = buildAccessTokenKey();
@@ -375,7 +365,11 @@ class StandardLogtoClient {
375
365
  });
376
366
  await this.saveAccessTokenMap();
377
367
  await this.setSignInSession(null);
368
+ if (postRedirectUri) {
369
+ await this.adapter.navigate(postRedirectUri, { for: 'post-sign-in' });
370
+ }
378
371
  }
379
372
  }
373
+ /* eslint-enable max-lines */
380
374
 
381
375
  export { StandardLogtoClient };
@@ -62,6 +62,7 @@ export declare const isLogtoSignInSessionItem: (data: unknown) => data is LogtoS
62
62
  export declare const isLogtoAccessTokenMap: (data: unknown) => data is Record<string, AccessToken>;
63
63
  export type LogtoSignInSessionItem = {
64
64
  redirectUri: string;
65
+ postRedirectUri?: string;
65
66
  codeVerifier: string;
66
67
  state: string;
67
68
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/client",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "main": "./lib/index.cjs",
6
6
  "module": "./lib/index.js",