better-auth
Version:
The most comprehensive authentication framework for TypeScript.
1 lines • 9.57 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugins/generic-oauth/index.ts"],"sourcesContent":["import type { AuthContext, BetterAuthPlugin } from \"@better-auth/core\";\nimport type { OAuth2Tokens, OAuthProvider } from \"@better-auth/core/oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"@better-auth/core/oauth2\";\nimport { betterFetch } from \"@better-fetch/fetch\";\nimport { APIError } from \"better-call\";\nimport { GENERIC_OAUTH_ERROR_CODES } from \"./error-codes\";\nimport {\n\tgetUserInfo,\n\toAuth2Callback,\n\toAuth2LinkAccount,\n\tsignInWithOAuth2,\n} from \"./routes\";\nimport type { GenericOAuthConfig, GenericOAuthOptions } from \"./types\";\n\nexport * from \"./providers\";\nexport type { GenericOAuthConfig, GenericOAuthOptions } from \"./types\";\n\n/**\n * Base type for OAuth provider options.\n * Extracts common fields from GenericOAuthConfig and makes clientSecret required.\n */\nexport type BaseOAuthProviderOptions = Omit<\n\tPick<\n\t\tGenericOAuthConfig,\n\t\t| \"clientId\"\n\t\t| \"clientSecret\"\n\t\t| \"scopes\"\n\t\t| \"redirectURI\"\n\t\t| \"pkce\"\n\t\t| \"disableImplicitSignUp\"\n\t\t| \"disableSignUp\"\n\t\t| \"overrideUserInfo\"\n\t>,\n\t\"clientSecret\"\n> & {\n\t/** OAuth client secret (required for provider options) */\n\tclientSecret: string;\n};\n\n/**\n * A generic OAuth plugin that can be used to add OAuth support to any provider\n */\nexport const genericOAuth = (options: GenericOAuthOptions) => {\n\treturn {\n\t\tid: \"generic-oauth\",\n\t\tinit: (ctx: AuthContext) => {\n\t\t\tconst genericProviders = options.config.map((c) => {\n\t\t\t\tlet finalUserInfoUrl = c.userInfoUrl;\n\t\t\t\treturn {\n\t\t\t\t\tid: c.providerId,\n\t\t\t\t\tname: c.providerId,\n\t\t\t\t\tasync createAuthorizationURL(data: {\n\t\t\t\t\t\tstate: string;\n\t\t\t\t\t\tcodeVerifier: string;\n\t\t\t\t\t\tscopes?: string[] | undefined;\n\t\t\t\t\t\tredirectURI: string;\n\t\t\t\t\t\tdisplay?: string | undefined;\n\t\t\t\t\t\tloginHint?: string | undefined;\n\t\t\t\t\t}) {\n\t\t\t\t\t\tlet finalAuthUrl = c.authorizationUrl;\n\t\t\t\t\t\tif (!finalAuthUrl && c.discoveryUrl) {\n\t\t\t\t\t\t\tconst discovery = await betterFetch<{\n\t\t\t\t\t\t\t\tauthorization_endpoint: string;\n\t\t\t\t\t\t\t\tuserinfo_endpoint: string;\n\t\t\t\t\t\t\t}>(c.discoveryUrl, {\n\t\t\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\t\t\theaders: c.discoveryHeaders,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (discovery.data) {\n\t\t\t\t\t\t\t\tfinalAuthUrl = discovery.data.authorization_endpoint;\n\t\t\t\t\t\t\t\tfinalUserInfoUrl =\n\t\t\t\t\t\t\t\t\tfinalUserInfoUrl ?? discovery.data.userinfo_endpoint;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!finalAuthUrl) {\n\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: GENERIC_OAUTH_ERROR_CODES.INVALID_OAUTH_CONFIGURATION,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn createAuthorizationURL({\n\t\t\t\t\t\t\tid: c.providerId,\n\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\tclientId: c.clientId,\n\t\t\t\t\t\t\t\tclientSecret: c.clientSecret,\n\t\t\t\t\t\t\t\tredirectURI: c.redirectURI,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tauthorizationEndpoint: finalAuthUrl,\n\t\t\t\t\t\t\tstate: data.state,\n\t\t\t\t\t\t\tcodeVerifier: c.pkce ? data.codeVerifier : undefined,\n\t\t\t\t\t\t\tscopes: c.scopes || [],\n\t\t\t\t\t\t\tredirectURI: `${ctx.baseURL}/oauth2/callback/${c.providerId}`,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tasync validateAuthorizationCode(data: {\n\t\t\t\t\t\tcode: string;\n\t\t\t\t\t\tredirectURI: string;\n\t\t\t\t\t\tcodeVerifier?: string | undefined;\n\t\t\t\t\t\tdeviceId?: string | undefined;\n\t\t\t\t\t}) {\n\t\t\t\t\t\t// Use custom getToken if provided\n\t\t\t\t\t\tif (c.getToken) {\n\t\t\t\t\t\t\treturn c.getToken(data);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Standard token exchange flow\n\t\t\t\t\t\tlet finalTokenUrl = c.tokenUrl;\n\t\t\t\t\t\tif (c.discoveryUrl) {\n\t\t\t\t\t\t\tconst discovery = await betterFetch<{\n\t\t\t\t\t\t\t\ttoken_endpoint: string;\n\t\t\t\t\t\t\t\tuserinfo_endpoint: string;\n\t\t\t\t\t\t\t}>(c.discoveryUrl, {\n\t\t\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\t\t\theaders: c.discoveryHeaders,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (discovery.data) {\n\t\t\t\t\t\t\t\tfinalTokenUrl = discovery.data.token_endpoint;\n\t\t\t\t\t\t\t\tfinalUserInfoUrl = discovery.data.userinfo_endpoint;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!finalTokenUrl) {\n\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: GENERIC_OAUTH_ERROR_CODES.TOKEN_URL_NOT_FOUND,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn validateAuthorizationCode({\n\t\t\t\t\t\t\theaders: c.authorizationHeaders,\n\t\t\t\t\t\t\tcode: data.code,\n\t\t\t\t\t\t\tcodeVerifier: data.codeVerifier,\n\t\t\t\t\t\t\tredirectURI: data.redirectURI,\n\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\tclientId: c.clientId,\n\t\t\t\t\t\t\t\tclientSecret: c.clientSecret,\n\t\t\t\t\t\t\t\tredirectURI: c.redirectURI,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\ttokenEndpoint: finalTokenUrl,\n\t\t\t\t\t\t\tauthentication: c.authentication,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tasync refreshAccessToken(\n\t\t\t\t\t\trefreshToken: string,\n\t\t\t\t\t): Promise<OAuth2Tokens> {\n\t\t\t\t\t\tlet finalTokenUrl = c.tokenUrl;\n\t\t\t\t\t\tif (c.discoveryUrl) {\n\t\t\t\t\t\t\tconst discovery = await betterFetch<{\n\t\t\t\t\t\t\t\ttoken_endpoint: string;\n\t\t\t\t\t\t\t}>(c.discoveryUrl, {\n\t\t\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\t\t\theaders: c.discoveryHeaders,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (discovery.data) {\n\t\t\t\t\t\t\t\tfinalTokenUrl = discovery.data.token_endpoint;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!finalTokenUrl) {\n\t\t\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\t\t\tmessage: GENERIC_OAUTH_ERROR_CODES.TOKEN_URL_NOT_FOUND,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn refreshAccessToken({\n\t\t\t\t\t\t\trefreshToken,\n\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\tclientId: c.clientId,\n\t\t\t\t\t\t\t\tclientSecret: c.clientSecret,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tauthentication: c.authentication,\n\t\t\t\t\t\t\ttokenEndpoint: finalTokenUrl,\n\t\t\t\t\t\t});\n\t\t\t\t\t},\n\t\t\t\t\tasync getUserInfo(tokens: OAuth2Tokens) {\n\t\t\t\t\t\tconst userInfo = c.getUserInfo\n\t\t\t\t\t\t\t? await c.getUserInfo(tokens)\n\t\t\t\t\t\t\t: await getUserInfo(tokens, finalUserInfoUrl);\n\t\t\t\t\t\tif (!userInfo) {\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst userMap = await c.mapProfileToUser?.(userInfo);\n\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?.id,\n\t\t\t\t\t\t\t\temail: userInfo?.email,\n\t\t\t\t\t\t\t\temailVerified: userInfo?.emailVerified,\n\t\t\t\t\t\t\t\timage: userInfo?.image,\n\t\t\t\t\t\t\t\tname: userInfo?.name,\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\toptions: {\n\t\t\t\t\t\toverrideUserInfoOnSignIn: c.overrideUserInfo,\n\t\t\t\t\t},\n\t\t\t\t} as OAuthProvider;\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tcontext: {\n\t\t\t\t\tsocialProviders: genericProviders.concat(ctx.socialProviders),\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tendpoints: {\n\t\t\tsignInWithOAuth2: signInWithOAuth2(options),\n\t\t\toAuth2Callback: oAuth2Callback(options),\n\t\t\toAuth2LinkAccount: oAuth2LinkAccount(options),\n\t\t},\n\t\toptions,\n\t\t$ERROR_CODES: GENERIC_OAUTH_ERROR_CODES,\n\t} satisfies BetterAuthPlugin;\n};\n"],"mappings":"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"}