@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":"apple.cjs","names":["createAuthorizationUri","exchangeAuthorizationCode","OAuth2Error","verifyIdToken","parseAppleUser","nameClaims","cleanPersonName"],"sources":["../../../src/oauth2/provider/apple.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport { type AppleUser, cleanPersonName, nameClaims, parseAppleUser } from '../name';\nimport type {\n LoginOAuth2NativeParams,\n NativeCredential,\n OAuth2Token,\n PersonNameComponents,\n Provider,\n UserInfo,\n} from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n// ref: https://account.apple.com/.well-known/openid-configuration\nexport function createAppleProvider(): Provider {\n return {\n // important notice: response_mode=form_post is required for apple\n authorizationUri: 'https://appleid.apple.com/auth/authorize?response_mode=form_post',\n tokenUri: 'https://appleid.apple.com/auth/token',\n jwkSetUri: 'https://appleid.apple.com/auth/keys',\n userNameAttribute: 'sub',\n defaultScope: ['openid', 'name', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as AppleErrorResponse;\n throw new OAuth2Error(response.status, error);\n }\n return (await response.json()) as AppleToken;\n },\n async getUserInfo({ id_token }, response) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const token = await verifyIdToken<AppleIdToken>(id_token, this.jwkSetUri);\n // Apple's id token never carries a name; on the first authorization of the web flow the\n // name comes in the form_post `user` instead, unsigned, so only its name parts are used\n // and the signed id token's email always wins over the one beside them.\n const user = parseAppleUser(response?.user);\n const data: AppleUserInfo = user ? { ...token, user } : token;\n return {\n data,\n claims: {\n sub: token.sub,\n email: token.email,\n email_verified: true,\n ...nameClaims({\n given_name: user?.name?.firstName,\n family_name: user?.name?.lastName,\n }),\n },\n };\n },\n async loginOAuth2Native({ pkce: _, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { tokenUri } = this;\n const { code } = credentials;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error } = (await response.json()) as AppleErrorResponse;\n throw new OAuth2Error(response.status, error);\n }\n const token = (await response.json()) as AppleToken;\n const { data, claims } = await this.getUserInfo(token);\n // The app gets the name from Apple on the first authorization only and passes it along in\n // the login body as the platform's `fullName`.\n const fullName = cleanPersonName(credentials.fullName);\n const name = nameClaims({\n given_name: fullName?.givenName ?? undefined,\n family_name: fullName?.familyName ?? undefined,\n middle_name: fullName?.middleName ?? undefined,\n nickname: fullName?.nickname ?? undefined,\n });\n const userInfo: UserInfo<AppleUserInfo> = {\n data: fullName ? { ...(data as AppleUserInfo), fullName } : (data as AppleUserInfo),\n claims: { ...claims, ...name },\n };\n return { token, userInfo };\n },\n };\n}\n\nexport const apple = createAppleProvider();\n\n// Claims of Apple's id token. It carries no name or picture, not even on the first authorization.\n// https://developer.apple.com/documentation/signinwithapple/authenticating-users-with-sign-in-with-apple\nexport interface AppleIdToken {\n iss: string;\n aud: string;\n exp: number;\n iat: number;\n sub: string;\n email: string;\n email_verified: true | 'true';\n is_private_email: boolean;\n /** Native flow, first authorization only: 0 unsupported, 1 unknown, 2 likely real. */\n real_user_status?: number;\n nonce?: string;\n nonce_supported?: boolean;\n}\n\n/**\n * `userInfo.data` of an Apple login: the id token's claims, plus the unsigned name the client\n * passed on the first authorization, cleaned (see `nameClaims` for the standard claims made of it).\n */\nexport interface AppleUserInfo extends AppleIdToken {\n /** Web flow: Apple's form_post `user`, as Apple names it. */\n user?: AppleUser;\n /** Native flow: the login body's `fullName`, which alone keeps `namePrefix` / `nameSuffix`. */\n fullName?: PersonNameComponents;\n}\n\n// https://developer.apple.com/documentation/devicemanagement/implementing-the-oauth2-authentication-user-enrollment-flow\n// https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens\nexport interface AppleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n}\n\nexport interface AppleNativeCredential extends NativeCredential {\n state: string;\n code: string;\n id_token: string;\n}\n\n// https://developer.apple.com/documentation/sign_in_with_apple/errorresponse\nexport interface AppleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'invalid_scope';\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"apple.cjs","names":["createAuthorizationUri","exchangeAuthorizationCode","OAuth2Error","verifyIdToken","parseAppleUser","nameClaims","cleanPersonName"],"sources":["../../../src/oauth2/provider/apple.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport { type AppleUser, cleanPersonName, nameClaims, parseAppleUser } from '../name';\nimport type {\n LoginOAuth2NativeParams,\n NativeCredential,\n OAuth2Token,\n PersonNameComponents,\n Provider,\n UserInfo,\n} from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n/** `iss` of every Apple id token (https://appleid.apple.com/.well-known/openid-configuration). */\nconst APPLE_ISSUER = 'https://appleid.apple.com';\n\n// ref: https://account.apple.com/.well-known/openid-configuration\nexport function createAppleProvider(): Provider {\n return {\n // important notice: response_mode=form_post is required for apple\n authorizationUri: 'https://appleid.apple.com/auth/authorize?response_mode=form_post',\n tokenUri: 'https://appleid.apple.com/auth/token',\n jwkSetUri: 'https://appleid.apple.com/auth/keys',\n userNameAttribute: 'sub',\n defaultScope: ['openid', 'name', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as AppleErrorResponse;\n throw new OAuth2Error(response.status, error);\n }\n return (await response.json()) as AppleToken;\n },\n async getUserInfo({ id_token }, { clientId, response }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n // The audience is the client the code was exchanged as: the bundle id for an app, the\n // Services ID for the web.\n const token = await verifyIdToken<AppleIdToken>(id_token, {\n jwkSetUri: this.jwkSetUri,\n issuer: APPLE_ISSUER,\n audience: clientId,\n });\n // Apple's id token never carries a name; on the first authorization of the web flow the\n // name comes in the form_post `user` instead, unsigned, so only its name parts are used\n // and the signed id token's email always wins over the one beside them.\n const user = parseAppleUser(response?.user);\n const data: AppleUserInfo = user ? { ...token, user } : token;\n return {\n data,\n claims: {\n sub: token.sub,\n email: token.email,\n email_verified: true,\n ...nameClaims({\n given_name: user?.name?.firstName,\n family_name: user?.name?.lastName,\n }),\n },\n };\n },\n async loginOAuth2Native({ pkce: _, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { tokenUri } = this;\n const { code } = credentials;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error } = (await response.json()) as AppleErrorResponse;\n throw new OAuth2Error(response.status, error);\n }\n const token = (await response.json()) as AppleToken;\n const { data, claims } = await this.getUserInfo(token, { clientId: params.clientId });\n // The app gets the name from Apple on the first authorization only and passes it along in\n // the login body as the platform's `fullName`.\n const fullName = cleanPersonName(credentials.fullName);\n const name = nameClaims({\n given_name: fullName?.givenName ?? undefined,\n family_name: fullName?.familyName ?? undefined,\n middle_name: fullName?.middleName ?? undefined,\n nickname: fullName?.nickname ?? undefined,\n });\n const userInfo: UserInfo<AppleUserInfo> = {\n data: fullName ? { ...(data as AppleUserInfo), fullName } : (data as AppleUserInfo),\n claims: { ...claims, ...name },\n };\n return { token, userInfo };\n },\n };\n}\n\nexport const apple = createAppleProvider();\n\n// Claims of Apple's id token. It carries no name or picture, not even on the first authorization.\n// https://developer.apple.com/documentation/signinwithapple/authenticating-users-with-sign-in-with-apple\nexport interface AppleIdToken {\n iss: string;\n aud: string;\n exp: number;\n iat: number;\n sub: string;\n email: string;\n email_verified: true | 'true';\n is_private_email: boolean;\n /** Native flow, first authorization only: 0 unsupported, 1 unknown, 2 likely real. */\n real_user_status?: number;\n nonce?: string;\n nonce_supported?: boolean;\n}\n\n/**\n * `userInfo.data` of an Apple login: the id token's claims, plus the unsigned name the client\n * passed on the first authorization, cleaned (see `nameClaims` for the standard claims made of it).\n */\nexport interface AppleUserInfo extends AppleIdToken {\n /** Web flow: Apple's form_post `user`, as Apple names it. */\n user?: AppleUser;\n /** Native flow: the login body's `fullName`, which alone keeps `namePrefix` / `nameSuffix`. */\n fullName?: PersonNameComponents;\n}\n\n// https://developer.apple.com/documentation/devicemanagement/implementing-the-oauth2-authentication-user-enrollment-flow\n// https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens\nexport interface AppleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n}\n\nexport interface AppleNativeCredential extends NativeCredential {\n state: string;\n code: string;\n id_token: string;\n}\n\n// https://developer.apple.com/documentation/sign_in_with_apple/errorresponse\nexport interface AppleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'invalid_scope';\n}\n"],"mappings":";;;;;;;AAcA,MAAM,eAAe;AAGrB,SAAgB,sBAAgC;CAC9C,OAAO;EAEL,kBAAkB;EAClB,UAAU;EACV,WAAW;EACX,mBAAmB;EACnB,cAAc;GAAC;GAAU;GAAQ;EAAO;EACxC,uBAAuB,EAAE,MAAM,GAAG,GAAG,UAAU;GAC7C,OAAOA,+BAAAA,uBAAuB;IAC5B,GAAG;IACH,OAAO,OAAO,SAAS,KAAK;IAC5B,kBAAkB,KAAK;GACzB,CAAC;EACH;EACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,GAAG,UAAU;GACtD,MAAM,WAAW,MAAMC,+BAAAA,0BAA0B;IAAE,GAAG;IAAQ,UAAU,KAAK;GAAS,CAAC;GACvF,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,EAAE,UAAW,MAAM,SAAS,KAAK;IACvC,MAAM,IAAIC,qBAAAA,YAAY,SAAS,QAAQ,KAAK;GAC9C;GACA,OAAQ,MAAM,SAAS,KAAK;EAC9B;EACA,MAAM,YAAY,EAAE,YAAY,EAAE,UAAU,YAAY;GACtD,CAAA,GAAA,cAAA,UAAA,CAAU,UAAU,sBAAsB;GAC1C,CAAA,GAAA,cAAA,UAAA,CAAU,KAAK,WAAW,uBAAuB;GAGjD,MAAM,QAAQ,MAAMC,+BAAAA,cAA4B,UAAU;IACxD,WAAW,KAAK;IAChB,QAAQ;IACR,UAAU;GACZ,CAAC;GAID,MAAM,OAAOC,oBAAAA,eAAe,UAAU,IAAI;GAE1C,OAAO;IACL,MAF0B,OAAO;KAAE,GAAG;KAAO;IAAK,IAAI;IAGtD,QAAQ;KACN,KAAK,MAAM;KACX,OAAO,MAAM;KACb,gBAAgB;KAChB,GAAGC,oBAAAA,WAAW;MACZ,YAAY,MAAM,MAAM;MACxB,aAAa,MAAM,MAAM;KAC3B,CAAC;IACH;GACF;EACF;EACA,MAAM,kBAAkB,EAAE,MAAM,GAAG,aAAa,GAAG,UAAmC;GACpF,CAAA,GAAA,cAAA,UAAA,CAAU,YAAY,MAAM,kBAAkB;GAC9C,MAAM,EAAE,aAAa;GACrB,MAAM,EAAE,SAAS;GACjB,MAAM,WAAW,MAAMJ,+BAAAA,0BAA0B;IAAE;IAAM;IAAU,GAAG;GAAO,CAAC;GAC9E,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,EAAE,UAAW,MAAM,SAAS,KAAK;IACvC,MAAM,IAAIC,qBAAAA,YAAY,SAAS,QAAQ,KAAK;GAC9C;GACA,MAAM,QAAS,MAAM,SAAS,KAAK;GACnC,MAAM,EAAE,MAAM,WAAW,MAAM,KAAK,YAAY,OAAO,EAAE,UAAU,OAAO,SAAS,CAAC;GAGpF,MAAM,WAAWI,oBAAAA,gBAAgB,YAAY,QAAQ;GACrD,MAAM,OAAOD,oBAAAA,WAAW;IACtB,YAAY,UAAU,aAAa,KAAA;IACnC,aAAa,UAAU,cAAc,KAAA;IACrC,aAAa,UAAU,cAAc,KAAA;IACrC,UAAU,UAAU,YAAY,KAAA;GAClC,CAAC;GAKD,OAAO;IAAE;IAAO,UAAA;KAHd,MAAM,WAAW;MAAE,GAAI;MAAwB;KAAS,IAAK;KAC7D,QAAQ;MAAE,GAAG;MAAQ,GAAG;KAAK;IAER;GAAE;EAC3B;CACF;AACF;AAEA,MAAa,QAAQ,oBAAoB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple.d.cts","names":[],"sources":["../../../src/oauth2/provider/apple.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"apple.d.cts","names":[],"sources":["../../../src/oauth2/provider/apple.ts"],"mappings":";;;wBAiBgB,uBAAuB;qBAgF1B,OAAK;iBAID;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;;;;;;iBAOe,sBAAsB;;EAErC,OAAO;;EAEP,WAAW;;iBAKI,mBAAmB;EAClC;EACA;EACA;EACA;EACA;;iBAGe,8BAA8B;EAC7C;EACA;EACA;;iBAIe;EACf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple.d.mts","names":[],"sources":["../../../src/oauth2/provider/apple.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"apple.d.mts","names":[],"sources":["../../../src/oauth2/provider/apple.ts"],"mappings":";;;wBAiBgB,uBAAuB;qBAgF1B,OAAK;iBAID;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;;;;;;iBAOe,sBAAsB;;EAErC,OAAO;;EAEP,WAAW;;iBAKI,mBAAmB;EAClC;EACA;EACA;EACA;EACA;;iBAGe,8BAA8B;EAC7C;EACA;EACA;;iBAIe;EACf"}
|
|
@@ -3,6 +3,8 @@ import { cleanPersonName, nameClaims, parseAppleUser } from "../name.mjs";
|
|
|
3
3
|
import { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from "./common.mjs";
|
|
4
4
|
import { invariant } from "@shware/utils";
|
|
5
5
|
//#region src/oauth2/provider/apple.ts
|
|
6
|
+
/** `iss` of every Apple id token (https://appleid.apple.com/.well-known/openid-configuration). */
|
|
7
|
+
const APPLE_ISSUER = "https://appleid.apple.com";
|
|
6
8
|
function createAppleProvider() {
|
|
7
9
|
return {
|
|
8
10
|
authorizationUri: "https://appleid.apple.com/auth/authorize?response_mode=form_post",
|
|
@@ -32,10 +34,14 @@ function createAppleProvider() {
|
|
|
32
34
|
}
|
|
33
35
|
return await response.json();
|
|
34
36
|
},
|
|
35
|
-
async getUserInfo({ id_token }, response) {
|
|
37
|
+
async getUserInfo({ id_token }, { clientId, response }) {
|
|
36
38
|
invariant(id_token, "id_token is required");
|
|
37
39
|
invariant(this.jwkSetUri, "jwkSetUri is required");
|
|
38
|
-
const token = await verifyIdToken(id_token,
|
|
40
|
+
const token = await verifyIdToken(id_token, {
|
|
41
|
+
jwkSetUri: this.jwkSetUri,
|
|
42
|
+
issuer: APPLE_ISSUER,
|
|
43
|
+
audience: clientId
|
|
44
|
+
});
|
|
39
45
|
const user = parseAppleUser(response?.user);
|
|
40
46
|
return {
|
|
41
47
|
data: user ? {
|
|
@@ -67,7 +73,7 @@ function createAppleProvider() {
|
|
|
67
73
|
throw new OAuth2Error(response.status, error);
|
|
68
74
|
}
|
|
69
75
|
const token = await response.json();
|
|
70
|
-
const { data, claims } = await this.getUserInfo(token);
|
|
76
|
+
const { data, claims } = await this.getUserInfo(token, { clientId: params.clientId });
|
|
71
77
|
const fullName = cleanPersonName(credentials.fullName);
|
|
72
78
|
const name = nameClaims({
|
|
73
79
|
given_name: fullName?.givenName ?? void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apple.mjs","names":[],"sources":["../../../src/oauth2/provider/apple.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport { type AppleUser, cleanPersonName, nameClaims, parseAppleUser } from '../name';\nimport type {\n LoginOAuth2NativeParams,\n NativeCredential,\n OAuth2Token,\n PersonNameComponents,\n Provider,\n UserInfo,\n} from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n// ref: https://account.apple.com/.well-known/openid-configuration\nexport function createAppleProvider(): Provider {\n return {\n // important notice: response_mode=form_post is required for apple\n authorizationUri: 'https://appleid.apple.com/auth/authorize?response_mode=form_post',\n tokenUri: 'https://appleid.apple.com/auth/token',\n jwkSetUri: 'https://appleid.apple.com/auth/keys',\n userNameAttribute: 'sub',\n defaultScope: ['openid', 'name', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as AppleErrorResponse;\n throw new OAuth2Error(response.status, error);\n }\n return (await response.json()) as AppleToken;\n },\n async getUserInfo({ id_token }, response) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const token = await verifyIdToken<AppleIdToken>(id_token, this.jwkSetUri);\n // Apple's id token never carries a name; on the first authorization of the web flow the\n // name comes in the form_post `user` instead, unsigned, so only its name parts are used\n // and the signed id token's email always wins over the one beside them.\n const user = parseAppleUser(response?.user);\n const data: AppleUserInfo = user ? { ...token, user } : token;\n return {\n data,\n claims: {\n sub: token.sub,\n email: token.email,\n email_verified: true,\n ...nameClaims({\n given_name: user?.name?.firstName,\n family_name: user?.name?.lastName,\n }),\n },\n };\n },\n async loginOAuth2Native({ pkce: _, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { tokenUri } = this;\n const { code } = credentials;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error } = (await response.json()) as AppleErrorResponse;\n throw new OAuth2Error(response.status, error);\n }\n const token = (await response.json()) as AppleToken;\n const { data, claims } = await this.getUserInfo(token);\n // The app gets the name from Apple on the first authorization only and passes it along in\n // the login body as the platform's `fullName`.\n const fullName = cleanPersonName(credentials.fullName);\n const name = nameClaims({\n given_name: fullName?.givenName ?? undefined,\n family_name: fullName?.familyName ?? undefined,\n middle_name: fullName?.middleName ?? undefined,\n nickname: fullName?.nickname ?? undefined,\n });\n const userInfo: UserInfo<AppleUserInfo> = {\n data: fullName ? { ...(data as AppleUserInfo), fullName } : (data as AppleUserInfo),\n claims: { ...claims, ...name },\n };\n return { token, userInfo };\n },\n };\n}\n\nexport const apple = createAppleProvider();\n\n// Claims of Apple's id token. It carries no name or picture, not even on the first authorization.\n// https://developer.apple.com/documentation/signinwithapple/authenticating-users-with-sign-in-with-apple\nexport interface AppleIdToken {\n iss: string;\n aud: string;\n exp: number;\n iat: number;\n sub: string;\n email: string;\n email_verified: true | 'true';\n is_private_email: boolean;\n /** Native flow, first authorization only: 0 unsupported, 1 unknown, 2 likely real. */\n real_user_status?: number;\n nonce?: string;\n nonce_supported?: boolean;\n}\n\n/**\n * `userInfo.data` of an Apple login: the id token's claims, plus the unsigned name the client\n * passed on the first authorization, cleaned (see `nameClaims` for the standard claims made of it).\n */\nexport interface AppleUserInfo extends AppleIdToken {\n /** Web flow: Apple's form_post `user`, as Apple names it. */\n user?: AppleUser;\n /** Native flow: the login body's `fullName`, which alone keeps `namePrefix` / `nameSuffix`. */\n fullName?: PersonNameComponents;\n}\n\n// https://developer.apple.com/documentation/devicemanagement/implementing-the-oauth2-authentication-user-enrollment-flow\n// https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens\nexport interface AppleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n}\n\nexport interface AppleNativeCredential extends NativeCredential {\n state: string;\n code: string;\n id_token: string;\n}\n\n// https://developer.apple.com/documentation/sign_in_with_apple/errorresponse\nexport interface AppleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'invalid_scope';\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"apple.mjs","names":[],"sources":["../../../src/oauth2/provider/apple.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport { type AppleUser, cleanPersonName, nameClaims, parseAppleUser } from '../name';\nimport type {\n LoginOAuth2NativeParams,\n NativeCredential,\n OAuth2Token,\n PersonNameComponents,\n Provider,\n UserInfo,\n} from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n/** `iss` of every Apple id token (https://appleid.apple.com/.well-known/openid-configuration). */\nconst APPLE_ISSUER = 'https://appleid.apple.com';\n\n// ref: https://account.apple.com/.well-known/openid-configuration\nexport function createAppleProvider(): Provider {\n return {\n // important notice: response_mode=form_post is required for apple\n authorizationUri: 'https://appleid.apple.com/auth/authorize?response_mode=form_post',\n tokenUri: 'https://appleid.apple.com/auth/token',\n jwkSetUri: 'https://appleid.apple.com/auth/keys',\n userNameAttribute: 'sub',\n defaultScope: ['openid', 'name', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as AppleErrorResponse;\n throw new OAuth2Error(response.status, error);\n }\n return (await response.json()) as AppleToken;\n },\n async getUserInfo({ id_token }, { clientId, response }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n // The audience is the client the code was exchanged as: the bundle id for an app, the\n // Services ID for the web.\n const token = await verifyIdToken<AppleIdToken>(id_token, {\n jwkSetUri: this.jwkSetUri,\n issuer: APPLE_ISSUER,\n audience: clientId,\n });\n // Apple's id token never carries a name; on the first authorization of the web flow the\n // name comes in the form_post `user` instead, unsigned, so only its name parts are used\n // and the signed id token's email always wins over the one beside them.\n const user = parseAppleUser(response?.user);\n const data: AppleUserInfo = user ? { ...token, user } : token;\n return {\n data,\n claims: {\n sub: token.sub,\n email: token.email,\n email_verified: true,\n ...nameClaims({\n given_name: user?.name?.firstName,\n family_name: user?.name?.lastName,\n }),\n },\n };\n },\n async loginOAuth2Native({ pkce: _, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { tokenUri } = this;\n const { code } = credentials;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error } = (await response.json()) as AppleErrorResponse;\n throw new OAuth2Error(response.status, error);\n }\n const token = (await response.json()) as AppleToken;\n const { data, claims } = await this.getUserInfo(token, { clientId: params.clientId });\n // The app gets the name from Apple on the first authorization only and passes it along in\n // the login body as the platform's `fullName`.\n const fullName = cleanPersonName(credentials.fullName);\n const name = nameClaims({\n given_name: fullName?.givenName ?? undefined,\n family_name: fullName?.familyName ?? undefined,\n middle_name: fullName?.middleName ?? undefined,\n nickname: fullName?.nickname ?? undefined,\n });\n const userInfo: UserInfo<AppleUserInfo> = {\n data: fullName ? { ...(data as AppleUserInfo), fullName } : (data as AppleUserInfo),\n claims: { ...claims, ...name },\n };\n return { token, userInfo };\n },\n };\n}\n\nexport const apple = createAppleProvider();\n\n// Claims of Apple's id token. It carries no name or picture, not even on the first authorization.\n// https://developer.apple.com/documentation/signinwithapple/authenticating-users-with-sign-in-with-apple\nexport interface AppleIdToken {\n iss: string;\n aud: string;\n exp: number;\n iat: number;\n sub: string;\n email: string;\n email_verified: true | 'true';\n is_private_email: boolean;\n /** Native flow, first authorization only: 0 unsupported, 1 unknown, 2 likely real. */\n real_user_status?: number;\n nonce?: string;\n nonce_supported?: boolean;\n}\n\n/**\n * `userInfo.data` of an Apple login: the id token's claims, plus the unsigned name the client\n * passed on the first authorization, cleaned (see `nameClaims` for the standard claims made of it).\n */\nexport interface AppleUserInfo extends AppleIdToken {\n /** Web flow: Apple's form_post `user`, as Apple names it. */\n user?: AppleUser;\n /** Native flow: the login body's `fullName`, which alone keeps `namePrefix` / `nameSuffix`. */\n fullName?: PersonNameComponents;\n}\n\n// https://developer.apple.com/documentation/devicemanagement/implementing-the-oauth2-authentication-user-enrollment-flow\n// https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens\nexport interface AppleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n}\n\nexport interface AppleNativeCredential extends NativeCredential {\n state: string;\n code: string;\n id_token: string;\n}\n\n// https://developer.apple.com/documentation/sign_in_with_apple/errorresponse\nexport interface AppleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'invalid_scope';\n}\n"],"mappings":";;;;;;AAcA,MAAM,eAAe;AAGrB,SAAgB,sBAAgC;CAC9C,OAAO;EAEL,kBAAkB;EAClB,UAAU;EACV,WAAW;EACX,mBAAmB;EACnB,cAAc;GAAC;GAAU;GAAQ;EAAO;EACxC,uBAAuB,EAAE,MAAM,GAAG,GAAG,UAAU;GAC7C,OAAO,uBAAuB;IAC5B,GAAG;IACH,OAAO,OAAO,SAAS,KAAK;IAC5B,kBAAkB,KAAK;GACzB,CAAC;EACH;EACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,GAAG,UAAU;GACtD,MAAM,WAAW,MAAM,0BAA0B;IAAE,GAAG;IAAQ,UAAU,KAAK;GAAS,CAAC;GACvF,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,EAAE,UAAW,MAAM,SAAS,KAAK;IACvC,MAAM,IAAI,YAAY,SAAS,QAAQ,KAAK;GAC9C;GACA,OAAQ,MAAM,SAAS,KAAK;EAC9B;EACA,MAAM,YAAY,EAAE,YAAY,EAAE,UAAU,YAAY;GACtD,UAAU,UAAU,sBAAsB;GAC1C,UAAU,KAAK,WAAW,uBAAuB;GAGjD,MAAM,QAAQ,MAAM,cAA4B,UAAU;IACxD,WAAW,KAAK;IAChB,QAAQ;IACR,UAAU;GACZ,CAAC;GAID,MAAM,OAAO,eAAe,UAAU,IAAI;GAE1C,OAAO;IACL,MAF0B,OAAO;KAAE,GAAG;KAAO;IAAK,IAAI;IAGtD,QAAQ;KACN,KAAK,MAAM;KACX,OAAO,MAAM;KACb,gBAAgB;KAChB,GAAG,WAAW;MACZ,YAAY,MAAM,MAAM;MACxB,aAAa,MAAM,MAAM;KAC3B,CAAC;IACH;GACF;EACF;EACA,MAAM,kBAAkB,EAAE,MAAM,GAAG,aAAa,GAAG,UAAmC;GACpF,UAAU,YAAY,MAAM,kBAAkB;GAC9C,MAAM,EAAE,aAAa;GACrB,MAAM,EAAE,SAAS;GACjB,MAAM,WAAW,MAAM,0BAA0B;IAAE;IAAM;IAAU,GAAG;GAAO,CAAC;GAC9E,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,EAAE,UAAW,MAAM,SAAS,KAAK;IACvC,MAAM,IAAI,YAAY,SAAS,QAAQ,KAAK;GAC9C;GACA,MAAM,QAAS,MAAM,SAAS,KAAK;GACnC,MAAM,EAAE,MAAM,WAAW,MAAM,KAAK,YAAY,OAAO,EAAE,UAAU,OAAO,SAAS,CAAC;GAGpF,MAAM,WAAW,gBAAgB,YAAY,QAAQ;GACrD,MAAM,OAAO,WAAW;IACtB,YAAY,UAAU,aAAa,KAAA;IACnC,aAAa,UAAU,cAAc,KAAA;IACrC,aAAa,UAAU,cAAc,KAAA;IACrC,UAAU,UAAU,YAAY,KAAA;GAClC,CAAC;GAKD,OAAO;IAAE;IAAO,UAAA;KAHd,MAAM,WAAW;MAAE,GAAI;MAAwB;KAAS,IAAK;KAC7D,QAAQ;MAAE,GAAG;MAAQ,GAAG;KAAK;IAER;GAAE;EAC3B;CACF;AACF;AAEA,MAAa,QAAQ,oBAAoB"}
|
|
@@ -25,7 +25,7 @@ async function exchangeAuthorizationCode(params) {
|
|
|
25
25
|
Accept: "application/json",
|
|
26
26
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
27
27
|
};
|
|
28
|
-
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret}`).toString("base64")}`;
|
|
28
|
+
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret ?? ""}`).toString("base64")}`;
|
|
29
29
|
else {
|
|
30
30
|
body.append("client_id", params.clientId);
|
|
31
31
|
if (params.clientSecret) body.append("client_secret", params.clientSecret);
|
|
@@ -44,10 +44,10 @@ async function refreshAccessToken(params) {
|
|
|
44
44
|
Accept: "application/json",
|
|
45
45
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
46
46
|
};
|
|
47
|
-
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret}`).toString("base64")}`;
|
|
47
|
+
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret ?? ""}`).toString("base64")}`;
|
|
48
48
|
else {
|
|
49
49
|
body.append("client_id", params.clientId);
|
|
50
|
-
body.append("client_secret", params.clientSecret);
|
|
50
|
+
if (params.clientSecret) body.append("client_secret", params.clientSecret);
|
|
51
51
|
}
|
|
52
52
|
return fetch(params.tokenUri, {
|
|
53
53
|
method: "POST",
|
|
@@ -62,10 +62,10 @@ async function revokeToken(params) {
|
|
|
62
62
|
Accept: "application/json",
|
|
63
63
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
64
64
|
};
|
|
65
|
-
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret}`).toString("base64")}`;
|
|
65
|
+
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret ?? ""}`).toString("base64")}`;
|
|
66
66
|
else {
|
|
67
67
|
body.append("client_id", params.clientId);
|
|
68
|
-
body.append("client_secret", params.clientSecret);
|
|
68
|
+
if (params.clientSecret) body.append("client_secret", params.clientSecret);
|
|
69
69
|
}
|
|
70
70
|
const response = await fetch(params.tokenRevokeUri, {
|
|
71
71
|
method: "POST",
|
|
@@ -78,13 +78,17 @@ async function revokeToken(params) {
|
|
|
78
78
|
throw new require_oauth2_error.OAuth2Error(response.status, "invalid_request", "Failed to revoke token");
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
async function verifyIdToken(idToken,
|
|
81
|
+
async function verifyIdToken(idToken, options) {
|
|
82
|
+
const { jwkSetUri, issuer, audience } = options;
|
|
82
83
|
try {
|
|
83
84
|
const jwks = (0, jose.createRemoteJWKSet)(new URL(jwkSetUri));
|
|
84
|
-
const { payload } = await (0, jose.jwtVerify)(idToken, jwks
|
|
85
|
+
const { payload } = await (0, jose.jwtVerify)(idToken, jwks, {
|
|
86
|
+
issuer,
|
|
87
|
+
audience
|
|
88
|
+
});
|
|
85
89
|
return payload;
|
|
86
|
-
} catch {
|
|
87
|
-
console.error("Failed to verify id_token");
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("Failed to verify id_token", error);
|
|
88
92
|
throw new require_oauth2_error.OAuth2Error(400, "invalid_request", "Failed to verify id_token");
|
|
89
93
|
}
|
|
90
94
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.cjs","names":["OAuth2Error","createRemoteJWKSet","jwtVerify"],"sources":["../../../src/oauth2/provider/common.ts"],"sourcesContent":["import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { OAuth2Error } from '../error';\nimport type { ExchangeCodeParams, PkceParameters, RefreshTokenParams } from '../types';\n\nexport function createAuthorizationUri(options: {\n state: string;\n scope: string[];\n clientId: string;\n redirectUri: string;\n authorizationUri: string;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}) {\n const url = new URL(options.authorizationUri);\n url.searchParams.append('response_type', 'code');\n url.searchParams.append('client_id', options.clientId);\n url.searchParams.append('state', options.state);\n url.searchParams.append('scope', options.scope.join(' '));\n url.searchParams.append('redirect_uri', options.redirectUri);\n if (options.pkce) {\n url.searchParams.append('code_challenge_method', options.pkce.code_challenge_method);\n url.searchParams.append('code_challenge', options.pkce.code_challenge);\n }\n return url;\n}\n\nexport async function exchangeAuthorizationCode(\n params: ExchangeCodeParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('code', params.code);\n body.append('redirect_uri', params.redirectUri);\n body.append('grant_type', 'authorization_code');\n if (params.pkce) body.append('code_verifier', params.pkce.code_verifier);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n // Public clients (Google's installed-app clients, PKCE-only clients) have no secret.\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\nexport async function refreshAccessToken(\n params: RefreshTokenParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('grant_type', 'refresh_token');\n body.append('refresh_token', params.refreshToken);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\ninterface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret
|
|
1
|
+
{"version":3,"file":"common.cjs","names":["OAuth2Error","createRemoteJWKSet","jwtVerify"],"sources":["../../../src/oauth2/provider/common.ts"],"sourcesContent":["import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { OAuth2Error } from '../error';\nimport type { ExchangeCodeParams, PkceParameters, RefreshTokenParams } from '../types';\n\nexport function createAuthorizationUri(options: {\n state: string;\n scope: string[];\n clientId: string;\n redirectUri: string;\n authorizationUri: string;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}) {\n const url = new URL(options.authorizationUri);\n url.searchParams.append('response_type', 'code');\n url.searchParams.append('client_id', options.clientId);\n url.searchParams.append('state', options.state);\n url.searchParams.append('scope', options.scope.join(' '));\n url.searchParams.append('redirect_uri', options.redirectUri);\n if (options.pkce) {\n url.searchParams.append('code_challenge_method', options.pkce.code_challenge_method);\n url.searchParams.append('code_challenge', options.pkce.code_challenge);\n }\n return url;\n}\n\nexport async function exchangeAuthorizationCode(\n params: ExchangeCodeParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('code', params.code);\n body.append('redirect_uri', params.redirectUri);\n body.append('grant_type', 'authorization_code');\n if (params.pkce) body.append('code_verifier', params.pkce.code_verifier);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n // Public clients (Google's installed-app clients, PKCE-only clients) have no secret.\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\nexport async function refreshAccessToken(\n params: RefreshTokenParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('grant_type', 'refresh_token');\n body.append('refresh_token', params.refreshToken);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\ninterface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret?: string;\n tokenRevokeUri: string;\n authentication?: 'basic' | 'post';\n}\n\nexport async function revokeToken(params: RevokeTokenParams) {\n const body = new URLSearchParams();\n body.append('token', params.token);\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n const response = await fetch(params.tokenRevokeUri, { method: 'POST', headers, body });\n if (!response.ok) {\n const error = await response.json();\n console.error('Refresh token error:', error);\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to revoke token');\n }\n}\n\nexport interface VerifyIdTokenOptions {\n jwkSetUri: string;\n /** The `iss` values the provider signs with; anything else is refused. */\n issuer: string | string[];\n /**\n * The client the token must be issued to. An id token proves who the user is only to the client\n * it names in `aud`: any other app on the same identity provider can obtain a validly signed one\n * for the same user, so a token for a foreign client must never sign anyone in here.\n */\n audience: string;\n}\n\nexport async function verifyIdToken<T>(idToken: string, options: VerifyIdTokenOptions) {\n const { jwkSetUri, issuer, audience } = options;\n try {\n const jwks = createRemoteJWKSet(new URL(jwkSetUri));\n const { payload } = await jwtVerify(idToken, jwks, { issuer, audience });\n return payload as T;\n } catch (error) {\n console.error('Failed to verify id_token', error);\n throw new OAuth2Error(400, 'invalid_request', 'Failed to verify id_token');\n }\n}\n"],"mappings":"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"}
|
|
@@ -19,12 +19,23 @@ export declare function refreshAccessToken(params: RefreshTokenParams & {
|
|
|
19
19
|
interface RevokeTokenParams {
|
|
20
20
|
token: string;
|
|
21
21
|
clientId: string;
|
|
22
|
-
clientSecret
|
|
22
|
+
clientSecret?: string;
|
|
23
23
|
tokenRevokeUri: string;
|
|
24
24
|
authentication?: 'basic' | 'post';
|
|
25
25
|
}
|
|
26
26
|
export declare function revokeToken(params: RevokeTokenParams): Promise<void>;
|
|
27
|
-
export
|
|
27
|
+
export interface VerifyIdTokenOptions {
|
|
28
|
+
jwkSetUri: string;
|
|
29
|
+
/** The `iss` values the provider signs with; anything else is refused. */
|
|
30
|
+
issuer: string | string[];
|
|
31
|
+
/**
|
|
32
|
+
* The client the token must be issued to. An id token proves who the user is only to the client
|
|
33
|
+
* it names in `aud`: any other app on the same identity provider can obtain a validly signed one
|
|
34
|
+
* for the same user, so a token for a foreign client must never sign anyone in here.
|
|
35
|
+
*/
|
|
36
|
+
audience: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function verifyIdToken<T>(idToken: string, options: VerifyIdTokenOptions): Promise<T>;
|
|
28
39
|
//#endregion
|
|
29
40
|
export {};
|
|
30
41
|
//# sourceMappingURL=common.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.cts","names":[],"sources":["../../../src/oauth2/provider/common.ts"],"mappings":";;wBAIgB,uBAAuB;EACrC;EACA;EACA;EACA;EACA;EACA,OAAO,KAAK;IACb;wBAcqB,0BACpB,QAAQ;EACN;EACA;IACD,QAAA;wBAwBmB,mBACpB,QAAQ;EACN;EACA;IACD,QAAA;UAsBO;EACR;EACA;EACA;EACA;EACA;;wBAGoB,YAAY,QAAQ,oBAAiB;
|
|
1
|
+
{"version":3,"file":"common.d.cts","names":[],"sources":["../../../src/oauth2/provider/common.ts"],"mappings":";;wBAIgB,uBAAuB;EACrC;EACA;EACA;EACA;EACA;EACA,OAAO,KAAK;IACb;wBAcqB,0BACpB,QAAQ;EACN;EACA;IACD,QAAA;wBAwBmB,mBACpB,QAAQ;EACN;EACA;IACD,QAAA;UAsBO;EACR;EACA;EACA;EACA;EACA;;wBAGoB,YAAY,QAAQ,oBAAiB;iBAwB1C;EACf;;EAEA;;;;;;EAMA;;wBAGoB,cAAc,GAAG,iBAAiB,SAAS,uBAAoB,QAAA"}
|
|
@@ -19,12 +19,23 @@ export declare function refreshAccessToken(params: RefreshTokenParams & {
|
|
|
19
19
|
interface RevokeTokenParams {
|
|
20
20
|
token: string;
|
|
21
21
|
clientId: string;
|
|
22
|
-
clientSecret
|
|
22
|
+
clientSecret?: string;
|
|
23
23
|
tokenRevokeUri: string;
|
|
24
24
|
authentication?: 'basic' | 'post';
|
|
25
25
|
}
|
|
26
26
|
export declare function revokeToken(params: RevokeTokenParams): Promise<void>;
|
|
27
|
-
export
|
|
27
|
+
export interface VerifyIdTokenOptions {
|
|
28
|
+
jwkSetUri: string;
|
|
29
|
+
/** The `iss` values the provider signs with; anything else is refused. */
|
|
30
|
+
issuer: string | string[];
|
|
31
|
+
/**
|
|
32
|
+
* The client the token must be issued to. An id token proves who the user is only to the client
|
|
33
|
+
* it names in `aud`: any other app on the same identity provider can obtain a validly signed one
|
|
34
|
+
* for the same user, so a token for a foreign client must never sign anyone in here.
|
|
35
|
+
*/
|
|
36
|
+
audience: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function verifyIdToken<T>(idToken: string, options: VerifyIdTokenOptions): Promise<T>;
|
|
28
39
|
//#endregion
|
|
29
40
|
export {};
|
|
30
41
|
//# sourceMappingURL=common.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.mts","names":[],"sources":["../../../src/oauth2/provider/common.ts"],"mappings":";;wBAIgB,uBAAuB;EACrC;EACA;EACA;EACA;EACA;EACA,OAAO,KAAK;IACb;wBAcqB,0BACpB,QAAQ;EACN;EACA;IACD,QAAA;wBAwBmB,mBACpB,QAAQ;EACN;EACA;IACD,QAAA;UAsBO;EACR;EACA;EACA;EACA;EACA;;wBAGoB,YAAY,QAAQ,oBAAiB;
|
|
1
|
+
{"version":3,"file":"common.d.mts","names":[],"sources":["../../../src/oauth2/provider/common.ts"],"mappings":";;wBAIgB,uBAAuB;EACrC;EACA;EACA;EACA;EACA;EACA,OAAO,KAAK;IACb;wBAcqB,0BACpB,QAAQ;EACN;EACA;IACD,QAAA;wBAwBmB,mBACpB,QAAQ;EACN;EACA;IACD,QAAA;UAsBO;EACR;EACA;EACA;EACA;EACA;;wBAGoB,YAAY,QAAQ,oBAAiB;iBAwB1C;EACf;;EAEA;;;;;;EAMA;;wBAGoB,cAAc,GAAG,iBAAiB,SAAS,uBAAoB,QAAA"}
|
|
@@ -24,7 +24,7 @@ async function exchangeAuthorizationCode(params) {
|
|
|
24
24
|
Accept: "application/json",
|
|
25
25
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
26
26
|
};
|
|
27
|
-
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret}`).toString("base64")}`;
|
|
27
|
+
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret ?? ""}`).toString("base64")}`;
|
|
28
28
|
else {
|
|
29
29
|
body.append("client_id", params.clientId);
|
|
30
30
|
if (params.clientSecret) body.append("client_secret", params.clientSecret);
|
|
@@ -43,10 +43,10 @@ async function refreshAccessToken(params) {
|
|
|
43
43
|
Accept: "application/json",
|
|
44
44
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
45
45
|
};
|
|
46
|
-
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret}`).toString("base64")}`;
|
|
46
|
+
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret ?? ""}`).toString("base64")}`;
|
|
47
47
|
else {
|
|
48
48
|
body.append("client_id", params.clientId);
|
|
49
|
-
body.append("client_secret", params.clientSecret);
|
|
49
|
+
if (params.clientSecret) body.append("client_secret", params.clientSecret);
|
|
50
50
|
}
|
|
51
51
|
return fetch(params.tokenUri, {
|
|
52
52
|
method: "POST",
|
|
@@ -61,10 +61,10 @@ async function revokeToken(params) {
|
|
|
61
61
|
Accept: "application/json",
|
|
62
62
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
63
63
|
};
|
|
64
|
-
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret}`).toString("base64")}`;
|
|
64
|
+
if (params.authentication === "basic") headers.Authorization = `Basic ${Buffer.from(`${params.clientId}:${params.clientSecret ?? ""}`).toString("base64")}`;
|
|
65
65
|
else {
|
|
66
66
|
body.append("client_id", params.clientId);
|
|
67
|
-
body.append("client_secret", params.clientSecret);
|
|
67
|
+
if (params.clientSecret) body.append("client_secret", params.clientSecret);
|
|
68
68
|
}
|
|
69
69
|
const response = await fetch(params.tokenRevokeUri, {
|
|
70
70
|
method: "POST",
|
|
@@ -77,13 +77,17 @@ async function revokeToken(params) {
|
|
|
77
77
|
throw new OAuth2Error(response.status, "invalid_request", "Failed to revoke token");
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
async function verifyIdToken(idToken,
|
|
80
|
+
async function verifyIdToken(idToken, options) {
|
|
81
|
+
const { jwkSetUri, issuer, audience } = options;
|
|
81
82
|
try {
|
|
82
83
|
const jwks = createRemoteJWKSet(new URL(jwkSetUri));
|
|
83
|
-
const { payload } = await jwtVerify(idToken, jwks
|
|
84
|
+
const { payload } = await jwtVerify(idToken, jwks, {
|
|
85
|
+
issuer,
|
|
86
|
+
audience
|
|
87
|
+
});
|
|
84
88
|
return payload;
|
|
85
|
-
} catch {
|
|
86
|
-
console.error("Failed to verify id_token");
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error("Failed to verify id_token", error);
|
|
87
91
|
throw new OAuth2Error(400, "invalid_request", "Failed to verify id_token");
|
|
88
92
|
}
|
|
89
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.mjs","names":[],"sources":["../../../src/oauth2/provider/common.ts"],"sourcesContent":["import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { OAuth2Error } from '../error';\nimport type { ExchangeCodeParams, PkceParameters, RefreshTokenParams } from '../types';\n\nexport function createAuthorizationUri(options: {\n state: string;\n scope: string[];\n clientId: string;\n redirectUri: string;\n authorizationUri: string;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}) {\n const url = new URL(options.authorizationUri);\n url.searchParams.append('response_type', 'code');\n url.searchParams.append('client_id', options.clientId);\n url.searchParams.append('state', options.state);\n url.searchParams.append('scope', options.scope.join(' '));\n url.searchParams.append('redirect_uri', options.redirectUri);\n if (options.pkce) {\n url.searchParams.append('code_challenge_method', options.pkce.code_challenge_method);\n url.searchParams.append('code_challenge', options.pkce.code_challenge);\n }\n return url;\n}\n\nexport async function exchangeAuthorizationCode(\n params: ExchangeCodeParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('code', params.code);\n body.append('redirect_uri', params.redirectUri);\n body.append('grant_type', 'authorization_code');\n if (params.pkce) body.append('code_verifier', params.pkce.code_verifier);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n // Public clients (Google's installed-app clients, PKCE-only clients) have no secret.\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\nexport async function refreshAccessToken(\n params: RefreshTokenParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('grant_type', 'refresh_token');\n body.append('refresh_token', params.refreshToken);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\ninterface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret
|
|
1
|
+
{"version":3,"file":"common.mjs","names":[],"sources":["../../../src/oauth2/provider/common.ts"],"sourcesContent":["import { createRemoteJWKSet, jwtVerify } from 'jose';\nimport { OAuth2Error } from '../error';\nimport type { ExchangeCodeParams, PkceParameters, RefreshTokenParams } from '../types';\n\nexport function createAuthorizationUri(options: {\n state: string;\n scope: string[];\n clientId: string;\n redirectUri: string;\n authorizationUri: string;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}) {\n const url = new URL(options.authorizationUri);\n url.searchParams.append('response_type', 'code');\n url.searchParams.append('client_id', options.clientId);\n url.searchParams.append('state', options.state);\n url.searchParams.append('scope', options.scope.join(' '));\n url.searchParams.append('redirect_uri', options.redirectUri);\n if (options.pkce) {\n url.searchParams.append('code_challenge_method', options.pkce.code_challenge_method);\n url.searchParams.append('code_challenge', options.pkce.code_challenge);\n }\n return url;\n}\n\nexport async function exchangeAuthorizationCode(\n params: ExchangeCodeParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('code', params.code);\n body.append('redirect_uri', params.redirectUri);\n body.append('grant_type', 'authorization_code');\n if (params.pkce) body.append('code_verifier', params.pkce.code_verifier);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n // Public clients (Google's installed-app clients, PKCE-only clients) have no secret.\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\nexport async function refreshAccessToken(\n params: RefreshTokenParams & {\n tokenUri: string;\n authentication?: 'basic' | 'post';\n }\n) {\n const body = new URLSearchParams();\n body.append('grant_type', 'refresh_token');\n body.append('refresh_token', params.refreshToken);\n\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n return fetch(params.tokenUri, { method: 'POST', headers, body });\n}\n\ninterface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret?: string;\n tokenRevokeUri: string;\n authentication?: 'basic' | 'post';\n}\n\nexport async function revokeToken(params: RevokeTokenParams) {\n const body = new URLSearchParams();\n body.append('token', params.token);\n const headers: Record<string, string> = {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n };\n\n if (params.authentication === 'basic') {\n const token = Buffer.from(`${params.clientId}:${params.clientSecret ?? ''}`).toString('base64');\n headers.Authorization = `Basic ${token}`;\n } else {\n body.append('client_id', params.clientId);\n if (params.clientSecret) body.append('client_secret', params.clientSecret);\n }\n\n const response = await fetch(params.tokenRevokeUri, { method: 'POST', headers, body });\n if (!response.ok) {\n const error = await response.json();\n console.error('Refresh token error:', error);\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to revoke token');\n }\n}\n\nexport interface VerifyIdTokenOptions {\n jwkSetUri: string;\n /** The `iss` values the provider signs with; anything else is refused. */\n issuer: string | string[];\n /**\n * The client the token must be issued to. An id token proves who the user is only to the client\n * it names in `aud`: any other app on the same identity provider can obtain a validly signed one\n * for the same user, so a token for a foreign client must never sign anyone in here.\n */\n audience: string;\n}\n\nexport async function verifyIdToken<T>(idToken: string, options: VerifyIdTokenOptions) {\n const { jwkSetUri, issuer, audience } = options;\n try {\n const jwks = createRemoteJWKSet(new URL(jwkSetUri));\n const { payload } = await jwtVerify(idToken, jwks, { issuer, audience });\n return payload as T;\n } catch (error) {\n console.error('Failed to verify id_token', error);\n throw new OAuth2Error(400, 'invalid_request', 'Failed to verify id_token');\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_oauth2_error = require("../error.cjs");
|
|
3
3
|
const require_oauth2_provider_common = require("./common.cjs");
|
|
4
|
+
let node_crypto = require("node:crypto");
|
|
4
5
|
let _shware_utils = require("@shware/utils");
|
|
5
6
|
//#region src/oauth2/provider/facebook.ts
|
|
6
7
|
/**
|
|
8
|
+
* `iss` of Limited Login id tokens, per https://limited.facebook.com/.well-known/openid-configuration/
|
|
9
|
+
* (their `aud` is the app id).
|
|
10
|
+
*/
|
|
11
|
+
const FACEBOOK_ISSUER = "https://www.facebook.com";
|
|
12
|
+
/**
|
|
7
13
|
* ref: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/
|
|
8
14
|
* ref: https://developers.facebook.com/tools/explorer
|
|
9
15
|
* */
|
|
@@ -33,10 +39,14 @@ function createFacebookProvider() {
|
|
|
33
39
|
}
|
|
34
40
|
return await response.json();
|
|
35
41
|
},
|
|
36
|
-
async getUserInfo({ id_token, access_token }) {
|
|
42
|
+
async getUserInfo({ id_token, access_token }, { clientId }) {
|
|
37
43
|
(0, _shware_utils.invariant)(this.jwkSetUri, "jwkSetUri is required");
|
|
38
44
|
if (id_token) {
|
|
39
|
-
const data = await require_oauth2_provider_common.verifyIdToken(id_token,
|
|
45
|
+
const data = await require_oauth2_provider_common.verifyIdToken(id_token, {
|
|
46
|
+
jwkSetUri: this.jwkSetUri,
|
|
47
|
+
issuer: FACEBOOK_ISSUER,
|
|
48
|
+
audience: clientId
|
|
49
|
+
});
|
|
40
50
|
return {
|
|
41
51
|
data,
|
|
42
52
|
claims: {
|
|
@@ -50,45 +60,59 @@ function createFacebookProvider() {
|
|
|
50
60
|
}
|
|
51
61
|
(0, _shware_utils.invariant)(access_token, "access_token is required");
|
|
52
62
|
(0, _shware_utils.invariant)(this.userInfoUri, "userInfoUri is required");
|
|
53
|
-
|
|
54
|
-
const fields = [
|
|
55
|
-
"id",
|
|
56
|
-
"name",
|
|
57
|
-
"email",
|
|
58
|
-
"picture.width(320).height(320)"
|
|
59
|
-
];
|
|
60
|
-
url.searchParams.append("access_token", access_token);
|
|
61
|
-
url.searchParams.append("fields", fields.join(","));
|
|
62
|
-
const response = await fetch(url.href, { headers: { Accept: "application/json" } });
|
|
63
|
-
if (!response.ok) throw new require_oauth2_error.OAuth2Error(response.status, "invalid_request", "Failed to fetch user info");
|
|
64
|
-
const data = await response.json();
|
|
65
|
-
return {
|
|
66
|
-
data,
|
|
67
|
-
claims: {
|
|
68
|
-
sub: data.id,
|
|
69
|
-
name: data.name,
|
|
70
|
-
email: data.email,
|
|
71
|
-
picture: data.picture.data.url,
|
|
72
|
-
email_verified: data.email_verified
|
|
73
|
-
}
|
|
74
|
-
};
|
|
63
|
+
return fetchUserInfo(this.userInfoUri, access_token);
|
|
75
64
|
},
|
|
76
|
-
async loginOAuth2Native({ credentials }) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
65
|
+
async loginOAuth2Native({ clientId, clientSecret, credentials }) {
|
|
66
|
+
const { id_token, access_token } = credentials;
|
|
67
|
+
let userInfo;
|
|
68
|
+
if (id_token) userInfo = await this.getUserInfo({
|
|
69
|
+
token_type: "bearer",
|
|
70
|
+
access_token: "",
|
|
71
|
+
id_token
|
|
72
|
+
}, { clientId });
|
|
73
|
+
else if (access_token) {
|
|
74
|
+
if (!clientSecret) throw new require_oauth2_error.OAuth2Error(400, "invalid_client", "access_token login needs the app secret");
|
|
75
|
+
(0, _shware_utils.invariant)(this.userInfoUri, "userInfoUri is required");
|
|
76
|
+
const proof = (0, node_crypto.createHmac)("sha256", clientSecret).update(access_token).digest("hex");
|
|
77
|
+
userInfo = await fetchUserInfo(this.userInfoUri, access_token, proof);
|
|
87
78
|
} else throw new require_oauth2_error.OAuth2Error(400, "invalid_request", "id_token or access_token is required");
|
|
79
|
+
return {
|
|
80
|
+
token: {
|
|
81
|
+
token_type: "bearer",
|
|
82
|
+
access_token: access_token ?? "",
|
|
83
|
+
id_token
|
|
84
|
+
},
|
|
85
|
+
userInfo
|
|
86
|
+
};
|
|
88
87
|
}
|
|
89
88
|
};
|
|
90
89
|
}
|
|
91
90
|
const facebook = createFacebookProvider();
|
|
91
|
+
async function fetchUserInfo(userInfoUri, accessToken, appSecretProof) {
|
|
92
|
+
const url = new URL(userInfoUri);
|
|
93
|
+
const fields = [
|
|
94
|
+
"id",
|
|
95
|
+
"name",
|
|
96
|
+
"email",
|
|
97
|
+
"picture.width(320).height(320)"
|
|
98
|
+
];
|
|
99
|
+
url.searchParams.append("access_token", accessToken);
|
|
100
|
+
url.searchParams.append("fields", fields.join(","));
|
|
101
|
+
if (appSecretProof) url.searchParams.append("appsecret_proof", appSecretProof);
|
|
102
|
+
const response = await fetch(url.href, { headers: { Accept: "application/json" } });
|
|
103
|
+
if (!response.ok) throw new require_oauth2_error.OAuth2Error(response.status, "invalid_request", "Failed to fetch user info");
|
|
104
|
+
const data = await response.json();
|
|
105
|
+
return {
|
|
106
|
+
data,
|
|
107
|
+
claims: {
|
|
108
|
+
sub: data.id,
|
|
109
|
+
name: data.name,
|
|
110
|
+
email: data.email,
|
|
111
|
+
picture: data.picture.data.url,
|
|
112
|
+
email_verified: data.email_verified
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
92
116
|
//#endregion
|
|
93
117
|
exports.createFacebookProvider = createFacebookProvider;
|
|
94
118
|
exports.facebook = facebook;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facebook.cjs","names":["createAuthorizationUri","exchangeAuthorizationCode","OAuth2Error","verifyIdToken"],"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { LoginOAuth2NativeParams, OAuth2Token, Provider } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n/**\n * ref: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/\n * ref: https://developers.facebook.com/tools/explorer\n * */\nexport function createFacebookProvider(): Provider {\n return {\n authorizationUri: 'https://www.facebook.com/v21.0/dialog/oauth',\n tokenUri: 'https://graph.facebook.com/v19.0/oauth/access_token',\n userInfoUri: 'https://graph.facebook.com/me',\n jwkSetUri: 'https://limited.facebook.com/.well-known/oauth/openid/jwks/',\n userNameAttribute: 'id',\n defaultScope: ['profile', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as FacebookErrorResponse;\n throw new OAuth2Error(response.status, 'invalid_request', error.message);\n }\n return (await response.json()) as FacebookToken;\n },\n async getUserInfo({ id_token, access_token }) {\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n if (id_token) {\n const data = await verifyIdToken<FacebookDecodedIdToken>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: true,\n },\n };\n }\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n\n const
|
|
1
|
+
{"version":3,"file":"facebook.cjs","names":["createAuthorizationUri","exchangeAuthorizationCode","OAuth2Error","verifyIdToken","createHmac"],"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import { createHmac } from 'node:crypto';\nimport { invariant } from '@shware/utils';\nimport { OAuth2Error } from '../error';\nimport type { LoginOAuth2NativeParams, OAuth2Token, Provider, UserInfo } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\n/**\n * `iss` of Limited Login id tokens, per https://limited.facebook.com/.well-known/openid-configuration/\n * (their `aud` is the app id).\n */\nconst FACEBOOK_ISSUER = 'https://www.facebook.com';\n\n/**\n * ref: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/\n * ref: https://developers.facebook.com/tools/explorer\n * */\nexport function createFacebookProvider(): Provider {\n return {\n authorizationUri: 'https://www.facebook.com/v21.0/dialog/oauth',\n tokenUri: 'https://graph.facebook.com/v19.0/oauth/access_token',\n userInfoUri: 'https://graph.facebook.com/me',\n jwkSetUri: 'https://limited.facebook.com/.well-known/oauth/openid/jwks/',\n userNameAttribute: 'id',\n defaultScope: ['profile', 'email'],\n createAuthorizationUri({ pkce: _, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce: _, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error } = (await response.json()) as FacebookErrorResponse;\n throw new OAuth2Error(response.status, 'invalid_request', error.message);\n }\n return (await response.json()) as FacebookToken;\n },\n async getUserInfo({ id_token, access_token }, { clientId }) {\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n if (id_token) {\n const data = await verifyIdToken<FacebookDecodedIdToken>(id_token, {\n jwkSetUri: this.jwkSetUri,\n issuer: FACEBOOK_ISSUER,\n audience: clientId,\n });\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: true,\n },\n };\n }\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n return fetchUserInfo(this.userInfoUri, access_token);\n },\n async loginOAuth2Native({ clientId, clientSecret, credentials }: LoginOAuth2NativeParams) {\n const { id_token, access_token } = credentials;\n let userInfo: UserInfo;\n if (id_token) {\n // Limited Login: the id token's `aud` must be this app.\n userInfo = await this.getUserInfo(\n { token_type: 'bearer', access_token: '', id_token },\n {\n clientId,\n }\n );\n } else if (access_token) {\n // The Graph API serves any app's user access token, so `/me` alone would accept a token a\n // foreign app obtained for the same user. With `appsecret_proof` Facebook refuses tokens\n // not issued to the app whose secret signed the proof, binding the token to this client.\n if (!clientSecret) {\n throw new OAuth2Error(400, 'invalid_client', 'access_token login needs the app secret');\n }\n invariant(this.userInfoUri, 'userInfoUri is required');\n const proof = createHmac('sha256', clientSecret).update(access_token).digest('hex');\n userInfo = await fetchUserInfo(this.userInfoUri, access_token, proof);\n } else {\n throw new OAuth2Error(400, 'invalid_request', 'id_token or access_token is required');\n }\n const token: OAuth2Token = {\n token_type: 'bearer',\n access_token: access_token ?? '',\n id_token,\n };\n return { token, userInfo };\n },\n };\n}\n\nexport const facebook = createFacebookProvider();\n\nasync function fetchUserInfo(\n userInfoUri: string,\n accessToken: string,\n appSecretProof?: string\n): Promise<UserInfo<FacebookUserInfo>> {\n const url = new URL(userInfoUri);\n const fields = ['id', 'name', 'email', 'picture.width(320).height(320)'];\n url.searchParams.append('access_token', accessToken);\n url.searchParams.append('fields', fields.join(','));\n if (appSecretProof) url.searchParams.append('appsecret_proof', appSecretProof);\n const headers = { Accept: 'application/json' };\n const response = await fetch(url.href, { headers });\n if (!response.ok) {\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to fetch user info');\n }\n const data = (await response.json()) as FacebookUserInfo;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name,\n email: data.email,\n picture: data.picture.data.url,\n email_verified: data.email_verified,\n },\n };\n}\n\nexport interface FacebookDecodedIdToken {\n sub: string;\n name: string;\n email: string;\n picture: string;\n nonce?: string;\n}\n\nexport interface FacebookUserInfo {\n id: string;\n name: string;\n email: string;\n email_verified: boolean;\n picture: {\n data: {\n url: string;\n width: number;\n height: number;\n is_silhouette: boolean;\n };\n };\n}\n\nexport interface FacebookToken extends OAuth2Token {\n access_token: string;\n token_type: 'bearer';\n expires_in: number;\n id_token?: string;\n}\n\nexport type FacebookAppCredential =\n | { state: string; id_token: string } // platform=ios and tracking=limited\n | { state: string; access_token: string }; // tracking=enabled\n\ninterface FacebookErrorResponse {\n error: {\n message: string;\n type: 'OAuthException';\n code: number;\n error_subcode: number;\n fbtrace_id: string;\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facebook.d.cts","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"facebook.d.cts","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"mappings":";;;;;;wBAgBgB,0BAA0B;qBAgF7B,UAAQ;iBA8BJ;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;IACE;MACE;MACA;MACA;MACA;;;;iBAKW,sBAAsB;EACrC;EACA;EACA;EACA;;YAGU;EACN;EAAe;;EACf;EAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facebook.d.mts","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"facebook.d.mts","names":[],"sources":["../../../src/oauth2/provider/facebook.ts"],"mappings":";;;;;;wBAgBgB,0BAA0B;qBAgF7B,UAAQ;iBA8BJ;EACf;EACA;EACA;EACA;EACA;;iBAGe;EACf;EACA;EACA;EACA;EACA;IACE;MACE;MACA;MACA;MACA;;;;iBAKW,sBAAsB;EACrC;EACA;EACA;EACA;;YAGU;EACN;EAAe;;EACf;EAAe"}
|