@logto/client 2.2.4 → 2.3.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.
- package/lib/errors.cjs +3 -0
- package/lib/errors.d.ts +1 -0
- package/lib/errors.js +3 -0
- package/lib/index.cjs +56 -12
- package/lib/index.d.ts +22 -3
- package/lib/index.js +43 -16
- package/lib/mock.d.ts +1 -1
- package/lib/types/index.cjs +22 -0
- package/lib/types/index.d.ts +11 -1
- package/lib/types/index.js +23 -2
- package/lib/types/index.test.d.ts +1 -0
- package/lib/utils/index.cjs +2 -1
- package/lib/utils/index.d.ts +1 -1
- package/lib/utils/index.js +2 -1
- package/package.json +3 -3
package/lib/errors.cjs
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var js = require('@logto/js');
|
|
4
|
+
|
|
3
5
|
const logtoClientErrorCodes = Object.freeze({
|
|
4
6
|
'sign_in_session.invalid': 'Invalid sign-in session.',
|
|
5
7
|
'sign_in_session.not_found': 'Sign-in session not found.',
|
|
6
8
|
not_authenticated: 'Not authenticated.',
|
|
7
9
|
fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.',
|
|
8
10
|
user_cancelled: 'The user cancelled the action.',
|
|
11
|
+
missing_scope_organizations: `The \`${js.UserScope.Organizations}\` scope is required`,
|
|
9
12
|
});
|
|
10
13
|
class LogtoClientError extends Error {
|
|
11
14
|
constructor(code, data) {
|
package/lib/errors.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ declare const logtoClientErrorCodes: Readonly<{
|
|
|
4
4
|
not_authenticated: "Not authenticated.";
|
|
5
5
|
fetch_user_info_failed: "Unable to fetch user info. The access token may be invalid.";
|
|
6
6
|
user_cancelled: "The user cancelled the action.";
|
|
7
|
+
missing_scope_organizations: "The `urn:logto:scope:organizations` scope is required";
|
|
7
8
|
}>;
|
|
8
9
|
export type LogtoClientErrorCode = keyof typeof logtoClientErrorCodes;
|
|
9
10
|
export declare class LogtoClientError extends Error {
|
package/lib/errors.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { UserScope } from '@logto/js';
|
|
2
|
+
|
|
1
3
|
const logtoClientErrorCodes = Object.freeze({
|
|
2
4
|
'sign_in_session.invalid': 'Invalid sign-in session.',
|
|
3
5
|
'sign_in_session.not_found': 'Sign-in session not found.',
|
|
4
6
|
not_authenticated: 'Not authenticated.',
|
|
5
7
|
fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.',
|
|
6
8
|
user_cancelled: 'The user cancelled the action.',
|
|
9
|
+
missing_scope_organizations: `The \`${UserScope.Organizations}\` scope is required`,
|
|
7
10
|
});
|
|
8
11
|
class LogtoClientError extends Error {
|
|
9
12
|
constructor(code, data) {
|
package/lib/index.cjs
CHANGED
|
@@ -14,6 +14,7 @@ var once = require('./utils/once.cjs');
|
|
|
14
14
|
var types = require('./adapter/types.cjs');
|
|
15
15
|
var requester = require('./utils/requester.cjs');
|
|
16
16
|
|
|
17
|
+
/* eslint-disable max-lines */
|
|
17
18
|
/**
|
|
18
19
|
* The Logto base client class that provides the essential methods for
|
|
19
20
|
* interacting with the Logto server.
|
|
@@ -25,7 +26,7 @@ class LogtoClient {
|
|
|
25
26
|
constructor(logtoConfig, adapter) {
|
|
26
27
|
this.getOidcConfig = memoize.memoize(this.#getOidcConfig);
|
|
27
28
|
/**
|
|
28
|
-
* Get the access token from the storage.
|
|
29
|
+
* Get the access token from the storage with refresh strategy.
|
|
29
30
|
*
|
|
30
31
|
* - If the access token has expired, it will try to fetch a new one using the Refresh Token.
|
|
31
32
|
* - If there's an ongoing Promise to fetch the access token, it will return the Promise.
|
|
@@ -39,13 +40,22 @@ class LogtoClient {
|
|
|
39
40
|
* @throws LogtoClientError if the user is not authenticated.
|
|
40
41
|
*/
|
|
41
42
|
this.getAccessToken = memoize.memoize(this.#getAccessToken);
|
|
43
|
+
/**
|
|
44
|
+
* Get the access token for the specified organization from the storage with refresh strategy.
|
|
45
|
+
*
|
|
46
|
+
* Scope {@link UserScope.Organizations} is required in the config to use organization-related
|
|
47
|
+
* methods.
|
|
48
|
+
*
|
|
49
|
+
* @param organizationId The ID of the organization that the access token is granted for.
|
|
50
|
+
* @returns The access token string.
|
|
51
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
52
|
+
* @remarks
|
|
53
|
+
* It uses the same refresh strategy as {@link getAccessToken}.
|
|
54
|
+
*/
|
|
55
|
+
this.getOrganizationToken = memoize.memoize(this.#getOrganizationToken);
|
|
42
56
|
this.getJwtVerifyGetKey = once.once(this.#getJwtVerifyGetKey);
|
|
43
57
|
this.accessTokenMap = new Map();
|
|
44
|
-
this.logtoConfig =
|
|
45
|
-
...logtoConfig,
|
|
46
|
-
prompt: logtoConfig.prompt ?? js.Prompt.Consent,
|
|
47
|
-
scopes: js.withDefaultScopes(logtoConfig.scopes).split(' '),
|
|
48
|
-
};
|
|
58
|
+
this.logtoConfig = index.normalizeLogtoConfig(logtoConfig);
|
|
49
59
|
this.adapter = new index$1.ClientAdapterInstance(adapter);
|
|
50
60
|
void this.loadAccessTokenMap();
|
|
51
61
|
}
|
|
@@ -89,6 +99,15 @@ class LogtoClient {
|
|
|
89
99
|
const accessToken = await this.getAccessToken(resource);
|
|
90
100
|
return js.decodeAccessToken(accessToken);
|
|
91
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Get the organization token claims for the specified organization.
|
|
104
|
+
*
|
|
105
|
+
* @param organizationId The ID of the organization that the access token is granted for.
|
|
106
|
+
*/
|
|
107
|
+
async getOrganizationTokenClaims(organizationId) {
|
|
108
|
+
const accessToken = await this.getOrganizationToken(organizationId);
|
|
109
|
+
return js.decodeAccessToken(accessToken);
|
|
110
|
+
}
|
|
92
111
|
/**
|
|
93
112
|
* Get the user information from the Userinfo Endpoint.
|
|
94
113
|
*
|
|
@@ -263,12 +282,12 @@ class LogtoClient {
|
|
|
263
282
|
async setRefreshToken(value) {
|
|
264
283
|
return this.adapter.setStorageItem(types.PersistKey.RefreshToken, value);
|
|
265
284
|
}
|
|
266
|
-
async getAccessTokenByRefreshToken(resource) {
|
|
285
|
+
async getAccessTokenByRefreshToken(resource, organizationId) {
|
|
267
286
|
const currentRefreshToken = await this.getRefreshToken();
|
|
268
287
|
if (!currentRefreshToken) {
|
|
269
288
|
throw new errors.LogtoClientError('not_authenticated');
|
|
270
289
|
}
|
|
271
|
-
const accessTokenKey = index$2.buildAccessTokenKey(resource);
|
|
290
|
+
const accessTokenKey = index$2.buildAccessTokenKey(resource, organizationId);
|
|
272
291
|
const { appId: clientId } = this.logtoConfig;
|
|
273
292
|
const { tokenEndpoint } = await this.getOidcConfig();
|
|
274
293
|
const requestedAt = Math.round(Date.now() / 1000);
|
|
@@ -277,6 +296,7 @@ class LogtoClient {
|
|
|
277
296
|
tokenEndpoint,
|
|
278
297
|
refreshToken: currentRefreshToken,
|
|
279
298
|
resource,
|
|
299
|
+
organizationId,
|
|
280
300
|
}, this.adapter.requester);
|
|
281
301
|
this.accessTokenMap.set(accessTokenKey, {
|
|
282
302
|
token: accessToken,
|
|
@@ -343,11 +363,11 @@ class LogtoClient {
|
|
|
343
363
|
const cachedJwkSet = new remoteJwkSet.CachedRemoteJwkSet(new URL(jwksUri), this.adapter);
|
|
344
364
|
return async (...args) => cachedJwkSet.getKey(...args);
|
|
345
365
|
}
|
|
346
|
-
async #getAccessToken(resource) {
|
|
347
|
-
if (!(await this.
|
|
366
|
+
async #getAccessToken(resource, organizationId) {
|
|
367
|
+
if (!(await this.isAuthenticated())) {
|
|
348
368
|
throw new errors.LogtoClientError('not_authenticated');
|
|
349
369
|
}
|
|
350
|
-
const accessTokenKey = index$2.buildAccessTokenKey(resource);
|
|
370
|
+
const accessTokenKey = index$2.buildAccessTokenKey(resource, organizationId);
|
|
351
371
|
const accessToken = this.accessTokenMap.get(accessTokenKey);
|
|
352
372
|
if (accessToken && accessToken.expiresAt > Date.now() / 1000) {
|
|
353
373
|
return accessToken.token;
|
|
@@ -359,9 +379,16 @@ class LogtoClient {
|
|
|
359
379
|
/**
|
|
360
380
|
* Need to fetch a new access token using refresh token.
|
|
361
381
|
*/
|
|
362
|
-
return this.getAccessTokenByRefreshToken(resource);
|
|
382
|
+
return this.getAccessTokenByRefreshToken(resource, organizationId);
|
|
383
|
+
}
|
|
384
|
+
async #getOrganizationToken(organizationId) {
|
|
385
|
+
if (!this.logtoConfig.scopes?.includes(js.UserScope.Organizations)) {
|
|
386
|
+
throw new errors.LogtoClientError('missing_scope_organizations');
|
|
387
|
+
}
|
|
388
|
+
return this.#getAccessToken(undefined, organizationId);
|
|
363
389
|
}
|
|
364
390
|
}
|
|
391
|
+
/* eslint-enable max-lines */
|
|
365
392
|
|
|
366
393
|
Object.defineProperty(exports, 'LogtoError', {
|
|
367
394
|
enumerable: true,
|
|
@@ -379,6 +406,10 @@ Object.defineProperty(exports, 'Prompt', {
|
|
|
379
406
|
enumerable: true,
|
|
380
407
|
get: function () { return js.Prompt; }
|
|
381
408
|
});
|
|
409
|
+
Object.defineProperty(exports, 'ReservedResource', {
|
|
410
|
+
enumerable: true,
|
|
411
|
+
get: function () { return js.ReservedResource; }
|
|
412
|
+
});
|
|
382
413
|
Object.defineProperty(exports, 'ReservedScope', {
|
|
383
414
|
enumerable: true,
|
|
384
415
|
get: function () { return js.ReservedScope; }
|
|
@@ -387,9 +418,22 @@ Object.defineProperty(exports, 'UserScope', {
|
|
|
387
418
|
enumerable: true,
|
|
388
419
|
get: function () { return js.UserScope; }
|
|
389
420
|
});
|
|
421
|
+
Object.defineProperty(exports, 'buildOrganizationUrn', {
|
|
422
|
+
enumerable: true,
|
|
423
|
+
get: function () { return js.buildOrganizationUrn; }
|
|
424
|
+
});
|
|
425
|
+
Object.defineProperty(exports, 'getOrganizationIdFromUrn', {
|
|
426
|
+
enumerable: true,
|
|
427
|
+
get: function () { return js.getOrganizationIdFromUrn; }
|
|
428
|
+
});
|
|
429
|
+
Object.defineProperty(exports, 'organizationUrnPrefix', {
|
|
430
|
+
enumerable: true,
|
|
431
|
+
get: function () { return js.organizationUrnPrefix; }
|
|
432
|
+
});
|
|
390
433
|
exports.LogtoClientError = errors.LogtoClientError;
|
|
391
434
|
exports.isLogtoAccessTokenMap = index.isLogtoAccessTokenMap;
|
|
392
435
|
exports.isLogtoSignInSessionItem = index.isLogtoSignInSessionItem;
|
|
436
|
+
exports.normalizeLogtoConfig = index.normalizeLogtoConfig;
|
|
393
437
|
Object.defineProperty(exports, 'CacheKey', {
|
|
394
438
|
enumerable: true,
|
|
395
439
|
get: function () { return types.CacheKey; }
|
package/lib/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { type JWTVerifyGetKey } from 'jose';
|
|
|
4
4
|
import { ClientAdapterInstance, type ClientAdapter } from './adapter/index.js';
|
|
5
5
|
import type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types/index.js';
|
|
6
6
|
export type { IdTokenClaims, LogtoErrorCode, UserInfoResponse, InteractionMode } from '@logto/js';
|
|
7
|
-
export { LogtoError, LogtoRequestError, OidcError, Prompt, ReservedScope, UserScope, } from '@logto/js';
|
|
7
|
+
export { LogtoError, LogtoRequestError, OidcError, Prompt, ReservedScope, ReservedResource, UserScope, organizationUrnPrefix, buildOrganizationUrn, getOrganizationIdFromUrn, } from '@logto/js';
|
|
8
8
|
export * from './errors.js';
|
|
9
9
|
export type { Storage, StorageKey, ClientAdapter } from './adapter/index.js';
|
|
10
10
|
export { PersistKey, CacheKey } from './adapter/index.js';
|
|
@@ -30,7 +30,7 @@ export default class LogtoClient {
|
|
|
30
30
|
issuer: string;
|
|
31
31
|
}>>;
|
|
32
32
|
/**
|
|
33
|
-
* Get the access token from the storage.
|
|
33
|
+
* Get the access token from the storage with refresh strategy.
|
|
34
34
|
*
|
|
35
35
|
* - If the access token has expired, it will try to fetch a new one using the Refresh Token.
|
|
36
36
|
* - If there's an ongoing Promise to fetch the access token, it will return the Promise.
|
|
@@ -43,7 +43,20 @@ export default class LogtoClient {
|
|
|
43
43
|
* @returns The access token string.
|
|
44
44
|
* @throws LogtoClientError if the user is not authenticated.
|
|
45
45
|
*/
|
|
46
|
-
readonly getAccessToken: (this: unknown, resource?: string | undefined) => Promise<string>;
|
|
46
|
+
readonly getAccessToken: (this: unknown, resource?: string | undefined, organizationId?: string | undefined) => Promise<string>;
|
|
47
|
+
/**
|
|
48
|
+
* Get the access token for the specified organization from the storage with refresh strategy.
|
|
49
|
+
*
|
|
50
|
+
* Scope {@link UserScope.Organizations} is required in the config to use organization-related
|
|
51
|
+
* methods.
|
|
52
|
+
*
|
|
53
|
+
* @param organizationId The ID of the organization that the access token is granted for.
|
|
54
|
+
* @returns The access token string.
|
|
55
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
56
|
+
* @remarks
|
|
57
|
+
* It uses the same refresh strategy as {@link getAccessToken}.
|
|
58
|
+
*/
|
|
59
|
+
readonly getOrganizationToken: (this: unknown, organizationId: string) => Promise<string>;
|
|
47
60
|
protected readonly getJwtVerifyGetKey: (...args: unknown[]) => Promise<JWTVerifyGetKey>;
|
|
48
61
|
protected readonly adapter: ClientAdapterInstance;
|
|
49
62
|
protected readonly accessTokenMap: Map<string, AccessToken>;
|
|
@@ -73,6 +86,12 @@ export default class LogtoClient {
|
|
|
73
86
|
* resource, as specified in the Logto Console.
|
|
74
87
|
*/
|
|
75
88
|
getAccessTokenClaims(resource?: string): Promise<AccessTokenClaims>;
|
|
89
|
+
/**
|
|
90
|
+
* Get the organization token claims for the specified organization.
|
|
91
|
+
*
|
|
92
|
+
* @param organizationId The ID of the organization that the access token is granted for.
|
|
93
|
+
*/
|
|
94
|
+
getOrganizationTokenClaims(organizationId: string): Promise<AccessTokenClaims>;
|
|
76
95
|
/**
|
|
77
96
|
* Get the user information from the Userinfo Endpoint.
|
|
78
97
|
*
|
package/lib/index.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { LogtoError, LogtoRequestError, OidcError, Prompt, ReservedScope, UserScope } from '@logto/js';
|
|
1
|
+
import { decodeIdToken, decodeAccessToken, fetchUserInfo, generateSignInUri, verifyAndParseCodeFromCallbackUri, fetchTokenByAuthorizationCode, revoke, generateSignOutUri, fetchTokenByRefreshToken, verifyIdToken, fetchOidcConfig, UserScope } from '@logto/js';
|
|
2
|
+
export { LogtoError, LogtoRequestError, OidcError, Prompt, ReservedResource, ReservedScope, UserScope, buildOrganizationUrn, getOrganizationIdFromUrn, organizationUrnPrefix } from '@logto/js';
|
|
3
3
|
import { createRemoteJWKSet } from 'jose';
|
|
4
4
|
import { ClientAdapterInstance } from './adapter/index.js';
|
|
5
5
|
import { LogtoClientError } from './errors.js';
|
|
6
6
|
import { CachedRemoteJwkSet } from './remote-jwk-set.js';
|
|
7
|
-
import { isLogtoSignInSessionItem, isLogtoAccessTokenMap } from './types/index.js';
|
|
7
|
+
import { normalizeLogtoConfig, isLogtoSignInSessionItem, isLogtoAccessTokenMap } from './types/index.js';
|
|
8
8
|
import { buildAccessTokenKey, getDiscoveryEndpoint } from './utils/index.js';
|
|
9
9
|
import { memoize } from './utils/memoize.js';
|
|
10
10
|
import { once } from './utils/once.js';
|
|
11
11
|
import { PersistKey, CacheKey } from './adapter/types.js';
|
|
12
12
|
export { createRequester } from './utils/requester.js';
|
|
13
13
|
|
|
14
|
+
/* eslint-disable max-lines */
|
|
14
15
|
/**
|
|
15
16
|
* The Logto base client class that provides the essential methods for
|
|
16
17
|
* interacting with the Logto server.
|
|
@@ -22,7 +23,7 @@ class LogtoClient {
|
|
|
22
23
|
constructor(logtoConfig, adapter) {
|
|
23
24
|
this.getOidcConfig = memoize(this.#getOidcConfig);
|
|
24
25
|
/**
|
|
25
|
-
* Get the access token from the storage.
|
|
26
|
+
* Get the access token from the storage with refresh strategy.
|
|
26
27
|
*
|
|
27
28
|
* - If the access token has expired, it will try to fetch a new one using the Refresh Token.
|
|
28
29
|
* - If there's an ongoing Promise to fetch the access token, it will return the Promise.
|
|
@@ -36,13 +37,22 @@ class LogtoClient {
|
|
|
36
37
|
* @throws LogtoClientError if the user is not authenticated.
|
|
37
38
|
*/
|
|
38
39
|
this.getAccessToken = memoize(this.#getAccessToken);
|
|
40
|
+
/**
|
|
41
|
+
* Get the access token for the specified organization from the storage with refresh strategy.
|
|
42
|
+
*
|
|
43
|
+
* Scope {@link UserScope.Organizations} is required in the config to use organization-related
|
|
44
|
+
* methods.
|
|
45
|
+
*
|
|
46
|
+
* @param organizationId The ID of the organization that the access token is granted for.
|
|
47
|
+
* @returns The access token string.
|
|
48
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
49
|
+
* @remarks
|
|
50
|
+
* It uses the same refresh strategy as {@link getAccessToken}.
|
|
51
|
+
*/
|
|
52
|
+
this.getOrganizationToken = memoize(this.#getOrganizationToken);
|
|
39
53
|
this.getJwtVerifyGetKey = once(this.#getJwtVerifyGetKey);
|
|
40
54
|
this.accessTokenMap = new Map();
|
|
41
|
-
this.logtoConfig =
|
|
42
|
-
...logtoConfig,
|
|
43
|
-
prompt: logtoConfig.prompt ?? Prompt.Consent,
|
|
44
|
-
scopes: withDefaultScopes(logtoConfig.scopes).split(' '),
|
|
45
|
-
};
|
|
55
|
+
this.logtoConfig = normalizeLogtoConfig(logtoConfig);
|
|
46
56
|
this.adapter = new ClientAdapterInstance(adapter);
|
|
47
57
|
void this.loadAccessTokenMap();
|
|
48
58
|
}
|
|
@@ -86,6 +96,15 @@ class LogtoClient {
|
|
|
86
96
|
const accessToken = await this.getAccessToken(resource);
|
|
87
97
|
return decodeAccessToken(accessToken);
|
|
88
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Get the organization token claims for the specified organization.
|
|
101
|
+
*
|
|
102
|
+
* @param organizationId The ID of the organization that the access token is granted for.
|
|
103
|
+
*/
|
|
104
|
+
async getOrganizationTokenClaims(organizationId) {
|
|
105
|
+
const accessToken = await this.getOrganizationToken(organizationId);
|
|
106
|
+
return decodeAccessToken(accessToken);
|
|
107
|
+
}
|
|
89
108
|
/**
|
|
90
109
|
* Get the user information from the Userinfo Endpoint.
|
|
91
110
|
*
|
|
@@ -260,12 +279,12 @@ class LogtoClient {
|
|
|
260
279
|
async setRefreshToken(value) {
|
|
261
280
|
return this.adapter.setStorageItem(PersistKey.RefreshToken, value);
|
|
262
281
|
}
|
|
263
|
-
async getAccessTokenByRefreshToken(resource) {
|
|
282
|
+
async getAccessTokenByRefreshToken(resource, organizationId) {
|
|
264
283
|
const currentRefreshToken = await this.getRefreshToken();
|
|
265
284
|
if (!currentRefreshToken) {
|
|
266
285
|
throw new LogtoClientError('not_authenticated');
|
|
267
286
|
}
|
|
268
|
-
const accessTokenKey = buildAccessTokenKey(resource);
|
|
287
|
+
const accessTokenKey = buildAccessTokenKey(resource, organizationId);
|
|
269
288
|
const { appId: clientId } = this.logtoConfig;
|
|
270
289
|
const { tokenEndpoint } = await this.getOidcConfig();
|
|
271
290
|
const requestedAt = Math.round(Date.now() / 1000);
|
|
@@ -274,6 +293,7 @@ class LogtoClient {
|
|
|
274
293
|
tokenEndpoint,
|
|
275
294
|
refreshToken: currentRefreshToken,
|
|
276
295
|
resource,
|
|
296
|
+
organizationId,
|
|
277
297
|
}, this.adapter.requester);
|
|
278
298
|
this.accessTokenMap.set(accessTokenKey, {
|
|
279
299
|
token: accessToken,
|
|
@@ -340,11 +360,11 @@ class LogtoClient {
|
|
|
340
360
|
const cachedJwkSet = new CachedRemoteJwkSet(new URL(jwksUri), this.adapter);
|
|
341
361
|
return async (...args) => cachedJwkSet.getKey(...args);
|
|
342
362
|
}
|
|
343
|
-
async #getAccessToken(resource) {
|
|
344
|
-
if (!(await this.
|
|
363
|
+
async #getAccessToken(resource, organizationId) {
|
|
364
|
+
if (!(await this.isAuthenticated())) {
|
|
345
365
|
throw new LogtoClientError('not_authenticated');
|
|
346
366
|
}
|
|
347
|
-
const accessTokenKey = buildAccessTokenKey(resource);
|
|
367
|
+
const accessTokenKey = buildAccessTokenKey(resource, organizationId);
|
|
348
368
|
const accessToken = this.accessTokenMap.get(accessTokenKey);
|
|
349
369
|
if (accessToken && accessToken.expiresAt > Date.now() / 1000) {
|
|
350
370
|
return accessToken.token;
|
|
@@ -356,8 +376,15 @@ class LogtoClient {
|
|
|
356
376
|
/**
|
|
357
377
|
* Need to fetch a new access token using refresh token.
|
|
358
378
|
*/
|
|
359
|
-
return this.getAccessTokenByRefreshToken(resource);
|
|
379
|
+
return this.getAccessTokenByRefreshToken(resource, organizationId);
|
|
380
|
+
}
|
|
381
|
+
async #getOrganizationToken(organizationId) {
|
|
382
|
+
if (!this.logtoConfig.scopes?.includes(UserScope.Organizations)) {
|
|
383
|
+
throw new LogtoClientError('missing_scope_organizations');
|
|
384
|
+
}
|
|
385
|
+
return this.#getAccessToken(undefined, organizationId);
|
|
360
386
|
}
|
|
361
387
|
}
|
|
388
|
+
/* eslint-enable max-lines */
|
|
362
389
|
|
|
363
|
-
export { CacheKey, LogtoClientError, PersistKey, LogtoClient as default, isLogtoAccessTokenMap, isLogtoSignInSessionItem };
|
|
390
|
+
export { CacheKey, LogtoClientError, PersistKey, LogtoClient as default, isLogtoAccessTokenMap, isLogtoSignInSessionItem, normalizeLogtoConfig };
|
package/lib/mock.d.ts
CHANGED
|
@@ -66,7 +66,7 @@ export declare const createAdapters: (withCache?: boolean) => {
|
|
|
66
66
|
generateCodeVerifier: jest.Mock<string, [], any>;
|
|
67
67
|
generateState: jest.Mock<string, [], any>;
|
|
68
68
|
};
|
|
69
|
-
export declare const createClient: (prompt?: Prompt, storage?: MockedStorage, withCache?: boolean) => LogtoClientWithAccessors;
|
|
69
|
+
export declare const createClient: (prompt?: Prompt, storage?: MockedStorage, withCache?: boolean, scopes?: string[]) => LogtoClientWithAccessors;
|
|
70
70
|
/**
|
|
71
71
|
* Make protected fields accessible for test
|
|
72
72
|
*/
|
package/lib/types/index.cjs
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var js = require('@logto/js');
|
|
4
|
+
var essentials = require('@silverhand/essentials');
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Normalize the Logto client configuration per the following rules:
|
|
8
|
+
*
|
|
9
|
+
* - Add default scopes (`openid`, `offline_access` and `profile`) if not provided.
|
|
10
|
+
* - Add {@link ReservedResource.Organization} to resources if {@link UserScope.Organizations} is included in scopes.
|
|
11
|
+
*
|
|
12
|
+
* @param config The Logto client configuration to be normalized.
|
|
13
|
+
* @returns The normalized Logto client configuration.
|
|
14
|
+
*/
|
|
15
|
+
const normalizeLogtoConfig = (config) => {
|
|
16
|
+
const { prompt = js.Prompt.Consent, scopes = [], resources, ...rest } = config;
|
|
17
|
+
return {
|
|
18
|
+
...rest,
|
|
19
|
+
prompt,
|
|
20
|
+
scopes: js.withDefaultScopes(scopes).split(' '),
|
|
21
|
+
resources: scopes.includes(js.UserScope.Organizations)
|
|
22
|
+
? essentials.deduplicate([...(resources ?? []), js.ReservedResource.Organization])
|
|
23
|
+
: resources,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
5
26
|
const isLogtoSignInSessionItem = (data) => {
|
|
6
27
|
if (!js.isArbitraryObject(data)) {
|
|
7
28
|
return false;
|
|
@@ -24,3 +45,4 @@ const isLogtoAccessTokenMap = (data) => {
|
|
|
24
45
|
|
|
25
46
|
exports.isLogtoAccessTokenMap = isLogtoAccessTokenMap;
|
|
26
47
|
exports.isLogtoSignInSessionItem = isLogtoSignInSessionItem;
|
|
48
|
+
exports.normalizeLogtoConfig = normalizeLogtoConfig;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Prompt } from '@logto/js';
|
|
2
2
|
/** The configuration object for the Logto client. */
|
|
3
3
|
export type LogtoConfig = {
|
|
4
4
|
/**
|
|
@@ -40,6 +40,16 @@ export type LogtoConfig = {
|
|
|
40
40
|
*/
|
|
41
41
|
prompt?: Prompt;
|
|
42
42
|
};
|
|
43
|
+
/**
|
|
44
|
+
* Normalize the Logto client configuration per the following rules:
|
|
45
|
+
*
|
|
46
|
+
* - Add default scopes (`openid`, `offline_access` and `profile`) if not provided.
|
|
47
|
+
* - Add {@link ReservedResource.Organization} to resources if {@link UserScope.Organizations} is included in scopes.
|
|
48
|
+
*
|
|
49
|
+
* @param config The Logto client configuration to be normalized.
|
|
50
|
+
* @returns The normalized Logto client configuration.
|
|
51
|
+
*/
|
|
52
|
+
export declare const normalizeLogtoConfig: (config: LogtoConfig) => LogtoConfig;
|
|
43
53
|
export type AccessToken = {
|
|
44
54
|
/** The access token string. */
|
|
45
55
|
token: string;
|
package/lib/types/index.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
|
-
import { isArbitraryObject } from '@logto/js';
|
|
1
|
+
import { Prompt, withDefaultScopes, UserScope, ReservedResource, isArbitraryObject } from '@logto/js';
|
|
2
|
+
import { deduplicate } from '@silverhand/essentials';
|
|
2
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Normalize the Logto client configuration per the following rules:
|
|
6
|
+
*
|
|
7
|
+
* - Add default scopes (`openid`, `offline_access` and `profile`) if not provided.
|
|
8
|
+
* - Add {@link ReservedResource.Organization} to resources if {@link UserScope.Organizations} is included in scopes.
|
|
9
|
+
*
|
|
10
|
+
* @param config The Logto client configuration to be normalized.
|
|
11
|
+
* @returns The normalized Logto client configuration.
|
|
12
|
+
*/
|
|
13
|
+
const normalizeLogtoConfig = (config) => {
|
|
14
|
+
const { prompt = Prompt.Consent, scopes = [], resources, ...rest } = config;
|
|
15
|
+
return {
|
|
16
|
+
...rest,
|
|
17
|
+
prompt,
|
|
18
|
+
scopes: withDefaultScopes(scopes).split(' '),
|
|
19
|
+
resources: scopes.includes(UserScope.Organizations)
|
|
20
|
+
? deduplicate([...(resources ?? []), ReservedResource.Organization])
|
|
21
|
+
: resources,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
3
24
|
const isLogtoSignInSessionItem = (data) => {
|
|
4
25
|
if (!isArbitraryObject(data)) {
|
|
5
26
|
return false;
|
|
@@ -20,4 +41,4 @@ const isLogtoAccessTokenMap = (data) => {
|
|
|
20
41
|
});
|
|
21
42
|
};
|
|
22
43
|
|
|
23
|
-
export { isLogtoAccessTokenMap, isLogtoSignInSessionItem };
|
|
44
|
+
export { isLogtoAccessTokenMap, isLogtoSignInSessionItem, normalizeLogtoConfig };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/utils/index.cjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var js = require('@logto/js');
|
|
4
|
+
var essentials = require('@silverhand/essentials');
|
|
4
5
|
|
|
5
|
-
const buildAccessTokenKey = (resource = '', scopes = []) => `${scopes.slice().sort().join(' ')}@${resource}`;
|
|
6
|
+
const buildAccessTokenKey = (resource = '', organizationId, scopes = []) => `${scopes.slice().sort().join(' ')}@${resource}${essentials.conditionalString(organizationId && `#${organizationId}`)}`;
|
|
6
7
|
const getDiscoveryEndpoint = (endpoint) => new URL(js.discoveryPath, endpoint).toString();
|
|
7
8
|
|
|
8
9
|
exports.buildAccessTokenKey = buildAccessTokenKey;
|
package/lib/utils/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export * from './requester.js';
|
|
2
|
-
export declare const buildAccessTokenKey: (resource?: string, scopes?: string[]) => string;
|
|
2
|
+
export declare const buildAccessTokenKey: (resource?: string, organizationId?: string, scopes?: string[]) => string;
|
|
3
3
|
export declare const getDiscoveryEndpoint: (endpoint: string) => string;
|
package/lib/utils/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { discoveryPath } from '@logto/js';
|
|
2
|
+
import { conditionalString } from '@silverhand/essentials';
|
|
2
3
|
|
|
3
|
-
const buildAccessTokenKey = (resource = '', scopes = []) => `${scopes.slice().sort().join(' ')}@${resource}`;
|
|
4
|
+
const buildAccessTokenKey = (resource = '', organizationId, scopes = []) => `${scopes.slice().sort().join(' ')}@${resource}${conditionalString(organizationId && `#${organizationId}`)}`;
|
|
4
5
|
const getDiscoveryEndpoint = (endpoint) => new URL(discoveryPath, endpoint).toString();
|
|
5
6
|
|
|
6
7
|
export { buildAccessTokenKey, getDiscoveryEndpoint };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./lib/index.cjs",
|
|
6
6
|
"module": "./lib/index.js",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"directory": "packages/client"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@logto/js": "^
|
|
24
|
+
"@logto/js": "^3.0.0",
|
|
25
25
|
"@silverhand/essentials": "^2.6.2",
|
|
26
26
|
"camelcase-keys": "^7.0.1",
|
|
27
|
-
"jose": "^
|
|
27
|
+
"jose": "^5.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@silverhand/eslint-config": "^4.0.1",
|