@shware/security 4.1.0 → 5.0.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/README.md +110 -99
- package/dist/auth/index.cjs +41 -34
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +6 -6
- package/dist/auth/index.d.cts.map +1 -1
- package/dist/auth/index.d.mts +6 -6
- package/dist/auth/index.d.mts.map +1 -1
- package/dist/auth/index.mjs +41 -34
- package/dist/auth/index.mjs.map +1 -1
- package/dist/auth/types.d.cts +12 -7
- package/dist/auth/types.d.cts.map +1 -1
- package/dist/auth/types.d.mts +12 -7
- package/dist/auth/types.d.mts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/oauth2/client.cjs +111 -48
- package/dist/oauth2/client.cjs.map +1 -1
- package/dist/oauth2/client.d.cts +51 -13
- package/dist/oauth2/client.d.cts.map +1 -1
- package/dist/oauth2/client.d.mts +51 -13
- package/dist/oauth2/client.d.mts.map +1 -1
- package/dist/oauth2/client.mjs +111 -48
- package/dist/oauth2/client.mjs.map +1 -1
- package/dist/oauth2/provider/apple-client-secret.cjs +3 -3
- package/dist/oauth2/provider/apple-client-secret.cjs.map +1 -1
- package/dist/oauth2/provider/apple-client-secret.d.cts +3 -3
- package/dist/oauth2/provider/apple-client-secret.d.mts +3 -3
- package/dist/oauth2/provider/apple-client-secret.mjs +3 -3
- package/dist/oauth2/provider/apple-client-secret.mjs.map +1 -1
- package/dist/oauth2/provider/apple.cjs +9 -3
- package/dist/oauth2/provider/apple.cjs.map +1 -1
- package/dist/oauth2/provider/apple.d.cts.map +1 -1
- package/dist/oauth2/provider/apple.d.mts.map +1 -1
- package/dist/oauth2/provider/apple.mjs +9 -3
- package/dist/oauth2/provider/apple.mjs.map +1 -1
- package/dist/oauth2/provider/common.cjs +13 -9
- package/dist/oauth2/provider/common.cjs.map +1 -1
- package/dist/oauth2/provider/common.d.cts +13 -2
- package/dist/oauth2/provider/common.d.cts.map +1 -1
- package/dist/oauth2/provider/common.d.mts +13 -2
- package/dist/oauth2/provider/common.d.mts.map +1 -1
- package/dist/oauth2/provider/common.mjs +13 -9
- package/dist/oauth2/provider/common.mjs.map +1 -1
- package/dist/oauth2/provider/facebook.cjs +59 -35
- package/dist/oauth2/provider/facebook.cjs.map +1 -1
- package/dist/oauth2/provider/facebook.d.cts.map +1 -1
- package/dist/oauth2/provider/facebook.d.mts.map +1 -1
- package/dist/oauth2/provider/facebook.mjs +59 -35
- package/dist/oauth2/provider/facebook.mjs.map +1 -1
- package/dist/oauth2/provider/google.cjs +31 -32
- package/dist/oauth2/provider/google.cjs.map +1 -1
- package/dist/oauth2/provider/google.d.cts.map +1 -1
- package/dist/oauth2/provider/google.d.mts.map +1 -1
- package/dist/oauth2/provider/google.mjs +31 -32
- package/dist/oauth2/provider/google.mjs.map +1 -1
- package/dist/oauth2/provider/index.cjs +1 -1
- package/dist/oauth2/provider/index.mjs +1 -1
- package/dist/oauth2/provider/wechat.cjs +1 -0
- package/dist/oauth2/provider/wechat.cjs.map +1 -1
- package/dist/oauth2/provider/wechat.d.cts.map +1 -1
- package/dist/oauth2/provider/wechat.d.mts.map +1 -1
- package/dist/oauth2/provider/wechat.mjs +1 -0
- package/dist/oauth2/provider/wechat.mjs.map +1 -1
- package/dist/oauth2/types.cjs.map +1 -1
- package/dist/oauth2/types.d.cts +69 -34
- package/dist/oauth2/types.d.cts.map +1 -1
- package/dist/oauth2/types.d.mts +69 -34
- package/dist/oauth2/types.d.mts.map +1 -1
- package/dist/oauth2/types.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.cjs","names":["object","optional","string"],"sources":["../../src/oauth2/client.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { object, optional, string } from 'zod/v4-mini';\nimport type {\n AuthorizationResponse,\n NativeCredential,\n OAuth2ClientConfig,\n OAuth2Token,\n OidcToken,\n PkceParameters,\n} from './types';\n\nexport const oauth2RedirectQuerySchema = object({\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n // Apple's form_post: the user's name as JSON, first authorization only.\n user: optional(string()),\n});\n\nexport const googleOneTapSchema = object({\n // login_uri\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n nonce: optional(string()),\n g_csrf_token: optional(string()),\n hd: optional(string()),\n // callback\n credential: optional(string()),\n});\n\nexport class OAuth2Client {\n private readonly config: OAuth2ClientConfig;\n constructor(config: OAuth2ClientConfig) {\n this.config = config;\n }\n\n get baseUri() {\n return this.config.baseUri;\n }\n\n get successUri() {\n return this.config.successUri;\n }\n\n get errorUri() {\n return this.config.errorUri;\n }\n\n private async getClientConfig(registrationId: string) {\n const registrations =\n typeof this.config.registration === 'function'\n ? await this.config.registration()\n : this.config.registration;\n\n const registration = registrations[registrationId];\n invariant(registration, `Registration ${registrationId} not found`);\n\n const provider = this.config.provider[registration.provider ?? registrationId];\n invariant(provider, `Provider ${registration.provider ?? registrationId} not found`);\n return { registration, provider };\n }\n\n async createAuthorizationUri({\n registrationId,\n state,\n pkce,\n }: {\n registrationId: string;\n state: string;\n pkce?: PkceParameters;\n }): Promise<URL> {\n const { provider, registration } = await this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { scope, clientId, redirectUri } = registration;\n return provider.createAuthorizationUri({\n pkce,\n state,\n scope,\n clientId,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async exchangeAuthorizationCode({\n registrationId,\n code,\n pkce,\n }: {\n registrationId: string;\n code: string;\n pkce?: PkceParameters;\n }): Promise<OAuth2Token> {\n const { provider, registration } = await this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n return provider.exchangeAuthorizationCode({\n code,\n pkce,\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async getUserInfo({\n registrationId,\n token,\n response,\n }: {\n registrationId: string;\n token: OAuth2Token | OidcToken;\n response?: AuthorizationResponse;\n }) {\n const { provider } = await this.getClientConfig(registrationId);\n return provider.getUserInfo(token, response);\n }\n\n async refreshAccessToken({\n registrationId,\n refreshToken,\n }: {\n registrationId: string;\n refreshToken: string;\n }): Promise<OAuth2Token> {\n const { provider, registration } = await this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.refreshAccessToken, 'Provider does not support refreshAccessToken');\n return provider.refreshAccessToken({ refreshToken, clientId, clientSecret });\n }\n\n async revokeToken(registrationId: string, token: string) {\n const { provider, registration } = await this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.revokeToken, 'Provider does not support revokeToken');\n await provider.revokeToken({ token, clientId, clientSecret });\n }\n\n async loginOAuth2Native({\n registrationId,\n credentials,\n pkce,\n }: {\n registrationId: string;\n credentials: NativeCredential;\n pkce?: PkceParameters;\n }) {\n const { provider, registration } = await this.getClientConfig(registrationId);\n invariant(provider.loginOAuth2Native, 'Provider does not support loginOAuth2Native');\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n\n return provider.loginOAuth2Native({\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n credentials,\n pkce,\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.cjs","names":["object","optional","string","OAuth2Error"],"sources":["../../src/oauth2/client.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { object, optional, string } from 'zod/v4-mini';\nimport { OAuth2Error } from './error';\nimport type {\n AuthorizationResponse,\n ClientSecret,\n NativeClient,\n NativeCredential,\n OAuth2ClientConfig,\n OAuth2Token,\n OidcToken,\n OneTapProvider,\n PkceParameters,\n Provider,\n ProviderConfig,\n WebClient,\n} from './types';\n\nexport const oauth2RedirectQuerySchema = object({\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n // Apple's form_post: the user's name as JSON, first authorization only.\n user: optional(string()),\n});\n\nexport const googleOneTapSchema = object({\n // login_uri\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n nonce: optional(string()),\n g_csrf_token: optional(string()),\n hd: optional(string()),\n // callback\n credential: optional(string()),\n});\n\nexport class OAuth2Client {\n private readonly config: OAuth2ClientConfig;\n constructor(config: OAuth2ClientConfig) {\n this.config = config;\n }\n\n get baseUri() {\n return this.config.baseUri;\n }\n\n get successUri() {\n return this.config.successUri;\n }\n\n get errorUri() {\n return this.config.errorUri;\n }\n\n /** The provider key comes from the request path, so an unknown one is the caller's error. */\n private async getProviderConfig(provider: string): Promise<ProviderConfig> {\n const providers =\n typeof this.config.providers === 'function'\n ? await this.config.providers()\n : this.config.providers;\n const config = Object.hasOwn(providers, provider) ? providers[provider] : undefined;\n if (!config) throw new OAuth2Error(400, 'invalid_request', `Unknown provider: ${provider}`);\n return config;\n }\n\n private defaultRedirectUri(provider: string) {\n return `${this.config.baseUri}/login/oauth2/code/${provider}`;\n }\n\n /** The redirect flow and One Tap sign in as the provider's web client. */\n private async getWebClient(provider: string) {\n const config = await this.getProviderConfig(provider);\n if (!config.web) {\n throw new OAuth2Error(400, 'invalid_request', `Provider ${provider} has no web client`);\n }\n return { implementation: config.provider, client: config.web };\n }\n\n /**\n * Any configured client of the provider, looked up by id: refresh and revoke use the client a\n * token was issued to, and the native login the one the app names. The web client counts too:\n * an Android app's server auth code and a Facebook app's tokens are issued to it.\n */\n private async getClient(provider: string, clientId: string) {\n const config = await this.getProviderConfig(provider);\n const client = [config.web, ...(config.native ?? [])].find((c) => c?.clientId === clientId);\n if (!client) {\n throw new OAuth2Error(400, 'invalid_client', `${clientId} is not a client of ${provider}`);\n }\n return { implementation: config.provider, client };\n }\n\n /**\n * The client a native login signs in as: the one the app names with `client_id`, which must be\n * configured; without one, the only native client, or the web client when there is no native\n * one. Several native clients and no `client_id` is ambiguous and refused rather than guessed.\n */\n private async getNativeClient(provider: string, clientId: string | undefined) {\n if (clientId !== undefined) return this.getClient(provider, clientId);\n const config = await this.getProviderConfig(provider);\n const native = config.native ?? [];\n const client: WebClient | NativeClient | undefined =\n native.length === 0 ? config.web : native.length === 1 ? native[0] : undefined;\n if (!client) {\n throw new OAuth2Error(400, 'invalid_request', `client_id is required for ${provider}`);\n }\n return { implementation: config.provider, client };\n }\n\n private async resolveSecret(secret: ClientSecret | undefined, clientId: string) {\n return typeof secret === 'function' ? secret(clientId) : secret;\n }\n\n async createAuthorizationUri({\n provider,\n state,\n pkce,\n }: {\n provider: string;\n state: string;\n pkce?: PkceParameters;\n }): Promise<URL> {\n const { implementation, client } = await this.getWebClient(provider);\n return implementation.createAuthorizationUri({\n pkce,\n state,\n scope: client.scope,\n clientId: client.clientId,\n redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider),\n });\n }\n\n /** Exchanges a code issued to the provider's web client: the redirect flow and One Tap. */\n async exchangeAuthorizationCode({\n provider,\n code,\n pkce,\n }: {\n provider: string;\n code: string;\n pkce?: PkceParameters;\n }): Promise<{ clientId: string; token: OAuth2Token }> {\n const { implementation, client } = await this.getWebClient(provider);\n const { clientId } = client;\n const token = await implementation.exchangeAuthorizationCode({\n code,\n pkce,\n clientId,\n clientSecret: await this.resolveSecret(client.clientSecret, clientId),\n redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider),\n });\n return { clientId, token };\n }\n\n /** `clientId` is the client the token was issued to, as `exchangeAuthorizationCode` returns it. */\n async getUserInfo({\n provider,\n clientId,\n token,\n response,\n }: {\n provider: string;\n clientId: string;\n token: OAuth2Token | OidcToken;\n response?: AuthorizationResponse;\n }) {\n const { implementation } = await this.getClient(provider, clientId);\n return implementation.getUserInfo(token, { clientId, response });\n }\n\n /** Verifies a Google One Tap / FedCM credential, which is issued to the web client. */\n async getTokenInfo({ provider, credential }: { provider: string; credential: string }) {\n const { implementation, client } = await this.getWebClient(provider);\n if (!isOneTapProvider(implementation)) {\n throw new OAuth2Error(400, 'invalid_request', `Provider ${provider} has no One Tap`);\n }\n const { clientId } = client;\n return { clientId, ...(await implementation.getTokenInfo(credential, clientId)) };\n }\n\n async refreshAccessToken({\n provider,\n clientId,\n refreshToken,\n }: {\n provider: string;\n clientId: string;\n refreshToken: string;\n }): Promise<OAuth2Token> {\n const { implementation, client } = await this.getClient(provider, clientId);\n invariant(implementation.refreshAccessToken, 'Provider does not support refreshAccessToken');\n const clientSecret = await this.resolveSecret(client.clientSecret, clientId);\n return implementation.refreshAccessToken({ refreshToken, clientId, clientSecret });\n }\n\n async revokeToken({\n provider,\n clientId,\n token,\n }: {\n provider: string;\n clientId: string;\n token: string;\n }) {\n const { implementation, client } = await this.getClient(provider, clientId);\n invariant(implementation.revokeToken, 'Provider does not support revokeToken');\n const clientSecret = await this.resolveSecret(client.clientSecret, clientId);\n await implementation.revokeToken({ token, clientId, clientSecret });\n }\n\n async loginOAuth2Native({\n provider,\n credentials,\n pkce,\n }: {\n provider: string;\n credentials: NativeCredential;\n pkce?: PkceParameters;\n }) {\n const { implementation, client } = await this.getNativeClient(provider, credentials.client_id);\n invariant(implementation.loginOAuth2Native, 'Provider does not support loginOAuth2Native');\n const { clientId } = client;\n\n const result = await implementation.loginOAuth2Native({\n clientId,\n clientSecret: await this.resolveSecret(client.clientSecret, clientId),\n redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider),\n credentials,\n pkce,\n });\n return { clientId, ...result };\n }\n}\n\nfunction isOneTapProvider(provider: Provider): provider is OneTapProvider {\n return typeof (provider as Partial<OneTapProvider>).getTokenInfo === 'function';\n}\n"],"mappings":"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"}
|
package/dist/oauth2/client.d.cts
CHANGED
|
@@ -25,34 +25,72 @@ export declare class OAuth2Client {
|
|
|
25
25
|
get baseUri(): string;
|
|
26
26
|
get successUri(): string;
|
|
27
27
|
get errorUri(): string;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
/** The provider key comes from the request path, so an unknown one is the caller's error. */
|
|
29
|
+
private getProviderConfig;
|
|
30
|
+
private defaultRedirectUri;
|
|
31
|
+
/** The redirect flow and One Tap sign in as the provider's web client. */
|
|
32
|
+
private getWebClient;
|
|
33
|
+
/**
|
|
34
|
+
* Any configured client of the provider, looked up by id: refresh and revoke use the client a
|
|
35
|
+
* token was issued to, and the native login the one the app names. The web client counts too:
|
|
36
|
+
* an Android app's server auth code and a Facebook app's tokens are issued to it.
|
|
37
|
+
*/
|
|
38
|
+
private getClient;
|
|
39
|
+
/**
|
|
40
|
+
* The client a native login signs in as: the one the app names with `client_id`, which must be
|
|
41
|
+
* configured; without one, the only native client, or the web client when there is no native
|
|
42
|
+
* one. Several native clients and no `client_id` is ambiguous and refused rather than guessed.
|
|
43
|
+
*/
|
|
44
|
+
private getNativeClient;
|
|
45
|
+
private resolveSecret;
|
|
46
|
+
createAuthorizationUri({ provider, state, pkce }: {
|
|
47
|
+
provider: string;
|
|
31
48
|
state: string;
|
|
32
49
|
pkce?: PkceParameters;
|
|
33
50
|
}): Promise<URL>;
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
/** Exchanges a code issued to the provider's web client: the redirect flow and One Tap. */
|
|
52
|
+
exchangeAuthorizationCode({ provider, code, pkce }: {
|
|
53
|
+
provider: string;
|
|
36
54
|
code: string;
|
|
37
55
|
pkce?: PkceParameters;
|
|
38
|
-
}): Promise<
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
}): Promise<{
|
|
57
|
+
clientId: string;
|
|
58
|
+
token: OAuth2Token;
|
|
59
|
+
}>;
|
|
60
|
+
/** `clientId` is the client the token was issued to, as `exchangeAuthorizationCode` returns it. */
|
|
61
|
+
getUserInfo({ provider, clientId, token, response }: {
|
|
62
|
+
provider: string;
|
|
63
|
+
clientId: string;
|
|
41
64
|
token: OAuth2Token | OidcToken;
|
|
42
65
|
response?: AuthorizationResponse;
|
|
43
66
|
}): Promise<UserInfo<unknown>>;
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
/** Verifies a Google One Tap / FedCM credential, which is issued to the web client. */
|
|
68
|
+
getTokenInfo({ provider, credential }: {
|
|
69
|
+
provider: string;
|
|
70
|
+
credential: string;
|
|
71
|
+
}): Promise<{
|
|
72
|
+
token: OAuth2Token;
|
|
73
|
+
userInfo: UserInfo;
|
|
74
|
+
clientId: string;
|
|
75
|
+
}>;
|
|
76
|
+
refreshAccessToken({ provider, clientId, refreshToken }: {
|
|
77
|
+
provider: string;
|
|
78
|
+
clientId: string;
|
|
46
79
|
refreshToken: string;
|
|
47
80
|
}): Promise<OAuth2Token>;
|
|
48
|
-
revokeToken(
|
|
49
|
-
|
|
50
|
-
|
|
81
|
+
revokeToken({ provider, clientId, token }: {
|
|
82
|
+
provider: string;
|
|
83
|
+
clientId: string;
|
|
84
|
+
token: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
loginOAuth2Native({ provider, credentials, pkce }: {
|
|
87
|
+
provider: string;
|
|
51
88
|
credentials: NativeCredential;
|
|
52
89
|
pkce?: PkceParameters;
|
|
53
90
|
}): Promise<{
|
|
54
91
|
token: OAuth2Token;
|
|
55
92
|
userInfo: UserInfo;
|
|
93
|
+
clientId: string;
|
|
56
94
|
}>;
|
|
57
95
|
}
|
|
58
96
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.cts","names":["_$__types0.UserInfo"],"sources":["../../src/oauth2/client.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"client.d.cts","names":["_$__types0.UserInfo"],"sources":["../../src/oauth2/client.ts"],"mappings":";;qBAkBa,8CAAyB;;;;;;;yBAQpC;qBAEW,uCAAkB;;;;;;;;;;yBAY7B;qBAEW;mBACM;EACjB,YAAY,QAAQ;MAIhB;MAIA;MAIA;;UAKU;UAUN;;UAKM;;;;;;UAaA;;;;;;UAcA;UAYA;EAIR,yBACJ,UACA,OACA;IAEA;IACA;IACA,OAAO;MACL,QAAQ;;EAYN,4BACJ,UACA,MACA;IAEA;IACA;IACA,OAAO;MACL;IAAU;IAAkB,OAAO;;;EAcjC,cACJ,UACA,UACA,OACA;IAEA;IACA;IACA,OAAO,cAAc;IACrB,WAAW;MACZ,QADYA;;EAOP,eAAe,UAAU;IAAgB;IAAkB;MAAoB;;;;;EAS/E,qBACJ,UACA,UACA;IAEA;IACA;IACA;MACE,QAAQ;EAON,cACJ,UACA,UACA;IAEA;IACA;IACA;MACD;EAOK,oBACJ,UACA,aACA;IAEA;IACA,aAAa;IACb,OAAO;MACR"}
|
package/dist/oauth2/client.d.mts
CHANGED
|
@@ -25,34 +25,72 @@ export declare class OAuth2Client {
|
|
|
25
25
|
get baseUri(): string;
|
|
26
26
|
get successUri(): string;
|
|
27
27
|
get errorUri(): string;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
/** The provider key comes from the request path, so an unknown one is the caller's error. */
|
|
29
|
+
private getProviderConfig;
|
|
30
|
+
private defaultRedirectUri;
|
|
31
|
+
/** The redirect flow and One Tap sign in as the provider's web client. */
|
|
32
|
+
private getWebClient;
|
|
33
|
+
/**
|
|
34
|
+
* Any configured client of the provider, looked up by id: refresh and revoke use the client a
|
|
35
|
+
* token was issued to, and the native login the one the app names. The web client counts too:
|
|
36
|
+
* an Android app's server auth code and a Facebook app's tokens are issued to it.
|
|
37
|
+
*/
|
|
38
|
+
private getClient;
|
|
39
|
+
/**
|
|
40
|
+
* The client a native login signs in as: the one the app names with `client_id`, which must be
|
|
41
|
+
* configured; without one, the only native client, or the web client when there is no native
|
|
42
|
+
* one. Several native clients and no `client_id` is ambiguous and refused rather than guessed.
|
|
43
|
+
*/
|
|
44
|
+
private getNativeClient;
|
|
45
|
+
private resolveSecret;
|
|
46
|
+
createAuthorizationUri({ provider, state, pkce }: {
|
|
47
|
+
provider: string;
|
|
31
48
|
state: string;
|
|
32
49
|
pkce?: PkceParameters;
|
|
33
50
|
}): Promise<URL>;
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
/** Exchanges a code issued to the provider's web client: the redirect flow and One Tap. */
|
|
52
|
+
exchangeAuthorizationCode({ provider, code, pkce }: {
|
|
53
|
+
provider: string;
|
|
36
54
|
code: string;
|
|
37
55
|
pkce?: PkceParameters;
|
|
38
|
-
}): Promise<
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
}): Promise<{
|
|
57
|
+
clientId: string;
|
|
58
|
+
token: OAuth2Token;
|
|
59
|
+
}>;
|
|
60
|
+
/** `clientId` is the client the token was issued to, as `exchangeAuthorizationCode` returns it. */
|
|
61
|
+
getUserInfo({ provider, clientId, token, response }: {
|
|
62
|
+
provider: string;
|
|
63
|
+
clientId: string;
|
|
41
64
|
token: OAuth2Token | OidcToken;
|
|
42
65
|
response?: AuthorizationResponse;
|
|
43
66
|
}): Promise<UserInfo<unknown>>;
|
|
44
|
-
|
|
45
|
-
|
|
67
|
+
/** Verifies a Google One Tap / FedCM credential, which is issued to the web client. */
|
|
68
|
+
getTokenInfo({ provider, credential }: {
|
|
69
|
+
provider: string;
|
|
70
|
+
credential: string;
|
|
71
|
+
}): Promise<{
|
|
72
|
+
token: OAuth2Token;
|
|
73
|
+
userInfo: UserInfo;
|
|
74
|
+
clientId: string;
|
|
75
|
+
}>;
|
|
76
|
+
refreshAccessToken({ provider, clientId, refreshToken }: {
|
|
77
|
+
provider: string;
|
|
78
|
+
clientId: string;
|
|
46
79
|
refreshToken: string;
|
|
47
80
|
}): Promise<OAuth2Token>;
|
|
48
|
-
revokeToken(
|
|
49
|
-
|
|
50
|
-
|
|
81
|
+
revokeToken({ provider, clientId, token }: {
|
|
82
|
+
provider: string;
|
|
83
|
+
clientId: string;
|
|
84
|
+
token: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
loginOAuth2Native({ provider, credentials, pkce }: {
|
|
87
|
+
provider: string;
|
|
51
88
|
credentials: NativeCredential;
|
|
52
89
|
pkce?: PkceParameters;
|
|
53
90
|
}): Promise<{
|
|
54
91
|
token: OAuth2Token;
|
|
55
92
|
userInfo: UserInfo;
|
|
93
|
+
clientId: string;
|
|
56
94
|
}>;
|
|
57
95
|
}
|
|
58
96
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.mts","names":["_$__types0.UserInfo"],"sources":["../../src/oauth2/client.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"client.d.mts","names":["_$__types0.UserInfo"],"sources":["../../src/oauth2/client.ts"],"mappings":";;qBAkBa,8CAAyB;;;;;;;yBAQpC;qBAEW,uCAAkB;;;;;;;;;;yBAY7B;qBAEW;mBACM;EACjB,YAAY,QAAQ;MAIhB;MAIA;MAIA;;UAKU;UAUN;;UAKM;;;;;;UAaA;;;;;;UAcA;UAYA;EAIR,yBACJ,UACA,OACA;IAEA;IACA;IACA,OAAO;MACL,QAAQ;;EAYN,4BACJ,UACA,MACA;IAEA;IACA;IACA,OAAO;MACL;IAAU;IAAkB,OAAO;;;EAcjC,cACJ,UACA,UACA,OACA;IAEA;IACA;IACA,OAAO,cAAc;IACrB,WAAW;MACZ,QADYA;;EAOP,eAAe,UAAU;IAAgB;IAAkB;MAAoB;;;;;EAS/E,qBACJ,UACA,UACA;IAEA;IACA;IACA;MACE,QAAQ;EAON,cACJ,UACA,UACA;IAEA;IACA;IACA;MACD;EAOK,oBACJ,UACA,aACA;IAEA;IACA,aAAa;IACb,OAAO;MACR"}
|
package/dist/oauth2/client.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OAuth2Error } from "./error.mjs";
|
|
1
2
|
import { invariant } from "@shware/utils";
|
|
2
3
|
import { object, optional, string } from "zod/v4-mini";
|
|
3
4
|
//#region src/oauth2/client.ts
|
|
@@ -34,78 +35,140 @@ var OAuth2Client = class {
|
|
|
34
35
|
get errorUri() {
|
|
35
36
|
return this.config.errorUri;
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
38
|
+
/** The provider key comes from the request path, so an unknown one is the caller's error. */
|
|
39
|
+
async getProviderConfig(provider) {
|
|
40
|
+
const providers = typeof this.config.providers === "function" ? await this.config.providers() : this.config.providers;
|
|
41
|
+
const config = Object.hasOwn(providers, provider) ? providers[provider] : void 0;
|
|
42
|
+
if (!config) throw new OAuth2Error(400, "invalid_request", `Unknown provider: ${provider}`);
|
|
43
|
+
return config;
|
|
44
|
+
}
|
|
45
|
+
defaultRedirectUri(provider) {
|
|
46
|
+
return `${this.config.baseUri}/login/oauth2/code/${provider}`;
|
|
47
|
+
}
|
|
48
|
+
/** The redirect flow and One Tap sign in as the provider's web client. */
|
|
49
|
+
async getWebClient(provider) {
|
|
50
|
+
const config = await this.getProviderConfig(provider);
|
|
51
|
+
if (!config.web) throw new OAuth2Error(400, "invalid_request", `Provider ${provider} has no web client`);
|
|
52
|
+
return {
|
|
53
|
+
implementation: config.provider,
|
|
54
|
+
client: config.web
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Any configured client of the provider, looked up by id: refresh and revoke use the client a
|
|
59
|
+
* token was issued to, and the native login the one the app names. The web client counts too:
|
|
60
|
+
* an Android app's server auth code and a Facebook app's tokens are issued to it.
|
|
61
|
+
*/
|
|
62
|
+
async getClient(provider, clientId) {
|
|
63
|
+
const config = await this.getProviderConfig(provider);
|
|
64
|
+
const client = [config.web, ...config.native ?? []].find((c) => c?.clientId === clientId);
|
|
65
|
+
if (!client) throw new OAuth2Error(400, "invalid_client", `${clientId} is not a client of ${provider}`);
|
|
42
66
|
return {
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
implementation: config.provider,
|
|
68
|
+
client
|
|
45
69
|
};
|
|
46
70
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
71
|
+
/**
|
|
72
|
+
* The client a native login signs in as: the one the app names with `client_id`, which must be
|
|
73
|
+
* configured; without one, the only native client, or the web client when there is no native
|
|
74
|
+
* one. Several native clients and no `client_id` is ambiguous and refused rather than guessed.
|
|
75
|
+
*/
|
|
76
|
+
async getNativeClient(provider, clientId) {
|
|
77
|
+
if (clientId !== void 0) return this.getClient(provider, clientId);
|
|
78
|
+
const config = await this.getProviderConfig(provider);
|
|
79
|
+
const native = config.native ?? [];
|
|
80
|
+
const client = native.length === 0 ? config.web : native.length === 1 ? native[0] : void 0;
|
|
81
|
+
if (!client) throw new OAuth2Error(400, "invalid_request", `client_id is required for ${provider}`);
|
|
82
|
+
return {
|
|
83
|
+
implementation: config.provider,
|
|
84
|
+
client
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
async resolveSecret(secret, clientId) {
|
|
88
|
+
return typeof secret === "function" ? secret(clientId) : secret;
|
|
89
|
+
}
|
|
90
|
+
async createAuthorizationUri({ provider, state, pkce }) {
|
|
91
|
+
const { implementation, client } = await this.getWebClient(provider);
|
|
92
|
+
return implementation.createAuthorizationUri({
|
|
52
93
|
pkce,
|
|
53
94
|
state,
|
|
54
|
-
scope,
|
|
55
|
-
clientId,
|
|
56
|
-
redirectUri: redirectUri ??
|
|
95
|
+
scope: client.scope,
|
|
96
|
+
clientId: client.clientId,
|
|
97
|
+
redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider)
|
|
57
98
|
});
|
|
58
99
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const {
|
|
62
|
-
const { clientId
|
|
63
|
-
return
|
|
64
|
-
code,
|
|
65
|
-
pkce,
|
|
100
|
+
/** Exchanges a code issued to the provider's web client: the redirect flow and One Tap. */
|
|
101
|
+
async exchangeAuthorizationCode({ provider, code, pkce }) {
|
|
102
|
+
const { implementation, client } = await this.getWebClient(provider);
|
|
103
|
+
const { clientId } = client;
|
|
104
|
+
return {
|
|
66
105
|
clientId,
|
|
67
|
-
|
|
68
|
-
|
|
106
|
+
token: await implementation.exchangeAuthorizationCode({
|
|
107
|
+
code,
|
|
108
|
+
pkce,
|
|
109
|
+
clientId,
|
|
110
|
+
clientSecret: await this.resolveSecret(client.clientSecret, clientId),
|
|
111
|
+
redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider)
|
|
112
|
+
})
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** `clientId` is the client the token was issued to, as `exchangeAuthorizationCode` returns it. */
|
|
116
|
+
async getUserInfo({ provider, clientId, token, response }) {
|
|
117
|
+
const { implementation } = await this.getClient(provider, clientId);
|
|
118
|
+
return implementation.getUserInfo(token, {
|
|
119
|
+
clientId,
|
|
120
|
+
response
|
|
69
121
|
});
|
|
70
122
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
123
|
+
/** Verifies a Google One Tap / FedCM credential, which is issued to the web client. */
|
|
124
|
+
async getTokenInfo({ provider, credential }) {
|
|
125
|
+
const { implementation, client } = await this.getWebClient(provider);
|
|
126
|
+
if (!isOneTapProvider(implementation)) throw new OAuth2Error(400, "invalid_request", `Provider ${provider} has no One Tap`);
|
|
127
|
+
const { clientId } = client;
|
|
128
|
+
return {
|
|
129
|
+
clientId,
|
|
130
|
+
...await implementation.getTokenInfo(credential, clientId)
|
|
131
|
+
};
|
|
74
132
|
}
|
|
75
|
-
async refreshAccessToken({
|
|
76
|
-
const {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return
|
|
133
|
+
async refreshAccessToken({ provider, clientId, refreshToken }) {
|
|
134
|
+
const { implementation, client } = await this.getClient(provider, clientId);
|
|
135
|
+
invariant(implementation.refreshAccessToken, "Provider does not support refreshAccessToken");
|
|
136
|
+
const clientSecret = await this.resolveSecret(client.clientSecret, clientId);
|
|
137
|
+
return implementation.refreshAccessToken({
|
|
80
138
|
refreshToken,
|
|
81
139
|
clientId,
|
|
82
140
|
clientSecret
|
|
83
141
|
});
|
|
84
142
|
}
|
|
85
|
-
async revokeToken(
|
|
86
|
-
const {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
await
|
|
143
|
+
async revokeToken({ provider, clientId, token }) {
|
|
144
|
+
const { implementation, client } = await this.getClient(provider, clientId);
|
|
145
|
+
invariant(implementation.revokeToken, "Provider does not support revokeToken");
|
|
146
|
+
const clientSecret = await this.resolveSecret(client.clientSecret, clientId);
|
|
147
|
+
await implementation.revokeToken({
|
|
90
148
|
token,
|
|
91
149
|
clientId,
|
|
92
150
|
clientSecret
|
|
93
151
|
});
|
|
94
152
|
}
|
|
95
|
-
async loginOAuth2Native({
|
|
96
|
-
const {
|
|
97
|
-
invariant(
|
|
98
|
-
const {
|
|
99
|
-
|
|
100
|
-
return provider.loginOAuth2Native({
|
|
153
|
+
async loginOAuth2Native({ provider, credentials, pkce }) {
|
|
154
|
+
const { implementation, client } = await this.getNativeClient(provider, credentials.client_id);
|
|
155
|
+
invariant(implementation.loginOAuth2Native, "Provider does not support loginOAuth2Native");
|
|
156
|
+
const { clientId } = client;
|
|
157
|
+
return {
|
|
101
158
|
clientId,
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
159
|
+
...await implementation.loginOAuth2Native({
|
|
160
|
+
clientId,
|
|
161
|
+
clientSecret: await this.resolveSecret(client.clientSecret, clientId),
|
|
162
|
+
redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider),
|
|
163
|
+
credentials,
|
|
164
|
+
pkce
|
|
165
|
+
})
|
|
166
|
+
};
|
|
107
167
|
}
|
|
108
168
|
};
|
|
169
|
+
function isOneTapProvider(provider) {
|
|
170
|
+
return typeof provider.getTokenInfo === "function";
|
|
171
|
+
}
|
|
109
172
|
//#endregion
|
|
110
173
|
export { OAuth2Client, googleOneTapSchema, oauth2RedirectQuerySchema };
|
|
111
174
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../../src/oauth2/client.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { object, optional, string } from 'zod/v4-mini';\nimport type {\n AuthorizationResponse,\n NativeCredential,\n OAuth2ClientConfig,\n OAuth2Token,\n OidcToken,\n PkceParameters,\n} from './types';\n\nexport const oauth2RedirectQuerySchema = object({\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n // Apple's form_post: the user's name as JSON, first authorization only.\n user: optional(string()),\n});\n\nexport const googleOneTapSchema = object({\n // login_uri\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n nonce: optional(string()),\n g_csrf_token: optional(string()),\n hd: optional(string()),\n // callback\n credential: optional(string()),\n});\n\nexport class OAuth2Client {\n private readonly config: OAuth2ClientConfig;\n constructor(config: OAuth2ClientConfig) {\n this.config = config;\n }\n\n get baseUri() {\n return this.config.baseUri;\n }\n\n get successUri() {\n return this.config.successUri;\n }\n\n get errorUri() {\n return this.config.errorUri;\n }\n\n private async getClientConfig(registrationId: string) {\n const registrations =\n typeof this.config.registration === 'function'\n ? await this.config.registration()\n : this.config.registration;\n\n const registration = registrations[registrationId];\n invariant(registration, `Registration ${registrationId} not found`);\n\n const provider = this.config.provider[registration.provider ?? registrationId];\n invariant(provider, `Provider ${registration.provider ?? registrationId} not found`);\n return { registration, provider };\n }\n\n async createAuthorizationUri({\n registrationId,\n state,\n pkce,\n }: {\n registrationId: string;\n state: string;\n pkce?: PkceParameters;\n }): Promise<URL> {\n const { provider, registration } = await this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { scope, clientId, redirectUri } = registration;\n return provider.createAuthorizationUri({\n pkce,\n state,\n scope,\n clientId,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async exchangeAuthorizationCode({\n registrationId,\n code,\n pkce,\n }: {\n registrationId: string;\n code: string;\n pkce?: PkceParameters;\n }): Promise<OAuth2Token> {\n const { provider, registration } = await this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n return provider.exchangeAuthorizationCode({\n code,\n pkce,\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async getUserInfo({\n registrationId,\n token,\n response,\n }: {\n registrationId: string;\n token: OAuth2Token | OidcToken;\n response?: AuthorizationResponse;\n }) {\n const { provider } = await this.getClientConfig(registrationId);\n return provider.getUserInfo(token, response);\n }\n\n async refreshAccessToken({\n registrationId,\n refreshToken,\n }: {\n registrationId: string;\n refreshToken: string;\n }): Promise<OAuth2Token> {\n const { provider, registration } = await this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.refreshAccessToken, 'Provider does not support refreshAccessToken');\n return provider.refreshAccessToken({ refreshToken, clientId, clientSecret });\n }\n\n async revokeToken(registrationId: string, token: string) {\n const { provider, registration } = await this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.revokeToken, 'Provider does not support revokeToken');\n await provider.revokeToken({ token, clientId, clientSecret });\n }\n\n async loginOAuth2Native({\n registrationId,\n credentials,\n pkce,\n }: {\n registrationId: string;\n credentials: NativeCredential;\n pkce?: PkceParameters;\n }) {\n const { provider, registration } = await this.getClientConfig(registrationId);\n invariant(provider.loginOAuth2Native, 'Provider does not support loginOAuth2Native');\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n\n return provider.loginOAuth2Native({\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n credentials,\n pkce,\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../../src/oauth2/client.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { object, optional, string } from 'zod/v4-mini';\nimport { OAuth2Error } from './error';\nimport type {\n AuthorizationResponse,\n ClientSecret,\n NativeClient,\n NativeCredential,\n OAuth2ClientConfig,\n OAuth2Token,\n OidcToken,\n OneTapProvider,\n PkceParameters,\n Provider,\n ProviderConfig,\n WebClient,\n} from './types';\n\nexport const oauth2RedirectQuerySchema = object({\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n // Apple's form_post: the user's name as JSON, first authorization only.\n user: optional(string()),\n});\n\nexport const googleOneTapSchema = object({\n // login_uri\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n nonce: optional(string()),\n g_csrf_token: optional(string()),\n hd: optional(string()),\n // callback\n credential: optional(string()),\n});\n\nexport class OAuth2Client {\n private readonly config: OAuth2ClientConfig;\n constructor(config: OAuth2ClientConfig) {\n this.config = config;\n }\n\n get baseUri() {\n return this.config.baseUri;\n }\n\n get successUri() {\n return this.config.successUri;\n }\n\n get errorUri() {\n return this.config.errorUri;\n }\n\n /** The provider key comes from the request path, so an unknown one is the caller's error. */\n private async getProviderConfig(provider: string): Promise<ProviderConfig> {\n const providers =\n typeof this.config.providers === 'function'\n ? await this.config.providers()\n : this.config.providers;\n const config = Object.hasOwn(providers, provider) ? providers[provider] : undefined;\n if (!config) throw new OAuth2Error(400, 'invalid_request', `Unknown provider: ${provider}`);\n return config;\n }\n\n private defaultRedirectUri(provider: string) {\n return `${this.config.baseUri}/login/oauth2/code/${provider}`;\n }\n\n /** The redirect flow and One Tap sign in as the provider's web client. */\n private async getWebClient(provider: string) {\n const config = await this.getProviderConfig(provider);\n if (!config.web) {\n throw new OAuth2Error(400, 'invalid_request', `Provider ${provider} has no web client`);\n }\n return { implementation: config.provider, client: config.web };\n }\n\n /**\n * Any configured client of the provider, looked up by id: refresh and revoke use the client a\n * token was issued to, and the native login the one the app names. The web client counts too:\n * an Android app's server auth code and a Facebook app's tokens are issued to it.\n */\n private async getClient(provider: string, clientId: string) {\n const config = await this.getProviderConfig(provider);\n const client = [config.web, ...(config.native ?? [])].find((c) => c?.clientId === clientId);\n if (!client) {\n throw new OAuth2Error(400, 'invalid_client', `${clientId} is not a client of ${provider}`);\n }\n return { implementation: config.provider, client };\n }\n\n /**\n * The client a native login signs in as: the one the app names with `client_id`, which must be\n * configured; without one, the only native client, or the web client when there is no native\n * one. Several native clients and no `client_id` is ambiguous and refused rather than guessed.\n */\n private async getNativeClient(provider: string, clientId: string | undefined) {\n if (clientId !== undefined) return this.getClient(provider, clientId);\n const config = await this.getProviderConfig(provider);\n const native = config.native ?? [];\n const client: WebClient | NativeClient | undefined =\n native.length === 0 ? config.web : native.length === 1 ? native[0] : undefined;\n if (!client) {\n throw new OAuth2Error(400, 'invalid_request', `client_id is required for ${provider}`);\n }\n return { implementation: config.provider, client };\n }\n\n private async resolveSecret(secret: ClientSecret | undefined, clientId: string) {\n return typeof secret === 'function' ? secret(clientId) : secret;\n }\n\n async createAuthorizationUri({\n provider,\n state,\n pkce,\n }: {\n provider: string;\n state: string;\n pkce?: PkceParameters;\n }): Promise<URL> {\n const { implementation, client } = await this.getWebClient(provider);\n return implementation.createAuthorizationUri({\n pkce,\n state,\n scope: client.scope,\n clientId: client.clientId,\n redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider),\n });\n }\n\n /** Exchanges a code issued to the provider's web client: the redirect flow and One Tap. */\n async exchangeAuthorizationCode({\n provider,\n code,\n pkce,\n }: {\n provider: string;\n code: string;\n pkce?: PkceParameters;\n }): Promise<{ clientId: string; token: OAuth2Token }> {\n const { implementation, client } = await this.getWebClient(provider);\n const { clientId } = client;\n const token = await implementation.exchangeAuthorizationCode({\n code,\n pkce,\n clientId,\n clientSecret: await this.resolveSecret(client.clientSecret, clientId),\n redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider),\n });\n return { clientId, token };\n }\n\n /** `clientId` is the client the token was issued to, as `exchangeAuthorizationCode` returns it. */\n async getUserInfo({\n provider,\n clientId,\n token,\n response,\n }: {\n provider: string;\n clientId: string;\n token: OAuth2Token | OidcToken;\n response?: AuthorizationResponse;\n }) {\n const { implementation } = await this.getClient(provider, clientId);\n return implementation.getUserInfo(token, { clientId, response });\n }\n\n /** Verifies a Google One Tap / FedCM credential, which is issued to the web client. */\n async getTokenInfo({ provider, credential }: { provider: string; credential: string }) {\n const { implementation, client } = await this.getWebClient(provider);\n if (!isOneTapProvider(implementation)) {\n throw new OAuth2Error(400, 'invalid_request', `Provider ${provider} has no One Tap`);\n }\n const { clientId } = client;\n return { clientId, ...(await implementation.getTokenInfo(credential, clientId)) };\n }\n\n async refreshAccessToken({\n provider,\n clientId,\n refreshToken,\n }: {\n provider: string;\n clientId: string;\n refreshToken: string;\n }): Promise<OAuth2Token> {\n const { implementation, client } = await this.getClient(provider, clientId);\n invariant(implementation.refreshAccessToken, 'Provider does not support refreshAccessToken');\n const clientSecret = await this.resolveSecret(client.clientSecret, clientId);\n return implementation.refreshAccessToken({ refreshToken, clientId, clientSecret });\n }\n\n async revokeToken({\n provider,\n clientId,\n token,\n }: {\n provider: string;\n clientId: string;\n token: string;\n }) {\n const { implementation, client } = await this.getClient(provider, clientId);\n invariant(implementation.revokeToken, 'Provider does not support revokeToken');\n const clientSecret = await this.resolveSecret(client.clientSecret, clientId);\n await implementation.revokeToken({ token, clientId, clientSecret });\n }\n\n async loginOAuth2Native({\n provider,\n credentials,\n pkce,\n }: {\n provider: string;\n credentials: NativeCredential;\n pkce?: PkceParameters;\n }) {\n const { implementation, client } = await this.getNativeClient(provider, credentials.client_id);\n invariant(implementation.loginOAuth2Native, 'Provider does not support loginOAuth2Native');\n const { clientId } = client;\n\n const result = await implementation.loginOAuth2Native({\n clientId,\n clientSecret: await this.resolveSecret(client.clientSecret, clientId),\n redirectUri: client.redirectUri ?? this.defaultRedirectUri(provider),\n credentials,\n pkce,\n });\n return { clientId, ...result };\n }\n}\n\nfunction isOneTapProvider(provider: Provider): provider is OneTapProvider {\n return typeof (provider as Partial<OneTapProvider>).getTokenInfo === 'function';\n}\n"],"mappings":"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"}
|
|
@@ -8,9 +8,9 @@ let jose = require("jose");
|
|
|
8
8
|
* differ only in `sub`.
|
|
9
9
|
*
|
|
10
10
|
* Returns `clientId => secret`, signing on demand and caching per client id until `renewBefore`
|
|
11
|
-
* seconds before expiry
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* seconds before expiry. Pass it as the `clientSecret` of every Apple client (web and native): it
|
|
12
|
+
* is called with each client's own id on every use, so the server mints the secrets at runtime
|
|
13
|
+
* instead of generating, storing and rotating static ones by hand.
|
|
14
14
|
*/
|
|
15
15
|
function createAppleClientSecret(options) {
|
|
16
16
|
const { teamId, keyId, privateKey, expiresIn = 3600, renewBefore = 300 } = options;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple-client-secret.cjs","names":["importPKCS8","SignJWT"],"sources":["../../../src/oauth2/provider/apple-client-secret.ts"],"sourcesContent":["import { SignJWT, importPKCS8 } from 'jose';\n\nexport interface AppleClientSecretOptions {\n /** Apple Developer Team ID (Membership details). */\n teamId: string;\n /** Key ID of a key with Sign in with Apple enabled (Certificates, Identifiers & Profiles → Keys). */\n keyId: string;\n /** Contents of that key's `.p8` file: a PKCS#8 PEM. */\n privateKey: string;\n /** Lifetime of each signed secret, in seconds. Apple caps it at six months. Default: one hour. */\n expiresIn?: number;\n /** Re-sign once fewer than this many seconds remain, so a cached secret never reaches Apple expired. Default: five minutes. */\n renewBefore?: number;\n}\n\n/**\n * Sign in with Apple has no static client secret: the secret is a JWT the server signs with its\n * Sign in with Apple key (ES256, `iss` = team, `sub` = client id, `aud` = appleid.apple.com), and\n * a team signs one per client id — the app's bundle id and the web Services ID share the key and\n * differ only in `sub`.\n *\n * Returns `clientId => secret`, signing on demand and caching per client id until `renewBefore`\n * seconds before expiry
|
|
1
|
+
{"version":3,"file":"apple-client-secret.cjs","names":["importPKCS8","SignJWT"],"sources":["../../../src/oauth2/provider/apple-client-secret.ts"],"sourcesContent":["import { SignJWT, importPKCS8 } from 'jose';\n\nexport interface AppleClientSecretOptions {\n /** Apple Developer Team ID (Membership details). */\n teamId: string;\n /** Key ID of a key with Sign in with Apple enabled (Certificates, Identifiers & Profiles → Keys). */\n keyId: string;\n /** Contents of that key's `.p8` file: a PKCS#8 PEM. */\n privateKey: string;\n /** Lifetime of each signed secret, in seconds. Apple caps it at six months. Default: one hour. */\n expiresIn?: number;\n /** Re-sign once fewer than this many seconds remain, so a cached secret never reaches Apple expired. Default: five minutes. */\n renewBefore?: number;\n}\n\n/**\n * Sign in with Apple has no static client secret: the secret is a JWT the server signs with its\n * Sign in with Apple key (ES256, `iss` = team, `sub` = client id, `aud` = appleid.apple.com), and\n * a team signs one per client id — the app's bundle id and the web Services ID share the key and\n * differ only in `sub`.\n *\n * Returns `clientId => secret`, signing on demand and caching per client id until `renewBefore`\n * seconds before expiry. Pass it as the `clientSecret` of every Apple client (web and native): it\n * is called with each client's own id on every use, so the server mints the secrets at runtime\n * instead of generating, storing and rotating static ones by hand.\n */\nexport function createAppleClientSecret(options: AppleClientSecretOptions) {\n const { teamId, keyId, privateKey, expiresIn = 3600, renewBefore = 300 } = options;\n if (renewBefore >= expiresIn) {\n throw new Error('renewBefore must be shorter than expiresIn');\n }\n\n // The import is cached as a promise so concurrent first calls share one; a failed import is\n // dropped so the next call retries instead of failing forever.\n let key: Promise<CryptoKey> | undefined;\n const cache = new Map<string, { secret: string; exp: number }>();\n\n function getKey() {\n key ??= importPKCS8(privateKey, 'ES256').catch((error: unknown) => {\n key = undefined;\n throw error;\n });\n return key;\n }\n\n return async function appleClientSecret(clientId: string): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n const cached = cache.get(clientId);\n if (cached && cached.exp - now > renewBefore) return cached.secret;\n\n const exp = now + expiresIn;\n const secret = await new SignJWT({})\n .setProtectedHeader({ alg: 'ES256', kid: keyId })\n .setIssuer(teamId)\n .setSubject(clientId)\n .setAudience('https://appleid.apple.com')\n .setIssuedAt(now)\n .setExpirationTime(exp)\n .sign(await getKey());\n cache.set(clientId, { secret, exp });\n return secret;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AA0BA,SAAgB,wBAAwB,SAAmC;CACzE,MAAM,EAAE,QAAQ,OAAO,YAAY,YAAY,MAAM,cAAc,QAAQ;CAC3E,IAAI,eAAe,WACjB,MAAM,IAAI,MAAM,4CAA4C;CAK9D,IAAI;CACJ,MAAM,wBAAQ,IAAI,IAA6C;CAE/D,SAAS,SAAS;EAChB,SAAA,GAAQA,KAAAA,YAAAA,CAAY,YAAY,OAAO,CAAC,CAAC,OAAO,UAAmB;GACjE,MAAM,KAAA;GACN,MAAM;EACR,CAAC;EACD,OAAO;CACT;CAEA,OAAO,eAAe,kBAAkB,UAAmC;EACzE,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EACxC,MAAM,SAAS,MAAM,IAAI,QAAQ;EACjC,IAAI,UAAU,OAAO,MAAM,MAAM,aAAa,OAAO,OAAO;EAE5D,MAAM,MAAM,MAAM;EAClB,MAAM,SAAS,MAAM,IAAIC,KAAAA,QAAQ,CAAC,CAAC,CAAC,CACjC,mBAAmB;GAAE,KAAK;GAAS,KAAK;EAAM,CAAC,CAAC,CAChD,UAAU,MAAM,CAAC,CACjB,WAAW,QAAQ,CAAC,CACpB,YAAY,2BAA2B,CAAC,CACxC,YAAY,GAAG,CAAC,CAChB,kBAAkB,GAAG,CAAC,CACtB,KAAK,MAAM,OAAO,CAAC;EACtB,MAAM,IAAI,UAAU;GAAE;GAAQ;EAAI,CAAC;EACnC,OAAO;CACT;AACF"}
|
|
@@ -18,9 +18,9 @@ export interface AppleClientSecretOptions {
|
|
|
18
18
|
* differ only in `sub`.
|
|
19
19
|
*
|
|
20
20
|
* Returns `clientId => secret`, signing on demand and caching per client id until `renewBefore`
|
|
21
|
-
* seconds before expiry
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* seconds before expiry. Pass it as the `clientSecret` of every Apple client (web and native): it
|
|
22
|
+
* is called with each client's own id on every use, so the server mints the secrets at runtime
|
|
23
|
+
* instead of generating, storing and rotating static ones by hand.
|
|
24
24
|
*/
|
|
25
25
|
export declare function createAppleClientSecret(options: AppleClientSecretOptions): (clientId: string) => Promise<string>;
|
|
26
26
|
//#endregion
|
|
@@ -18,9 +18,9 @@ export interface AppleClientSecretOptions {
|
|
|
18
18
|
* differ only in `sub`.
|
|
19
19
|
*
|
|
20
20
|
* Returns `clientId => secret`, signing on demand and caching per client id until `renewBefore`
|
|
21
|
-
* seconds before expiry
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* seconds before expiry. Pass it as the `clientSecret` of every Apple client (web and native): it
|
|
22
|
+
* is called with each client's own id on every use, so the server mints the secrets at runtime
|
|
23
|
+
* instead of generating, storing and rotating static ones by hand.
|
|
24
24
|
*/
|
|
25
25
|
export declare function createAppleClientSecret(options: AppleClientSecretOptions): (clientId: string) => Promise<string>;
|
|
26
26
|
//#endregion
|
|
@@ -7,9 +7,9 @@ import { SignJWT, importPKCS8 } from "jose";
|
|
|
7
7
|
* differ only in `sub`.
|
|
8
8
|
*
|
|
9
9
|
* Returns `clientId => secret`, signing on demand and caching per client id until `renewBefore`
|
|
10
|
-
* seconds before expiry
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* seconds before expiry. Pass it as the `clientSecret` of every Apple client (web and native): it
|
|
11
|
+
* is called with each client's own id on every use, so the server mints the secrets at runtime
|
|
12
|
+
* instead of generating, storing and rotating static ones by hand.
|
|
13
13
|
*/
|
|
14
14
|
function createAppleClientSecret(options) {
|
|
15
15
|
const { teamId, keyId, privateKey, expiresIn = 3600, renewBefore = 300 } = options;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple-client-secret.mjs","names":[],"sources":["../../../src/oauth2/provider/apple-client-secret.ts"],"sourcesContent":["import { SignJWT, importPKCS8 } from 'jose';\n\nexport interface AppleClientSecretOptions {\n /** Apple Developer Team ID (Membership details). */\n teamId: string;\n /** Key ID of a key with Sign in with Apple enabled (Certificates, Identifiers & Profiles → Keys). */\n keyId: string;\n /** Contents of that key's `.p8` file: a PKCS#8 PEM. */\n privateKey: string;\n /** Lifetime of each signed secret, in seconds. Apple caps it at six months. Default: one hour. */\n expiresIn?: number;\n /** Re-sign once fewer than this many seconds remain, so a cached secret never reaches Apple expired. Default: five minutes. */\n renewBefore?: number;\n}\n\n/**\n * Sign in with Apple has no static client secret: the secret is a JWT the server signs with its\n * Sign in with Apple key (ES256, `iss` = team, `sub` = client id, `aud` = appleid.apple.com), and\n * a team signs one per client id — the app's bundle id and the web Services ID share the key and\n * differ only in `sub`.\n *\n * Returns `clientId => secret`, signing on demand and caching per client id until `renewBefore`\n * seconds before expiry
|
|
1
|
+
{"version":3,"file":"apple-client-secret.mjs","names":[],"sources":["../../../src/oauth2/provider/apple-client-secret.ts"],"sourcesContent":["import { SignJWT, importPKCS8 } from 'jose';\n\nexport interface AppleClientSecretOptions {\n /** Apple Developer Team ID (Membership details). */\n teamId: string;\n /** Key ID of a key with Sign in with Apple enabled (Certificates, Identifiers & Profiles → Keys). */\n keyId: string;\n /** Contents of that key's `.p8` file: a PKCS#8 PEM. */\n privateKey: string;\n /** Lifetime of each signed secret, in seconds. Apple caps it at six months. Default: one hour. */\n expiresIn?: number;\n /** Re-sign once fewer than this many seconds remain, so a cached secret never reaches Apple expired. Default: five minutes. */\n renewBefore?: number;\n}\n\n/**\n * Sign in with Apple has no static client secret: the secret is a JWT the server signs with its\n * Sign in with Apple key (ES256, `iss` = team, `sub` = client id, `aud` = appleid.apple.com), and\n * a team signs one per client id — the app's bundle id and the web Services ID share the key and\n * differ only in `sub`.\n *\n * Returns `clientId => secret`, signing on demand and caching per client id until `renewBefore`\n * seconds before expiry. Pass it as the `clientSecret` of every Apple client (web and native): it\n * is called with each client's own id on every use, so the server mints the secrets at runtime\n * instead of generating, storing and rotating static ones by hand.\n */\nexport function createAppleClientSecret(options: AppleClientSecretOptions) {\n const { teamId, keyId, privateKey, expiresIn = 3600, renewBefore = 300 } = options;\n if (renewBefore >= expiresIn) {\n throw new Error('renewBefore must be shorter than expiresIn');\n }\n\n // The import is cached as a promise so concurrent first calls share one; a failed import is\n // dropped so the next call retries instead of failing forever.\n let key: Promise<CryptoKey> | undefined;\n const cache = new Map<string, { secret: string; exp: number }>();\n\n function getKey() {\n key ??= importPKCS8(privateKey, 'ES256').catch((error: unknown) => {\n key = undefined;\n throw error;\n });\n return key;\n }\n\n return async function appleClientSecret(clientId: string): Promise<string> {\n const now = Math.floor(Date.now() / 1000);\n const cached = cache.get(clientId);\n if (cached && cached.exp - now > renewBefore) return cached.secret;\n\n const exp = now + expiresIn;\n const secret = await new SignJWT({})\n .setProtectedHeader({ alg: 'ES256', kid: keyId })\n .setIssuer(teamId)\n .setSubject(clientId)\n .setAudience('https://appleid.apple.com')\n .setIssuedAt(now)\n .setExpirationTime(exp)\n .sign(await getKey());\n cache.set(clientId, { secret, exp });\n return secret;\n };\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,SAAgB,wBAAwB,SAAmC;CACzE,MAAM,EAAE,QAAQ,OAAO,YAAY,YAAY,MAAM,cAAc,QAAQ;CAC3E,IAAI,eAAe,WACjB,MAAM,IAAI,MAAM,4CAA4C;CAK9D,IAAI;CACJ,MAAM,wBAAQ,IAAI,IAA6C;CAE/D,SAAS,SAAS;EAChB,QAAQ,YAAY,YAAY,OAAO,CAAC,CAAC,OAAO,UAAmB;GACjE,MAAM,KAAA;GACN,MAAM;EACR,CAAC;EACD,OAAO;CACT;CAEA,OAAO,eAAe,kBAAkB,UAAmC;EACzE,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EACxC,MAAM,SAAS,MAAM,IAAI,QAAQ;EACjC,IAAI,UAAU,OAAO,MAAM,MAAM,aAAa,OAAO,OAAO;EAE5D,MAAM,MAAM,MAAM;EAClB,MAAM,SAAS,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,CACjC,mBAAmB;GAAE,KAAK;GAAS,KAAK;EAAM,CAAC,CAAC,CAChD,UAAU,MAAM,CAAC,CACjB,WAAW,QAAQ,CAAC,CACpB,YAAY,2BAA2B,CAAC,CACxC,YAAY,GAAG,CAAC,CAChB,kBAAkB,GAAG,CAAC,CACtB,KAAK,MAAM,OAAO,CAAC;EACtB,MAAM,IAAI,UAAU;GAAE;GAAQ;EAAI,CAAC;EACnC,OAAO;CACT;AACF"}
|
|
@@ -4,6 +4,8 @@ const require_oauth2_name = require("../name.cjs");
|
|
|
4
4
|
const require_oauth2_provider_common = require("./common.cjs");
|
|
5
5
|
let _shware_utils = require("@shware/utils");
|
|
6
6
|
//#region src/oauth2/provider/apple.ts
|
|
7
|
+
/** `iss` of every Apple id token (https://appleid.apple.com/.well-known/openid-configuration). */
|
|
8
|
+
const APPLE_ISSUER = "https://appleid.apple.com";
|
|
7
9
|
function createAppleProvider() {
|
|
8
10
|
return {
|
|
9
11
|
authorizationUri: "https://appleid.apple.com/auth/authorize?response_mode=form_post",
|
|
@@ -33,10 +35,14 @@ function createAppleProvider() {
|
|
|
33
35
|
}
|
|
34
36
|
return await response.json();
|
|
35
37
|
},
|
|
36
|
-
async getUserInfo({ id_token }, response) {
|
|
38
|
+
async getUserInfo({ id_token }, { clientId, response }) {
|
|
37
39
|
(0, _shware_utils.invariant)(id_token, "id_token is required");
|
|
38
40
|
(0, _shware_utils.invariant)(this.jwkSetUri, "jwkSetUri is required");
|
|
39
|
-
const token = await require_oauth2_provider_common.verifyIdToken(id_token,
|
|
41
|
+
const token = await require_oauth2_provider_common.verifyIdToken(id_token, {
|
|
42
|
+
jwkSetUri: this.jwkSetUri,
|
|
43
|
+
issuer: APPLE_ISSUER,
|
|
44
|
+
audience: clientId
|
|
45
|
+
});
|
|
40
46
|
const user = require_oauth2_name.parseAppleUser(response?.user);
|
|
41
47
|
return {
|
|
42
48
|
data: user ? {
|
|
@@ -68,7 +74,7 @@ function createAppleProvider() {
|
|
|
68
74
|
throw new require_oauth2_error.OAuth2Error(response.status, error);
|
|
69
75
|
}
|
|
70
76
|
const token = await response.json();
|
|
71
|
-
const { data, claims } = await this.getUserInfo(token);
|
|
77
|
+
const { data, claims } = await this.getUserInfo(token, { clientId: params.clientId });
|
|
72
78
|
const fullName = require_oauth2_name.cleanPersonName(credentials.fullName);
|
|
73
79
|
const name = require_oauth2_name.nameClaims({
|
|
74
80
|
given_name: fullName?.givenName ?? void 0,
|