{"version":3,"file":"cognito.mjs","names":[],"sources":["../../src/social-providers/cognito.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport { APIError } from \"better-call\";\nimport { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from \"jose\";\nimport { logger } from \"../env\";\nimport { BetterAuthError } from \"../error\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\n\nexport interface CognitoProfile {\n\tsub: string;\n\temail: string;\n\temail_verified: boolean;\n\tname: string;\n\tgiven_name?: string | undefined;\n\tfamily_name?: string | undefined;\n\tpicture?: string | undefined;\n\tusername?: string | undefined;\n\tlocale?: string | undefined;\n\tphone_number?: string | undefined;\n\tphone_number_verified?: boolean | undefined;\n\taud: string;\n\tiss: string;\n\texp: number;\n\tiat: number;\n\t// Custom attributes from Cognito can be added here\n\t[key: string]: any;\n}\n\nexport interface CognitoOptions extends ProviderOptions {\n\tclientId: string;\n\t/**\n\t * The Cognito domain (e.g., \"your-app.auth.us-east-1.amazoncognito.com\")\n\t */\n\tdomain: string;\n\t/**\n\t * AWS region where User Pool is hosted (e.g., \"us-east-1\")\n\t */\n\tregion: string;\n\tuserPoolId: string;\n\trequireClientSecret?: boolean | undefined;\n}\n\nexport const cognito = (options: CognitoOptions) => {\n\tif (!options.domain || !options.region || !options.userPoolId) {\n\t\tlogger.error(\n\t\t\t\"Domain, region and userPoolId are required for Amazon Cognito. Make sure to provide them in the options.\",\n\t\t);\n\t\tthrow new BetterAuthError(\"DOMAIN_AND_REGION_REQUIRED\");\n\t}\n\n\tconst cleanDomain = options.domain.replace(/^https?:\\/\\//, \"\");\n\tconst authorizationEndpoint = `https://${cleanDomain}/oauth2/authorize`;\n\tconst tokenEndpoint = `https://${cleanDomain}/oauth2/token`;\n\tconst userInfoEndpoint = `https://${cleanDomain}/oauth2/userinfo`;\n\n\treturn {\n\t\tid: \"cognito\",\n\t\tname: \"Cognito\",\n\t\tasync createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {\n\t\t\tif (!options.clientId) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t\"ClientId is required for Amazon Cognito. Make sure to provide them in the options.\",\n\t\t\t\t);\n\t\t\t\tthrow new BetterAuthError(\"CLIENT_ID_AND_SECRET_REQUIRED\");\n\t\t\t}\n\n\t\t\tif (options.requireClientSecret && !options.clientSecret) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t\"Client Secret is required when requireClientSecret is true. Make sure to provide it in the options.\",\n\t\t\t\t);\n\t\t\t\tthrow new BetterAuthError(\"CLIENT_SECRET_REQUIRED\");\n\t\t\t}\n\t\t\tconst _scopes = options.disableDefaultScope\n\t\t\t\t? []\n\t\t\t\t: [\"openid\", \"profile\", \"email\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (scopes) _scopes.push(...scopes);\n\n\t\t\tconst url = await createAuthorizationURL({\n\t\t\t\tid: \"cognito\",\n\t\t\t\toptions: {\n\t\t\t\t\t...options,\n\t\t\t\t},\n\t\t\t\tauthorizationEndpoint,\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI,\n\t\t\t\tprompt: options.prompt,\n\t\t\t});\n\t\t\t// AWS Cognito requires scopes to be encoded with %20 instead of +\n\t\t\t// URLSearchParams encodes spaces as + by default, so we need to fix this\n\t\t\tconst scopeValue = url.searchParams.get(\"scope\");\n\t\t\tif (scopeValue) {\n\t\t\t\turl.searchParams.delete(\"scope\");\n\t\t\t\tconst encodedScope = encodeURIComponent(scopeValue);\n\t\t\t\t// Manually append the scope with proper encoding to the URL\n\t\t\t\tconst urlString = url.toString();\n\t\t\t\tconst separator = urlString.includes(\"?\") ? \"&\" : \"?\";\n\t\t\t\treturn new URL(`${urlString}${separator}scope=${encodedScope}`);\n\t\t\t}\n\t\t\treturn url;\n\t\t},\n\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\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,\n\t\t\t\t\t});\n\t\t\t\t},\n\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\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\n\t\t\t\tconst publicKey = await getCognitoPublicKey(\n\t\t\t\t\tkid,\n\t\t\t\t\toptions.region,\n\t\t\t\t\toptions.userPoolId,\n\t\t\t\t);\n\t\t\t\tconst expectedIssuer = `https://cognito-idp.${options.region}.amazonaws.com/${options.userPoolId}`;\n\n\t\t\t\tconst { payload: jwtClaims } = await jwtVerify(token, publicKey, {\n\t\t\t\t\talgorithms: [jwtAlg],\n\t\t\t\t\tissuer: expectedIssuer,\n\t\t\t\t\taudience: options.clientId,\n\t\t\t\t\tmaxTokenAge: \"1h\",\n\t\t\t\t});\n\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 true;\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\"Failed to verify ID token:\", error);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\n\t\tasync getUserInfo(token) {\n\t\t\tif (options.getUserInfo) {\n\t\t\t\treturn options.getUserInfo(token);\n\t\t\t}\n\n\t\t\tif (token.idToken) {\n\t\t\t\ttry {\n\t\t\t\t\tconst profile = decodeJwt(token.idToken);\n\t\t\t\t\tif (!profile) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tconst name =\n\t\t\t\t\t\tprofile.name ||\n\t\t\t\t\t\tprofile.given_name ||\n\t\t\t\t\t\tprofile.username ||\n\t\t\t\t\t\tprofile.email;\n\t\t\t\t\tconst enrichedProfile = {\n\t\t\t\t\t\t...profile,\n\t\t\t\t\t\tname,\n\t\t\t\t\t};\n\t\t\t\t\tconst userMap = await options.mapProfileToUser?.(enrichedProfile);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\tid: profile.sub,\n\t\t\t\t\t\t\tname: enrichedProfile.name,\n\t\t\t\t\t\t\temail: profile.email,\n\t\t\t\t\t\t\timage: profile.picture,\n\t\t\t\t\t\t\temailVerified: profile.email_verified,\n\t\t\t\t\t\t\t...userMap,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdata: enrichedProfile,\n\t\t\t\t\t};\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(\"Failed to decode ID token:\", error);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (token.accessToken) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { data: userInfo } = await betterFetch(\n\t\t\t\t\t\tuserInfoEndpoint,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\tAuthorization: `Bearer ${token.accessToken}`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\n\t\t\t\t\tif (userInfo) {\n\t\t\t\t\t\tconst userMap = await options.mapProfileToUser?.(userInfo);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\tid: userInfo.sub,\n\t\t\t\t\t\t\t\tname: userInfo.name || userInfo.given_name || userInfo.username,\n\t\t\t\t\t\t\t\temail: userInfo.email,\n\t\t\t\t\t\t\t\timage: userInfo.picture,\n\t\t\t\t\t\t\t\temailVerified: userInfo.email_verified,\n\t\t\t\t\t\t\t\t...userMap,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tdata: userInfo,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(\"Failed to fetch user info from Cognito:\", error);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t},\n\n\t\toptions,\n\t} satisfies OAuthProvider;\n};\n\nexport const getCognitoPublicKey = async (\n\tkid: string,\n\tregion: string,\n\tuserPoolId: string,\n) => {\n\tconst COGNITO_JWKS_URI = `https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json`;\n\n\ttry {\n\t\tconst { data } = await betterFetch<{\n\t\t\tkeys: Array<{\n\t\t\t\tkid: string;\n\t\t\t\talg: string;\n\t\t\t\tkty: string;\n\t\t\t\tuse: string;\n\t\t\t\tn: string;\n\t\t\t\te: string;\n\t\t\t}>;\n\t\t}>(COGNITO_JWKS_URI);\n\n\t\tif (!data?.keys) {\n\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\tmessage: \"Keys not found\",\n\t\t\t});\n\t\t}\n\n\t\tconst jwk = data.keys.find((key) => key.kid === kid);\n\t\tif (!jwk) {\n\t\t\tthrow new Error(`JWK with kid ${kid} not found`);\n\t\t}\n\n\t\treturn await importJWK(jwk, jwk.alg);\n\t} catch (error) {\n\t\tlogger.error(\"Failed to fetch Cognito public key:\", error);\n\t\tthrow error;\n\t}\n};\n"],"mappings":"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"}