{"version":3,"file":"apple.mjs","names":["name: string"],"sources":["../../src/social-providers/apple.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport { APIError } from \"better-call\";\nimport { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from \"jose\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\nexport interface AppleProfile {\n\t/**\n\t * The subject registered claim identifies the principal that’s the subject\n\t * of the identity token. Because this token is for your app, the value is\n\t * the unique identifier for the user.\n\t */\n\tsub: string;\n\t/**\n\t * A String value representing the user's email address.\n\t * The email address is either the user's real email address or the proxy\n\t * address, depending on their status private email relay service.\n\t */\n\temail: string;\n\t/**\n\t * A string or Boolean value that indicates whether the service verifies\n\t * the email. The value can either be a string (\"true\" or \"false\") or a\n\t * Boolean (true or false). The system may not verify email addresses for\n\t * Sign in with Apple at Work & School users, and this claim is \"false\" or\n\t * false for those users.\n\t */\n\temail_verified: true | \"true\";\n\t/**\n\t * A string or Boolean value that indicates whether the email that the user\n\t * shares is the proxy address. The value can either be a string (\"true\" or\n\t * \"false\") or a Boolean (true or false).\n\t */\n\tis_private_email: boolean;\n\t/**\n\t * An Integer value that indicates whether the user appears to be a real\n\t * person. Use the value of this claim to mitigate fraud. The possible\n\t * values are: 0 (or Unsupported), 1 (or Unknown), 2 (or LikelyReal). For\n\t * more information, see ASUserDetectionStatus. This claim is present only\n\t * in iOS 14 and later, macOS 11 and later, watchOS 7 and later, tvOS 14\n\t * and later. The claim isn’t present or supported for web-based apps.\n\t */\n\treal_user_status: number;\n\t/**\n\t * The user’s full name in the format provided during the authorization\n\t * process.\n\t */\n\tname: string;\n\t/**\n\t * The URL to the user's profile picture.\n\t */\n\tpicture: string;\n\tuser?: AppleNonConformUser | undefined;\n}\n\n/**\n * This is the shape of the `user` query parameter that Apple sends the first\n * time the user consents to the app.\n * @see https://developer.apple.com/documentation/signinwithapplerestapi/request-an-authorization-to-the-sign-in-with-apple-server./\n */\nexport interface AppleNonConformUser {\n\tname: {\n\t\tfirstName: string;\n\t\tlastName: string;\n\t};\n\temail: string;\n}\n\nexport interface AppleOptions extends ProviderOptions {\n\tclientId: string;\n\tappBundleIdentifier?: string | undefined;\n\taudience?: (string | string[]) | undefined;\n}\n\nexport const apple = (options: AppleOptions) => {\n\tconst tokenEndpoint = \"https://appleid.apple.com/auth/token\";\n\treturn {\n\t\tid: \"apple\",\n\t\tname: \"Apple\",\n\t\tasync createAuthorizationURL({ state, scopes, redirectURI }) {\n\t\t\tconst _scope = options.disableDefaultScope ? [] : [\"email\", \"name\"];\n\t\t\tif (options.scope) _scope.push(...options.scope);\n\t\t\tif (scopes) _scope.push(...scopes);\n\t\t\tconst url = await createAuthorizationURL({\n\t\t\t\tid: \"apple\",\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint: \"https://appleid.apple.com/auth/authorize\",\n\t\t\t\tscopes: _scope,\n\t\t\t\tstate,\n\t\t\t\tredirectURI,\n\t\t\t\tresponseMode: \"form_post\",\n\t\t\t\tresponseType: \"code id_token\",\n\t\t\t});\n\t\t\treturn url;\n\t\t},\n\t\tvalidateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\ttokenEndpoint,\n\t\t\t});\n\t\t},\n\t\tasync verifyIdToken(token, nonce) {\n\t\t\tif (options.disableIdTokenSignIn) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (options.verifyIdToken) {\n\t\t\t\treturn options.verifyIdToken(token, nonce);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst decodedHeader = decodeProtectedHeader(token);\n\t\t\t\tconst { kid, alg: jwtAlg } = decodedHeader;\n\t\t\t\tif (!kid || !jwtAlg) return false;\n\t\t\t\tconst publicKey = await getApplePublicKey(kid);\n\t\t\t\tconst { payload: jwtClaims } = await jwtVerify(token, publicKey, {\n\t\t\t\t\talgorithms: [jwtAlg],\n\t\t\t\t\tissuer: \"https://appleid.apple.com\",\n\t\t\t\t\taudience:\n\t\t\t\t\t\toptions.audience && options.audience.length\n\t\t\t\t\t\t\t? options.audience\n\t\t\t\t\t\t\t: options.appBundleIdentifier\n\t\t\t\t\t\t\t\t? options.appBundleIdentifier\n\t\t\t\t\t\t\t\t: options.clientId,\n\t\t\t\t\tmaxTokenAge: \"1h\",\n\t\t\t\t});\n\t\t\t\t[\"email_verified\", \"is_private_email\"].forEach((field) => {\n\t\t\t\t\tif (jwtClaims[field] !== undefined) {\n\t\t\t\t\t\tjwtClaims[field] = Boolean(jwtClaims[field]);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (nonce && jwtClaims.nonce !== nonce) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn !!jwtClaims;\n\t\t\t} catch {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\t\trefreshAccessToken: options.refreshAccessToken\n\t\t\t? options.refreshAccessToken\n\t\t\t: async (refreshToken) => {\n\t\t\t\t\treturn refreshAccessToken({\n\t\t\t\t\t\trefreshToken,\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tclientId: options.clientId,\n\t\t\t\t\t\t\tclientKey: options.clientKey,\n\t\t\t\t\t\t\tclientSecret: options.clientSecret,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttokenEndpoint: \"https://appleid.apple.com/auth/token\",\n\t\t\t\t\t});\n\t\t\t\t},\n\t\tasync getUserInfo(token) {\n\t\t\tif (options.getUserInfo) {\n\t\t\t\treturn options.getUserInfo(token);\n\t\t\t}\n\t\t\tif (!token.idToken) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst profile = decodeJwt(token.idToken);\n\t\t\tif (!profile) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// TODO: \" \" masking will be removed when the name field is made optional\n\t\t\tlet name: string;\n\t\t\tif (token.user?.name) {\n\t\t\t\tconst firstName = token.user.name.firstName || \"\";\n\t\t\t\tconst lastName = token.user.name.lastName || \"\";\n\t\t\t\tconst fullName = `${firstName} ${lastName}`.trim();\n\t\t\t\tname = fullName || \" \";\n\t\t\t} else {\n\t\t\t\tname = profile.name || \" \";\n\t\t\t}\n\n\t\t\tconst emailVerified =\n\t\t\t\ttypeof profile.email_verified === \"boolean\"\n\t\t\t\t\t? profile.email_verified\n\t\t\t\t\t: profile.email_verified === \"true\";\n\t\t\tconst enrichedProfile = {\n\t\t\t\t...profile,\n\t\t\t\tname,\n\t\t\t};\n\t\t\tconst userMap = await options.mapProfileToUser?.(enrichedProfile);\n\t\t\treturn {\n\t\t\t\tuser: {\n\t\t\t\t\tid: profile.sub,\n\t\t\t\t\tname: enrichedProfile.name,\n\t\t\t\t\temailVerified: emailVerified,\n\t\t\t\t\temail: profile.email,\n\t\t\t\t\t...userMap,\n\t\t\t\t},\n\t\t\t\tdata: enrichedProfile,\n\t\t\t};\n\t\t},\n\t\toptions,\n\t} satisfies OAuthProvider;\n};\n\nexport const getApplePublicKey = async (kid: string) => {\n\tconst APPLE_BASE_URL = \"https://appleid.apple.com\";\n\tconst JWKS_APPLE_URI = \"/auth/keys\";\n\tconst { data } = await betterFetch<{\n\t\tkeys: Array<{\n\t\t\tkid: string;\n\t\t\talg: string;\n\t\t\tkty: string;\n\t\t\tuse: string;\n\t\t\tn: string;\n\t\t\te: string;\n\t\t}>;\n\t}>(`${APPLE_BASE_URL}${JWKS_APPLE_URI}`);\n\tif (!data?.keys) {\n\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\tmessage: \"Keys not found\",\n\t\t});\n\t}\n\tconst jwk = data.keys.find((key) => key.kid === kid);\n\tif (!jwk) {\n\t\tthrow new Error(`JWK with kid ${kid} not found`);\n\t}\n\treturn await importJWK(jwk, jwk.alg);\n};\n"],"mappings":";;;;;;;;;AA4EA,MAAa,SAAS,YAA0B;CAC/C,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,MAAM,uBAAuB,EAAE,OAAO,QAAQ,eAAe;GAC5D,MAAM,SAAS,QAAQ,sBAAsB,EAAE,GAAG,CAAC,SAAS,OAAO;AACnE,OAAI,QAAQ,MAAO,QAAO,KAAK,GAAG,QAAQ,MAAM;AAChD,OAAI,OAAQ,QAAO,KAAK,GAAG,OAAO;AAWlC,UAVY,MAAM,uBAAuB;IACxC,IAAI;IACJ;IACA,uBAAuB;IACvB,QAAQ;IACR;IACA;IACA,cAAc;IACd,cAAc;IACd,CAAC;;EAGH,2BAA2B,OAAO,EAAE,MAAM,cAAc,kBAAkB;AACzE,UAAO,0BAA0B;IAChC;IACA;IACA;IACA;IACA;IACA,CAAC;;EAEH,MAAM,cAAc,OAAO,OAAO;AACjC,OAAI,QAAQ,qBACX,QAAO;AAER,OAAI,QAAQ,cACX,QAAO,QAAQ,cAAc,OAAO,MAAM;AAE3C,OAAI;IAEH,MAAM,EAAE,KAAK,KAAK,WADI,sBAAsB,MAAM;AAElD,QAAI,CAAC,OAAO,CAAC,OAAQ,QAAO;IAE5B,MAAM,EAAE,SAAS,cAAc,MAAM,UAAU,OAD7B,MAAM,kBAAkB,IAAI,EACmB;KAChE,YAAY,CAAC,OAAO;KACpB,QAAQ;KACR,UACC,QAAQ,YAAY,QAAQ,SAAS,SAClC,QAAQ,WACR,QAAQ,sBACP,QAAQ,sBACR,QAAQ;KACb,aAAa;KACb,CAAC;AACF,KAAC,kBAAkB,mBAAmB,CAAC,SAAS,UAAU;AACzD,SAAI,UAAU,WAAW,OACxB,WAAU,SAAS,QAAQ,UAAU,OAAO;MAE5C;AACF,QAAI,SAAS,UAAU,UAAU,MAChC,QAAO;AAER,WAAO,CAAC,CAAC;WACF;AACP,WAAO;;;EAGT,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS;KACR,UAAU,QAAQ;KAClB,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD,eAAe;IACf,CAAC;;EAEL,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;AAElC,OAAI,CAAC,MAAM,QACV,QAAO;GAER,MAAM,UAAU,UAAwB,MAAM,QAAQ;AACtD,OAAI,CAAC,QACJ,QAAO;GAIR,IAAIA;AACJ,OAAI,MAAM,MAAM,KAIf,QADiB,GAFC,MAAM,KAAK,KAAK,aAAa,GAEjB,GADb,MAAM,KAAK,KAAK,YAAY,KACD,MAAM,IAC/B;OAEnB,QAAO,QAAQ,QAAQ;GAGxB,MAAM,gBACL,OAAO,QAAQ,mBAAmB,YAC/B,QAAQ,iBACR,QAAQ,mBAAmB;GAC/B,MAAM,kBAAkB;IACvB,GAAG;IACH;IACA;GACD,MAAM,UAAU,MAAM,QAAQ,mBAAmB,gBAAgB;AACjE,UAAO;IACN,MAAM;KACL,IAAI,QAAQ;KACZ,MAAM,gBAAgB;KACP;KACf,OAAO,QAAQ;KACf,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA;;AAGF,MAAa,oBAAoB,OAAO,QAAgB;CAGvD,MAAM,EAAE,SAAS,MAAM,YASpB,sCAAqC;AACxC,KAAI,CAAC,MAAM,KACV,OAAM,IAAI,SAAS,eAAe,EACjC,SAAS,kBACT,CAAC;CAEH,MAAM,MAAM,KAAK,KAAK,MAAM,QAAQ,IAAI,QAAQ,IAAI;AACpD,KAAI,CAAC,IACJ,OAAM,IAAI,MAAM,gBAAgB,IAAI,YAAY;AAEjD,QAAO,MAAM,UAAU,KAAK,IAAI,IAAI"}