@logto/client 1.0.0 → 1.1.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/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -2
- package/lib/index.js.map +1 -1
- package/lib/module.d.mts +3 -3
- package/lib/module.mjs +3 -2
- package/lib/module.mjs.map +1 -1
- package/package.json +3 -3
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Requester, Prompt, IdTokenClaims, UserInfoResponse } from "@logto/js";
|
|
1
|
+
import { Requester, Prompt, IdTokenClaims, UserInfoResponse, InteractionMode } from "@logto/js";
|
|
2
2
|
import { Nullable, NormalizeKeyPaths } from "@silverhand/essentials";
|
|
3
3
|
export type StorageKey = 'idToken' | 'refreshToken' | 'accessToken' | 'signInSession';
|
|
4
4
|
export type Storage = {
|
|
@@ -52,7 +52,7 @@ export type LogtoSignInSessionItem = {
|
|
|
52
52
|
state: string;
|
|
53
53
|
};
|
|
54
54
|
export const createRequester: (fetchFunction: typeof fetch) => Requester;
|
|
55
|
-
export type { IdTokenClaims, LogtoErrorCode, UserInfoResponse } from '@logto/js';
|
|
55
|
+
export type { IdTokenClaims, LogtoErrorCode, UserInfoResponse, InteractionMode } from '@logto/js';
|
|
56
56
|
export { LogtoError, OidcError, Prompt, LogtoRequestError, ReservedScope, UserScope, } from '@logto/js';
|
|
57
57
|
export default class LogtoClient {
|
|
58
58
|
protected readonly logtoConfig: LogtoConfig;
|
|
@@ -67,7 +67,7 @@ export default class LogtoClient {
|
|
|
67
67
|
getAccessToken(resource?: string): Promise<string>;
|
|
68
68
|
getIdTokenClaims(): Promise<IdTokenClaims>;
|
|
69
69
|
fetchUserInfo(): Promise<UserInfoResponse>;
|
|
70
|
-
signIn(redirectUri: string): Promise<void>;
|
|
70
|
+
signIn(redirectUri: string, interactionMode?: InteractionMode): Promise<void>;
|
|
71
71
|
isSignInRedirected(url: string): Promise<boolean>;
|
|
72
72
|
handleSignInCallback(callbackUri: string): Promise<void>;
|
|
73
73
|
signOut(postLogoutRedirectUri?: string): Promise<void>;
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;AAGA,yBAAyB,SAAS,GAAG,cAAc,GAAG,aAAa,GAAG,eAAe,CAAC;AAEtF,sBAAsB;IACpB,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,MAAM,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C,CAAC;AAEF,gBAAuB,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;AAE7C,4BAA4B;IAC1B,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,MAAM,CAAC;IACnC,qBAAqB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAClE,CAAC;ACjBF,QAAA,MAAM;;;;;;;;;EASJ,CAAC;AAEH,mCAAmC,kBAAkB,4BAA4B,CAAC,CAAC;AAanF,6BAA8B,SAAQ,KAAK;IACzC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,IAAI,EAAE,OAAO,CAAC;gBAEF,IAAI,EAAE,oBAAoB,EAAE,IAAI,CAAC,EAAE,OAAO;CAKvD;ACjCD,0BAA0B;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,0BAA0B;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,OAAO,MAAM,iCAAkC,OAAO,mCAMrD,CAAC;AAEF,OAAO,MAAM,8BAA+B,OAAO,wCAgBlD,CAAC;AAEF,qCAAqC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AC7CF,OAAO,MAAM,iCAAkC,YAAY,KAAG,SAkB7D,CAAC;AEIF,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EACL,UAAU,EACV,SAAS,EACT,MAAM,EACN,iBAAiB,EACjB,aAAa,EACb,SAAS,GACV,MAAM,WAAW,CAAC;AAMnB;IACE,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,aAAa,mHAA6B;IAC7D,SAAS,CAAC,QAAQ,CAAC,kBAAkB,sIAAkC;IACvE,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAC1C,SAAS,CAAC,QAAQ,CAAC,cAAc,2BAAkC;gBAEvD,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa;IAWtD,eAAe;IAIf,eAAe;IAIf,UAAU;IAIV,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBlD,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC;IAU1C,aAAa,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAW1C,MAAM,CAAC,WAAW,EAAE,MAAM;IAyB1B,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYjD,oBAAoB,CAAC,WAAW,EAAE,MAAM;IA8BxC,OAAO,CAAC,qBAAqB,CAAC,EAAE,MAAM;cAiC5B,gBAAgB,IAAI,OAAO,CAAC,SAAS,sBAAsB,CAAC,CAAC;cAgB7D,gBAAgB,CAAC,sBAAsB,EAAE,SAAS,sBAAsB,CAAC;CAmJ1F","sources":["packages/client/src/src/adapter.ts","packages/client/src/src/errors.ts","packages/client/src/src/types/index.ts","packages/client/src/src/utils/requester.ts","packages/client/src/src/utils/index.ts","packages/client/src/src/index.ts","packages/client/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,"import type { CodeTokenResponse, IdTokenClaims, UserInfoResponse } from '@logto/js';\nimport {\n decodeIdToken,\n fetchOidcConfig,\n fetchTokenByAuthorizationCode,\n fetchTokenByRefreshToken,\n fetchUserInfo,\n generateSignInUri,\n generateSignOutUri,\n Prompt,\n revoke,\n verifyAndParseCodeFromCallbackUri,\n verifyIdToken,\n withDefaultScopes,\n} from '@logto/js';\nimport type { Nullable } from '@silverhand/essentials';\nimport { createRemoteJWKSet } from 'jose';\nimport once from 'lodash.once';\n\nimport type { ClientAdapter } from './adapter';\nimport { LogtoClientError } from './errors';\nimport type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types';\nimport { isLogtoAccessTokenMap, isLogtoSignInSessionItem } from './types';\nimport { buildAccessTokenKey, getDiscoveryEndpoint } from './utils';\n\nexport type { IdTokenClaims, LogtoErrorCode, UserInfoResponse } from '@logto/js';\nexport {\n LogtoError,\n OidcError,\n Prompt,\n LogtoRequestError,\n ReservedScope,\n UserScope,\n} from '@logto/js';\nexport * from './errors';\nexport type { Storage, StorageKey, ClientAdapter } from './adapter';\nexport { createRequester } from './utils';\nexport * from './types';\n\nexport default class LogtoClient {\n protected readonly logtoConfig: LogtoConfig;\n protected readonly getOidcConfig = once(this._getOidcConfig);\n protected readonly getJwtVerifyGetKey = once(this._getJwtVerifyGetKey);\n protected readonly adapter: ClientAdapter;\n protected readonly accessTokenMap = new Map<string, AccessToken>();\n\n constructor(logtoConfig: LogtoConfig, adapter: ClientAdapter) {\n this.logtoConfig = {\n ...logtoConfig,\n prompt: logtoConfig.prompt ?? Prompt.Consent,\n scopes: withDefaultScopes(logtoConfig.scopes).split(' '),\n };\n this.adapter = adapter;\n\n void this.loadAccessTokenMap();\n }\n\n async isAuthenticated() {\n return Boolean(await this.getIdToken());\n }\n\n async getRefreshToken() {\n return this.adapter.storage.getItem('refreshToken');\n }\n\n async getIdToken() {\n return this.adapter.storage.getItem('idToken');\n }\n\n async getAccessToken(resource?: string): Promise<string> {\n if (!(await this.getIdToken())) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const accessTokenKey = buildAccessTokenKey(resource);\n const accessToken = this.accessTokenMap.get(accessTokenKey);\n\n if (accessToken && accessToken.expiresAt > Date.now() / 1000) {\n return accessToken.token;\n }\n\n // Since the access token has expired, delete it from the map.\n if (accessToken) {\n this.accessTokenMap.delete(accessTokenKey);\n }\n\n /**\n * Need to fetch a new access token using refresh token.\n */\n return this.getAccessTokenByRefreshToken(resource);\n }\n\n async getIdTokenClaims(): Promise<IdTokenClaims> {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n return decodeIdToken(idToken);\n }\n\n async fetchUserInfo(): Promise<UserInfoResponse> {\n const { userinfoEndpoint } = await this.getOidcConfig();\n const accessToken = await this.getAccessToken();\n\n if (!accessToken) {\n throw new LogtoClientError('fetch_user_info_failed');\n }\n\n return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);\n }\n\n async signIn(redirectUri: string) {\n const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;\n const { authorizationEndpoint } = await this.getOidcConfig();\n const codeVerifier = this.adapter.generateCodeVerifier();\n const codeChallenge = await this.adapter.generateCodeChallenge(codeVerifier);\n const state = this.adapter.generateState();\n\n const signInUri = generateSignInUri({\n authorizationEndpoint,\n clientId,\n redirectUri,\n codeChallenge,\n state,\n scopes,\n resources,\n prompt,\n });\n\n await this.setSignInSession({ redirectUri, codeVerifier, state });\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n\n this.adapter.navigate(signInUri);\n }\n\n async isSignInRedirected(url: string): Promise<boolean> {\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n return false;\n }\n const { redirectUri } = signInSession;\n const { origin, pathname } = new URL(url);\n\n return `${origin}${pathname}` === redirectUri;\n }\n\n async handleSignInCallback(callbackUri: string) {\n const { logtoConfig, adapter } = this;\n const { requester } = adapter;\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n throw new LogtoClientError('sign_in_session.not_found');\n }\n\n const { redirectUri, state, codeVerifier } = signInSession;\n const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);\n\n const { appId: clientId } = logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const codeTokenResponse = await fetchTokenByAuthorizationCode(\n {\n clientId,\n tokenEndpoint,\n redirectUri,\n codeVerifier,\n code,\n },\n requester\n );\n\n await this.verifyIdToken(codeTokenResponse.idToken);\n await this.saveCodeToken(codeTokenResponse);\n await this.setSignInSession(null);\n }\n\n async signOut(postLogoutRedirectUri?: string) {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const { appId: clientId } = this.logtoConfig;\n const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();\n const refreshToken = await this.getRefreshToken();\n\n if (refreshToken) {\n try {\n await revoke(revocationEndpoint, clientId, refreshToken, this.adapter.requester);\n } catch {\n // Do nothing at this point, as we don't want to break the sign-out flow even if the revocation is failed\n }\n }\n\n const url = generateSignOutUri({\n endSessionEndpoint,\n postLogoutRedirectUri,\n clientId,\n });\n\n this.accessTokenMap.clear();\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n await this.adapter.storage.removeItem('accessToken');\n\n this.adapter.navigate(url);\n }\n\n protected async getSignInSession(): Promise<Nullable<LogtoSignInSessionItem>> {\n const jsonItem = await this.adapter.storage.getItem('signInSession');\n\n if (!jsonItem) {\n return null;\n }\n\n const item: unknown = JSON.parse(jsonItem);\n\n if (!isLogtoSignInSessionItem(item)) {\n throw new LogtoClientError('sign_in_session.invalid');\n }\n\n return item;\n }\n\n protected async setSignInSession(logtoSignInSessionItem: Nullable<LogtoSignInSessionItem>) {\n if (!logtoSignInSessionItem) {\n await this.adapter.storage.removeItem('signInSession');\n\n return;\n }\n\n const jsonItem = JSON.stringify(logtoSignInSessionItem);\n await this.adapter.storage.setItem('signInSession', jsonItem);\n }\n\n private async setIdToken(idToken: Nullable<string>) {\n if (!idToken) {\n await this.adapter.storage.removeItem('idToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('idToken', idToken);\n }\n\n private async setRefreshToken(refreshToken: Nullable<string>) {\n if (!refreshToken) {\n await this.adapter.storage.removeItem('refreshToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('refreshToken', refreshToken);\n }\n\n private async getAccessTokenByRefreshToken(resource?: string): Promise<string> {\n const currentRefreshToken = await this.getRefreshToken();\n\n if (!currentRefreshToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n try {\n const accessTokenKey = buildAccessTokenKey(resource);\n const { appId: clientId } = this.logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const { accessToken, refreshToken, idToken, scope, expiresIn } =\n await fetchTokenByRefreshToken(\n {\n clientId,\n tokenEndpoint,\n refreshToken: currentRefreshToken,\n resource,\n },\n this.adapter.requester\n );\n\n this.accessTokenMap.set(accessTokenKey, {\n token: accessToken,\n scope,\n expiresAt: Math.round(Date.now() / 1000) + expiresIn,\n });\n\n await this.saveAccessTokenMap();\n await this.setRefreshToken(refreshToken);\n\n if (idToken) {\n await this.verifyIdToken(idToken);\n await this.setIdToken(idToken);\n }\n\n return accessToken;\n } catch (error: unknown) {\n throw new LogtoClientError('get_access_token_by_refresh_token_failed', error);\n }\n }\n\n private async _getOidcConfig() {\n const { endpoint } = this.logtoConfig;\n const discoveryEndpoint = getDiscoveryEndpoint(endpoint);\n\n return fetchOidcConfig(discoveryEndpoint, this.adapter.requester);\n }\n\n private async _getJwtVerifyGetKey() {\n const { jwksUri } = await this.getOidcConfig();\n\n return createRemoteJWKSet(new URL(jwksUri));\n }\n\n private async verifyIdToken(idToken: string) {\n const { appId } = this.logtoConfig;\n const { issuer } = await this.getOidcConfig();\n const jwtVerifyGetKey = await this.getJwtVerifyGetKey();\n\n try {\n await verifyIdToken(idToken, appId, issuer, jwtVerifyGetKey);\n } catch (error: unknown) {\n throw new LogtoClientError('invalid_id_token', error);\n }\n }\n\n private async saveCodeToken({\n refreshToken,\n idToken,\n scope,\n accessToken,\n expiresIn,\n }: CodeTokenResponse) {\n await this.setRefreshToken(refreshToken ?? null);\n await this.setIdToken(idToken);\n\n // NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)\n const accessTokenKey = buildAccessTokenKey();\n const expiresAt = Date.now() / 1000 + expiresIn;\n this.accessTokenMap.set(accessTokenKey, { token: accessToken, scope, expiresAt });\n await this.saveAccessTokenMap();\n }\n\n private async saveAccessTokenMap() {\n const data: Record<string, AccessToken> = {};\n\n for (const [key, accessToken] of this.accessTokenMap.entries()) {\n // eslint-disable-next-line @silverhand/fp/no-mutation\n data[key] = accessToken;\n }\n\n await this.adapter.storage.setItem('accessToken', JSON.stringify(data));\n }\n\n private async loadAccessTokenMap() {\n const raw = await this.adapter.storage.getItem('accessToken');\n\n if (!raw) {\n return;\n }\n\n try {\n const json: unknown = JSON.parse(raw);\n\n if (!isLogtoAccessTokenMap(json)) {\n return;\n }\n this.accessTokenMap.clear();\n\n for (const [key, accessToken] of Object.entries(json)) {\n this.accessTokenMap.set(key, accessToken);\n }\n } catch {}\n }\n // FIXME: @charles @sijie\n}\n"],"names":[],"version":3,"file":"index.d.ts.map"}
|
|
1
|
+
{"mappings":"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","sources":["packages/client/src/src/adapter.ts","packages/client/src/src/errors.ts","packages/client/src/src/types/index.ts","packages/client/src/src/utils/requester.ts","packages/client/src/src/utils/index.ts","packages/client/src/src/index.ts","packages/client/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,"import type {\n CodeTokenResponse,\n IdTokenClaims,\n UserInfoResponse,\n InteractionMode,\n} from '@logto/js';\nimport {\n decodeIdToken,\n fetchOidcConfig,\n fetchTokenByAuthorizationCode,\n fetchTokenByRefreshToken,\n fetchUserInfo,\n generateSignInUri,\n generateSignOutUri,\n Prompt,\n revoke,\n verifyAndParseCodeFromCallbackUri,\n verifyIdToken,\n withDefaultScopes,\n} from '@logto/js';\nimport type { Nullable } from '@silverhand/essentials';\nimport { createRemoteJWKSet } from 'jose';\nimport once from 'lodash.once';\n\nimport type { ClientAdapter } from './adapter';\nimport { LogtoClientError } from './errors';\nimport type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types';\nimport { isLogtoAccessTokenMap, isLogtoSignInSessionItem } from './types';\nimport { buildAccessTokenKey, getDiscoveryEndpoint } from './utils';\n\nexport type { IdTokenClaims, LogtoErrorCode, UserInfoResponse, InteractionMode } from '@logto/js';\nexport {\n LogtoError,\n OidcError,\n Prompt,\n LogtoRequestError,\n ReservedScope,\n UserScope,\n} from '@logto/js';\nexport * from './errors';\nexport type { Storage, StorageKey, ClientAdapter } from './adapter';\nexport { createRequester } from './utils';\nexport * from './types';\n\nexport default class LogtoClient {\n protected readonly logtoConfig: LogtoConfig;\n protected readonly getOidcConfig = once(this._getOidcConfig);\n protected readonly getJwtVerifyGetKey = once(this._getJwtVerifyGetKey);\n protected readonly adapter: ClientAdapter;\n protected readonly accessTokenMap = new Map<string, AccessToken>();\n\n constructor(logtoConfig: LogtoConfig, adapter: ClientAdapter) {\n this.logtoConfig = {\n ...logtoConfig,\n prompt: logtoConfig.prompt ?? Prompt.Consent,\n scopes: withDefaultScopes(logtoConfig.scopes).split(' '),\n };\n this.adapter = adapter;\n\n void this.loadAccessTokenMap();\n }\n\n async isAuthenticated() {\n return Boolean(await this.getIdToken());\n }\n\n async getRefreshToken() {\n return this.adapter.storage.getItem('refreshToken');\n }\n\n async getIdToken() {\n return this.adapter.storage.getItem('idToken');\n }\n\n async getAccessToken(resource?: string): Promise<string> {\n if (!(await this.getIdToken())) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const accessTokenKey = buildAccessTokenKey(resource);\n const accessToken = this.accessTokenMap.get(accessTokenKey);\n\n if (accessToken && accessToken.expiresAt > Date.now() / 1000) {\n return accessToken.token;\n }\n\n // Since the access token has expired, delete it from the map.\n if (accessToken) {\n this.accessTokenMap.delete(accessTokenKey);\n }\n\n /**\n * Need to fetch a new access token using refresh token.\n */\n return this.getAccessTokenByRefreshToken(resource);\n }\n\n async getIdTokenClaims(): Promise<IdTokenClaims> {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n return decodeIdToken(idToken);\n }\n\n async fetchUserInfo(): Promise<UserInfoResponse> {\n const { userinfoEndpoint } = await this.getOidcConfig();\n const accessToken = await this.getAccessToken();\n\n if (!accessToken) {\n throw new LogtoClientError('fetch_user_info_failed');\n }\n\n return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);\n }\n\n async signIn(redirectUri: string, interactionMode?: InteractionMode) {\n const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;\n const { authorizationEndpoint } = await this.getOidcConfig();\n const codeVerifier = this.adapter.generateCodeVerifier();\n const codeChallenge = await this.adapter.generateCodeChallenge(codeVerifier);\n const state = this.adapter.generateState();\n\n const signInUri = generateSignInUri({\n authorizationEndpoint,\n clientId,\n redirectUri,\n codeChallenge,\n state,\n scopes,\n resources,\n prompt,\n interactionMode,\n });\n\n await this.setSignInSession({ redirectUri, codeVerifier, state });\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n\n this.adapter.navigate(signInUri);\n }\n\n async isSignInRedirected(url: string): Promise<boolean> {\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n return false;\n }\n const { redirectUri } = signInSession;\n const { origin, pathname } = new URL(url);\n\n return `${origin}${pathname}` === redirectUri;\n }\n\n async handleSignInCallback(callbackUri: string) {\n const { logtoConfig, adapter } = this;\n const { requester } = adapter;\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n throw new LogtoClientError('sign_in_session.not_found');\n }\n\n const { redirectUri, state, codeVerifier } = signInSession;\n const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);\n\n const { appId: clientId } = logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const codeTokenResponse = await fetchTokenByAuthorizationCode(\n {\n clientId,\n tokenEndpoint,\n redirectUri,\n codeVerifier,\n code,\n },\n requester\n );\n\n await this.verifyIdToken(codeTokenResponse.idToken);\n await this.saveCodeToken(codeTokenResponse);\n await this.setSignInSession(null);\n }\n\n async signOut(postLogoutRedirectUri?: string) {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const { appId: clientId } = this.logtoConfig;\n const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();\n const refreshToken = await this.getRefreshToken();\n\n if (refreshToken) {\n try {\n await revoke(revocationEndpoint, clientId, refreshToken, this.adapter.requester);\n } catch {\n // Do nothing at this point, as we don't want to break the sign-out flow even if the revocation is failed\n }\n }\n\n const url = generateSignOutUri({\n endSessionEndpoint,\n postLogoutRedirectUri,\n clientId,\n });\n\n this.accessTokenMap.clear();\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n await this.adapter.storage.removeItem('accessToken');\n\n this.adapter.navigate(url);\n }\n\n protected async getSignInSession(): Promise<Nullable<LogtoSignInSessionItem>> {\n const jsonItem = await this.adapter.storage.getItem('signInSession');\n\n if (!jsonItem) {\n return null;\n }\n\n const item: unknown = JSON.parse(jsonItem);\n\n if (!isLogtoSignInSessionItem(item)) {\n throw new LogtoClientError('sign_in_session.invalid');\n }\n\n return item;\n }\n\n protected async setSignInSession(logtoSignInSessionItem: Nullable<LogtoSignInSessionItem>) {\n if (!logtoSignInSessionItem) {\n await this.adapter.storage.removeItem('signInSession');\n\n return;\n }\n\n const jsonItem = JSON.stringify(logtoSignInSessionItem);\n await this.adapter.storage.setItem('signInSession', jsonItem);\n }\n\n private async setIdToken(idToken: Nullable<string>) {\n if (!idToken) {\n await this.adapter.storage.removeItem('idToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('idToken', idToken);\n }\n\n private async setRefreshToken(refreshToken: Nullable<string>) {\n if (!refreshToken) {\n await this.adapter.storage.removeItem('refreshToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('refreshToken', refreshToken);\n }\n\n private async getAccessTokenByRefreshToken(resource?: string): Promise<string> {\n const currentRefreshToken = await this.getRefreshToken();\n\n if (!currentRefreshToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n try {\n const accessTokenKey = buildAccessTokenKey(resource);\n const { appId: clientId } = this.logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const { accessToken, refreshToken, idToken, scope, expiresIn } =\n await fetchTokenByRefreshToken(\n {\n clientId,\n tokenEndpoint,\n refreshToken: currentRefreshToken,\n resource,\n },\n this.adapter.requester\n );\n\n this.accessTokenMap.set(accessTokenKey, {\n token: accessToken,\n scope,\n expiresAt: Math.round(Date.now() / 1000) + expiresIn,\n });\n\n await this.saveAccessTokenMap();\n await this.setRefreshToken(refreshToken);\n\n if (idToken) {\n await this.verifyIdToken(idToken);\n await this.setIdToken(idToken);\n }\n\n return accessToken;\n } catch (error: unknown) {\n throw new LogtoClientError('get_access_token_by_refresh_token_failed', error);\n }\n }\n\n private async _getOidcConfig() {\n const { endpoint } = this.logtoConfig;\n const discoveryEndpoint = getDiscoveryEndpoint(endpoint);\n\n return fetchOidcConfig(discoveryEndpoint, this.adapter.requester);\n }\n\n private async _getJwtVerifyGetKey() {\n const { jwksUri } = await this.getOidcConfig();\n\n return createRemoteJWKSet(new URL(jwksUri));\n }\n\n private async verifyIdToken(idToken: string) {\n const { appId } = this.logtoConfig;\n const { issuer } = await this.getOidcConfig();\n const jwtVerifyGetKey = await this.getJwtVerifyGetKey();\n\n try {\n await verifyIdToken(idToken, appId, issuer, jwtVerifyGetKey);\n } catch (error: unknown) {\n throw new LogtoClientError('invalid_id_token', error);\n }\n }\n\n private async saveCodeToken({\n refreshToken,\n idToken,\n scope,\n accessToken,\n expiresIn,\n }: CodeTokenResponse) {\n await this.setRefreshToken(refreshToken ?? null);\n await this.setIdToken(idToken);\n\n // NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)\n const accessTokenKey = buildAccessTokenKey();\n const expiresAt = Date.now() / 1000 + expiresIn;\n this.accessTokenMap.set(accessTokenKey, { token: accessToken, scope, expiresAt });\n await this.saveAccessTokenMap();\n }\n\n private async saveAccessTokenMap() {\n const data: Record<string, AccessToken> = {};\n\n for (const [key, accessToken] of this.accessTokenMap.entries()) {\n // eslint-disable-next-line @silverhand/fp/no-mutation\n data[key] = accessToken;\n }\n\n await this.adapter.storage.setItem('accessToken', JSON.stringify(data));\n }\n\n private async loadAccessTokenMap() {\n const raw = await this.adapter.storage.getItem('accessToken');\n\n if (!raw) {\n return;\n }\n\n try {\n const json: unknown = JSON.parse(raw);\n\n if (!isLogtoAccessTokenMap(json)) {\n return;\n }\n this.accessTokenMap.clear();\n\n for (const [key, accessToken] of Object.entries(json)) {\n this.accessTokenMap.set(key, accessToken);\n }\n } catch {}\n }\n // FIXME: @charles @sijie\n}\n"],"names":[],"version":3,"file":"index.d.ts.map"}
|
package/lib/index.js
CHANGED
|
@@ -162,7 +162,7 @@ class $f73788ae50447ce9$export$2e2bcd8739ae039 {
|
|
|
162
162
|
if (!accessToken) throw new (0, $9166104b36889c59$export$877962ca249b8fc8)("fetch_user_info_failed");
|
|
163
163
|
return (0, $4R6L3$logtojs.fetchUserInfo)(userinfoEndpoint, accessToken, this.adapter.requester);
|
|
164
164
|
}
|
|
165
|
-
async signIn(redirectUri) {
|
|
165
|
+
async signIn(redirectUri, interactionMode) {
|
|
166
166
|
const { appId: clientId , prompt: prompt , resources: resources , scopes: scopes } = this.logtoConfig;
|
|
167
167
|
const { authorizationEndpoint: authorizationEndpoint } = await this.getOidcConfig();
|
|
168
168
|
const codeVerifier = this.adapter.generateCodeVerifier();
|
|
@@ -176,7 +176,8 @@ class $f73788ae50447ce9$export$2e2bcd8739ae039 {
|
|
|
176
176
|
state: state,
|
|
177
177
|
scopes: scopes,
|
|
178
178
|
resources: resources,
|
|
179
|
-
prompt: prompt
|
|
179
|
+
prompt: prompt,
|
|
180
|
+
interactionMode: interactionMode
|
|
180
181
|
});
|
|
181
182
|
await this.setSignInSession({
|
|
182
183
|
redirectUri: redirectUri,
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["packages/client/src/index.ts","packages/client/src/errors.ts","packages/client/src/types/index.ts","packages/client/src/utils/index.ts","packages/client/src/utils/requester.ts"],"sourcesContent":["import type { CodeTokenResponse, IdTokenClaims, UserInfoResponse } from '@logto/js';\nimport {\n decodeIdToken,\n fetchOidcConfig,\n fetchTokenByAuthorizationCode,\n fetchTokenByRefreshToken,\n fetchUserInfo,\n generateSignInUri,\n generateSignOutUri,\n Prompt,\n revoke,\n verifyAndParseCodeFromCallbackUri,\n verifyIdToken,\n withDefaultScopes,\n} from '@logto/js';\nimport type { Nullable } from '@silverhand/essentials';\nimport { createRemoteJWKSet } from 'jose';\nimport once from 'lodash.once';\n\nimport type { ClientAdapter } from './adapter';\nimport { LogtoClientError } from './errors';\nimport type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types';\nimport { isLogtoAccessTokenMap, isLogtoSignInSessionItem } from './types';\nimport { buildAccessTokenKey, getDiscoveryEndpoint } from './utils';\n\nexport type { IdTokenClaims, LogtoErrorCode, UserInfoResponse } from '@logto/js';\nexport {\n LogtoError,\n OidcError,\n Prompt,\n LogtoRequestError,\n ReservedScope,\n UserScope,\n} from '@logto/js';\nexport * from './errors';\nexport type { Storage, StorageKey, ClientAdapter } from './adapter';\nexport { createRequester } from './utils';\nexport * from './types';\n\nexport default class LogtoClient {\n protected readonly logtoConfig: LogtoConfig;\n protected readonly getOidcConfig = once(this._getOidcConfig);\n protected readonly getJwtVerifyGetKey = once(this._getJwtVerifyGetKey);\n protected readonly adapter: ClientAdapter;\n protected readonly accessTokenMap = new Map<string, AccessToken>();\n\n constructor(logtoConfig: LogtoConfig, adapter: ClientAdapter) {\n this.logtoConfig = {\n ...logtoConfig,\n prompt: logtoConfig.prompt ?? Prompt.Consent,\n scopes: withDefaultScopes(logtoConfig.scopes).split(' '),\n };\n this.adapter = adapter;\n\n void this.loadAccessTokenMap();\n }\n\n async isAuthenticated() {\n return Boolean(await this.getIdToken());\n }\n\n async getRefreshToken() {\n return this.adapter.storage.getItem('refreshToken');\n }\n\n async getIdToken() {\n return this.adapter.storage.getItem('idToken');\n }\n\n async getAccessToken(resource?: string): Promise<string> {\n if (!(await this.getIdToken())) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const accessTokenKey = buildAccessTokenKey(resource);\n const accessToken = this.accessTokenMap.get(accessTokenKey);\n\n if (accessToken && accessToken.expiresAt > Date.now() / 1000) {\n return accessToken.token;\n }\n\n // Since the access token has expired, delete it from the map.\n if (accessToken) {\n this.accessTokenMap.delete(accessTokenKey);\n }\n\n /**\n * Need to fetch a new access token using refresh token.\n */\n return this.getAccessTokenByRefreshToken(resource);\n }\n\n async getIdTokenClaims(): Promise<IdTokenClaims> {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n return decodeIdToken(idToken);\n }\n\n async fetchUserInfo(): Promise<UserInfoResponse> {\n const { userinfoEndpoint } = await this.getOidcConfig();\n const accessToken = await this.getAccessToken();\n\n if (!accessToken) {\n throw new LogtoClientError('fetch_user_info_failed');\n }\n\n return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);\n }\n\n async signIn(redirectUri: string) {\n const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;\n const { authorizationEndpoint } = await this.getOidcConfig();\n const codeVerifier = this.adapter.generateCodeVerifier();\n const codeChallenge = await this.adapter.generateCodeChallenge(codeVerifier);\n const state = this.adapter.generateState();\n\n const signInUri = generateSignInUri({\n authorizationEndpoint,\n clientId,\n redirectUri,\n codeChallenge,\n state,\n scopes,\n resources,\n prompt,\n });\n\n await this.setSignInSession({ redirectUri, codeVerifier, state });\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n\n this.adapter.navigate(signInUri);\n }\n\n async isSignInRedirected(url: string): Promise<boolean> {\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n return false;\n }\n const { redirectUri } = signInSession;\n const { origin, pathname } = new URL(url);\n\n return `${origin}${pathname}` === redirectUri;\n }\n\n async handleSignInCallback(callbackUri: string) {\n const { logtoConfig, adapter } = this;\n const { requester } = adapter;\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n throw new LogtoClientError('sign_in_session.not_found');\n }\n\n const { redirectUri, state, codeVerifier } = signInSession;\n const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);\n\n const { appId: clientId } = logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const codeTokenResponse = await fetchTokenByAuthorizationCode(\n {\n clientId,\n tokenEndpoint,\n redirectUri,\n codeVerifier,\n code,\n },\n requester\n );\n\n await this.verifyIdToken(codeTokenResponse.idToken);\n await this.saveCodeToken(codeTokenResponse);\n await this.setSignInSession(null);\n }\n\n async signOut(postLogoutRedirectUri?: string) {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const { appId: clientId } = this.logtoConfig;\n const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();\n const refreshToken = await this.getRefreshToken();\n\n if (refreshToken) {\n try {\n await revoke(revocationEndpoint, clientId, refreshToken, this.adapter.requester);\n } catch {\n // Do nothing at this point, as we don't want to break the sign-out flow even if the revocation is failed\n }\n }\n\n const url = generateSignOutUri({\n endSessionEndpoint,\n postLogoutRedirectUri,\n clientId,\n });\n\n this.accessTokenMap.clear();\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n await this.adapter.storage.removeItem('accessToken');\n\n this.adapter.navigate(url);\n }\n\n protected async getSignInSession(): Promise<Nullable<LogtoSignInSessionItem>> {\n const jsonItem = await this.adapter.storage.getItem('signInSession');\n\n if (!jsonItem) {\n return null;\n }\n\n const item: unknown = JSON.parse(jsonItem);\n\n if (!isLogtoSignInSessionItem(item)) {\n throw new LogtoClientError('sign_in_session.invalid');\n }\n\n return item;\n }\n\n protected async setSignInSession(logtoSignInSessionItem: Nullable<LogtoSignInSessionItem>) {\n if (!logtoSignInSessionItem) {\n await this.adapter.storage.removeItem('signInSession');\n\n return;\n }\n\n const jsonItem = JSON.stringify(logtoSignInSessionItem);\n await this.adapter.storage.setItem('signInSession', jsonItem);\n }\n\n private async setIdToken(idToken: Nullable<string>) {\n if (!idToken) {\n await this.adapter.storage.removeItem('idToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('idToken', idToken);\n }\n\n private async setRefreshToken(refreshToken: Nullable<string>) {\n if (!refreshToken) {\n await this.adapter.storage.removeItem('refreshToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('refreshToken', refreshToken);\n }\n\n private async getAccessTokenByRefreshToken(resource?: string): Promise<string> {\n const currentRefreshToken = await this.getRefreshToken();\n\n if (!currentRefreshToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n try {\n const accessTokenKey = buildAccessTokenKey(resource);\n const { appId: clientId } = this.logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const { accessToken, refreshToken, idToken, scope, expiresIn } =\n await fetchTokenByRefreshToken(\n {\n clientId,\n tokenEndpoint,\n refreshToken: currentRefreshToken,\n resource,\n },\n this.adapter.requester\n );\n\n this.accessTokenMap.set(accessTokenKey, {\n token: accessToken,\n scope,\n expiresAt: Math.round(Date.now() / 1000) + expiresIn,\n });\n\n await this.saveAccessTokenMap();\n await this.setRefreshToken(refreshToken);\n\n if (idToken) {\n await this.verifyIdToken(idToken);\n await this.setIdToken(idToken);\n }\n\n return accessToken;\n } catch (error: unknown) {\n throw new LogtoClientError('get_access_token_by_refresh_token_failed', error);\n }\n }\n\n private async _getOidcConfig() {\n const { endpoint } = this.logtoConfig;\n const discoveryEndpoint = getDiscoveryEndpoint(endpoint);\n\n return fetchOidcConfig(discoveryEndpoint, this.adapter.requester);\n }\n\n private async _getJwtVerifyGetKey() {\n const { jwksUri } = await this.getOidcConfig();\n\n return createRemoteJWKSet(new URL(jwksUri));\n }\n\n private async verifyIdToken(idToken: string) {\n const { appId } = this.logtoConfig;\n const { issuer } = await this.getOidcConfig();\n const jwtVerifyGetKey = await this.getJwtVerifyGetKey();\n\n try {\n await verifyIdToken(idToken, appId, issuer, jwtVerifyGetKey);\n } catch (error: unknown) {\n throw new LogtoClientError('invalid_id_token', error);\n }\n }\n\n private async saveCodeToken({\n refreshToken,\n idToken,\n scope,\n accessToken,\n expiresIn,\n }: CodeTokenResponse) {\n await this.setRefreshToken(refreshToken ?? null);\n await this.setIdToken(idToken);\n\n // NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)\n const accessTokenKey = buildAccessTokenKey();\n const expiresAt = Date.now() / 1000 + expiresIn;\n this.accessTokenMap.set(accessTokenKey, { token: accessToken, scope, expiresAt });\n await this.saveAccessTokenMap();\n }\n\n private async saveAccessTokenMap() {\n const data: Record<string, AccessToken> = {};\n\n for (const [key, accessToken] of this.accessTokenMap.entries()) {\n // eslint-disable-next-line @silverhand/fp/no-mutation\n data[key] = accessToken;\n }\n\n await this.adapter.storage.setItem('accessToken', JSON.stringify(data));\n }\n\n private async loadAccessTokenMap() {\n const raw = await this.adapter.storage.getItem('accessToken');\n\n if (!raw) {\n return;\n }\n\n try {\n const json: unknown = JSON.parse(raw);\n\n if (!isLogtoAccessTokenMap(json)) {\n return;\n }\n this.accessTokenMap.clear();\n\n for (const [key, accessToken] of Object.entries(json)) {\n this.accessTokenMap.set(key, accessToken);\n }\n } catch {}\n }\n // FIXME: @charles @sijie\n}\n","import type { NormalizeKeyPaths } from '@silverhand/essentials';\nimport get from 'lodash.get';\n\nconst logtoClientErrorCodes = Object.freeze({\n sign_in_session: {\n invalid: 'Invalid sign-in session.',\n not_found: 'Sign-in session not found.',\n },\n not_authenticated: 'Not authenticated.',\n get_access_token_by_refresh_token_failed: 'Failed to get access token by refresh token.',\n fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.',\n invalid_id_token: 'Invalid id token.',\n});\n\nexport type LogtoClientErrorCode = NormalizeKeyPaths<typeof logtoClientErrorCodes>;\n\nconst getMessageByErrorCode = (errorCode: LogtoClientErrorCode): string => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const message = get(logtoClientErrorCodes, errorCode);\n\n if (typeof message === 'string') {\n return message;\n }\n\n return errorCode;\n};\n\nexport class LogtoClientError extends Error {\n code: LogtoClientErrorCode;\n data: unknown;\n\n constructor(code: LogtoClientErrorCode, data?: unknown) {\n super(getMessageByErrorCode(code));\n this.code = code;\n this.data = data;\n }\n}\n","import type { Prompt } from '@logto/js';\nimport { isArbitraryObject } from '@logto/js';\n\nexport type LogtoConfig = {\n endpoint: string;\n appId: string;\n appSecret?: string;\n scopes?: string[];\n resources?: string[];\n prompt?: Prompt;\n};\n\nexport type AccessToken = {\n token: string;\n scope: string;\n expiresAt: number;\n};\n\nexport const isLogtoSignInSessionItem = (data: unknown): data is LogtoSignInSessionItem => {\n if (!isArbitraryObject(data)) {\n return false;\n }\n\n return ['redirectUri', 'codeVerifier', 'state'].every((key) => typeof data[key] === 'string');\n};\n\nexport const isLogtoAccessTokenMap = (data: unknown): data is Record<string, AccessToken> => {\n if (!isArbitraryObject(data)) {\n return false;\n }\n\n return Object.values(data).every((value) => {\n if (!isArbitraryObject(value)) {\n return false;\n }\n\n return (\n typeof value.token === 'string' &&\n typeof value.scope === 'string' &&\n typeof value.expiresAt === 'number'\n );\n });\n};\n\nexport type LogtoSignInSessionItem = {\n redirectUri: string;\n codeVerifier: string;\n state: string;\n};\n","import { discoveryPath } from '@logto/js';\n\nexport * from './requester';\n\nexport const buildAccessTokenKey = (resource = '', scopes: string[] = []): string =>\n `${scopes.slice().sort().join(' ')}@${resource}`;\n\nexport const getDiscoveryEndpoint = (endpoint: string): string =>\n new URL(discoveryPath, endpoint).toString();\n","import type { Requester } from '@logto/js';\nimport { LogtoError, LogtoRequestError, isLogtoRequestError } from '@logto/js';\n\nexport const createRequester = (fetchFunction: typeof fetch): Requester => {\n return async <T>(...args: Parameters<typeof fetch>): Promise<T> => {\n const response = await fetchFunction(...args);\n\n if (!response.ok) {\n const responseJson = await response.json();\n\n if (!isLogtoRequestError(responseJson)) {\n throw new LogtoError('unexpected_response_error', responseJson);\n }\n\n // Expected request error from server\n const { code, message } = responseJson;\n throw new LogtoRequestError(code, message);\n }\n\n return response.json();\n };\n};\n"],"names":[],"version":3,"file":"index.js.map"}
|
|
1
|
+
{"mappings":"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","sources":["packages/client/src/index.ts","packages/client/src/errors.ts","packages/client/src/types/index.ts","packages/client/src/utils/index.ts","packages/client/src/utils/requester.ts"],"sourcesContent":["import type {\n CodeTokenResponse,\n IdTokenClaims,\n UserInfoResponse,\n InteractionMode,\n} from '@logto/js';\nimport {\n decodeIdToken,\n fetchOidcConfig,\n fetchTokenByAuthorizationCode,\n fetchTokenByRefreshToken,\n fetchUserInfo,\n generateSignInUri,\n generateSignOutUri,\n Prompt,\n revoke,\n verifyAndParseCodeFromCallbackUri,\n verifyIdToken,\n withDefaultScopes,\n} from '@logto/js';\nimport type { Nullable } from '@silverhand/essentials';\nimport { createRemoteJWKSet } from 'jose';\nimport once from 'lodash.once';\n\nimport type { ClientAdapter } from './adapter';\nimport { LogtoClientError } from './errors';\nimport type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types';\nimport { isLogtoAccessTokenMap, isLogtoSignInSessionItem } from './types';\nimport { buildAccessTokenKey, getDiscoveryEndpoint } from './utils';\n\nexport type { IdTokenClaims, LogtoErrorCode, UserInfoResponse, InteractionMode } from '@logto/js';\nexport {\n LogtoError,\n OidcError,\n Prompt,\n LogtoRequestError,\n ReservedScope,\n UserScope,\n} from '@logto/js';\nexport * from './errors';\nexport type { Storage, StorageKey, ClientAdapter } from './adapter';\nexport { createRequester } from './utils';\nexport * from './types';\n\nexport default class LogtoClient {\n protected readonly logtoConfig: LogtoConfig;\n protected readonly getOidcConfig = once(this._getOidcConfig);\n protected readonly getJwtVerifyGetKey = once(this._getJwtVerifyGetKey);\n protected readonly adapter: ClientAdapter;\n protected readonly accessTokenMap = new Map<string, AccessToken>();\n\n constructor(logtoConfig: LogtoConfig, adapter: ClientAdapter) {\n this.logtoConfig = {\n ...logtoConfig,\n prompt: logtoConfig.prompt ?? Prompt.Consent,\n scopes: withDefaultScopes(logtoConfig.scopes).split(' '),\n };\n this.adapter = adapter;\n\n void this.loadAccessTokenMap();\n }\n\n async isAuthenticated() {\n return Boolean(await this.getIdToken());\n }\n\n async getRefreshToken() {\n return this.adapter.storage.getItem('refreshToken');\n }\n\n async getIdToken() {\n return this.adapter.storage.getItem('idToken');\n }\n\n async getAccessToken(resource?: string): Promise<string> {\n if (!(await this.getIdToken())) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const accessTokenKey = buildAccessTokenKey(resource);\n const accessToken = this.accessTokenMap.get(accessTokenKey);\n\n if (accessToken && accessToken.expiresAt > Date.now() / 1000) {\n return accessToken.token;\n }\n\n // Since the access token has expired, delete it from the map.\n if (accessToken) {\n this.accessTokenMap.delete(accessTokenKey);\n }\n\n /**\n * Need to fetch a new access token using refresh token.\n */\n return this.getAccessTokenByRefreshToken(resource);\n }\n\n async getIdTokenClaims(): Promise<IdTokenClaims> {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n return decodeIdToken(idToken);\n }\n\n async fetchUserInfo(): Promise<UserInfoResponse> {\n const { userinfoEndpoint } = await this.getOidcConfig();\n const accessToken = await this.getAccessToken();\n\n if (!accessToken) {\n throw new LogtoClientError('fetch_user_info_failed');\n }\n\n return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);\n }\n\n async signIn(redirectUri: string, interactionMode?: InteractionMode) {\n const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;\n const { authorizationEndpoint } = await this.getOidcConfig();\n const codeVerifier = this.adapter.generateCodeVerifier();\n const codeChallenge = await this.adapter.generateCodeChallenge(codeVerifier);\n const state = this.adapter.generateState();\n\n const signInUri = generateSignInUri({\n authorizationEndpoint,\n clientId,\n redirectUri,\n codeChallenge,\n state,\n scopes,\n resources,\n prompt,\n interactionMode,\n });\n\n await this.setSignInSession({ redirectUri, codeVerifier, state });\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n\n this.adapter.navigate(signInUri);\n }\n\n async isSignInRedirected(url: string): Promise<boolean> {\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n return false;\n }\n const { redirectUri } = signInSession;\n const { origin, pathname } = new URL(url);\n\n return `${origin}${pathname}` === redirectUri;\n }\n\n async handleSignInCallback(callbackUri: string) {\n const { logtoConfig, adapter } = this;\n const { requester } = adapter;\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n throw new LogtoClientError('sign_in_session.not_found');\n }\n\n const { redirectUri, state, codeVerifier } = signInSession;\n const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);\n\n const { appId: clientId } = logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const codeTokenResponse = await fetchTokenByAuthorizationCode(\n {\n clientId,\n tokenEndpoint,\n redirectUri,\n codeVerifier,\n code,\n },\n requester\n );\n\n await this.verifyIdToken(codeTokenResponse.idToken);\n await this.saveCodeToken(codeTokenResponse);\n await this.setSignInSession(null);\n }\n\n async signOut(postLogoutRedirectUri?: string) {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const { appId: clientId } = this.logtoConfig;\n const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();\n const refreshToken = await this.getRefreshToken();\n\n if (refreshToken) {\n try {\n await revoke(revocationEndpoint, clientId, refreshToken, this.adapter.requester);\n } catch {\n // Do nothing at this point, as we don't want to break the sign-out flow even if the revocation is failed\n }\n }\n\n const url = generateSignOutUri({\n endSessionEndpoint,\n postLogoutRedirectUri,\n clientId,\n });\n\n this.accessTokenMap.clear();\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n await this.adapter.storage.removeItem('accessToken');\n\n this.adapter.navigate(url);\n }\n\n protected async getSignInSession(): Promise<Nullable<LogtoSignInSessionItem>> {\n const jsonItem = await this.adapter.storage.getItem('signInSession');\n\n if (!jsonItem) {\n return null;\n }\n\n const item: unknown = JSON.parse(jsonItem);\n\n if (!isLogtoSignInSessionItem(item)) {\n throw new LogtoClientError('sign_in_session.invalid');\n }\n\n return item;\n }\n\n protected async setSignInSession(logtoSignInSessionItem: Nullable<LogtoSignInSessionItem>) {\n if (!logtoSignInSessionItem) {\n await this.adapter.storage.removeItem('signInSession');\n\n return;\n }\n\n const jsonItem = JSON.stringify(logtoSignInSessionItem);\n await this.adapter.storage.setItem('signInSession', jsonItem);\n }\n\n private async setIdToken(idToken: Nullable<string>) {\n if (!idToken) {\n await this.adapter.storage.removeItem('idToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('idToken', idToken);\n }\n\n private async setRefreshToken(refreshToken: Nullable<string>) {\n if (!refreshToken) {\n await this.adapter.storage.removeItem('refreshToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('refreshToken', refreshToken);\n }\n\n private async getAccessTokenByRefreshToken(resource?: string): Promise<string> {\n const currentRefreshToken = await this.getRefreshToken();\n\n if (!currentRefreshToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n try {\n const accessTokenKey = buildAccessTokenKey(resource);\n const { appId: clientId } = this.logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const { accessToken, refreshToken, idToken, scope, expiresIn } =\n await fetchTokenByRefreshToken(\n {\n clientId,\n tokenEndpoint,\n refreshToken: currentRefreshToken,\n resource,\n },\n this.adapter.requester\n );\n\n this.accessTokenMap.set(accessTokenKey, {\n token: accessToken,\n scope,\n expiresAt: Math.round(Date.now() / 1000) + expiresIn,\n });\n\n await this.saveAccessTokenMap();\n await this.setRefreshToken(refreshToken);\n\n if (idToken) {\n await this.verifyIdToken(idToken);\n await this.setIdToken(idToken);\n }\n\n return accessToken;\n } catch (error: unknown) {\n throw new LogtoClientError('get_access_token_by_refresh_token_failed', error);\n }\n }\n\n private async _getOidcConfig() {\n const { endpoint } = this.logtoConfig;\n const discoveryEndpoint = getDiscoveryEndpoint(endpoint);\n\n return fetchOidcConfig(discoveryEndpoint, this.adapter.requester);\n }\n\n private async _getJwtVerifyGetKey() {\n const { jwksUri } = await this.getOidcConfig();\n\n return createRemoteJWKSet(new URL(jwksUri));\n }\n\n private async verifyIdToken(idToken: string) {\n const { appId } = this.logtoConfig;\n const { issuer } = await this.getOidcConfig();\n const jwtVerifyGetKey = await this.getJwtVerifyGetKey();\n\n try {\n await verifyIdToken(idToken, appId, issuer, jwtVerifyGetKey);\n } catch (error: unknown) {\n throw new LogtoClientError('invalid_id_token', error);\n }\n }\n\n private async saveCodeToken({\n refreshToken,\n idToken,\n scope,\n accessToken,\n expiresIn,\n }: CodeTokenResponse) {\n await this.setRefreshToken(refreshToken ?? null);\n await this.setIdToken(idToken);\n\n // NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)\n const accessTokenKey = buildAccessTokenKey();\n const expiresAt = Date.now() / 1000 + expiresIn;\n this.accessTokenMap.set(accessTokenKey, { token: accessToken, scope, expiresAt });\n await this.saveAccessTokenMap();\n }\n\n private async saveAccessTokenMap() {\n const data: Record<string, AccessToken> = {};\n\n for (const [key, accessToken] of this.accessTokenMap.entries()) {\n // eslint-disable-next-line @silverhand/fp/no-mutation\n data[key] = accessToken;\n }\n\n await this.adapter.storage.setItem('accessToken', JSON.stringify(data));\n }\n\n private async loadAccessTokenMap() {\n const raw = await this.adapter.storage.getItem('accessToken');\n\n if (!raw) {\n return;\n }\n\n try {\n const json: unknown = JSON.parse(raw);\n\n if (!isLogtoAccessTokenMap(json)) {\n return;\n }\n this.accessTokenMap.clear();\n\n for (const [key, accessToken] of Object.entries(json)) {\n this.accessTokenMap.set(key, accessToken);\n }\n } catch {}\n }\n // FIXME: @charles @sijie\n}\n","import type { NormalizeKeyPaths } from '@silverhand/essentials';\nimport get from 'lodash.get';\n\nconst logtoClientErrorCodes = Object.freeze({\n sign_in_session: {\n invalid: 'Invalid sign-in session.',\n not_found: 'Sign-in session not found.',\n },\n not_authenticated: 'Not authenticated.',\n get_access_token_by_refresh_token_failed: 'Failed to get access token by refresh token.',\n fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.',\n invalid_id_token: 'Invalid id token.',\n});\n\nexport type LogtoClientErrorCode = NormalizeKeyPaths<typeof logtoClientErrorCodes>;\n\nconst getMessageByErrorCode = (errorCode: LogtoClientErrorCode): string => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const message = get(logtoClientErrorCodes, errorCode);\n\n if (typeof message === 'string') {\n return message;\n }\n\n return errorCode;\n};\n\nexport class LogtoClientError extends Error {\n code: LogtoClientErrorCode;\n data: unknown;\n\n constructor(code: LogtoClientErrorCode, data?: unknown) {\n super(getMessageByErrorCode(code));\n this.code = code;\n this.data = data;\n }\n}\n","import type { Prompt } from '@logto/js';\nimport { isArbitraryObject } from '@logto/js';\n\nexport type LogtoConfig = {\n endpoint: string;\n appId: string;\n appSecret?: string;\n scopes?: string[];\n resources?: string[];\n prompt?: Prompt;\n};\n\nexport type AccessToken = {\n token: string;\n scope: string;\n expiresAt: number;\n};\n\nexport const isLogtoSignInSessionItem = (data: unknown): data is LogtoSignInSessionItem => {\n if (!isArbitraryObject(data)) {\n return false;\n }\n\n return ['redirectUri', 'codeVerifier', 'state'].every((key) => typeof data[key] === 'string');\n};\n\nexport const isLogtoAccessTokenMap = (data: unknown): data is Record<string, AccessToken> => {\n if (!isArbitraryObject(data)) {\n return false;\n }\n\n return Object.values(data).every((value) => {\n if (!isArbitraryObject(value)) {\n return false;\n }\n\n return (\n typeof value.token === 'string' &&\n typeof value.scope === 'string' &&\n typeof value.expiresAt === 'number'\n );\n });\n};\n\nexport type LogtoSignInSessionItem = {\n redirectUri: string;\n codeVerifier: string;\n state: string;\n};\n","import { discoveryPath } from '@logto/js';\n\nexport * from './requester';\n\nexport const buildAccessTokenKey = (resource = '', scopes: string[] = []): string =>\n `${scopes.slice().sort().join(' ')}@${resource}`;\n\nexport const getDiscoveryEndpoint = (endpoint: string): string =>\n new URL(discoveryPath, endpoint).toString();\n","import type { Requester } from '@logto/js';\nimport { LogtoError, LogtoRequestError, isLogtoRequestError } from '@logto/js';\n\nexport const createRequester = (fetchFunction: typeof fetch): Requester => {\n return async <T>(...args: Parameters<typeof fetch>): Promise<T> => {\n const response = await fetchFunction(...args);\n\n if (!response.ok) {\n const responseJson = await response.json();\n\n if (!isLogtoRequestError(responseJson)) {\n throw new LogtoError('unexpected_response_error', responseJson);\n }\n\n // Expected request error from server\n const { code, message } = responseJson;\n throw new LogtoRequestError(code, message);\n }\n\n return response.json();\n };\n};\n"],"names":[],"version":3,"file":"index.js.map"}
|
package/lib/module.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Requester, Prompt, IdTokenClaims, UserInfoResponse } from "@logto/js";
|
|
1
|
+
import { Requester, Prompt, IdTokenClaims, UserInfoResponse, InteractionMode } from "@logto/js";
|
|
2
2
|
import { Nullable, NormalizeKeyPaths } from "@silverhand/essentials";
|
|
3
3
|
export type StorageKey = 'idToken' | 'refreshToken' | 'accessToken' | 'signInSession';
|
|
4
4
|
export type Storage = {
|
|
@@ -52,7 +52,7 @@ export type LogtoSignInSessionItem = {
|
|
|
52
52
|
state: string;
|
|
53
53
|
};
|
|
54
54
|
export const createRequester: (fetchFunction: typeof fetch) => Requester;
|
|
55
|
-
export type { IdTokenClaims, LogtoErrorCode, UserInfoResponse } from '@logto/js';
|
|
55
|
+
export type { IdTokenClaims, LogtoErrorCode, UserInfoResponse, InteractionMode } from '@logto/js';
|
|
56
56
|
export { LogtoError, OidcError, Prompt, LogtoRequestError, ReservedScope, UserScope, } from '@logto/js';
|
|
57
57
|
export default class LogtoClient {
|
|
58
58
|
protected readonly logtoConfig: LogtoConfig;
|
|
@@ -67,7 +67,7 @@ export default class LogtoClient {
|
|
|
67
67
|
getAccessToken(resource?: string): Promise<string>;
|
|
68
68
|
getIdTokenClaims(): Promise<IdTokenClaims>;
|
|
69
69
|
fetchUserInfo(): Promise<UserInfoResponse>;
|
|
70
|
-
signIn(redirectUri: string): Promise<void>;
|
|
70
|
+
signIn(redirectUri: string, interactionMode?: InteractionMode): Promise<void>;
|
|
71
71
|
isSignInRedirected(url: string): Promise<boolean>;
|
|
72
72
|
handleSignInCallback(callbackUri: string): Promise<void>;
|
|
73
73
|
signOut(postLogoutRedirectUri?: string): Promise<void>;
|
package/lib/module.mjs
CHANGED
|
@@ -129,7 +129,7 @@ class $19775a679e2952df$export$2e2bcd8739ae039 {
|
|
|
129
129
|
if (!accessToken) throw new (0, $4ec05cedcef20733$export$877962ca249b8fc8)("fetch_user_info_failed");
|
|
130
130
|
return (0, $kqBTI$fetchUserInfo)(userinfoEndpoint, accessToken, this.adapter.requester);
|
|
131
131
|
}
|
|
132
|
-
async signIn(redirectUri) {
|
|
132
|
+
async signIn(redirectUri, interactionMode) {
|
|
133
133
|
const { appId: clientId , prompt: prompt , resources: resources , scopes: scopes } = this.logtoConfig;
|
|
134
134
|
const { authorizationEndpoint: authorizationEndpoint } = await this.getOidcConfig();
|
|
135
135
|
const codeVerifier = this.adapter.generateCodeVerifier();
|
|
@@ -143,7 +143,8 @@ class $19775a679e2952df$export$2e2bcd8739ae039 {
|
|
|
143
143
|
state: state,
|
|
144
144
|
scopes: scopes,
|
|
145
145
|
resources: resources,
|
|
146
|
-
prompt: prompt
|
|
146
|
+
prompt: prompt,
|
|
147
|
+
interactionMode: interactionMode
|
|
147
148
|
});
|
|
148
149
|
await this.setSignInSession({
|
|
149
150
|
redirectUri: redirectUri,
|
package/lib/module.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["packages/client/src/index.ts","packages/client/src/errors.ts","packages/client/src/types/index.ts","packages/client/src/utils/index.ts","packages/client/src/utils/requester.ts"],"sourcesContent":["import type { CodeTokenResponse, IdTokenClaims, UserInfoResponse } from '@logto/js';\nimport {\n decodeIdToken,\n fetchOidcConfig,\n fetchTokenByAuthorizationCode,\n fetchTokenByRefreshToken,\n fetchUserInfo,\n generateSignInUri,\n generateSignOutUri,\n Prompt,\n revoke,\n verifyAndParseCodeFromCallbackUri,\n verifyIdToken,\n withDefaultScopes,\n} from '@logto/js';\nimport type { Nullable } from '@silverhand/essentials';\nimport { createRemoteJWKSet } from 'jose';\nimport once from 'lodash.once';\n\nimport type { ClientAdapter } from './adapter';\nimport { LogtoClientError } from './errors';\nimport type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types';\nimport { isLogtoAccessTokenMap, isLogtoSignInSessionItem } from './types';\nimport { buildAccessTokenKey, getDiscoveryEndpoint } from './utils';\n\nexport type { IdTokenClaims, LogtoErrorCode, UserInfoResponse } from '@logto/js';\nexport {\n LogtoError,\n OidcError,\n Prompt,\n LogtoRequestError,\n ReservedScope,\n UserScope,\n} from '@logto/js';\nexport * from './errors';\nexport type { Storage, StorageKey, ClientAdapter } from './adapter';\nexport { createRequester } from './utils';\nexport * from './types';\n\nexport default class LogtoClient {\n protected readonly logtoConfig: LogtoConfig;\n protected readonly getOidcConfig = once(this._getOidcConfig);\n protected readonly getJwtVerifyGetKey = once(this._getJwtVerifyGetKey);\n protected readonly adapter: ClientAdapter;\n protected readonly accessTokenMap = new Map<string, AccessToken>();\n\n constructor(logtoConfig: LogtoConfig, adapter: ClientAdapter) {\n this.logtoConfig = {\n ...logtoConfig,\n prompt: logtoConfig.prompt ?? Prompt.Consent,\n scopes: withDefaultScopes(logtoConfig.scopes).split(' '),\n };\n this.adapter = adapter;\n\n void this.loadAccessTokenMap();\n }\n\n async isAuthenticated() {\n return Boolean(await this.getIdToken());\n }\n\n async getRefreshToken() {\n return this.adapter.storage.getItem('refreshToken');\n }\n\n async getIdToken() {\n return this.adapter.storage.getItem('idToken');\n }\n\n async getAccessToken(resource?: string): Promise<string> {\n if (!(await this.getIdToken())) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const accessTokenKey = buildAccessTokenKey(resource);\n const accessToken = this.accessTokenMap.get(accessTokenKey);\n\n if (accessToken && accessToken.expiresAt > Date.now() / 1000) {\n return accessToken.token;\n }\n\n // Since the access token has expired, delete it from the map.\n if (accessToken) {\n this.accessTokenMap.delete(accessTokenKey);\n }\n\n /**\n * Need to fetch a new access token using refresh token.\n */\n return this.getAccessTokenByRefreshToken(resource);\n }\n\n async getIdTokenClaims(): Promise<IdTokenClaims> {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n return decodeIdToken(idToken);\n }\n\n async fetchUserInfo(): Promise<UserInfoResponse> {\n const { userinfoEndpoint } = await this.getOidcConfig();\n const accessToken = await this.getAccessToken();\n\n if (!accessToken) {\n throw new LogtoClientError('fetch_user_info_failed');\n }\n\n return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);\n }\n\n async signIn(redirectUri: string) {\n const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;\n const { authorizationEndpoint } = await this.getOidcConfig();\n const codeVerifier = this.adapter.generateCodeVerifier();\n const codeChallenge = await this.adapter.generateCodeChallenge(codeVerifier);\n const state = this.adapter.generateState();\n\n const signInUri = generateSignInUri({\n authorizationEndpoint,\n clientId,\n redirectUri,\n codeChallenge,\n state,\n scopes,\n resources,\n prompt,\n });\n\n await this.setSignInSession({ redirectUri, codeVerifier, state });\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n\n this.adapter.navigate(signInUri);\n }\n\n async isSignInRedirected(url: string): Promise<boolean> {\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n return false;\n }\n const { redirectUri } = signInSession;\n const { origin, pathname } = new URL(url);\n\n return `${origin}${pathname}` === redirectUri;\n }\n\n async handleSignInCallback(callbackUri: string) {\n const { logtoConfig, adapter } = this;\n const { requester } = adapter;\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n throw new LogtoClientError('sign_in_session.not_found');\n }\n\n const { redirectUri, state, codeVerifier } = signInSession;\n const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);\n\n const { appId: clientId } = logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const codeTokenResponse = await fetchTokenByAuthorizationCode(\n {\n clientId,\n tokenEndpoint,\n redirectUri,\n codeVerifier,\n code,\n },\n requester\n );\n\n await this.verifyIdToken(codeTokenResponse.idToken);\n await this.saveCodeToken(codeTokenResponse);\n await this.setSignInSession(null);\n }\n\n async signOut(postLogoutRedirectUri?: string) {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const { appId: clientId } = this.logtoConfig;\n const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();\n const refreshToken = await this.getRefreshToken();\n\n if (refreshToken) {\n try {\n await revoke(revocationEndpoint, clientId, refreshToken, this.adapter.requester);\n } catch {\n // Do nothing at this point, as we don't want to break the sign-out flow even if the revocation is failed\n }\n }\n\n const url = generateSignOutUri({\n endSessionEndpoint,\n postLogoutRedirectUri,\n clientId,\n });\n\n this.accessTokenMap.clear();\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n await this.adapter.storage.removeItem('accessToken');\n\n this.adapter.navigate(url);\n }\n\n protected async getSignInSession(): Promise<Nullable<LogtoSignInSessionItem>> {\n const jsonItem = await this.adapter.storage.getItem('signInSession');\n\n if (!jsonItem) {\n return null;\n }\n\n const item: unknown = JSON.parse(jsonItem);\n\n if (!isLogtoSignInSessionItem(item)) {\n throw new LogtoClientError('sign_in_session.invalid');\n }\n\n return item;\n }\n\n protected async setSignInSession(logtoSignInSessionItem: Nullable<LogtoSignInSessionItem>) {\n if (!logtoSignInSessionItem) {\n await this.adapter.storage.removeItem('signInSession');\n\n return;\n }\n\n const jsonItem = JSON.stringify(logtoSignInSessionItem);\n await this.adapter.storage.setItem('signInSession', jsonItem);\n }\n\n private async setIdToken(idToken: Nullable<string>) {\n if (!idToken) {\n await this.adapter.storage.removeItem('idToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('idToken', idToken);\n }\n\n private async setRefreshToken(refreshToken: Nullable<string>) {\n if (!refreshToken) {\n await this.adapter.storage.removeItem('refreshToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('refreshToken', refreshToken);\n }\n\n private async getAccessTokenByRefreshToken(resource?: string): Promise<string> {\n const currentRefreshToken = await this.getRefreshToken();\n\n if (!currentRefreshToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n try {\n const accessTokenKey = buildAccessTokenKey(resource);\n const { appId: clientId } = this.logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const { accessToken, refreshToken, idToken, scope, expiresIn } =\n await fetchTokenByRefreshToken(\n {\n clientId,\n tokenEndpoint,\n refreshToken: currentRefreshToken,\n resource,\n },\n this.adapter.requester\n );\n\n this.accessTokenMap.set(accessTokenKey, {\n token: accessToken,\n scope,\n expiresAt: Math.round(Date.now() / 1000) + expiresIn,\n });\n\n await this.saveAccessTokenMap();\n await this.setRefreshToken(refreshToken);\n\n if (idToken) {\n await this.verifyIdToken(idToken);\n await this.setIdToken(idToken);\n }\n\n return accessToken;\n } catch (error: unknown) {\n throw new LogtoClientError('get_access_token_by_refresh_token_failed', error);\n }\n }\n\n private async _getOidcConfig() {\n const { endpoint } = this.logtoConfig;\n const discoveryEndpoint = getDiscoveryEndpoint(endpoint);\n\n return fetchOidcConfig(discoveryEndpoint, this.adapter.requester);\n }\n\n private async _getJwtVerifyGetKey() {\n const { jwksUri } = await this.getOidcConfig();\n\n return createRemoteJWKSet(new URL(jwksUri));\n }\n\n private async verifyIdToken(idToken: string) {\n const { appId } = this.logtoConfig;\n const { issuer } = await this.getOidcConfig();\n const jwtVerifyGetKey = await this.getJwtVerifyGetKey();\n\n try {\n await verifyIdToken(idToken, appId, issuer, jwtVerifyGetKey);\n } catch (error: unknown) {\n throw new LogtoClientError('invalid_id_token', error);\n }\n }\n\n private async saveCodeToken({\n refreshToken,\n idToken,\n scope,\n accessToken,\n expiresIn,\n }: CodeTokenResponse) {\n await this.setRefreshToken(refreshToken ?? null);\n await this.setIdToken(idToken);\n\n // NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)\n const accessTokenKey = buildAccessTokenKey();\n const expiresAt = Date.now() / 1000 + expiresIn;\n this.accessTokenMap.set(accessTokenKey, { token: accessToken, scope, expiresAt });\n await this.saveAccessTokenMap();\n }\n\n private async saveAccessTokenMap() {\n const data: Record<string, AccessToken> = {};\n\n for (const [key, accessToken] of this.accessTokenMap.entries()) {\n // eslint-disable-next-line @silverhand/fp/no-mutation\n data[key] = accessToken;\n }\n\n await this.adapter.storage.setItem('accessToken', JSON.stringify(data));\n }\n\n private async loadAccessTokenMap() {\n const raw = await this.adapter.storage.getItem('accessToken');\n\n if (!raw) {\n return;\n }\n\n try {\n const json: unknown = JSON.parse(raw);\n\n if (!isLogtoAccessTokenMap(json)) {\n return;\n }\n this.accessTokenMap.clear();\n\n for (const [key, accessToken] of Object.entries(json)) {\n this.accessTokenMap.set(key, accessToken);\n }\n } catch {}\n }\n // FIXME: @charles @sijie\n}\n","import type { NormalizeKeyPaths } from '@silverhand/essentials';\nimport get from 'lodash.get';\n\nconst logtoClientErrorCodes = Object.freeze({\n sign_in_session: {\n invalid: 'Invalid sign-in session.',\n not_found: 'Sign-in session not found.',\n },\n not_authenticated: 'Not authenticated.',\n get_access_token_by_refresh_token_failed: 'Failed to get access token by refresh token.',\n fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.',\n invalid_id_token: 'Invalid id token.',\n});\n\nexport type LogtoClientErrorCode = NormalizeKeyPaths<typeof logtoClientErrorCodes>;\n\nconst getMessageByErrorCode = (errorCode: LogtoClientErrorCode): string => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const message = get(logtoClientErrorCodes, errorCode);\n\n if (typeof message === 'string') {\n return message;\n }\n\n return errorCode;\n};\n\nexport class LogtoClientError extends Error {\n code: LogtoClientErrorCode;\n data: unknown;\n\n constructor(code: LogtoClientErrorCode, data?: unknown) {\n super(getMessageByErrorCode(code));\n this.code = code;\n this.data = data;\n }\n}\n","import type { Prompt } from '@logto/js';\nimport { isArbitraryObject } from '@logto/js';\n\nexport type LogtoConfig = {\n endpoint: string;\n appId: string;\n appSecret?: string;\n scopes?: string[];\n resources?: string[];\n prompt?: Prompt;\n};\n\nexport type AccessToken = {\n token: string;\n scope: string;\n expiresAt: number;\n};\n\nexport const isLogtoSignInSessionItem = (data: unknown): data is LogtoSignInSessionItem => {\n if (!isArbitraryObject(data)) {\n return false;\n }\n\n return ['redirectUri', 'codeVerifier', 'state'].every((key) => typeof data[key] === 'string');\n};\n\nexport const isLogtoAccessTokenMap = (data: unknown): data is Record<string, AccessToken> => {\n if (!isArbitraryObject(data)) {\n return false;\n }\n\n return Object.values(data).every((value) => {\n if (!isArbitraryObject(value)) {\n return false;\n }\n\n return (\n typeof value.token === 'string' &&\n typeof value.scope === 'string' &&\n typeof value.expiresAt === 'number'\n );\n });\n};\n\nexport type LogtoSignInSessionItem = {\n redirectUri: string;\n codeVerifier: string;\n state: string;\n};\n","import { discoveryPath } from '@logto/js';\n\nexport * from './requester';\n\nexport const buildAccessTokenKey = (resource = '', scopes: string[] = []): string =>\n `${scopes.slice().sort().join(' ')}@${resource}`;\n\nexport const getDiscoveryEndpoint = (endpoint: string): string =>\n new URL(discoveryPath, endpoint).toString();\n","import type { Requester } from '@logto/js';\nimport { LogtoError, LogtoRequestError, isLogtoRequestError } from '@logto/js';\n\nexport const createRequester = (fetchFunction: typeof fetch): Requester => {\n return async <T>(...args: Parameters<typeof fetch>): Promise<T> => {\n const response = await fetchFunction(...args);\n\n if (!response.ok) {\n const responseJson = await response.json();\n\n if (!isLogtoRequestError(responseJson)) {\n throw new LogtoError('unexpected_response_error', responseJson);\n }\n\n // Expected request error from server\n const { code, message } = responseJson;\n throw new LogtoRequestError(code, message);\n }\n\n return response.json();\n };\n};\n"],"names":[],"version":3,"file":"module.mjs.map"}
|
|
1
|
+
{"mappings":";;;;;;;;AAAA;;;;;;ACAA;AAGA,MAAM,8CAAwB,OAAO,MAAM,CAAC;IAC1C,iBAAiB;QACf,SAAS;QACT,WAAW;IACb;IACA,mBAAmB;IACnB,0CAA0C;IAC1C,wBAAwB;IACxB,kBAAkB;AACpB;AAIA,MAAM,8CAAwB,CAAC,YAA4C;IACzE,mEAAmE;IACnE,MAAM,UAAU,CAAA,GAAA,gBAAE,EAAE,6CAAuB;IAE3C,IAAI,OAAO,YAAY,UACrB,OAAO;IAGT,OAAO;AACT;AAEO,MAAM,kDAAyB;IAIpC,YAAY,IAA0B,EAAE,IAAc,CAAE;QACtD,KAAK,CAAC,4CAAsB;QAC5B,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,IAAI,GAAG;IACd;AACF;;;;;;;ACpCA;AAkBO,MAAM,4CAA2B,CAAC,OAAkD;IACzF,IAAI,CAAC,CAAA,GAAA,wBAAiB,AAAD,EAAE,OACrB,OAAO,KAAK;IAGd,OAAO;QAAC;QAAe;QAAgB;KAAQ,CAAC,KAAK,CAAC,CAAC,MAAQ,OAAO,IAAI,CAAC,IAAI,KAAK;AACtF;AAEO,MAAM,4CAAwB,CAAC,OAAuD;IAC3F,IAAI,CAAC,CAAA,GAAA,wBAAiB,AAAD,EAAE,OACrB,OAAO,KAAK;IAGd,OAAO,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,QAAU;QAC1C,IAAI,CAAC,CAAA,GAAA,wBAAiB,AAAD,EAAE,QACrB,OAAO,KAAK;QAGd,OACE,OAAO,MAAM,KAAK,KAAK,YACvB,OAAO,MAAM,KAAK,KAAK,YACvB,OAAO,MAAM,SAAS,KAAK;IAE/B;AACF;;;AC1CA;ACAA;AAGO,MAAM,4CAAkB,CAAC,gBAA2C;IACzE,OAAO,OAAU,GAAG,OAA+C;QACjE,MAAM,WAAW,MAAM,iBAAiB;QAExC,IAAI,CAAC,SAAS,EAAE,EAAE;YAChB,MAAM,eAAe,MAAM,SAAS,IAAI;YAExC,IAAI,CAAC,CAAA,GAAA,0BAAkB,EAAE,eACvB,MAAM,IAAI,CAAA,GAAA,sCAAS,EAAE,6BAA6B,cAAc;YAGlE,qCAAqC;YACrC,MAAM,QAAE,KAAI,WAAE,QAAO,EAAE,GAAG;YAC1B,MAAM,IAAI,CAAA,GAAA,6CAAiB,AAAD,EAAE,MAAM,SAAS;QAC7C,CAAC;QAED,OAAO,SAAS,IAAI;IACtB;AACF;;;ADjBO,MAAM,4CAAsB,CAAC,WAAW,EAAE,EAAE,SAAmB,EAAE,GACtE,CAAC,EAAE,OAAO,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;AAE3C,MAAM,4CAAuB,CAAC,WACnC,IAAI,IAAI,CAAA,GAAA,oBAAa,AAAD,GAAG,UAAU,QAAQ;;;;;;;AHoC5B;IAEM,gBAAgB,CAAA,GAAA,iBAAG,EAAE,IAAI,CAAC,cAAc,EAAE;IAC1C,qBAAqB,CAAA,GAAA,iBAAG,EAAE,IAAI,CAAC,mBAAmB,EAAE;IAEpD,iBAAiB,IAAI,MAA2B;IAEnE,YAAY,WAAwB,EAAE,OAAsB,CAAE;QAC5D,IAAI,CAAC,WAAW,GAAG;YACjB,GAAG,WAAW;YACd,QAAQ,YAAY,MAAM,IAAI,CAAA,GAAA,0DAAM,AAAD,EAAE,OAAO;YAC5C,QAAQ,CAAA,GAAA,wBAAgB,EAAE,YAAY,MAAM,EAAE,KAAK,CAAC;QACtD;QACA,IAAI,CAAC,OAAO,GAAG;QAEV,IAAI,CAAC,kBAAkB;IAC9B;IAEA,MAAM,kBAAkB;QACtB,OAAO,QAAQ,MAAM,IAAI,CAAC,UAAU;IACtC;IAEA,MAAM,kBAAkB;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;IACtC;IAEA,MAAM,aAAa;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;IACtC;IAEA,MAAM,eAAe,QAAiB,EAAmB;QACvD,IAAI,CAAE,MAAM,IAAI,CAAC,UAAU,IACzB,MAAM,IAAI,CAAA,GAAA,yCAAgB,AAAD,EAAE,qBAAqB;QAGlD,MAAM,iBAAiB,CAAA,GAAA,yCAAkB,EAAE;QAC3C,MAAM,cAAc,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC;QAE5C,IAAI,eAAe,YAAY,SAAS,GAAG,KAAK,GAAG,KAAK,MACtD,OAAO,YAAY,KAAK;QAG1B,8DAA8D;QAC9D,IAAI,aACF,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;QAG7B;;KAEC,GACD,OAAO,IAAI,CAAC,4BAA4B,CAAC;IAC3C;IAEA,MAAM,mBAA2C;QAC/C,MAAM,UAAU,MAAM,IAAI,CAAC,UAAU;QAErC,IAAI,CAAC,SACH,MAAM,IAAI,CAAA,GAAA,yCAAe,EAAE,qBAAqB;QAGlD,OAAO,CAAA,GAAA,oBAAY,EAAE;IACvB;IAEA,MAAM,gBAA2C;QAC/C,MAAM,oBAAE,iBAAgB,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa;QACrD,MAAM,cAAc,MAAM,IAAI,CAAC,cAAc;QAE7C,IAAI,CAAC,aACH,MAAM,IAAI,CAAA,GAAA,yCAAe,EAAE,0BAA0B;QAGvD,OAAO,CAAA,GAAA,oBAAY,EAAE,kBAAkB,aAAa,IAAI,CAAC,OAAO,CAAC,SAAS;IAC5E;IAEA,MAAM,OAAO,WAAmB,EAAE,eAAiC,EAAE;QACnE,MAAM,EAAE,OAAO,SAAQ,UAAE,OAAM,aAAE,UAAS,UAAE,OAAM,EAAE,GAAG,IAAI,CAAC,WAAW;QACvE,MAAM,yBAAE,sBAAqB,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa;QAC1D,MAAM,eAAe,IAAI,CAAC,OAAO,CAAC,oBAAoB;QACtD,MAAM,gBAAgB,MAAM,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC;QAC/D,MAAM,QAAQ,IAAI,CAAC,OAAO,CAAC,aAAa;QAExC,MAAM,YAAY,CAAA,GAAA,wBAAgB,EAAE;mCAClC;sBACA;yBACA;2BACA;mBACA;oBACA;uBACA;oBACA;6BACA;QACF;QAEA,MAAM,IAAI,CAAC,gBAAgB,CAAC;yBAAE;0BAAa;mBAAc;QAAM;QAC/D,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI;QAC/B,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI;QAE1B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IACxB;IAEA,MAAM,mBAAmB,GAAW,EAAoB;QACtD,MAAM,gBAAgB,MAAM,IAAI,CAAC,gBAAgB;QAEjD,IAAI,CAAC,eACH,OAAO,KAAK;QAEd,MAAM,eAAE,YAAW,EAAE,GAAG;QACxB,MAAM,UAAE,OAAM,YAAE,SAAQ,EAAE,GAAG,IAAI,IAAI;QAErC,OAAO,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,KAAK;IACpC;IAEA,MAAM,qBAAqB,WAAmB,EAAE;QAC9C,MAAM,eAAE,YAAW,WAAE,QAAO,EAAE,GAAG,IAAI;QACrC,MAAM,aAAE,UAAS,EAAE,GAAG;QACtB,MAAM,gBAAgB,MAAM,IAAI,CAAC,gBAAgB;QAEjD,IAAI,CAAC,eACH,MAAM,IAAI,CAAA,GAAA,yCAAe,EAAE,6BAA6B;QAG1D,MAAM,eAAE,YAAW,SAAE,MAAK,gBAAE,aAAY,EAAE,GAAG;QAC7C,MAAM,OAAO,CAAA,GAAA,wCAAiC,AAAD,EAAE,aAAa,aAAa;QAEzE,MAAM,EAAE,OAAO,SAAQ,EAAE,GAAG;QAC5B,MAAM,iBAAE,cAAa,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa;QAClD,MAAM,oBAAoB,MAAM,CAAA,GAAA,oCAA4B,EAC1D;sBACE;2BACA;yBACA;0BACA;kBACA;QACF,GACA;QAGF,MAAM,IAAI,CAAC,aAAa,CAAC,kBAAkB,OAAO;QAClD,MAAM,IAAI,CAAC,aAAa,CAAC;QACzB,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI;IAClC;IAEA,MAAM,QAAQ,qBAA8B,EAAE;QAC5C,MAAM,UAAU,MAAM,IAAI,CAAC,UAAU;QAErC,IAAI,CAAC,SACH,MAAM,IAAI,CAAA,GAAA,yCAAe,EAAE,qBAAqB;QAGlD,MAAM,EAAE,OAAO,SAAQ,EAAE,GAAG,IAAI,CAAC,WAAW;QAC5C,MAAM,sBAAE,mBAAkB,sBAAE,mBAAkB,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa;QAC3E,MAAM,eAAe,MAAM,IAAI,CAAC,eAAe;QAE/C,IAAI,cACF,IAAI;YACF,MAAM,CAAA,GAAA,aAAK,EAAE,oBAAoB,UAAU,cAAc,IAAI,CAAC,OAAO,CAAC,SAAS;QACjF,EAAE,OAAM;QACN,yGAAyG;QAC3G;QAGF,MAAM,MAAM,CAAA,GAAA,yBAAiB,EAAE;gCAC7B;mCACA;sBACA;QACF;QAEA,IAAI,CAAC,cAAc,CAAC,KAAK;QACzB,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI;QAC/B,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI;QAC1B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;QAEtC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IACxB;IAEA,MAAgB,mBAA8D;QAC5E,MAAM,WAAW,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAEpD,IAAI,CAAC,UACH,OAAO,IAAI;QAGb,MAAM,OAAgB,KAAK,KAAK,CAAC;QAEjC,IAAI,CAAC,CAAA,GAAA,yCAAuB,EAAE,OAC5B,MAAM,IAAI,CAAA,GAAA,yCAAgB,AAAD,EAAE,2BAA2B;QAGxD,OAAO;IACT;IAEA,MAAgB,iBAAiB,sBAAwD,EAAE;QACzF,IAAI,CAAC,wBAAwB;YAC3B,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;YAEtC;QACF,CAAC;QAED,MAAM,WAAW,KAAK,SAAS,CAAC;QAChC,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB;IACtD;IAEA,MAAc,WAAW,OAAyB,EAAE;QAClD,IAAI,CAAC,SAAS;YACZ,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;YAEtC;QACF,CAAC;QAED,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW;IAChD;IAEA,MAAc,gBAAgB,YAA8B,EAAE;QAC5D,IAAI,CAAC,cAAc;YACjB,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC;YAEtC;QACF,CAAC;QAED,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB;IACrD;IAEA,MAAc,6BAA6B,QAAiB,EAAmB;QAC7E,MAAM,sBAAsB,MAAM,IAAI,CAAC,eAAe;QAEtD,IAAI,CAAC,qBACH,MAAM,IAAI,CAAA,GAAA,yCAAe,EAAE,qBAAqB;QAGlD,IAAI;YACF,MAAM,iBAAiB,CAAA,GAAA,yCAAkB,EAAE;YAC3C,MAAM,EAAE,OAAO,SAAQ,EAAE,GAAG,IAAI,CAAC,WAAW;YAC5C,MAAM,iBAAE,cAAa,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa;YAClD,MAAM,eAAE,YAAW,gBAAE,aAAY,WAAE,QAAO,SAAE,MAAK,aAAE,UAAS,EAAE,GAC5D,MAAM,CAAA,GAAA,+BAAuB,EAC3B;0BACE;+BACA;gBACA,cAAc;0BACd;YACF,GACA,IAAI,CAAC,OAAO,CAAC,SAAS;YAG1B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,gBAAgB;gBACtC,OAAO;uBACP;gBACA,WAAW,KAAK,KAAK,CAAC,KAAK,GAAG,KAAK,QAAQ;YAC7C;YAEA,MAAM,IAAI,CAAC,kBAAkB;YAC7B,MAAM,IAAI,CAAC,eAAe,CAAC;YAE3B,IAAI,SAAS;gBACX,MAAM,IAAI,CAAC,aAAa,CAAC;gBACzB,MAAM,IAAI,CAAC,UAAU,CAAC;YACxB,CAAC;YAED,OAAO;QACT,EAAE,OAAO,OAAgB;YACvB,MAAM,IAAI,CAAA,GAAA,yCAAgB,AAAD,EAAE,4CAA4C,OAAO;QAChF;IACF;IAEA,MAAc,iBAAiB;QAC7B,MAAM,YAAE,SAAQ,EAAE,GAAG,IAAI,CAAC,WAAW;QACrC,MAAM,oBAAoB,CAAA,GAAA,yCAAmB,EAAE;QAE/C,OAAO,CAAA,GAAA,sBAAc,EAAE,mBAAmB,IAAI,CAAC,OAAO,CAAC,SAAS;IAClE;IAEA,MAAc,sBAAsB;QAClC,MAAM,WAAE,QAAO,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa;QAE5C,OAAO,CAAA,GAAA,yBAAkB,AAAD,EAAE,IAAI,IAAI;IACpC;IAEA,MAAc,cAAc,OAAe,EAAE;QAC3C,MAAM,SAAE,MAAK,EAAE,GAAG,IAAI,CAAC,WAAW;QAClC,MAAM,UAAE,OAAM,EAAE,GAAG,MAAM,IAAI,CAAC,aAAa;QAC3C,MAAM,kBAAkB,MAAM,IAAI,CAAC,kBAAkB;QAErD,IAAI;YACF,MAAM,CAAA,GAAA,oBAAY,EAAE,SAAS,OAAO,QAAQ;QAC9C,EAAE,OAAO,OAAgB;YACvB,MAAM,IAAI,CAAA,GAAA,yCAAgB,AAAD,EAAE,oBAAoB,OAAO;QACxD;IACF;IAEA,MAAc,cAAc,gBAC1B,aAAY,WACZ,QAAO,SACP,MAAK,eACL,YAAW,aACX,UAAS,EACS,EAAE;QACpB,MAAM,IAAI,CAAC,eAAe,CAAC,gBAAgB,IAAI;QAC/C,MAAM,IAAI,CAAC,UAAU,CAAC;QAEtB,8EAA8E;QAC9E,MAAM,iBAAiB,CAAA,GAAA,yCAAmB,AAAD;QACzC,MAAM,YAAY,KAAK,GAAG,KAAK,OAAO;QACtC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,gBAAgB;YAAE,OAAO;mBAAa;uBAAO;QAAU;QAC/E,MAAM,IAAI,CAAC,kBAAkB;IAC/B;IAEA,MAAc,qBAAqB;QACjC,MAAM,OAAoC,CAAC;QAE3C,KAAK,MAAM,CAAC,KAAK,YAAY,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,GAC1D,sDAAsD;QACtD,IAAI,CAAC,IAAI,GAAG;QAGd,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS,CAAC;IACnE;IAEA,MAAc,qBAAqB;QACjC,MAAM,MAAM,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAE/C,IAAI,CAAC,KACH;QAGF,IAAI;YACF,MAAM,OAAgB,KAAK,KAAK,CAAC;YAEjC,IAAI,CAAC,CAAA,GAAA,yCAAoB,EAAE,OACzB;YAEF,IAAI,CAAC,cAAc,CAAC,KAAK;YAEzB,KAAK,MAAM,CAAC,KAAK,YAAY,IAAI,OAAO,OAAO,CAAC,MAC9C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK;QAEjC,EAAE,OAAM,CAAC;IACX;AAEF","sources":["packages/client/src/index.ts","packages/client/src/errors.ts","packages/client/src/types/index.ts","packages/client/src/utils/index.ts","packages/client/src/utils/requester.ts"],"sourcesContent":["import type {\n CodeTokenResponse,\n IdTokenClaims,\n UserInfoResponse,\n InteractionMode,\n} from '@logto/js';\nimport {\n decodeIdToken,\n fetchOidcConfig,\n fetchTokenByAuthorizationCode,\n fetchTokenByRefreshToken,\n fetchUserInfo,\n generateSignInUri,\n generateSignOutUri,\n Prompt,\n revoke,\n verifyAndParseCodeFromCallbackUri,\n verifyIdToken,\n withDefaultScopes,\n} from '@logto/js';\nimport type { Nullable } from '@silverhand/essentials';\nimport { createRemoteJWKSet } from 'jose';\nimport once from 'lodash.once';\n\nimport type { ClientAdapter } from './adapter';\nimport { LogtoClientError } from './errors';\nimport type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types';\nimport { isLogtoAccessTokenMap, isLogtoSignInSessionItem } from './types';\nimport { buildAccessTokenKey, getDiscoveryEndpoint } from './utils';\n\nexport type { IdTokenClaims, LogtoErrorCode, UserInfoResponse, InteractionMode } from '@logto/js';\nexport {\n LogtoError,\n OidcError,\n Prompt,\n LogtoRequestError,\n ReservedScope,\n UserScope,\n} from '@logto/js';\nexport * from './errors';\nexport type { Storage, StorageKey, ClientAdapter } from './adapter';\nexport { createRequester } from './utils';\nexport * from './types';\n\nexport default class LogtoClient {\n protected readonly logtoConfig: LogtoConfig;\n protected readonly getOidcConfig = once(this._getOidcConfig);\n protected readonly getJwtVerifyGetKey = once(this._getJwtVerifyGetKey);\n protected readonly adapter: ClientAdapter;\n protected readonly accessTokenMap = new Map<string, AccessToken>();\n\n constructor(logtoConfig: LogtoConfig, adapter: ClientAdapter) {\n this.logtoConfig = {\n ...logtoConfig,\n prompt: logtoConfig.prompt ?? Prompt.Consent,\n scopes: withDefaultScopes(logtoConfig.scopes).split(' '),\n };\n this.adapter = adapter;\n\n void this.loadAccessTokenMap();\n }\n\n async isAuthenticated() {\n return Boolean(await this.getIdToken());\n }\n\n async getRefreshToken() {\n return this.adapter.storage.getItem('refreshToken');\n }\n\n async getIdToken() {\n return this.adapter.storage.getItem('idToken');\n }\n\n async getAccessToken(resource?: string): Promise<string> {\n if (!(await this.getIdToken())) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const accessTokenKey = buildAccessTokenKey(resource);\n const accessToken = this.accessTokenMap.get(accessTokenKey);\n\n if (accessToken && accessToken.expiresAt > Date.now() / 1000) {\n return accessToken.token;\n }\n\n // Since the access token has expired, delete it from the map.\n if (accessToken) {\n this.accessTokenMap.delete(accessTokenKey);\n }\n\n /**\n * Need to fetch a new access token using refresh token.\n */\n return this.getAccessTokenByRefreshToken(resource);\n }\n\n async getIdTokenClaims(): Promise<IdTokenClaims> {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n return decodeIdToken(idToken);\n }\n\n async fetchUserInfo(): Promise<UserInfoResponse> {\n const { userinfoEndpoint } = await this.getOidcConfig();\n const accessToken = await this.getAccessToken();\n\n if (!accessToken) {\n throw new LogtoClientError('fetch_user_info_failed');\n }\n\n return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);\n }\n\n async signIn(redirectUri: string, interactionMode?: InteractionMode) {\n const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;\n const { authorizationEndpoint } = await this.getOidcConfig();\n const codeVerifier = this.adapter.generateCodeVerifier();\n const codeChallenge = await this.adapter.generateCodeChallenge(codeVerifier);\n const state = this.adapter.generateState();\n\n const signInUri = generateSignInUri({\n authorizationEndpoint,\n clientId,\n redirectUri,\n codeChallenge,\n state,\n scopes,\n resources,\n prompt,\n interactionMode,\n });\n\n await this.setSignInSession({ redirectUri, codeVerifier, state });\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n\n this.adapter.navigate(signInUri);\n }\n\n async isSignInRedirected(url: string): Promise<boolean> {\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n return false;\n }\n const { redirectUri } = signInSession;\n const { origin, pathname } = new URL(url);\n\n return `${origin}${pathname}` === redirectUri;\n }\n\n async handleSignInCallback(callbackUri: string) {\n const { logtoConfig, adapter } = this;\n const { requester } = adapter;\n const signInSession = await this.getSignInSession();\n\n if (!signInSession) {\n throw new LogtoClientError('sign_in_session.not_found');\n }\n\n const { redirectUri, state, codeVerifier } = signInSession;\n const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);\n\n const { appId: clientId } = logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const codeTokenResponse = await fetchTokenByAuthorizationCode(\n {\n clientId,\n tokenEndpoint,\n redirectUri,\n codeVerifier,\n code,\n },\n requester\n );\n\n await this.verifyIdToken(codeTokenResponse.idToken);\n await this.saveCodeToken(codeTokenResponse);\n await this.setSignInSession(null);\n }\n\n async signOut(postLogoutRedirectUri?: string) {\n const idToken = await this.getIdToken();\n\n if (!idToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n const { appId: clientId } = this.logtoConfig;\n const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();\n const refreshToken = await this.getRefreshToken();\n\n if (refreshToken) {\n try {\n await revoke(revocationEndpoint, clientId, refreshToken, this.adapter.requester);\n } catch {\n // Do nothing at this point, as we don't want to break the sign-out flow even if the revocation is failed\n }\n }\n\n const url = generateSignOutUri({\n endSessionEndpoint,\n postLogoutRedirectUri,\n clientId,\n });\n\n this.accessTokenMap.clear();\n await this.setRefreshToken(null);\n await this.setIdToken(null);\n await this.adapter.storage.removeItem('accessToken');\n\n this.adapter.navigate(url);\n }\n\n protected async getSignInSession(): Promise<Nullable<LogtoSignInSessionItem>> {\n const jsonItem = await this.adapter.storage.getItem('signInSession');\n\n if (!jsonItem) {\n return null;\n }\n\n const item: unknown = JSON.parse(jsonItem);\n\n if (!isLogtoSignInSessionItem(item)) {\n throw new LogtoClientError('sign_in_session.invalid');\n }\n\n return item;\n }\n\n protected async setSignInSession(logtoSignInSessionItem: Nullable<LogtoSignInSessionItem>) {\n if (!logtoSignInSessionItem) {\n await this.adapter.storage.removeItem('signInSession');\n\n return;\n }\n\n const jsonItem = JSON.stringify(logtoSignInSessionItem);\n await this.adapter.storage.setItem('signInSession', jsonItem);\n }\n\n private async setIdToken(idToken: Nullable<string>) {\n if (!idToken) {\n await this.adapter.storage.removeItem('idToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('idToken', idToken);\n }\n\n private async setRefreshToken(refreshToken: Nullable<string>) {\n if (!refreshToken) {\n await this.adapter.storage.removeItem('refreshToken');\n\n return;\n }\n\n await this.adapter.storage.setItem('refreshToken', refreshToken);\n }\n\n private async getAccessTokenByRefreshToken(resource?: string): Promise<string> {\n const currentRefreshToken = await this.getRefreshToken();\n\n if (!currentRefreshToken) {\n throw new LogtoClientError('not_authenticated');\n }\n\n try {\n const accessTokenKey = buildAccessTokenKey(resource);\n const { appId: clientId } = this.logtoConfig;\n const { tokenEndpoint } = await this.getOidcConfig();\n const { accessToken, refreshToken, idToken, scope, expiresIn } =\n await fetchTokenByRefreshToken(\n {\n clientId,\n tokenEndpoint,\n refreshToken: currentRefreshToken,\n resource,\n },\n this.adapter.requester\n );\n\n this.accessTokenMap.set(accessTokenKey, {\n token: accessToken,\n scope,\n expiresAt: Math.round(Date.now() / 1000) + expiresIn,\n });\n\n await this.saveAccessTokenMap();\n await this.setRefreshToken(refreshToken);\n\n if (idToken) {\n await this.verifyIdToken(idToken);\n await this.setIdToken(idToken);\n }\n\n return accessToken;\n } catch (error: unknown) {\n throw new LogtoClientError('get_access_token_by_refresh_token_failed', error);\n }\n }\n\n private async _getOidcConfig() {\n const { endpoint } = this.logtoConfig;\n const discoveryEndpoint = getDiscoveryEndpoint(endpoint);\n\n return fetchOidcConfig(discoveryEndpoint, this.adapter.requester);\n }\n\n private async _getJwtVerifyGetKey() {\n const { jwksUri } = await this.getOidcConfig();\n\n return createRemoteJWKSet(new URL(jwksUri));\n }\n\n private async verifyIdToken(idToken: string) {\n const { appId } = this.logtoConfig;\n const { issuer } = await this.getOidcConfig();\n const jwtVerifyGetKey = await this.getJwtVerifyGetKey();\n\n try {\n await verifyIdToken(idToken, appId, issuer, jwtVerifyGetKey);\n } catch (error: unknown) {\n throw new LogtoClientError('invalid_id_token', error);\n }\n }\n\n private async saveCodeToken({\n refreshToken,\n idToken,\n scope,\n accessToken,\n expiresIn,\n }: CodeTokenResponse) {\n await this.setRefreshToken(refreshToken ?? null);\n await this.setIdToken(idToken);\n\n // NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)\n const accessTokenKey = buildAccessTokenKey();\n const expiresAt = Date.now() / 1000 + expiresIn;\n this.accessTokenMap.set(accessTokenKey, { token: accessToken, scope, expiresAt });\n await this.saveAccessTokenMap();\n }\n\n private async saveAccessTokenMap() {\n const data: Record<string, AccessToken> = {};\n\n for (const [key, accessToken] of this.accessTokenMap.entries()) {\n // eslint-disable-next-line @silverhand/fp/no-mutation\n data[key] = accessToken;\n }\n\n await this.adapter.storage.setItem('accessToken', JSON.stringify(data));\n }\n\n private async loadAccessTokenMap() {\n const raw = await this.adapter.storage.getItem('accessToken');\n\n if (!raw) {\n return;\n }\n\n try {\n const json: unknown = JSON.parse(raw);\n\n if (!isLogtoAccessTokenMap(json)) {\n return;\n }\n this.accessTokenMap.clear();\n\n for (const [key, accessToken] of Object.entries(json)) {\n this.accessTokenMap.set(key, accessToken);\n }\n } catch {}\n }\n // FIXME: @charles @sijie\n}\n","import type { NormalizeKeyPaths } from '@silverhand/essentials';\nimport get from 'lodash.get';\n\nconst logtoClientErrorCodes = Object.freeze({\n sign_in_session: {\n invalid: 'Invalid sign-in session.',\n not_found: 'Sign-in session not found.',\n },\n not_authenticated: 'Not authenticated.',\n get_access_token_by_refresh_token_failed: 'Failed to get access token by refresh token.',\n fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.',\n invalid_id_token: 'Invalid id token.',\n});\n\nexport type LogtoClientErrorCode = NormalizeKeyPaths<typeof logtoClientErrorCodes>;\n\nconst getMessageByErrorCode = (errorCode: LogtoClientErrorCode): string => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const message = get(logtoClientErrorCodes, errorCode);\n\n if (typeof message === 'string') {\n return message;\n }\n\n return errorCode;\n};\n\nexport class LogtoClientError extends Error {\n code: LogtoClientErrorCode;\n data: unknown;\n\n constructor(code: LogtoClientErrorCode, data?: unknown) {\n super(getMessageByErrorCode(code));\n this.code = code;\n this.data = data;\n }\n}\n","import type { Prompt } from '@logto/js';\nimport { isArbitraryObject } from '@logto/js';\n\nexport type LogtoConfig = {\n endpoint: string;\n appId: string;\n appSecret?: string;\n scopes?: string[];\n resources?: string[];\n prompt?: Prompt;\n};\n\nexport type AccessToken = {\n token: string;\n scope: string;\n expiresAt: number;\n};\n\nexport const isLogtoSignInSessionItem = (data: unknown): data is LogtoSignInSessionItem => {\n if (!isArbitraryObject(data)) {\n return false;\n }\n\n return ['redirectUri', 'codeVerifier', 'state'].every((key) => typeof data[key] === 'string');\n};\n\nexport const isLogtoAccessTokenMap = (data: unknown): data is Record<string, AccessToken> => {\n if (!isArbitraryObject(data)) {\n return false;\n }\n\n return Object.values(data).every((value) => {\n if (!isArbitraryObject(value)) {\n return false;\n }\n\n return (\n typeof value.token === 'string' &&\n typeof value.scope === 'string' &&\n typeof value.expiresAt === 'number'\n );\n });\n};\n\nexport type LogtoSignInSessionItem = {\n redirectUri: string;\n codeVerifier: string;\n state: string;\n};\n","import { discoveryPath } from '@logto/js';\n\nexport * from './requester';\n\nexport const buildAccessTokenKey = (resource = '', scopes: string[] = []): string =>\n `${scopes.slice().sort().join(' ')}@${resource}`;\n\nexport const getDiscoveryEndpoint = (endpoint: string): string =>\n new URL(discoveryPath, endpoint).toString();\n","import type { Requester } from '@logto/js';\nimport { LogtoError, LogtoRequestError, isLogtoRequestError } from '@logto/js';\n\nexport const createRequester = (fetchFunction: typeof fetch): Requester => {\n return async <T>(...args: Parameters<typeof fetch>): Promise<T> => {\n const response = await fetchFunction(...args);\n\n if (!response.ok) {\n const responseJson = await response.json();\n\n if (!isLogtoRequestError(responseJson)) {\n throw new LogtoError('unexpected_response_error', responseJson);\n }\n\n // Expected request error from server\n const { code, message } = responseJson;\n throw new LogtoRequestError(code, message);\n }\n\n return response.json();\n };\n};\n"],"names":[],"version":3,"file":"module.mjs.map"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"source": "./src/index.ts",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"prepack": "pnpm test"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@logto/js": "^1.
|
|
32
|
+
"@logto/js": "^1.1.0",
|
|
33
33
|
"@silverhand/essentials": "^1.2.1",
|
|
34
34
|
"camelcase-keys": "^7.0.1",
|
|
35
35
|
"jose": "^4.3.8",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"publishConfig": {
|
|
67
67
|
"access": "public"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "f24174a6f840b8db968ad3886878d1b6e92b1b9d"
|
|
70
70
|
}
|