@shware/security 1.4.3 → 1.4.5

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.
@@ -35,11 +35,11 @@ __export(auth_exports, {
35
35
  module.exports = __toCommonJS(auth_exports);
36
36
  var import_crypto = require("crypto");
37
37
  var import_tiny_invariant = __toESM(require("tiny-invariant"), 1);
38
+ var import_client = require("../oauth2/client.cjs");
39
+ var import_provider = require("../oauth2/provider/index.cjs");
38
40
  var import_common = require("../session/common.cjs");
39
41
  var import_http = require("../utils/http.cjs");
40
- var import_client = require("../oauth2/client.cjs");
41
42
  var import_timing = require("../utils/timing.cjs");
42
- var import_provider = require("../oauth2/provider/index.cjs");
43
43
  var Auth = class {
44
44
  timing;
45
45
  cookieName;
@@ -201,10 +201,10 @@ var Auth = class {
201
201
  session.setAttribute(import_common.PRINCIPAL_NAME_INDEX_NAME, principal.name);
202
202
  await this.repository.save(session);
203
203
  mark("update_session");
204
- const response = new Response(null, {
205
- status: 302,
206
- headers: { location: this.oauth2Client.successUri }
207
- });
204
+ const uri = new URL(this.oauth2Client.successUri);
205
+ uri.searchParams.set("status", "success");
206
+ uri.searchParams.set("registrationId", registrationId);
207
+ const response = new Response(null, { status: 302, headers: { location: uri.href } });
208
208
  const maxAge = session.getMaxInactiveInterval();
209
209
  (0, import_http.setCookie)(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });
210
210
  setTiming(response);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'crypto';\nimport invariant from 'tiny-invariant';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\nimport { param, query, getCookie, setCookie, deleteCookie } from '../utils/http';\nimport { googleOneTapSchema, OAuth2Client, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport { OAuth2ErrorType } from '../oauth2/error';\nimport { timing } from '../utils/timing';\nimport { google } from '../oauth2/provider/index';\nimport type { CookieOptions } from '../utils/http';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\nimport type { NativeCredential, OAuth2AuthorizationRequest, PkceParameters } from '../oauth2/types';\nimport type { AuthConfig, AuthService, LoggedHandler, OAuth2AuthorizedHandler } from './types';\nimport type { Principal } from '../core';\n\nexport class Auth implements AuthService {\n private readonly timing: boolean;\n private readonly cookieName;\n private readonly cookieOptions: CookieOptions;\n private readonly kv: KVRepository;\n private readonly repository: SessionRepository;\n\n private readonly oauth2Client: OAuth2Client | null;\n private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST = 'oauth2AuthorizationRequest';\n\n public readonly PATH_CSRF = '/csrf' as const;\n public readonly PATH_LOGOUT = '/logout' as const;\n public readonly PATH_LOGGED = '/logged' as const;\n\n public readonly PATH_OAUTH2_STATE = '/oauth2/state/:registrationId' as const;\n public readonly PATH_OAUTH2_NONCE = '/oauth2/nonce/:registrationId' as const;\n public readonly PATH_OAUTH2_AUTHORIZATION = '/oauth2/authorization/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_CODE = '/login/oauth2/code/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_NATIVE = '/login/oauth2/native/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_ONETAP = '/login/oauth2/onetap/google' as const;\n\n public readonly PATH_CLEANUP_EXPIRED_SESSIONS = '/sessions/expired/cleanup' as const;\n\n constructor({ sessionRepository, kvRepository, oauth2, cookie, timing }: AuthConfig) {\n this.timing = timing ?? false;\n this.kv = kvRepository;\n this.repository = sessionRepository;\n const { name, ...cookieOptions } = cookie ?? {};\n this.cookieName = name ?? 'SESSION';\n this.cookieOptions = {\n path: '/',\n sameSite: 'none',\n secure: true,\n httpOnly: true,\n ...cookieOptions,\n };\n this.oauth2Client = oauth2?.client ? new OAuth2Client(oauth2.client) : null;\n }\n\n logout = async (request: Request): Promise<Response> => {\n const sessionId = getCookie(request, this.cookieName);\n if (sessionId) await this.repository.deleteById(sessionId);\n const response = Response.json({});\n deleteCookie(response, this.cookieName);\n return response;\n };\n\n logged = async (request: Request, onLogged?: LoggedHandler): Promise<Response> => {\n // 1. get session from cookie\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return Response.json({ data: false });\n const session = await this.repository.findById(sessionId);\n if (!session) return Response.json({ data: false });\n\n // 2. get principal name from session\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n session.setLastAccessedTime(Date.now());\n await this.repository.save(session);\n\n // 3. call onLogged\n if (name !== null) await onLogged?.({ name: String(name) });\n\n // 4. renew cookie\n const response = Response.json({ data: name !== null });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n return response;\n };\n\n private createPkceParameters(): PkceParameters {\n const code_verifier = randomUUID();\n const code_challenge = createHash('sha256')\n .update(code_verifier)\n .digest()\n .toString('base64url');\n\n return { code_verifier, code_challenge, code_challenge_method: 'S256' };\n }\n\n private getOauth2StateKey(state: string) {\n return `oauth2:state:${state}`;\n }\n\n private getOauth2NonceKey(nonce: string) {\n return `oauth2:nonce:${nonce}`;\n }\n\n oauth2State = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.PATH_OAUTH2_STATE);\n const state = randomUUID();\n await this.kv.setItem(this.getOauth2StateKey(state), registrationId, 10 * 60);\n return Response.json({ state });\n };\n\n oauth2Nonce = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.PATH_OAUTH2_NONCE);\n const nonce = randomUUID();\n await this.kv.setItem(this.getOauth2NonceKey(nonce), registrationId, 10 * 60);\n return Response.json({ nonce });\n };\n\n oauth2Authorization = async (request: Request): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.PATH_OAUTH2_AUTHORIZATION);\n const state = randomUUID();\n const pkce = this.createPkceParameters();\n const uri = this.oauth2Client.createAuthorizationUri({ registrationId, state, pkce });\n const sessionId = getCookie(request, this.cookieName);\n const session = sessionId\n ? ((await this.repository.findById(sessionId)) ?? this.repository.createSession())\n : this.repository.createSession();\n mark('fetch_session');\n\n const authorizationRequest: OAuth2AuthorizationRequest = {\n state,\n registrationId,\n authorizationRequestUri: uri.href,\n additionalParameters: pkce,\n };\n\n const value = JSON.stringify(authorizationRequest);\n session.setAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST, value);\n await this.repository.save(session);\n mark('save_session');\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n private redirect = (error: OAuth2ErrorType, description?: string): Response => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const uri = new URL(this.oauth2Client.errorUri);\n uri.searchParams.set('error', error);\n if (description) uri.searchParams.set('error_description', description);\n return Response.redirect(uri.href, 302);\n };\n\n loginOAuth2Code = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // 1. get session from cookie\n const sessionId = getCookie(request, this.cookieName);\n const session = sessionId ? await this.repository.findById(sessionId) : null;\n if (!session) {\n return this.redirect('invalid_request', 'session not found');\n }\n mark('load_session');\n\n // 2. get cached authorization request from session\n const json = session.getAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST) as string | null;\n if (!json) {\n return this.redirect('invalid_request', 'authorization request not found');\n }\n const cached: OAuth2AuthorizationRequest = JSON.parse(json);\n\n // 3. validate redirect query/formdata\n const { registrationId } = param(request, this.PATH_LOGIN_OAUTH2_CODE);\n let data: Record<string, string>;\n // apple redirect: response_mode=form_post\n if (\n request.method === 'POST' &&\n request.headers.get('content-type')?.toLowerCase() === 'application/x-www-form-urlencoded'\n ) {\n const params = new URLSearchParams(await request.text());\n data = Object.fromEntries(params.entries());\n } else {\n data = query(request);\n }\n\n const parsed = oauth2RedirectQuerySchema.safeParse(data);\n if (!parsed.success) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (!parsed.data.code || !parsed.data.state) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (parsed.data.state !== cached.state) {\n return this.redirect('invalid_request', 'redirect state mismatch');\n }\n if (registrationId !== cached.registrationId) {\n return this.redirect('invalid_request', 'redirect registration mismatch');\n }\n mark('validate_session');\n\n // 4. exchange authorization code for token and get user info\n const { code } = parsed.data;\n\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code,\n pkce: cached.additionalParameters,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n mark('exchange_code');\n\n // 5. create or update principal\n const principal = await onAuthorized(request, registrationId, userInfo, token);\n mark('save_principal');\n\n // 6. update session\n session.setLastAccessedTime(Date.now());\n session.removeAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST);\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n mark('update_session');\n\n const response = new Response(null, {\n status: 302,\n headers: { location: this.oauth2Client.successUri },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Native = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.PATH_LOGIN_OAUTH2_NATIVE);\n const credentials = (await request.json()) as NativeCredential;\n const { userInfo, token } = await this.oauth2Client.loginOAuth2Native({\n registrationId,\n credentials,\n });\n mark('login_oauth2_native');\n\n const { nonce } = userInfo.data as { nonce?: string };\n if (nonce) {\n const value = await this.kv.getItem(this.getOauth2NonceKey(nonce));\n if (value !== registrationId) {\n const json = { error: 'invalid_request', error_description: 'nonce mismatch' };\n return Response.json(json, { status: 400 });\n }\n await this.kv.removeItem(this.getOauth2NonceKey(nonce));\n mark('check_nonce');\n }\n\n const principal = await onAuthorized(request, registrationId, userInfo, token);\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Onetap = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler,\n registrationId: string = 'google'\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // should support application/x-www-form-urlencoded and application/json\n let body: Record<string, string>;\n if (request.headers.get('Content-Type') === 'application/x-www-form-urlencoded') {\n const text = await request.text();\n body = Object.fromEntries(new URLSearchParams(text));\n } else if (request.headers.get('Content-Type') === 'application/json') {\n body = (await request.json()) as Record<string, string>;\n } else {\n const json = { error: 'invalid_request', error_description: 'invalid content type' };\n return Response.json(json, { status: 400 });\n }\n\n const parsed = googleOneTapSchema.parse(body);\n\n // csrf token is not supported in FedCM mode\n // const csrf_cookie = getCookie(request, 'g_csrf_token');\n // const csrf_request = parsed.g_csrf_token;\n // if (!csrf_request || csrf_cookie !== csrf_request) {\n // const json = { error: 'invalid_request', error_description: 'invalid csrf token' };\n // return Response.json(json, { status: 400 });\n // }\n\n let principal: Principal;\n if (parsed.credential) {\n const { token, userInfo } = await google.getTokenInfo(parsed.credential);\n principal = await onAuthorized(request, registrationId, userInfo, token);\n } else if (parsed.code) {\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code: parsed.code,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n principal = await onAuthorized(request, registrationId, userInfo, token);\n } else {\n throw new Error('invalid request');\n }\n\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n kick = async (principal: Principal): Promise<void> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n for (const sessionId of sessions.keys()) {\n await this.repository.deleteById(sessionId);\n }\n };\n\n isAuthenticated = async (request: Request): Promise<boolean> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return false;\n const session = await this.repository.findById(sessionId);\n if (!session) return false;\n const principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!principalName) return false;\n return true;\n };\n\n getSession = async <T extends boolean>(\n request: Request,\n create: T\n ): Promise<T extends true ? Session : Session | null> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n const session = await this.repository.findById(sessionId);\n if (!session) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n return session;\n };\n\n deleteSession = async (sessionId: string): Promise<void> => {\n await this.repository.deleteById(sessionId);\n };\n\n getPrincipal = async (request: Request): Promise<Principal | null> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return null;\n const session = await this.repository.findById(sessionId);\n if (!session) return null;\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!name) return null;\n return { name: String(name) };\n };\n\n listSessions = async (principal: Principal): Promise<Session[]> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n return Array.from(sessions.values());\n };\n\n cleanupExpiredSessions = async (cleanupCount?: number): Promise<Response> => {\n await this.repository.cleanupExpiredSessions(cleanupCount);\n return Response.json({});\n };\n}\n"],"mappings":"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","names":["timing","invariant","session"]}
1
+ {"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'crypto';\nimport invariant from 'tiny-invariant';\nimport { googleOneTapSchema, OAuth2Client, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport { OAuth2ErrorType } from '../oauth2/error';\nimport { google } from '../oauth2/provider/index';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\nimport { param, query, getCookie, setCookie, deleteCookie } from '../utils/http';\nimport { timing } from '../utils/timing';\nimport type { AuthConfig, AuthService, LoggedHandler, OAuth2AuthorizedHandler } from './types';\nimport type { Principal } from '../core';\nimport type { NativeCredential, OAuth2AuthorizationRequest, PkceParameters } from '../oauth2/types';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\nimport type { CookieOptions } from '../utils/http';\n\nexport class Auth implements AuthService {\n private readonly timing: boolean;\n private readonly cookieName;\n private readonly cookieOptions: CookieOptions;\n private readonly kv: KVRepository;\n private readonly repository: SessionRepository;\n\n private readonly oauth2Client: OAuth2Client | null;\n private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST = 'oauth2AuthorizationRequest';\n\n public readonly PATH_CSRF = '/csrf' as const;\n public readonly PATH_LOGOUT = '/logout' as const;\n public readonly PATH_LOGGED = '/logged' as const;\n\n public readonly PATH_OAUTH2_STATE = '/oauth2/state/:registrationId' as const;\n public readonly PATH_OAUTH2_NONCE = '/oauth2/nonce/:registrationId' as const;\n public readonly PATH_OAUTH2_AUTHORIZATION = '/oauth2/authorization/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_CODE = '/login/oauth2/code/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_NATIVE = '/login/oauth2/native/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_ONETAP = '/login/oauth2/onetap/google' as const;\n\n public readonly PATH_CLEANUP_EXPIRED_SESSIONS = '/sessions/expired/cleanup' as const;\n\n constructor({ sessionRepository, kvRepository, oauth2, cookie, timing }: AuthConfig) {\n this.timing = timing ?? false;\n this.kv = kvRepository;\n this.repository = sessionRepository;\n const { name, ...cookieOptions } = cookie ?? {};\n this.cookieName = name ?? 'SESSION';\n this.cookieOptions = {\n path: '/',\n sameSite: 'none',\n secure: true,\n httpOnly: true,\n ...cookieOptions,\n };\n this.oauth2Client = oauth2?.client ? new OAuth2Client(oauth2.client) : null;\n }\n\n logout = async (request: Request): Promise<Response> => {\n const sessionId = getCookie(request, this.cookieName);\n if (sessionId) await this.repository.deleteById(sessionId);\n const response = Response.json({});\n deleteCookie(response, this.cookieName);\n return response;\n };\n\n logged = async (request: Request, onLogged?: LoggedHandler): Promise<Response> => {\n // 1. get session from cookie\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return Response.json({ data: false });\n const session = await this.repository.findById(sessionId);\n if (!session) return Response.json({ data: false });\n\n // 2. get principal name from session\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n session.setLastAccessedTime(Date.now());\n await this.repository.save(session);\n\n // 3. call onLogged\n if (name !== null) await onLogged?.({ name: String(name) });\n\n // 4. renew cookie\n const response = Response.json({ data: name !== null });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n return response;\n };\n\n private createPkceParameters(): PkceParameters {\n const code_verifier = randomUUID();\n const code_challenge = createHash('sha256')\n .update(code_verifier)\n .digest()\n .toString('base64url');\n\n return { code_verifier, code_challenge, code_challenge_method: 'S256' };\n }\n\n private getOauth2StateKey(state: string) {\n return `oauth2:state:${state}`;\n }\n\n private getOauth2NonceKey(nonce: string) {\n return `oauth2:nonce:${nonce}`;\n }\n\n oauth2State = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.PATH_OAUTH2_STATE);\n const state = randomUUID();\n await this.kv.setItem(this.getOauth2StateKey(state), registrationId, 10 * 60);\n return Response.json({ state });\n };\n\n oauth2Nonce = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.PATH_OAUTH2_NONCE);\n const nonce = randomUUID();\n await this.kv.setItem(this.getOauth2NonceKey(nonce), registrationId, 10 * 60);\n return Response.json({ nonce });\n };\n\n oauth2Authorization = async (request: Request): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.PATH_OAUTH2_AUTHORIZATION);\n const state = randomUUID();\n const pkce = this.createPkceParameters();\n const uri = this.oauth2Client.createAuthorizationUri({ registrationId, state, pkce });\n const sessionId = getCookie(request, this.cookieName);\n const session = sessionId\n ? ((await this.repository.findById(sessionId)) ?? this.repository.createSession())\n : this.repository.createSession();\n mark('fetch_session');\n\n const authorizationRequest: OAuth2AuthorizationRequest = {\n state,\n registrationId,\n authorizationRequestUri: uri.href,\n additionalParameters: pkce,\n };\n\n const value = JSON.stringify(authorizationRequest);\n session.setAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST, value);\n await this.repository.save(session);\n mark('save_session');\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n private redirect = (error: OAuth2ErrorType, description?: string): Response => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const uri = new URL(this.oauth2Client.errorUri);\n uri.searchParams.set('error', error);\n if (description) uri.searchParams.set('error_description', description);\n return Response.redirect(uri.href, 302);\n };\n\n loginOAuth2Code = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // 1. get session from cookie\n const sessionId = getCookie(request, this.cookieName);\n const session = sessionId ? await this.repository.findById(sessionId) : null;\n if (!session) {\n return this.redirect('invalid_request', 'session not found');\n }\n mark('load_session');\n\n // 2. get cached authorization request from session\n const json = session.getAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST) as string | null;\n if (!json) {\n return this.redirect('invalid_request', 'authorization request not found');\n }\n const cached: OAuth2AuthorizationRequest = JSON.parse(json);\n\n // 3. validate redirect query/formdata\n const { registrationId } = param(request, this.PATH_LOGIN_OAUTH2_CODE);\n let data: Record<string, string>;\n // apple redirect: response_mode=form_post\n if (\n request.method === 'POST' &&\n request.headers.get('content-type')?.toLowerCase() === 'application/x-www-form-urlencoded'\n ) {\n const params = new URLSearchParams(await request.text());\n data = Object.fromEntries(params.entries());\n } else {\n data = query(request);\n }\n\n const parsed = oauth2RedirectQuerySchema.safeParse(data);\n if (!parsed.success) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (!parsed.data.code || !parsed.data.state) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (parsed.data.state !== cached.state) {\n return this.redirect('invalid_request', 'redirect state mismatch');\n }\n if (registrationId !== cached.registrationId) {\n return this.redirect('invalid_request', 'redirect registration mismatch');\n }\n mark('validate_session');\n\n // 4. exchange authorization code for token and get user info\n const { code } = parsed.data;\n\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code,\n pkce: cached.additionalParameters,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n mark('exchange_code');\n\n // 5. create or update principal\n const principal = await onAuthorized(request, registrationId, userInfo, token);\n mark('save_principal');\n\n // 6. update session\n session.setLastAccessedTime(Date.now());\n session.removeAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST);\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n mark('update_session');\n\n const uri = new URL(this.oauth2Client.successUri);\n uri.searchParams.set('status', 'success');\n uri.searchParams.set('registrationId', registrationId);\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Native = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.PATH_LOGIN_OAUTH2_NATIVE);\n const credentials = (await request.json()) as NativeCredential;\n const { userInfo, token } = await this.oauth2Client.loginOAuth2Native({\n registrationId,\n credentials,\n });\n mark('login_oauth2_native');\n\n const { nonce } = userInfo.data as { nonce?: string };\n if (nonce) {\n const value = await this.kv.getItem(this.getOauth2NonceKey(nonce));\n if (value !== registrationId) {\n const json = { error: 'invalid_request', error_description: 'nonce mismatch' };\n return Response.json(json, { status: 400 });\n }\n await this.kv.removeItem(this.getOauth2NonceKey(nonce));\n mark('check_nonce');\n }\n\n const principal = await onAuthorized(request, registrationId, userInfo, token);\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Onetap = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler,\n registrationId: string = 'google'\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // should support application/x-www-form-urlencoded and application/json\n let body: Record<string, string>;\n if (request.headers.get('Content-Type') === 'application/x-www-form-urlencoded') {\n const text = await request.text();\n body = Object.fromEntries(new URLSearchParams(text));\n } else if (request.headers.get('Content-Type') === 'application/json') {\n body = (await request.json()) as Record<string, string>;\n } else {\n const json = { error: 'invalid_request', error_description: 'invalid content type' };\n return Response.json(json, { status: 400 });\n }\n\n const parsed = googleOneTapSchema.parse(body);\n\n // csrf token is not supported in FedCM mode\n // const csrf_cookie = getCookie(request, 'g_csrf_token');\n // const csrf_request = parsed.g_csrf_token;\n // if (!csrf_request || csrf_cookie !== csrf_request) {\n // const json = { error: 'invalid_request', error_description: 'invalid csrf token' };\n // return Response.json(json, { status: 400 });\n // }\n\n let principal: Principal;\n if (parsed.credential) {\n const { token, userInfo } = await google.getTokenInfo(parsed.credential);\n principal = await onAuthorized(request, registrationId, userInfo, token);\n } else if (parsed.code) {\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code: parsed.code,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n principal = await onAuthorized(request, registrationId, userInfo, token);\n } else {\n throw new Error('invalid request');\n }\n\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n kick = async (principal: Principal): Promise<void> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n for (const sessionId of sessions.keys()) {\n await this.repository.deleteById(sessionId);\n }\n };\n\n isAuthenticated = async (request: Request): Promise<boolean> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return false;\n const session = await this.repository.findById(sessionId);\n if (!session) return false;\n const principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!principalName) return false;\n return true;\n };\n\n getSession = async <T extends boolean>(\n request: Request,\n create: T\n ): Promise<T extends true ? Session : Session | null> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n const session = await this.repository.findById(sessionId);\n if (!session) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n return session;\n };\n\n deleteSession = async (sessionId: string): Promise<void> => {\n await this.repository.deleteById(sessionId);\n };\n\n getPrincipal = async (request: Request): Promise<Principal | null> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return null;\n const session = await this.repository.findById(sessionId);\n if (!session) return null;\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!name) return null;\n return { name: String(name) };\n };\n\n listSessions = async (principal: Principal): Promise<Session[]> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n return Array.from(sessions.values());\n };\n\n cleanupExpiredSessions = async (cleanupCount?: number): Promise<Response> => {\n await this.repository.cleanupExpiredSessions(cleanupCount);\n return Response.json({});\n };\n}\n"],"mappings":"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","names":["timing","invariant","session"]}
@@ -1,6 +1,6 @@
1
- import { Session } from '../session/types.cjs';
2
1
  import { AuthService, AuthConfig, LoggedHandler, OAuth2AuthorizedHandler } from './types.cjs';
3
2
  import { Principal } from '../core/index.cjs';
3
+ import { Session } from '../session/types.cjs';
4
4
  import '../oauth2/types.cjs';
5
5
  import '../utils/http.cjs';
6
6
  import 'cookie';
@@ -1,6 +1,6 @@
1
- import { Session } from '../session/types.js';
2
1
  import { AuthService, AuthConfig, LoggedHandler, OAuth2AuthorizedHandler } from './types.js';
3
2
  import { Principal } from '../core/index.js';
3
+ import { Session } from '../session/types.js';
4
4
  import '../oauth2/types.js';
5
5
  import '../utils/http.js';
6
6
  import 'cookie';
@@ -1,11 +1,11 @@
1
1
  // src/auth/index.ts
2
2
  import { createHash, randomUUID } from "crypto";
3
3
  import invariant from "tiny-invariant";
4
+ import { googleOneTapSchema, OAuth2Client, oauth2RedirectQuerySchema } from "../oauth2/client.mjs";
5
+ import { google } from "../oauth2/provider/index.mjs";
4
6
  import { PRINCIPAL_NAME_INDEX_NAME } from "../session/common.mjs";
5
7
  import { param, query, getCookie, setCookie, deleteCookie } from "../utils/http.mjs";
6
- import { googleOneTapSchema, OAuth2Client, oauth2RedirectQuerySchema } from "../oauth2/client.mjs";
7
8
  import { timing } from "../utils/timing.mjs";
8
- import { google } from "../oauth2/provider/index.mjs";
9
9
  var Auth = class {
10
10
  timing;
11
11
  cookieName;
@@ -167,10 +167,10 @@ var Auth = class {
167
167
  session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);
168
168
  await this.repository.save(session);
169
169
  mark("update_session");
170
- const response = new Response(null, {
171
- status: 302,
172
- headers: { location: this.oauth2Client.successUri }
173
- });
170
+ const uri = new URL(this.oauth2Client.successUri);
171
+ uri.searchParams.set("status", "success");
172
+ uri.searchParams.set("registrationId", registrationId);
173
+ const response = new Response(null, { status: 302, headers: { location: uri.href } });
174
174
  const maxAge = session.getMaxInactiveInterval();
175
175
  setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });
176
176
  setTiming(response);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'crypto';\nimport invariant from 'tiny-invariant';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\nimport { param, query, getCookie, setCookie, deleteCookie } from '../utils/http';\nimport { googleOneTapSchema, OAuth2Client, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport { OAuth2ErrorType } from '../oauth2/error';\nimport { timing } from '../utils/timing';\nimport { google } from '../oauth2/provider/index';\nimport type { CookieOptions } from '../utils/http';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\nimport type { NativeCredential, OAuth2AuthorizationRequest, PkceParameters } from '../oauth2/types';\nimport type { AuthConfig, AuthService, LoggedHandler, OAuth2AuthorizedHandler } from './types';\nimport type { Principal } from '../core';\n\nexport class Auth implements AuthService {\n private readonly timing: boolean;\n private readonly cookieName;\n private readonly cookieOptions: CookieOptions;\n private readonly kv: KVRepository;\n private readonly repository: SessionRepository;\n\n private readonly oauth2Client: OAuth2Client | null;\n private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST = 'oauth2AuthorizationRequest';\n\n public readonly PATH_CSRF = '/csrf' as const;\n public readonly PATH_LOGOUT = '/logout' as const;\n public readonly PATH_LOGGED = '/logged' as const;\n\n public readonly PATH_OAUTH2_STATE = '/oauth2/state/:registrationId' as const;\n public readonly PATH_OAUTH2_NONCE = '/oauth2/nonce/:registrationId' as const;\n public readonly PATH_OAUTH2_AUTHORIZATION = '/oauth2/authorization/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_CODE = '/login/oauth2/code/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_NATIVE = '/login/oauth2/native/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_ONETAP = '/login/oauth2/onetap/google' as const;\n\n public readonly PATH_CLEANUP_EXPIRED_SESSIONS = '/sessions/expired/cleanup' as const;\n\n constructor({ sessionRepository, kvRepository, oauth2, cookie, timing }: AuthConfig) {\n this.timing = timing ?? false;\n this.kv = kvRepository;\n this.repository = sessionRepository;\n const { name, ...cookieOptions } = cookie ?? {};\n this.cookieName = name ?? 'SESSION';\n this.cookieOptions = {\n path: '/',\n sameSite: 'none',\n secure: true,\n httpOnly: true,\n ...cookieOptions,\n };\n this.oauth2Client = oauth2?.client ? new OAuth2Client(oauth2.client) : null;\n }\n\n logout = async (request: Request): Promise<Response> => {\n const sessionId = getCookie(request, this.cookieName);\n if (sessionId) await this.repository.deleteById(sessionId);\n const response = Response.json({});\n deleteCookie(response, this.cookieName);\n return response;\n };\n\n logged = async (request: Request, onLogged?: LoggedHandler): Promise<Response> => {\n // 1. get session from cookie\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return Response.json({ data: false });\n const session = await this.repository.findById(sessionId);\n if (!session) return Response.json({ data: false });\n\n // 2. get principal name from session\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n session.setLastAccessedTime(Date.now());\n await this.repository.save(session);\n\n // 3. call onLogged\n if (name !== null) await onLogged?.({ name: String(name) });\n\n // 4. renew cookie\n const response = Response.json({ data: name !== null });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n return response;\n };\n\n private createPkceParameters(): PkceParameters {\n const code_verifier = randomUUID();\n const code_challenge = createHash('sha256')\n .update(code_verifier)\n .digest()\n .toString('base64url');\n\n return { code_verifier, code_challenge, code_challenge_method: 'S256' };\n }\n\n private getOauth2StateKey(state: string) {\n return `oauth2:state:${state}`;\n }\n\n private getOauth2NonceKey(nonce: string) {\n return `oauth2:nonce:${nonce}`;\n }\n\n oauth2State = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.PATH_OAUTH2_STATE);\n const state = randomUUID();\n await this.kv.setItem(this.getOauth2StateKey(state), registrationId, 10 * 60);\n return Response.json({ state });\n };\n\n oauth2Nonce = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.PATH_OAUTH2_NONCE);\n const nonce = randomUUID();\n await this.kv.setItem(this.getOauth2NonceKey(nonce), registrationId, 10 * 60);\n return Response.json({ nonce });\n };\n\n oauth2Authorization = async (request: Request): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.PATH_OAUTH2_AUTHORIZATION);\n const state = randomUUID();\n const pkce = this.createPkceParameters();\n const uri = this.oauth2Client.createAuthorizationUri({ registrationId, state, pkce });\n const sessionId = getCookie(request, this.cookieName);\n const session = sessionId\n ? ((await this.repository.findById(sessionId)) ?? this.repository.createSession())\n : this.repository.createSession();\n mark('fetch_session');\n\n const authorizationRequest: OAuth2AuthorizationRequest = {\n state,\n registrationId,\n authorizationRequestUri: uri.href,\n additionalParameters: pkce,\n };\n\n const value = JSON.stringify(authorizationRequest);\n session.setAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST, value);\n await this.repository.save(session);\n mark('save_session');\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n private redirect = (error: OAuth2ErrorType, description?: string): Response => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const uri = new URL(this.oauth2Client.errorUri);\n uri.searchParams.set('error', error);\n if (description) uri.searchParams.set('error_description', description);\n return Response.redirect(uri.href, 302);\n };\n\n loginOAuth2Code = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // 1. get session from cookie\n const sessionId = getCookie(request, this.cookieName);\n const session = sessionId ? await this.repository.findById(sessionId) : null;\n if (!session) {\n return this.redirect('invalid_request', 'session not found');\n }\n mark('load_session');\n\n // 2. get cached authorization request from session\n const json = session.getAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST) as string | null;\n if (!json) {\n return this.redirect('invalid_request', 'authorization request not found');\n }\n const cached: OAuth2AuthorizationRequest = JSON.parse(json);\n\n // 3. validate redirect query/formdata\n const { registrationId } = param(request, this.PATH_LOGIN_OAUTH2_CODE);\n let data: Record<string, string>;\n // apple redirect: response_mode=form_post\n if (\n request.method === 'POST' &&\n request.headers.get('content-type')?.toLowerCase() === 'application/x-www-form-urlencoded'\n ) {\n const params = new URLSearchParams(await request.text());\n data = Object.fromEntries(params.entries());\n } else {\n data = query(request);\n }\n\n const parsed = oauth2RedirectQuerySchema.safeParse(data);\n if (!parsed.success) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (!parsed.data.code || !parsed.data.state) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (parsed.data.state !== cached.state) {\n return this.redirect('invalid_request', 'redirect state mismatch');\n }\n if (registrationId !== cached.registrationId) {\n return this.redirect('invalid_request', 'redirect registration mismatch');\n }\n mark('validate_session');\n\n // 4. exchange authorization code for token and get user info\n const { code } = parsed.data;\n\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code,\n pkce: cached.additionalParameters,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n mark('exchange_code');\n\n // 5. create or update principal\n const principal = await onAuthorized(request, registrationId, userInfo, token);\n mark('save_principal');\n\n // 6. update session\n session.setLastAccessedTime(Date.now());\n session.removeAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST);\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n mark('update_session');\n\n const response = new Response(null, {\n status: 302,\n headers: { location: this.oauth2Client.successUri },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Native = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.PATH_LOGIN_OAUTH2_NATIVE);\n const credentials = (await request.json()) as NativeCredential;\n const { userInfo, token } = await this.oauth2Client.loginOAuth2Native({\n registrationId,\n credentials,\n });\n mark('login_oauth2_native');\n\n const { nonce } = userInfo.data as { nonce?: string };\n if (nonce) {\n const value = await this.kv.getItem(this.getOauth2NonceKey(nonce));\n if (value !== registrationId) {\n const json = { error: 'invalid_request', error_description: 'nonce mismatch' };\n return Response.json(json, { status: 400 });\n }\n await this.kv.removeItem(this.getOauth2NonceKey(nonce));\n mark('check_nonce');\n }\n\n const principal = await onAuthorized(request, registrationId, userInfo, token);\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Onetap = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler,\n registrationId: string = 'google'\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // should support application/x-www-form-urlencoded and application/json\n let body: Record<string, string>;\n if (request.headers.get('Content-Type') === 'application/x-www-form-urlencoded') {\n const text = await request.text();\n body = Object.fromEntries(new URLSearchParams(text));\n } else if (request.headers.get('Content-Type') === 'application/json') {\n body = (await request.json()) as Record<string, string>;\n } else {\n const json = { error: 'invalid_request', error_description: 'invalid content type' };\n return Response.json(json, { status: 400 });\n }\n\n const parsed = googleOneTapSchema.parse(body);\n\n // csrf token is not supported in FedCM mode\n // const csrf_cookie = getCookie(request, 'g_csrf_token');\n // const csrf_request = parsed.g_csrf_token;\n // if (!csrf_request || csrf_cookie !== csrf_request) {\n // const json = { error: 'invalid_request', error_description: 'invalid csrf token' };\n // return Response.json(json, { status: 400 });\n // }\n\n let principal: Principal;\n if (parsed.credential) {\n const { token, userInfo } = await google.getTokenInfo(parsed.credential);\n principal = await onAuthorized(request, registrationId, userInfo, token);\n } else if (parsed.code) {\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code: parsed.code,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n principal = await onAuthorized(request, registrationId, userInfo, token);\n } else {\n throw new Error('invalid request');\n }\n\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n kick = async (principal: Principal): Promise<void> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n for (const sessionId of sessions.keys()) {\n await this.repository.deleteById(sessionId);\n }\n };\n\n isAuthenticated = async (request: Request): Promise<boolean> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return false;\n const session = await this.repository.findById(sessionId);\n if (!session) return false;\n const principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!principalName) return false;\n return true;\n };\n\n getSession = async <T extends boolean>(\n request: Request,\n create: T\n ): Promise<T extends true ? Session : Session | null> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n const session = await this.repository.findById(sessionId);\n if (!session) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n return session;\n };\n\n deleteSession = async (sessionId: string): Promise<void> => {\n await this.repository.deleteById(sessionId);\n };\n\n getPrincipal = async (request: Request): Promise<Principal | null> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return null;\n const session = await this.repository.findById(sessionId);\n if (!session) return null;\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!name) return null;\n return { name: String(name) };\n };\n\n listSessions = async (principal: Principal): Promise<Session[]> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n return Array.from(sessions.values());\n };\n\n cleanupExpiredSessions = async (cleanupCount?: number): Promise<Response> => {\n await this.repository.cleanupExpiredSessions(cleanupCount);\n return Response.json({});\n };\n}\n"],"mappings":"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","names":["timing","session"]}
1
+ {"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["import { createHash, randomUUID } from 'crypto';\nimport invariant from 'tiny-invariant';\nimport { googleOneTapSchema, OAuth2Client, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport { OAuth2ErrorType } from '../oauth2/error';\nimport { google } from '../oauth2/provider/index';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\nimport { param, query, getCookie, setCookie, deleteCookie } from '../utils/http';\nimport { timing } from '../utils/timing';\nimport type { AuthConfig, AuthService, LoggedHandler, OAuth2AuthorizedHandler } from './types';\nimport type { Principal } from '../core';\nimport type { NativeCredential, OAuth2AuthorizationRequest, PkceParameters } from '../oauth2/types';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\nimport type { CookieOptions } from '../utils/http';\n\nexport class Auth implements AuthService {\n private readonly timing: boolean;\n private readonly cookieName;\n private readonly cookieOptions: CookieOptions;\n private readonly kv: KVRepository;\n private readonly repository: SessionRepository;\n\n private readonly oauth2Client: OAuth2Client | null;\n private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST = 'oauth2AuthorizationRequest';\n\n public readonly PATH_CSRF = '/csrf' as const;\n public readonly PATH_LOGOUT = '/logout' as const;\n public readonly PATH_LOGGED = '/logged' as const;\n\n public readonly PATH_OAUTH2_STATE = '/oauth2/state/:registrationId' as const;\n public readonly PATH_OAUTH2_NONCE = '/oauth2/nonce/:registrationId' as const;\n public readonly PATH_OAUTH2_AUTHORIZATION = '/oauth2/authorization/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_CODE = '/login/oauth2/code/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_NATIVE = '/login/oauth2/native/:registrationId' as const;\n public readonly PATH_LOGIN_OAUTH2_ONETAP = '/login/oauth2/onetap/google' as const;\n\n public readonly PATH_CLEANUP_EXPIRED_SESSIONS = '/sessions/expired/cleanup' as const;\n\n constructor({ sessionRepository, kvRepository, oauth2, cookie, timing }: AuthConfig) {\n this.timing = timing ?? false;\n this.kv = kvRepository;\n this.repository = sessionRepository;\n const { name, ...cookieOptions } = cookie ?? {};\n this.cookieName = name ?? 'SESSION';\n this.cookieOptions = {\n path: '/',\n sameSite: 'none',\n secure: true,\n httpOnly: true,\n ...cookieOptions,\n };\n this.oauth2Client = oauth2?.client ? new OAuth2Client(oauth2.client) : null;\n }\n\n logout = async (request: Request): Promise<Response> => {\n const sessionId = getCookie(request, this.cookieName);\n if (sessionId) await this.repository.deleteById(sessionId);\n const response = Response.json({});\n deleteCookie(response, this.cookieName);\n return response;\n };\n\n logged = async (request: Request, onLogged?: LoggedHandler): Promise<Response> => {\n // 1. get session from cookie\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return Response.json({ data: false });\n const session = await this.repository.findById(sessionId);\n if (!session) return Response.json({ data: false });\n\n // 2. get principal name from session\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n session.setLastAccessedTime(Date.now());\n await this.repository.save(session);\n\n // 3. call onLogged\n if (name !== null) await onLogged?.({ name: String(name) });\n\n // 4. renew cookie\n const response = Response.json({ data: name !== null });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n return response;\n };\n\n private createPkceParameters(): PkceParameters {\n const code_verifier = randomUUID();\n const code_challenge = createHash('sha256')\n .update(code_verifier)\n .digest()\n .toString('base64url');\n\n return { code_verifier, code_challenge, code_challenge_method: 'S256' };\n }\n\n private getOauth2StateKey(state: string) {\n return `oauth2:state:${state}`;\n }\n\n private getOauth2NonceKey(nonce: string) {\n return `oauth2:nonce:${nonce}`;\n }\n\n oauth2State = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.PATH_OAUTH2_STATE);\n const state = randomUUID();\n await this.kv.setItem(this.getOauth2StateKey(state), registrationId, 10 * 60);\n return Response.json({ state });\n };\n\n oauth2Nonce = async (request: Request): Promise<Response> => {\n const { registrationId } = param(request, this.PATH_OAUTH2_NONCE);\n const nonce = randomUUID();\n await this.kv.setItem(this.getOauth2NonceKey(nonce), registrationId, 10 * 60);\n return Response.json({ nonce });\n };\n\n oauth2Authorization = async (request: Request): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.PATH_OAUTH2_AUTHORIZATION);\n const state = randomUUID();\n const pkce = this.createPkceParameters();\n const uri = this.oauth2Client.createAuthorizationUri({ registrationId, state, pkce });\n const sessionId = getCookie(request, this.cookieName);\n const session = sessionId\n ? ((await this.repository.findById(sessionId)) ?? this.repository.createSession())\n : this.repository.createSession();\n mark('fetch_session');\n\n const authorizationRequest: OAuth2AuthorizationRequest = {\n state,\n registrationId,\n authorizationRequestUri: uri.href,\n additionalParameters: pkce,\n };\n\n const value = JSON.stringify(authorizationRequest);\n session.setAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST, value);\n await this.repository.save(session);\n mark('save_session');\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n private redirect = (error: OAuth2ErrorType, description?: string): Response => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const uri = new URL(this.oauth2Client.errorUri);\n uri.searchParams.set('error', error);\n if (description) uri.searchParams.set('error_description', description);\n return Response.redirect(uri.href, 302);\n };\n\n loginOAuth2Code = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // 1. get session from cookie\n const sessionId = getCookie(request, this.cookieName);\n const session = sessionId ? await this.repository.findById(sessionId) : null;\n if (!session) {\n return this.redirect('invalid_request', 'session not found');\n }\n mark('load_session');\n\n // 2. get cached authorization request from session\n const json = session.getAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST) as string | null;\n if (!json) {\n return this.redirect('invalid_request', 'authorization request not found');\n }\n const cached: OAuth2AuthorizationRequest = JSON.parse(json);\n\n // 3. validate redirect query/formdata\n const { registrationId } = param(request, this.PATH_LOGIN_OAUTH2_CODE);\n let data: Record<string, string>;\n // apple redirect: response_mode=form_post\n if (\n request.method === 'POST' &&\n request.headers.get('content-type')?.toLowerCase() === 'application/x-www-form-urlencoded'\n ) {\n const params = new URLSearchParams(await request.text());\n data = Object.fromEntries(params.entries());\n } else {\n data = query(request);\n }\n\n const parsed = oauth2RedirectQuerySchema.safeParse(data);\n if (!parsed.success) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (!parsed.data.code || !parsed.data.state) {\n return this.redirect('invalid_request', 'invalid redirect query');\n }\n if (parsed.data.state !== cached.state) {\n return this.redirect('invalid_request', 'redirect state mismatch');\n }\n if (registrationId !== cached.registrationId) {\n return this.redirect('invalid_request', 'redirect registration mismatch');\n }\n mark('validate_session');\n\n // 4. exchange authorization code for token and get user info\n const { code } = parsed.data;\n\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code,\n pkce: cached.additionalParameters,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n mark('exchange_code');\n\n // 5. create or update principal\n const principal = await onAuthorized(request, registrationId, userInfo, token);\n mark('save_principal');\n\n // 6. update session\n session.setLastAccessedTime(Date.now());\n session.removeAttribute(this.ATTR_OAUTH2_AUTHORIZATION_REQUEST);\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n mark('update_session');\n\n const uri = new URL(this.oauth2Client.successUri);\n uri.searchParams.set('status', 'success');\n uri.searchParams.set('registrationId', registrationId);\n\n const response = new Response(null, { status: 302, headers: { location: uri.href } });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Native = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n const { registrationId } = param(request, this.PATH_LOGIN_OAUTH2_NATIVE);\n const credentials = (await request.json()) as NativeCredential;\n const { userInfo, token } = await this.oauth2Client.loginOAuth2Native({\n registrationId,\n credentials,\n });\n mark('login_oauth2_native');\n\n const { nonce } = userInfo.data as { nonce?: string };\n if (nonce) {\n const value = await this.kv.getItem(this.getOauth2NonceKey(nonce));\n if (value !== registrationId) {\n const json = { error: 'invalid_request', error_description: 'nonce mismatch' };\n return Response.json(json, { status: 400 });\n }\n await this.kv.removeItem(this.getOauth2NonceKey(nonce));\n mark('check_nonce');\n }\n\n const principal = await onAuthorized(request, registrationId, userInfo, token);\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n loginOAuth2Onetap = async (\n request: Request,\n onAuthorized: OAuth2AuthorizedHandler,\n registrationId: string = 'google'\n ): Promise<Response> => {\n invariant(this.oauth2Client, 'oauth2Client is not initialized');\n const { mark, setTiming } = timing({ enabled: this.timing });\n\n // should support application/x-www-form-urlencoded and application/json\n let body: Record<string, string>;\n if (request.headers.get('Content-Type') === 'application/x-www-form-urlencoded') {\n const text = await request.text();\n body = Object.fromEntries(new URLSearchParams(text));\n } else if (request.headers.get('Content-Type') === 'application/json') {\n body = (await request.json()) as Record<string, string>;\n } else {\n const json = { error: 'invalid_request', error_description: 'invalid content type' };\n return Response.json(json, { status: 400 });\n }\n\n const parsed = googleOneTapSchema.parse(body);\n\n // csrf token is not supported in FedCM mode\n // const csrf_cookie = getCookie(request, 'g_csrf_token');\n // const csrf_request = parsed.g_csrf_token;\n // if (!csrf_request || csrf_cookie !== csrf_request) {\n // const json = { error: 'invalid_request', error_description: 'invalid csrf token' };\n // return Response.json(json, { status: 400 });\n // }\n\n let principal: Principal;\n if (parsed.credential) {\n const { token, userInfo } = await google.getTokenInfo(parsed.credential);\n principal = await onAuthorized(request, registrationId, userInfo, token);\n } else if (parsed.code) {\n const token = await this.oauth2Client.exchangeAuthorizationCode({\n registrationId,\n code: parsed.code,\n });\n const userInfo = await this.oauth2Client.getUserInfo({ registrationId, token });\n principal = await onAuthorized(request, registrationId, userInfo, token);\n } else {\n throw new Error('invalid request');\n }\n\n mark('save_principal');\n\n const session = this.repository.createSession();\n session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, principal.name);\n await this.repository.save(session);\n const response = new Response(JSON.stringify(principal), {\n status: 200,\n headers: { 'Content-Type': 'application/json' },\n });\n const maxAge = session.getMaxInactiveInterval();\n setCookie(response, this.cookieName, session.getId(), { ...this.cookieOptions, maxAge });\n setTiming(response);\n\n return response;\n };\n\n kick = async (principal: Principal): Promise<void> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n for (const sessionId of sessions.keys()) {\n await this.repository.deleteById(sessionId);\n }\n };\n\n isAuthenticated = async (request: Request): Promise<boolean> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return false;\n const session = await this.repository.findById(sessionId);\n if (!session) return false;\n const principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!principalName) return false;\n return true;\n };\n\n getSession = async <T extends boolean>(\n request: Request,\n create: T\n ): Promise<T extends true ? Session : Session | null> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n const session = await this.repository.findById(sessionId);\n if (!session) {\n if (create) {\n const session = this.repository.createSession();\n await this.repository.save(session);\n return session;\n } else {\n return null as T extends true ? Session : Session | null;\n }\n }\n return session;\n };\n\n deleteSession = async (sessionId: string): Promise<void> => {\n await this.repository.deleteById(sessionId);\n };\n\n getPrincipal = async (request: Request): Promise<Principal | null> => {\n const sessionId = getCookie(request, this.cookieName);\n if (!sessionId) return null;\n const session = await this.repository.findById(sessionId);\n if (!session) return null;\n const name = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME);\n if (!name) return null;\n return { name: String(name) };\n };\n\n listSessions = async (principal: Principal): Promise<Session[]> => {\n const sessions = await this.repository.findByPrincipalName(principal.name);\n return Array.from(sessions.values());\n };\n\n cleanupExpiredSessions = async (cleanupCount?: number): Promise<Response> => {\n await this.repository.cleanupExpiredSessions(cleanupCount);\n return Response.json({});\n };\n}\n"],"mappings":"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","names":["timing","session"]}
@@ -35,8 +35,8 @@ __export(client_exports, {
35
35
  oauth2RedirectQuerySchema: () => oauth2RedirectQuerySchema
36
36
  });
37
37
  module.exports = __toCommonJS(client_exports);
38
- var import_v4_mini = require("zod/v4-mini");
39
38
  var import_tiny_invariant = __toESM(require("tiny-invariant"), 1);
39
+ var import_v4_mini = require("zod/v4-mini");
40
40
  var oauth2RedirectQuerySchema = (0, import_v4_mini.object)({
41
41
  code: (0, import_v4_mini.optional)((0, import_v4_mini.string)()),
42
42
  state: (0, import_v4_mini.optional)((0, import_v4_mini.string)()),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/oauth2/client.ts"],"sourcesContent":["import { string, object, optional } from 'zod/v4-mini';\nimport invariant from 'tiny-invariant';\nimport {\n NativeCredential,\n OAuth2ClientConfig,\n OAuth2Token,\n OidcToken,\n PkceParameters,\n} from './types';\n\nexport const oauth2RedirectQuerySchema = object({\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n});\n\nexport const googleOneTapSchema = object({\n // login_uri\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n nonce: optional(string()),\n g_csrf_token: optional(string()),\n hd: optional(string()),\n // callback\n credential: optional(string()),\n});\n\nexport class OAuth2Client {\n private config: OAuth2ClientConfig;\n constructor(config: OAuth2ClientConfig) {\n this.config = config;\n }\n\n get baseUri() {\n return this.config.baseUri;\n }\n\n get successUri() {\n return this.config.successUri;\n }\n\n get errorUri() {\n return this.config.errorUri;\n }\n\n getClientConfig(registrationId: string) {\n const registration = this.config.registration[registrationId];\n invariant(registration, `Registration ${registrationId} not found`);\n const provider = this.config.provider[registration.provider ?? registrationId];\n invariant(provider, `Provider ${registration.provider ?? registrationId} not found`);\n return { registration, provider };\n }\n\n createAuthorizationUri({\n registrationId,\n state,\n pkce,\n }: {\n registrationId: string;\n state: string;\n pkce?: PkceParameters;\n }): URL {\n const { provider, registration } = this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { scope, clientId, redirectUri } = registration;\n return provider.createAuthorizationUri({\n pkce,\n state,\n scope,\n clientId,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async exchangeAuthorizationCode({\n registrationId,\n code,\n pkce,\n }: {\n registrationId: string;\n code: string;\n pkce?: PkceParameters;\n }): Promise<OAuth2Token> {\n const { provider, registration } = this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n return provider.exchangeAuthorizationCode({\n code,\n pkce,\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async getUserInfo({\n registrationId,\n token,\n }: {\n registrationId: string;\n token: OAuth2Token | OidcToken;\n }) {\n const { provider } = this.getClientConfig(registrationId);\n return provider.getUserInfo(token);\n }\n\n async refreshAccessToken({\n registrationId,\n refreshToken,\n }: {\n registrationId: string;\n refreshToken: string;\n }): Promise<OAuth2Token> {\n const { provider, registration } = this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.refreshAccessToken, 'Provider does not support refreshAccessToken');\n return provider.refreshAccessToken({ refreshToken, clientId, clientSecret });\n }\n\n async revokeToken(registrationId: string, token: string) {\n const { provider, registration } = this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.revokeToken, 'Provider does not support revokeToken');\n await provider.revokeToken({ token, clientId, clientSecret });\n }\n\n async loginOAuth2Native({\n registrationId,\n credentials,\n pkce,\n }: {\n registrationId: string;\n credentials: NativeCredential;\n pkce?: PkceParameters;\n }) {\n const { provider, registration } = this.getClientConfig(registrationId);\n invariant(provider.loginOAuth2Native, 'Provider does not support loginOAuth2Native');\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n\n return provider.loginOAuth2Native({\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n credentials,\n pkce,\n });\n }\n}\n"],"mappings":"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","names":["invariant"]}
1
+ {"version":3,"sources":["../../src/oauth2/client.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { string, object, optional } from 'zod/v4-mini';\nimport {\n NativeCredential,\n OAuth2ClientConfig,\n OAuth2Token,\n OidcToken,\n PkceParameters,\n} from './types';\n\nexport const oauth2RedirectQuerySchema = object({\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n});\n\nexport const googleOneTapSchema = object({\n // login_uri\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n nonce: optional(string()),\n g_csrf_token: optional(string()),\n hd: optional(string()),\n // callback\n credential: optional(string()),\n});\n\nexport class OAuth2Client {\n private config: OAuth2ClientConfig;\n constructor(config: OAuth2ClientConfig) {\n this.config = config;\n }\n\n get baseUri() {\n return this.config.baseUri;\n }\n\n get successUri() {\n return this.config.successUri;\n }\n\n get errorUri() {\n return this.config.errorUri;\n }\n\n getClientConfig(registrationId: string) {\n const registration = this.config.registration[registrationId];\n invariant(registration, `Registration ${registrationId} not found`);\n const provider = this.config.provider[registration.provider ?? registrationId];\n invariant(provider, `Provider ${registration.provider ?? registrationId} not found`);\n return { registration, provider };\n }\n\n createAuthorizationUri({\n registrationId,\n state,\n pkce,\n }: {\n registrationId: string;\n state: string;\n pkce?: PkceParameters;\n }): URL {\n const { provider, registration } = this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { scope, clientId, redirectUri } = registration;\n return provider.createAuthorizationUri({\n pkce,\n state,\n scope,\n clientId,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async exchangeAuthorizationCode({\n registrationId,\n code,\n pkce,\n }: {\n registrationId: string;\n code: string;\n pkce?: PkceParameters;\n }): Promise<OAuth2Token> {\n const { provider, registration } = this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n return provider.exchangeAuthorizationCode({\n code,\n pkce,\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async getUserInfo({\n registrationId,\n token,\n }: {\n registrationId: string;\n token: OAuth2Token | OidcToken;\n }) {\n const { provider } = this.getClientConfig(registrationId);\n return provider.getUserInfo(token);\n }\n\n async refreshAccessToken({\n registrationId,\n refreshToken,\n }: {\n registrationId: string;\n refreshToken: string;\n }): Promise<OAuth2Token> {\n const { provider, registration } = this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.refreshAccessToken, 'Provider does not support refreshAccessToken');\n return provider.refreshAccessToken({ refreshToken, clientId, clientSecret });\n }\n\n async revokeToken(registrationId: string, token: string) {\n const { provider, registration } = this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.revokeToken, 'Provider does not support revokeToken');\n await provider.revokeToken({ token, clientId, clientSecret });\n }\n\n async loginOAuth2Native({\n registrationId,\n credentials,\n pkce,\n }: {\n registrationId: string;\n credentials: NativeCredential;\n pkce?: PkceParameters;\n }) {\n const { provider, registration } = this.getClientConfig(registrationId);\n invariant(provider.loginOAuth2Native, 'Provider does not support loginOAuth2Native');\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n\n return provider.loginOAuth2Native({\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n credentials,\n pkce,\n });\n }\n}\n"],"mappings":"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","names":["invariant"]}
@@ -1,6 +1,6 @@
1
1
  // src/oauth2/client.ts
2
- import { string, object, optional } from "zod/v4-mini";
3
2
  import invariant from "tiny-invariant";
3
+ import { string, object, optional } from "zod/v4-mini";
4
4
  var oauth2RedirectQuerySchema = object({
5
5
  code: optional(string()),
6
6
  state: optional(string()),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/oauth2/client.ts"],"sourcesContent":["import { string, object, optional } from 'zod/v4-mini';\nimport invariant from 'tiny-invariant';\nimport {\n NativeCredential,\n OAuth2ClientConfig,\n OAuth2Token,\n OidcToken,\n PkceParameters,\n} from './types';\n\nexport const oauth2RedirectQuerySchema = object({\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n});\n\nexport const googleOneTapSchema = object({\n // login_uri\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n nonce: optional(string()),\n g_csrf_token: optional(string()),\n hd: optional(string()),\n // callback\n credential: optional(string()),\n});\n\nexport class OAuth2Client {\n private config: OAuth2ClientConfig;\n constructor(config: OAuth2ClientConfig) {\n this.config = config;\n }\n\n get baseUri() {\n return this.config.baseUri;\n }\n\n get successUri() {\n return this.config.successUri;\n }\n\n get errorUri() {\n return this.config.errorUri;\n }\n\n getClientConfig(registrationId: string) {\n const registration = this.config.registration[registrationId];\n invariant(registration, `Registration ${registrationId} not found`);\n const provider = this.config.provider[registration.provider ?? registrationId];\n invariant(provider, `Provider ${registration.provider ?? registrationId} not found`);\n return { registration, provider };\n }\n\n createAuthorizationUri({\n registrationId,\n state,\n pkce,\n }: {\n registrationId: string;\n state: string;\n pkce?: PkceParameters;\n }): URL {\n const { provider, registration } = this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { scope, clientId, redirectUri } = registration;\n return provider.createAuthorizationUri({\n pkce,\n state,\n scope,\n clientId,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async exchangeAuthorizationCode({\n registrationId,\n code,\n pkce,\n }: {\n registrationId: string;\n code: string;\n pkce?: PkceParameters;\n }): Promise<OAuth2Token> {\n const { provider, registration } = this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n return provider.exchangeAuthorizationCode({\n code,\n pkce,\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async getUserInfo({\n registrationId,\n token,\n }: {\n registrationId: string;\n token: OAuth2Token | OidcToken;\n }) {\n const { provider } = this.getClientConfig(registrationId);\n return provider.getUserInfo(token);\n }\n\n async refreshAccessToken({\n registrationId,\n refreshToken,\n }: {\n registrationId: string;\n refreshToken: string;\n }): Promise<OAuth2Token> {\n const { provider, registration } = this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.refreshAccessToken, 'Provider does not support refreshAccessToken');\n return provider.refreshAccessToken({ refreshToken, clientId, clientSecret });\n }\n\n async revokeToken(registrationId: string, token: string) {\n const { provider, registration } = this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.revokeToken, 'Provider does not support revokeToken');\n await provider.revokeToken({ token, clientId, clientSecret });\n }\n\n async loginOAuth2Native({\n registrationId,\n credentials,\n pkce,\n }: {\n registrationId: string;\n credentials: NativeCredential;\n pkce?: PkceParameters;\n }) {\n const { provider, registration } = this.getClientConfig(registrationId);\n invariant(provider.loginOAuth2Native, 'Provider does not support loginOAuth2Native');\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n\n return provider.loginOAuth2Native({\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n credentials,\n pkce,\n });\n }\n}\n"],"mappings":";AAAA,SAAS,QAAQ,QAAQ,gBAAgB;AACzC,OAAO,eAAe;AASf,IAAM,4BAA4B,OAAO;AAAA,EAC9C,MAAM,SAAS,OAAO,CAAC;AAAA,EACvB,OAAO,SAAS,OAAO,CAAC;AAAA,EACxB,OAAO,SAAS,OAAO,CAAC;AAAA,EACxB,mBAAmB,SAAS,OAAO,CAAC;AAAA,EACpC,WAAW,SAAS,OAAO,CAAC;AAC9B,CAAC;AAEM,IAAM,qBAAqB,OAAO;AAAA;AAAA,EAEvC,MAAM,SAAS,OAAO,CAAC;AAAA,EACvB,OAAO,SAAS,OAAO,CAAC;AAAA,EACxB,OAAO,SAAS,OAAO,CAAC;AAAA,EACxB,mBAAmB,SAAS,OAAO,CAAC;AAAA,EACpC,WAAW,SAAS,OAAO,CAAC;AAAA,EAC5B,OAAO,SAAS,OAAO,CAAC;AAAA,EACxB,cAAc,SAAS,OAAO,CAAC;AAAA,EAC/B,IAAI,SAAS,OAAO,CAAC;AAAA;AAAA,EAErB,YAAY,SAAS,OAAO,CAAC;AAC/B,CAAC;AAEM,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EACR,YAAY,QAA4B;AACtC,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,IAAI,aAAa;AACf,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,IAAI,WAAW;AACb,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,gBAAgB,gBAAwB;AACtC,UAAM,eAAe,KAAK,OAAO,aAAa,cAAc;AAC5D,cAAU,cAAc,gBAAgB,cAAc,YAAY;AAClE,UAAM,WAAW,KAAK,OAAO,SAAS,aAAa,YAAY,cAAc;AAC7E,cAAU,UAAU,YAAY,aAAa,YAAY,cAAc,YAAY;AACnF,WAAO,EAAE,cAAc,SAAS;AAAA,EAClC;AAAA,EAEA,uBAAuB;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIQ;AACN,UAAM,EAAE,UAAU,aAAa,IAAI,KAAK,gBAAgB,cAAc;AAEtE,UAAM,EAAE,QAAQ,IAAI,KAAK;AACzB,UAAM,EAAE,OAAO,UAAU,YAAY,IAAI;AACzC,WAAO,SAAS,uBAAuB;AAAA,MACrC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,eAAe,GAAG,OAAO,sBAAsB,cAAc;AAAA,IAC5E,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,0BAA0B;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIyB;AACvB,UAAM,EAAE,UAAU,aAAa,IAAI,KAAK,gBAAgB,cAAc;AAEtE,UAAM,EAAE,QAAQ,IAAI,KAAK;AACzB,UAAM,EAAE,UAAU,cAAc,YAAY,IAAI;AAChD,WAAO,SAAS,0BAA0B;AAAA,MACxC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa,eAAe,GAAG,OAAO,sBAAsB,cAAc;AAAA,IAC5E,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,EACF,GAGG;AACD,UAAM,EAAE,SAAS,IAAI,KAAK,gBAAgB,cAAc;AACxD,WAAO,SAAS,YAAY,KAAK;AAAA,EACnC;AAAA,EAEA,MAAM,mBAAmB;AAAA,IACvB;AAAA,IACA;AAAA,EACF,GAGyB;AACvB,UAAM,EAAE,UAAU,aAAa,IAAI,KAAK,gBAAgB,cAAc;AACtE,UAAM,EAAE,UAAU,aAAa,IAAI;AAEnC,cAAU,SAAS,oBAAoB,8CAA8C;AACrF,WAAO,SAAS,mBAAmB,EAAE,cAAc,UAAU,aAAa,CAAC;AAAA,EAC7E;AAAA,EAEA,MAAM,YAAY,gBAAwB,OAAe;AACvD,UAAM,EAAE,UAAU,aAAa,IAAI,KAAK,gBAAgB,cAAc;AACtE,UAAM,EAAE,UAAU,aAAa,IAAI;AAEnC,cAAU,SAAS,aAAa,uCAAuC;AACvE,UAAM,SAAS,YAAY,EAAE,OAAO,UAAU,aAAa,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIG;AACD,UAAM,EAAE,UAAU,aAAa,IAAI,KAAK,gBAAgB,cAAc;AACtE,cAAU,SAAS,mBAAmB,6CAA6C;AACnF,UAAM,EAAE,QAAQ,IAAI,KAAK;AACzB,UAAM,EAAE,UAAU,cAAc,YAAY,IAAI;AAEhD,WAAO,SAAS,kBAAkB;AAAA,MAChC;AAAA,MACA;AAAA,MACA,aAAa,eAAe,GAAG,OAAO,sBAAsB,cAAc;AAAA,MAC1E;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/oauth2/client.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { string, object, optional } from 'zod/v4-mini';\nimport {\n NativeCredential,\n OAuth2ClientConfig,\n OAuth2Token,\n OidcToken,\n PkceParameters,\n} from './types';\n\nexport const oauth2RedirectQuerySchema = object({\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n});\n\nexport const googleOneTapSchema = object({\n // login_uri\n code: optional(string()),\n state: optional(string()),\n error: optional(string()),\n error_description: optional(string()),\n error_uri: optional(string()),\n nonce: optional(string()),\n g_csrf_token: optional(string()),\n hd: optional(string()),\n // callback\n credential: optional(string()),\n});\n\nexport class OAuth2Client {\n private config: OAuth2ClientConfig;\n constructor(config: OAuth2ClientConfig) {\n this.config = config;\n }\n\n get baseUri() {\n return this.config.baseUri;\n }\n\n get successUri() {\n return this.config.successUri;\n }\n\n get errorUri() {\n return this.config.errorUri;\n }\n\n getClientConfig(registrationId: string) {\n const registration = this.config.registration[registrationId];\n invariant(registration, `Registration ${registrationId} not found`);\n const provider = this.config.provider[registration.provider ?? registrationId];\n invariant(provider, `Provider ${registration.provider ?? registrationId} not found`);\n return { registration, provider };\n }\n\n createAuthorizationUri({\n registrationId,\n state,\n pkce,\n }: {\n registrationId: string;\n state: string;\n pkce?: PkceParameters;\n }): URL {\n const { provider, registration } = this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { scope, clientId, redirectUri } = registration;\n return provider.createAuthorizationUri({\n pkce,\n state,\n scope,\n clientId,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async exchangeAuthorizationCode({\n registrationId,\n code,\n pkce,\n }: {\n registrationId: string;\n code: string;\n pkce?: PkceParameters;\n }): Promise<OAuth2Token> {\n const { provider, registration } = this.getClientConfig(registrationId);\n\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n return provider.exchangeAuthorizationCode({\n code,\n pkce,\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n });\n }\n\n async getUserInfo({\n registrationId,\n token,\n }: {\n registrationId: string;\n token: OAuth2Token | OidcToken;\n }) {\n const { provider } = this.getClientConfig(registrationId);\n return provider.getUserInfo(token);\n }\n\n async refreshAccessToken({\n registrationId,\n refreshToken,\n }: {\n registrationId: string;\n refreshToken: string;\n }): Promise<OAuth2Token> {\n const { provider, registration } = this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.refreshAccessToken, 'Provider does not support refreshAccessToken');\n return provider.refreshAccessToken({ refreshToken, clientId, clientSecret });\n }\n\n async revokeToken(registrationId: string, token: string) {\n const { provider, registration } = this.getClientConfig(registrationId);\n const { clientId, clientSecret } = registration;\n\n invariant(provider.revokeToken, 'Provider does not support revokeToken');\n await provider.revokeToken({ token, clientId, clientSecret });\n }\n\n async loginOAuth2Native({\n registrationId,\n credentials,\n pkce,\n }: {\n registrationId: string;\n credentials: NativeCredential;\n pkce?: PkceParameters;\n }) {\n const { provider, registration } = this.getClientConfig(registrationId);\n invariant(provider.loginOAuth2Native, 'Provider does not support loginOAuth2Native');\n const { baseUri } = this.config;\n const { clientId, clientSecret, redirectUri } = registration;\n\n return provider.loginOAuth2Native({\n clientId,\n clientSecret,\n redirectUri: redirectUri ?? `${baseUri}/login/oauth2/code/${registrationId}`,\n credentials,\n pkce,\n });\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/apple.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { NativeCredential, LoginOAuth2NativeParams, OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\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 }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<AppleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n email: data.email,\n email_verified: true,\n picture: data.picture,\n given_name: data.user?.name.firstName,\n family_name: data.user?.name.lastName,\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 userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n };\n}\n\nexport const apple = createAppleProvider();\n\n// https://developer.apple.com/documentation/sign_in_with_apple/authenticating-users-with-sign-in-with-apple\nexport interface AppleUserInfo {\n sub: string;\n email: string;\n email_verified: true | 'true';\n is_private_email: boolean;\n real_user_status: number;\n name: string;\n picture: string;\n user?: {\n name: { firstName: string; lastName: string };\n email: string;\n };\n nonce?: string;\n nonce_supported?: boolean;\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 // no standard fields\n user: string;\n email?: 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAsB;AAEtB,mBAA4B;AAC5B,oBAAiF;AAG1E,SAAS,sBAAgC;AAC9C,SAAO;AAAA;AAAA,IAEL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc,CAAC,UAAU,QAAQ,OAAO;AAAA,IACxC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,yBAAY,SAAS,QAAQ,KAAK;AAAA,MAC9C;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,gCAAAA,SAAU,UAAU,sBAAsB;AAC1C,gCAAAA,SAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,UAAM,6BAA6B,UAAU,KAAK,SAAS;AACxE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,gBAAgB;AAAA,UAChB,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,MAAM,KAAK;AAAA,UAC5B,aAAa,KAAK,MAAM,KAAK;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,aAAa,GAAG,OAAO,GAA4B;AACjF,gCAAAA,SAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,WAAW,UAAM,yCAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,yBAAY,SAAS,QAAQ,KAAK;AAAA,MAC9C;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,EACF;AACF;AAEO,IAAM,QAAQ,oBAAoB;","names":["invariant"]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/apple.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { NativeCredential, LoginOAuth2NativeParams, OAuth2Token, Provider } 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 }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<AppleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n email: data.email,\n email_verified: true,\n picture: data.picture,\n given_name: data.user?.name.firstName,\n family_name: data.user?.name.lastName,\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 userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n };\n}\n\nexport const apple = createAppleProvider();\n\n// https://developer.apple.com/documentation/sign_in_with_apple/authenticating-users-with-sign-in-with-apple\nexport interface AppleUserInfo {\n sub: string;\n email: string;\n email_verified: true | 'true';\n is_private_email: boolean;\n real_user_status: number;\n name: string;\n picture: string;\n user?: {\n name: { firstName: string; lastName: string };\n email: string;\n };\n nonce?: string;\n nonce_supported?: boolean;\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 // no standard fields\n user: string;\n email?: 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAsB;AACtB,mBAA4B;AAE5B,oBAAiF;AAG1E,SAAS,sBAAgC;AAC9C,SAAO;AAAA;AAAA,IAEL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc,CAAC,UAAU,QAAQ,OAAO;AAAA,IACxC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,yBAAY,SAAS,QAAQ,KAAK;AAAA,MAC9C;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,gCAAAA,SAAU,UAAU,sBAAsB;AAC1C,gCAAAA,SAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,UAAM,6BAA6B,UAAU,KAAK,SAAS;AACxE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,gBAAgB;AAAA,UAChB,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,MAAM,KAAK;AAAA,UAC5B,aAAa,KAAK,MAAM,KAAK;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,aAAa,GAAG,OAAO,GAA4B;AACjF,gCAAAA,SAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,WAAW,UAAM,yCAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,yBAAY,SAAS,QAAQ,KAAK;AAAA,MAC9C;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,EACF;AACF;AAEO,IAAM,QAAQ,oBAAoB;","names":["invariant"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/apple.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { NativeCredential, LoginOAuth2NativeParams, OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\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 }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<AppleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n email: data.email,\n email_verified: true,\n picture: data.picture,\n given_name: data.user?.name.firstName,\n family_name: data.user?.name.lastName,\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 userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n };\n}\n\nexport const apple = createAppleProvider();\n\n// https://developer.apple.com/documentation/sign_in_with_apple/authenticating-users-with-sign-in-with-apple\nexport interface AppleUserInfo {\n sub: string;\n email: string;\n email_verified: true | 'true';\n is_private_email: boolean;\n real_user_status: number;\n name: string;\n picture: string;\n user?: {\n name: { firstName: string; lastName: string };\n email: string;\n };\n nonce?: string;\n nonce_supported?: boolean;\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 // no standard fields\n user: string;\n email?: 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":";AAAA,OAAO,eAAe;AAEtB,SAAS,mBAAmB;AAC5B,SAAS,wBAAwB,2BAA2B,qBAAqB;AAG1E,SAAS,sBAAgC;AAC9C,SAAO;AAAA;AAAA,IAEL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc,CAAC,UAAU,QAAQ,OAAO;AAAA,IACxC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,YAAY,SAAS,QAAQ,KAAK;AAAA,MAC9C;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,gBAAU,UAAU,sBAAsB;AAC1C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,MAAM,cAA6B,UAAU,KAAK,SAAS;AACxE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,gBAAgB;AAAA,UAChB,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,MAAM,KAAK;AAAA,UAC5B,aAAa,KAAK,MAAM,KAAK;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,aAAa,GAAG,OAAO,GAA4B;AACjF,gBAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,WAAW,MAAM,0BAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,YAAY,SAAS,QAAQ,KAAK;AAAA,MAC9C;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,EACF;AACF;AAEO,IAAM,QAAQ,oBAAoB;","names":[]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/apple.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { NativeCredential, LoginOAuth2NativeParams, OAuth2Token, Provider } 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 }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<AppleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n email: data.email,\n email_verified: true,\n picture: data.picture,\n given_name: data.user?.name.firstName,\n family_name: data.user?.name.lastName,\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 userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n };\n}\n\nexport const apple = createAppleProvider();\n\n// https://developer.apple.com/documentation/sign_in_with_apple/authenticating-users-with-sign-in-with-apple\nexport interface AppleUserInfo {\n sub: string;\n email: string;\n email_verified: true | 'true';\n is_private_email: boolean;\n real_user_status: number;\n name: string;\n picture: string;\n user?: {\n name: { firstName: string; lastName: string };\n email: string;\n };\n nonce?: string;\n nonce_supported?: boolean;\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 // no standard fields\n user: string;\n email?: 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":";AAAA,OAAO,eAAe;AACtB,SAAS,mBAAmB;AAE5B,SAAS,wBAAwB,2BAA2B,qBAAqB;AAG1E,SAAS,sBAAgC;AAC9C,SAAO;AAAA;AAAA,IAEL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc,CAAC,UAAU,QAAQ,OAAO;AAAA,IACxC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,YAAY,SAAS,QAAQ,KAAK;AAAA,MAC9C;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,gBAAU,UAAU,sBAAsB;AAC1C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,MAAM,cAA6B,UAAU,KAAK,SAAS;AACxE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,gBAAgB;AAAA,UAChB,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,MAAM,KAAK;AAAA,UAC5B,aAAa,KAAK,MAAM,KAAK;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,aAAa,GAAG,OAAO,GAA4B;AACjF,gBAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,WAAW,MAAM,0BAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,YAAY,SAAS,QAAQ,KAAK;AAAA,MAC9C;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,EACF;AACF;AAEO,IAAM,QAAQ,oBAAoB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { LoginOAuth2NativeParams, OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\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 url = new URL(this.userInfoUri);\n const fields = ['id', 'name', 'email', 'picture.width(320).height(320)'];\n url.searchParams.append('access_token', access_token);\n url.searchParams.append('fields', fields.join(','));\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 async loginOAuth2Native({ credentials }: LoginOAuth2NativeParams) {\n if (credentials.id_token || credentials.access_token) {\n const userInfo = await this.getUserInfo(credentials as unknown as OAuth2Token);\n const token: OAuth2Token = {\n token_type: 'bearer',\n access_token: credentials.access_token ?? '',\n id_token: credentials.id_token,\n };\n return { token, userInfo };\n } else {\n throw new OAuth2Error(400, 'invalid_request', 'id_token or access_token is required');\n }\n },\n };\n}\n\nexport const facebook = createFacebookProvider();\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAsB;AAEtB,mBAA4B;AAC5B,oBAAiF;AAM1E,SAAS,yBAAmC;AACjD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc,CAAC,WAAW,OAAO;AAAA,IACjC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,yBAAY,SAAS,QAAQ,mBAAmB,MAAM,OAAO;AAAA,MACzE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,UAAU,aAAa,GAAG;AAC5C,gCAAAA,SAAU,KAAK,WAAW,uBAAuB;AACjD,UAAI,UAAU;AACZ,cAAMC,QAAO,UAAM,6BAAsC,UAAU,KAAK,SAAS;AACjF,eAAO;AAAA,UACL,MAAAA;AAAA,UACA,QAAQ;AAAA,YACN,KAAKA,MAAK;AAAA,YACV,MAAMA,MAAK;AAAA,YACX,SAASA,MAAK;AAAA,YACd,OAAOA,MAAK;AAAA,YACZ,gBAAgB;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AACA,gCAAAD,SAAU,cAAc,0BAA0B;AAClD,gCAAAA,SAAU,KAAK,aAAa,yBAAyB;AAErD,YAAM,MAAM,IAAI,IAAI,KAAK,WAAW;AACpC,YAAM,SAAS,CAAC,MAAM,QAAQ,SAAS,gCAAgC;AACvE,UAAI,aAAa,OAAO,gBAAgB,YAAY;AACpD,UAAI,aAAa,OAAO,UAAU,OAAO,KAAK,GAAG,CAAC;AAClD,YAAM,UAAU,EAAE,QAAQ,mBAAmB;AAC7C,YAAM,WAAW,MAAM,MAAM,IAAI,MAAM,EAAE,QAAQ,CAAC;AAClD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,yBAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK,QAAQ,KAAK;AAAA,UAC3B,gBAAgB,KAAK;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,YAAY,GAA4B;AAChE,UAAI,YAAY,YAAY,YAAY,cAAc;AACpD,cAAM,WAAW,MAAM,KAAK,YAAY,WAAqC;AAC7E,cAAM,QAAqB;AAAA,UACzB,YAAY;AAAA,UACZ,cAAc,YAAY,gBAAgB;AAAA,UAC1C,UAAU,YAAY;AAAA,QACxB;AACA,eAAO,EAAE,OAAO,SAAS;AAAA,MAC3B,OAAO;AACL,cAAM,IAAI,yBAAY,KAAK,mBAAmB,sCAAsC;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,WAAW,uBAAuB;","names":["invariant","data"]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { 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 url = new URL(this.userInfoUri);\n const fields = ['id', 'name', 'email', 'picture.width(320).height(320)'];\n url.searchParams.append('access_token', access_token);\n url.searchParams.append('fields', fields.join(','));\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 async loginOAuth2Native({ credentials }: LoginOAuth2NativeParams) {\n if (credentials.id_token || credentials.access_token) {\n const userInfo = await this.getUserInfo(credentials as unknown as OAuth2Token);\n const token: OAuth2Token = {\n token_type: 'bearer',\n access_token: credentials.access_token ?? '',\n id_token: credentials.id_token,\n };\n return { token, userInfo };\n } else {\n throw new OAuth2Error(400, 'invalid_request', 'id_token or access_token is required');\n }\n },\n };\n}\n\nexport const facebook = createFacebookProvider();\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAsB;AACtB,mBAA4B;AAE5B,oBAAiF;AAM1E,SAAS,yBAAmC;AACjD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc,CAAC,WAAW,OAAO;AAAA,IACjC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,yBAAY,SAAS,QAAQ,mBAAmB,MAAM,OAAO;AAAA,MACzE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,UAAU,aAAa,GAAG;AAC5C,gCAAAA,SAAU,KAAK,WAAW,uBAAuB;AACjD,UAAI,UAAU;AACZ,cAAMC,QAAO,UAAM,6BAAsC,UAAU,KAAK,SAAS;AACjF,eAAO;AAAA,UACL,MAAAA;AAAA,UACA,QAAQ;AAAA,YACN,KAAKA,MAAK;AAAA,YACV,MAAMA,MAAK;AAAA,YACX,SAASA,MAAK;AAAA,YACd,OAAOA,MAAK;AAAA,YACZ,gBAAgB;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AACA,gCAAAD,SAAU,cAAc,0BAA0B;AAClD,gCAAAA,SAAU,KAAK,aAAa,yBAAyB;AAErD,YAAM,MAAM,IAAI,IAAI,KAAK,WAAW;AACpC,YAAM,SAAS,CAAC,MAAM,QAAQ,SAAS,gCAAgC;AACvE,UAAI,aAAa,OAAO,gBAAgB,YAAY;AACpD,UAAI,aAAa,OAAO,UAAU,OAAO,KAAK,GAAG,CAAC;AAClD,YAAM,UAAU,EAAE,QAAQ,mBAAmB;AAC7C,YAAM,WAAW,MAAM,MAAM,IAAI,MAAM,EAAE,QAAQ,CAAC;AAClD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,yBAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK,QAAQ,KAAK;AAAA,UAC3B,gBAAgB,KAAK;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,YAAY,GAA4B;AAChE,UAAI,YAAY,YAAY,YAAY,cAAc;AACpD,cAAM,WAAW,MAAM,KAAK,YAAY,WAAqC;AAC7E,cAAM,QAAqB;AAAA,UACzB,YAAY;AAAA,UACZ,cAAc,YAAY,gBAAgB;AAAA,UAC1C,UAAU,YAAY;AAAA,QACxB;AACA,eAAO,EAAE,OAAO,SAAS;AAAA,MAC3B,OAAO;AACL,cAAM,IAAI,yBAAY,KAAK,mBAAmB,sCAAsC;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,WAAW,uBAAuB;","names":["invariant","data"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { LoginOAuth2NativeParams, OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\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 url = new URL(this.userInfoUri);\n const fields = ['id', 'name', 'email', 'picture.width(320).height(320)'];\n url.searchParams.append('access_token', access_token);\n url.searchParams.append('fields', fields.join(','));\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 async loginOAuth2Native({ credentials }: LoginOAuth2NativeParams) {\n if (credentials.id_token || credentials.access_token) {\n const userInfo = await this.getUserInfo(credentials as unknown as OAuth2Token);\n const token: OAuth2Token = {\n token_type: 'bearer',\n access_token: credentials.access_token ?? '',\n id_token: credentials.id_token,\n };\n return { token, userInfo };\n } else {\n throw new OAuth2Error(400, 'invalid_request', 'id_token or access_token is required');\n }\n },\n };\n}\n\nexport const facebook = createFacebookProvider();\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":";AAAA,OAAO,eAAe;AAEtB,SAAS,mBAAmB;AAC5B,SAAS,wBAAwB,2BAA2B,qBAAqB;AAM1E,SAAS,yBAAmC;AACjD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc,CAAC,WAAW,OAAO;AAAA,IACjC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,MAAM,OAAO;AAAA,MACzE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,UAAU,aAAa,GAAG;AAC5C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,UAAI,UAAU;AACZ,cAAMA,QAAO,MAAM,cAAsC,UAAU,KAAK,SAAS;AACjF,eAAO;AAAA,UACL,MAAAA;AAAA,UACA,QAAQ;AAAA,YACN,KAAKA,MAAK;AAAA,YACV,MAAMA,MAAK;AAAA,YACX,SAASA,MAAK;AAAA,YACd,OAAOA,MAAK;AAAA,YACZ,gBAAgB;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AACA,gBAAU,cAAc,0BAA0B;AAClD,gBAAU,KAAK,aAAa,yBAAyB;AAErD,YAAM,MAAM,IAAI,IAAI,KAAK,WAAW;AACpC,YAAM,SAAS,CAAC,MAAM,QAAQ,SAAS,gCAAgC;AACvE,UAAI,aAAa,OAAO,gBAAgB,YAAY;AACpD,UAAI,aAAa,OAAO,UAAU,OAAO,KAAK,GAAG,CAAC;AAClD,YAAM,UAAU,EAAE,QAAQ,mBAAmB;AAC7C,YAAM,WAAW,MAAM,MAAM,IAAI,MAAM,EAAE,QAAQ,CAAC;AAClD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK,QAAQ,KAAK;AAAA,UAC3B,gBAAgB,KAAK;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,YAAY,GAA4B;AAChE,UAAI,YAAY,YAAY,YAAY,cAAc;AACpD,cAAM,WAAW,MAAM,KAAK,YAAY,WAAqC;AAC7E,cAAM,QAAqB;AAAA,UACzB,YAAY;AAAA,UACZ,cAAc,YAAY,gBAAgB;AAAA,UAC1C,UAAU,YAAY;AAAA,QACxB;AACA,eAAO,EAAE,OAAO,SAAS;AAAA,MAC3B,OAAO;AACL,cAAM,IAAI,YAAY,KAAK,mBAAmB,sCAAsC;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,WAAW,uBAAuB;","names":["data"]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/facebook.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { 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 url = new URL(this.userInfoUri);\n const fields = ['id', 'name', 'email', 'picture.width(320).height(320)'];\n url.searchParams.append('access_token', access_token);\n url.searchParams.append('fields', fields.join(','));\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 async loginOAuth2Native({ credentials }: LoginOAuth2NativeParams) {\n if (credentials.id_token || credentials.access_token) {\n const userInfo = await this.getUserInfo(credentials as unknown as OAuth2Token);\n const token: OAuth2Token = {\n token_type: 'bearer',\n access_token: credentials.access_token ?? '',\n id_token: credentials.id_token,\n };\n return { token, userInfo };\n } else {\n throw new OAuth2Error(400, 'invalid_request', 'id_token or access_token is required');\n }\n },\n };\n}\n\nexport const facebook = createFacebookProvider();\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":";AAAA,OAAO,eAAe;AACtB,SAAS,mBAAmB;AAE5B,SAAS,wBAAwB,2BAA2B,qBAAqB;AAM1E,SAAS,yBAAmC;AACjD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,WAAW;AAAA,IACX,mBAAmB;AAAA,IACnB,cAAc,CAAC,WAAW,OAAO;AAAA,IACjC,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,MAAM,IAAK,MAAM,SAAS,KAAK;AACvC,cAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,MAAM,OAAO;AAAA,MACzE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,UAAU,aAAa,GAAG;AAC5C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,UAAI,UAAU;AACZ,cAAMA,QAAO,MAAM,cAAsC,UAAU,KAAK,SAAS;AACjF,eAAO;AAAA,UACL,MAAAA;AAAA,UACA,QAAQ;AAAA,YACN,KAAKA,MAAK;AAAA,YACV,MAAMA,MAAK;AAAA,YACX,SAASA,MAAK;AAAA,YACd,OAAOA,MAAK;AAAA,YACZ,gBAAgB;AAAA,UAClB;AAAA,QACF;AAAA,MACF;AACA,gBAAU,cAAc,0BAA0B;AAClD,gBAAU,KAAK,aAAa,yBAAyB;AAErD,YAAM,MAAM,IAAI,IAAI,KAAK,WAAW;AACpC,YAAM,SAAS,CAAC,MAAM,QAAQ,SAAS,gCAAgC;AACvE,UAAI,aAAa,OAAO,gBAAgB,YAAY;AACpD,UAAI,aAAa,OAAO,UAAU,OAAO,KAAK,GAAG,CAAC;AAClD,YAAM,UAAU,EAAE,QAAQ,mBAAmB;AAC7C,YAAM,WAAW,MAAM,MAAM,IAAI,MAAM,EAAE,QAAQ,CAAC;AAClD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK,QAAQ,KAAK;AAAA,UAC3B,gBAAgB,KAAK;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,YAAY,GAA4B;AAChE,UAAI,YAAY,YAAY,YAAY,cAAc;AACpD,cAAM,WAAW,MAAM,KAAK,YAAY,WAAqC;AAC7E,cAAM,QAAqB;AAAA,UACzB,YAAY;AAAA,UACZ,cAAc,YAAY,gBAAgB;AAAA,UAC1C,UAAU,YAAY;AAAA,QACxB;AACA,eAAO,EAAE,OAAO,SAAS;AAAA,MAC3B,OAAO;AACL,cAAM,IAAI,YAAY,KAAK,mBAAmB,sCAAsC;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,WAAW,uBAAuB;","names":["data"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/oauth2/provider/github.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Token, Provider } from '../types';\nimport { OAuth2Error } from '../error';\nimport { createAuthorizationUri, exchangeAuthorizationCode } from './common';\n\nexport function createGithubProvider(): Provider {\n return {\n authorizationUri: 'https://github.com/login/oauth/authorize',\n tokenUri: 'https://github.com/login/oauth/access_token',\n userInfoUri: 'https://api.github.com/user',\n userNameAttribute: 'id',\n defaultScope: ['user: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, error_description } = (await response.json()) as GithubErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GithubToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n const response = await fetch(this.userInfoUri, { 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 GithubUserInfo;\n const emailResponse = await fetch('https://api.github.com/user/emails', { headers });\n if (!emailResponse.ok) {\n throw new OAuth2Error(\n emailResponse.status,\n 'invalid_request',\n 'Failed to fetch user email'\n );\n }\n const emails = (await emailResponse.json()) as GithubEmail[];\n data.email = emails.find((email) => email.primary)?.email || data.email;\n const email_verified = emails.find((email) => email.email === data.email)?.verified || false;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name || data.login,\n email_verified,\n email: data.email,\n picture: data.avatar_url,\n profile: data.html_url,\n website: data.html_url,\n updated_at: data.updated_at ? Date.parse(data.updated_at) : undefined,\n },\n };\n },\n };\n}\n\nexport const github = createGithubProvider();\n\nexport interface GithubUserInfo {\n login: string;\n id: string;\n node_id: string;\n avatar_url: string;\n gravatar_id: string;\n url: string;\n html_url: string;\n followers_url: string;\n following_url: string;\n gists_url: string;\n starred_url: string;\n subscriptions_url: string;\n organizations_url: string;\n repos_url: string;\n events_url: string;\n received_events_url: string;\n type: string;\n site_admin: boolean;\n name: string;\n company: string;\n blog: string;\n location: string;\n email: string;\n hireable: boolean;\n bio: string;\n twitter_username: string;\n public_repos: string;\n public_gists: string;\n followers: string;\n following: string;\n created_at: string;\n updated_at: string;\n private_gists: string;\n total_private_repos: string;\n owned_private_repos: string;\n disk_usage: string;\n collaborators: string;\n two_factor_authentication: boolean;\n plan: {\n name: string;\n space: string;\n private_repos: string;\n collaborators: string;\n };\n}\n\ninterface GithubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private';\n}\n\nexport interface GithubToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n scope: string;\n expires_in: number;\n refresh_token: string;\n}\n\ninterface GithubErrorResponse {\n error: string;\n error_description: string;\n error_uri: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAsB;AAEtB,mBAA4B;AAC5B,oBAAkE;AAE3D,SAAS,uBAAiC;AAC/C,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,cAAc,CAAC,YAAY;AAAA,IAC3B,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,yBAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,aAAa,GAAG;AAClC,gCAAAA,SAAU,cAAc,0BAA0B;AAClD,gCAAAA,SAAU,KAAK,aAAa,yBAAyB;AACrD,YAAM,UAAU,EAAE,QAAQ,oBAAoB,eAAe,UAAU,YAAY,GAAG;AACtF,YAAM,WAAW,MAAM,MAAM,KAAK,aAAa,EAAE,QAAQ,CAAC;AAC1D,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,yBAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,gBAAgB,MAAM,MAAM,sCAAsC,EAAE,QAAQ,CAAC;AACnF,UAAI,CAAC,cAAc,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,cAAc;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAU,MAAM,cAAc,KAAK;AACzC,WAAK,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,GAAG,SAAS,KAAK;AAClE,YAAM,iBAAiB,OAAO,KAAK,CAAC,UAAU,MAAM,UAAU,KAAK,KAAK,GAAG,YAAY;AACvF,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK,QAAQ,KAAK;AAAA,UACxB;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,aAAa,KAAK,MAAM,KAAK,UAAU,IAAI;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":["invariant"]}
1
+ {"version":3,"sources":["../../../src/oauth2/provider/github.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { OAuth2Token, Provider } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode } from './common';\n\nexport function createGithubProvider(): Provider {\n return {\n authorizationUri: 'https://github.com/login/oauth/authorize',\n tokenUri: 'https://github.com/login/oauth/access_token',\n userInfoUri: 'https://api.github.com/user',\n userNameAttribute: 'id',\n defaultScope: ['user: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, error_description } = (await response.json()) as GithubErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GithubToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n const response = await fetch(this.userInfoUri, { 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 GithubUserInfo;\n const emailResponse = await fetch('https://api.github.com/user/emails', { headers });\n if (!emailResponse.ok) {\n throw new OAuth2Error(\n emailResponse.status,\n 'invalid_request',\n 'Failed to fetch user email'\n );\n }\n const emails = (await emailResponse.json()) as GithubEmail[];\n data.email = emails.find((email) => email.primary)?.email || data.email;\n const email_verified = emails.find((email) => email.email === data.email)?.verified || false;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name || data.login,\n email_verified,\n email: data.email,\n picture: data.avatar_url,\n profile: data.html_url,\n website: data.html_url,\n updated_at: data.updated_at ? Date.parse(data.updated_at) : undefined,\n },\n };\n },\n };\n}\n\nexport const github = createGithubProvider();\n\nexport interface GithubUserInfo {\n login: string;\n id: string;\n node_id: string;\n avatar_url: string;\n gravatar_id: string;\n url: string;\n html_url: string;\n followers_url: string;\n following_url: string;\n gists_url: string;\n starred_url: string;\n subscriptions_url: string;\n organizations_url: string;\n repos_url: string;\n events_url: string;\n received_events_url: string;\n type: string;\n site_admin: boolean;\n name: string;\n company: string;\n blog: string;\n location: string;\n email: string;\n hireable: boolean;\n bio: string;\n twitter_username: string;\n public_repos: string;\n public_gists: string;\n followers: string;\n following: string;\n created_at: string;\n updated_at: string;\n private_gists: string;\n total_private_repos: string;\n owned_private_repos: string;\n disk_usage: string;\n collaborators: string;\n two_factor_authentication: boolean;\n plan: {\n name: string;\n space: string;\n private_repos: string;\n collaborators: string;\n };\n}\n\ninterface GithubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private';\n}\n\nexport interface GithubToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n scope: string;\n expires_in: number;\n refresh_token: string;\n}\n\ninterface GithubErrorResponse {\n error: string;\n error_description: string;\n error_uri: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAsB;AACtB,mBAA4B;AAE5B,oBAAkE;AAE3D,SAAS,uBAAiC;AAC/C,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,cAAc,CAAC,YAAY;AAAA,IAC3B,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,iBAAO,sCAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,UAAM,yCAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,yBAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,aAAa,GAAG;AAClC,gCAAAA,SAAU,cAAc,0BAA0B;AAClD,gCAAAA,SAAU,KAAK,aAAa,yBAAyB;AACrD,YAAM,UAAU,EAAE,QAAQ,oBAAoB,eAAe,UAAU,YAAY,GAAG;AACtF,YAAM,WAAW,MAAM,MAAM,KAAK,aAAa,EAAE,QAAQ,CAAC;AAC1D,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,yBAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,gBAAgB,MAAM,MAAM,sCAAsC,EAAE,QAAQ,CAAC;AACnF,UAAI,CAAC,cAAc,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,cAAc;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAU,MAAM,cAAc,KAAK;AACzC,WAAK,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,GAAG,SAAS,KAAK;AAClE,YAAM,iBAAiB,OAAO,KAAK,CAAC,UAAU,MAAM,UAAU,KAAK,KAAK,GAAG,YAAY;AACvF,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK,QAAQ,KAAK;AAAA,UACxB;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,aAAa,KAAK,MAAM,KAAK,UAAU,IAAI;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":["invariant"]}