{"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["import { createHash, randomInt, randomUUID, timingSafeEqual } from 'crypto';\nimport { invariant } from '@shware/utils';\nimport { type Principal, Provider } from '../core/index';\nimport { loginEmailSchema, sendEmailVerificationCodeSchema } from '../email/schema';\nimport { OAuth2Client, googleOneTapSchema, oauth2RedirectQuerySchema } from '../oauth2/client';\nimport type { OAuth2ErrorType } from '../oauth2/error';\nimport { google } from '../oauth2/provider/index';\nimport type {\n  NativeCredential,\n  OAuth2AuthorizationRequest,\n  PkceParameters,\n  StandardClaims,\n} from '../oauth2/types';\nimport { PRINCIPAL_NAME_INDEX_NAME } from '../session/common';\nimport type { KVRepository, Session, SessionRepository } from '../session/types';\nimport {\n  type CookieOptions,\n  deleteCookie,\n  getCookie,\n  param,\n  query,\n  setCookie,\n} from '../utils/http';\nimport { timing } from '../utils/timing';\nimport { invalidArgument, valid, verifyTurnstileToken } from '../utils/valid';\nimport type { AuthConfig, AuthService, AuthorizedHandler, LoggedHandler } from './types';\n\n// PKCE per RFC 7636: code_verifier / code_challenge are 43-128 chars from\n// the unreserved set. We restrict to base64url (drops \".\" and \"~\") since\n// that's what S256 produces and what callers should be sending; rejecting\n// the rest shrinks the accepted input shape without losing legit clients.\n// A 32-byte random verifier base64url-encodes to exactly 43 chars.\nconst PKCE_FORMAT = /^[A-Za-z0-9_-]{43,128}$/;\n\ninterface DesktopCodeRecord {\n  /** principal.name from the upstream cookie session */\n  name: string;\n  /** code_challenge (S256, base64url) — verifier must hash to this */\n  cc: string;\n}\n\nexport const PATH = {\n  CSRF: '/csrf',\n  LOGOUT: '/logout',\n  LOGGED: '/logged',\n\n  OAUTH2_STATE: '/oauth2/state/:registrationId',\n  OAUTH2_NONCE: '/oauth2/nonce/:registrationId',\n  OAUTH2_AUTHORIZATION: '/oauth2/authorization/:registrationId',\n  LOGIN_OAUTH2_CODE: '/login/oauth2/code/:registrationId',\n  LOGIN_OAUTH2_NATIVE: '/login/oauth2/native/:registrationId',\n  LOGIN_OAUTH2_ONETAP: '/login/oauth2/onetap/google',\n\n  LOGIN_EMAIL: '/login/email',\n  LOGIN_PHONE: '/login/phone',\n  SEND_EMAIL_VERIFICATION_CODE: '/auth/sendEmailVerificationCode',\n  SEND_PHONE_VERIFICATION_CODE: '/auth/sendPhoneVerificationCode',\n\n  // Desktop / native app sign-in via web loopback (RFC 8252 + OAuth 2.1 Auth Code Flow):\n  //   1. user already authenticated with cookie session → POST DESKTOP_AUTHORIZE → { code }\n  //   2. web redirects to desktop's loopback callback with the code\n  //   3. desktop renderer POSTs DESKTOP_EXCHANGE with the code → server returns\n  //      Set-Cookie carrying a fresh session, single-use code is invalidated\n  // The two-step flow keeps the actual session id off the redirect URL — only a\n  // 5-minute single-use code transits via the loopback.\n  DESKTOP_AUTHORIZE: '/auth/desktop/authorize',\n  DESKTOP_EXCHANGE: '/auth/desktop/exchange',\n\n  CLEANUP_EXPIRED_SESSIONS: '/sessions/expired/cleanup',\n} as const;\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  private readonly turnstileSecretKey?: string;\n  private readonly oauth2Client: OAuth2Client | null;\n  private readonly ATTR_OAUTH2_AUTHORIZATION_REQUEST = 'oauth2AuthorizationRequest';\n\n  constructor({\n    sessionRepository,\n    kvRepository,\n    turnstileSecretKey,\n    oauth2,\n    cookie,\n    timing,\n  }: AuthConfig) {\n    this.timing = timing ?? false;\n    this.kv = kvRepository;\n    this.repository = sessionRepository;\n    this.turnstileSecretKey = turnstileSecretKey;\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  private getSessionId(request: Request): string | undefined {\n    let sessionId: string | undefined;\n    const header = request.headers.get('Authorization');\n    if (header?.startsWith('Bearer ')) {\n      sessionId = header.replace('Bearer ', '');\n    }\n    if (!sessionId) {\n      sessionId = getCookie(request, this.cookieName);\n    }\n    return sessionId;\n  }\n\n  csrf = async (_request: Request): Promise<Response> => {\n    const token = randomUUID();\n    const response = Response.json({\n      token,\n      parameterName: '_csrf',\n      headerName: 'X-XSRF-TOKEN',\n    });\n\n    // Double Submit Cookie Protection\n    setCookie(response, 'XSRF-TOKEN', token, {\n      path: '/',\n      secure: true,\n      httpOnly: false,\n      sameSite: 'none',\n      domain: this.cookieOptions.domain,\n    });\n    return response;\n  };\n\n  logout = async (request: Request): Promise<Response> => {\n    const sessionId = this.getSessionId(request);\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 header or cookie\n    const sessionId = this.getSessionId(request);\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  private getEmailVerificationCodeKey(email: string) {\n    return `email:verification:${email}`;\n  }\n\n  private getPhoneVerificationCodeKey(phone: string) {\n    return `phone:verification:${phone}`;\n  }\n\n  private getDesktopCodeKey(code: string) {\n    return `desktop:code:${code}`;\n  }\n\n  oauth2State = async (request: Request): Promise<Response> => {\n    const { registrationId } = param(request, 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, 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, PATH.OAUTH2_AUTHORIZATION);\n    const state = randomUUID();\n    const pkce = this.createPkceParameters();\n    const uri = await this.oauth2Client.createAuthorizationUri({ registrationId, state, pkce });\n    const sessionId = this.getSessionId(request);\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 (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n    invariant(this.oauth2Client, 'oauth2Client is not initialized');\n    const { mark, setTiming } = timing({ enabled: this.timing });\n\n    // 1. get session from header or cookie\n    const sessionId = this.getSessionId(request);\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, 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 handler(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 (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n    invariant(this.oauth2Client, 'oauth2Client is not initialized');\n    const { mark, setTiming } = timing({ enabled: this.timing });\n    const { registrationId } = param(request, 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 handler(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    handler: AuthorizedHandler,\n    registrationId = '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')?.includes('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')?.includes('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 handler(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 handler(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  sendEmailVerificationCode = async (\n    request: Request,\n    sender: (data: { email: string; verificationCode: string }) => Promise<void>\n  ): Promise<Response> => {\n    invariant(this.turnstileSecretKey, 'turnstileSecretKey is not configured');\n\n    const { data, error } = valid(sendEmailVerificationCodeSchema, await request.json());\n    if (error) return Response.json(error, { status: error.error.code });\n    const { email, turnstileToken } = data;\n\n    const result = await verifyTurnstileToken(this.turnstileSecretKey, turnstileToken);\n    if (result.error) return Response.json(result.error, { status: result.error.error.code });\n\n    try {\n      const verificationCode = randomInt(1_000_000).toString().padStart(6, '0');\n      await sender({ email, verificationCode });\n      await this.kv.setItem(this.getEmailVerificationCodeKey(email), verificationCode, 10 * 60);\n      return Response.json({});\n    } catch (e) {\n      console.error('Failed to send email verification code', e);\n      return Response.json(invalidArgument('EMAIL_DELIVERY_FAILED'), { status: 400 });\n    }\n  };\n\n  loginEmail = async (request: Request, handler: AuthorizedHandler): Promise<Response> => {\n    const { data, error } = valid(loginEmailSchema, await request.json());\n    if (error) return Response.json(error, { status: error.error.code });\n    const { email, verificationCode } = data;\n\n    const key = this.getEmailVerificationCodeKey(email);\n    const stored = await this.kv.getItem(key);\n    if (\n      typeof stored !== 'string' ||\n      stored.length !== verificationCode.length ||\n      !timingSafeEqual(Buffer.from(stored), Buffer.from(verificationCode))\n    ) {\n      return Response.json(invalidArgument('INVALID_VERIFICATION_CODE'), { status: 400 });\n    }\n    await this.kv.removeItem(key);\n    const claims: StandardClaims = { sub: email, email, email_verified: true };\n    const principal = await handler(request, Provider.EMAIL, { claims, data: {} });\n\n    // save session\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\n    return response;\n  };\n\n  /**\n   * Step 1 of the desktop / native app sign-in handshake. The caller already\n   * completed normal sign-in on the web (so a cookie session exists); this\n   * issues a 5-minute single-use code that the web page hands off to the\n   * desktop client via a loopback redirect.\n   *\n   * PKCE (RFC 7636, S256 only): the desktop generates a random `code_verifier`\n   * before opening the browser, derives `code_challenge = base64url(sha256(verifier))`,\n   * and threads the challenge through the URL → web page → here. We pin\n   * the challenge to the code in KV; `desktopExchange` enforces that the\n   * caller knows the matching verifier. This blocks an attacker who only\n   * intercepts the auth code (browser history, malicious extension, packet\n   * capture on loopback) from exchanging it.\n   *\n   * Requires an authenticated cookie session — anonymous callers get 401.\n   * Throttling / abuse protection on this endpoint is the API layer's job\n   * (rate-limit per principal); the SDK doesn't gate it because the\n   * upstream sign-in flow already passed CAPTCHA / OAuth / verification.\n   */\n  desktopAuthorize = async (request: Request): Promise<Response> => {\n    const principal = await this.getPrincipal(request);\n    if (!principal) {\n      const body = {\n        error: {\n          code: 401,\n          status: 'UNAUTHENTICATED',\n          message: 'sign in first',\n          details: [],\n        },\n      };\n      return Response.json(body, { status: 401 });\n    }\n\n    let body: { code_challenge?: unknown; code_challenge_method?: unknown };\n    try {\n      body = (await request.json()) as {\n        code_challenge?: unknown;\n        code_challenge_method?: unknown;\n      };\n    } catch {\n      return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n    }\n\n    const codeChallenge = typeof body.code_challenge === 'string' ? body.code_challenge : null;\n    if (\n      !codeChallenge ||\n      body.code_challenge_method !== 'S256' ||\n      !PKCE_FORMAT.test(codeChallenge)\n    ) {\n      return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n    }\n\n    const code = randomUUID();\n    const record: DesktopCodeRecord = { name: principal.name, cc: codeChallenge };\n    await this.kv.setItem(this.getDesktopCodeKey(code), JSON.stringify(record), 5 * 60);\n\n    return Response.json({ code });\n  };\n\n  /**\n   * Step 2 of the desktop / native app sign-in handshake. The desktop\n   * client hands back the auth code it received via the loopback callback,\n   * and we mint a fresh session for it — independent of the web cookie\n   * session that originally `authorize`'d the code, so logging out on\n   * either side doesn't cascade to the other.\n   *\n   * Cookie attributes: defaults to `SameSite=None; Secure` so the cookie\n   * survives cross-site fetches from the desktop renderer's `file://` /\n   * `app://` origin. The configured base `cookieOptions` provide\n   * everything else (`domain`, `path`, `httpOnly`, `maxAge`); the override\n   * is targeted at the SameSite/Secure pair only. In a non-HTTPS dev\n   * backend this combination is rejected by Chromium — see the desktop\n   * sign-in PRD for workarounds.\n   *\n   * No CSRF check needed — the request carries no cookie, the auth code\n   * itself is the credential, and KV's single-use removal blocks replay.\n   */\n  desktopExchange = async (request: Request): Promise<Response> => {\n    let body: { code?: unknown; code_verifier?: unknown };\n    try {\n      body = (await request.json()) as { code?: unknown; code_verifier?: unknown };\n    } catch {\n      return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n    }\n\n    const code = typeof body.code === 'string' ? body.code : null;\n    const verifier = typeof body.code_verifier === 'string' ? body.code_verifier : null;\n    if (!code || !verifier || !PKCE_FORMAT.test(verifier)) {\n      return Response.json(invalidArgument('INVALID_ARGUMENT'), { status: 400 });\n    }\n\n    const key = this.getDesktopCodeKey(code);\n    const raw = await this.kv.getItem(key);\n    if (typeof raw !== 'string') {\n      return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n    }\n    let record: DesktopCodeRecord;\n    try {\n      record = JSON.parse(raw) as DesktopCodeRecord;\n    } catch {\n      // Malformed KV record (shouldn't happen with our own writes) — drop it\n      // and surface as an invalid code; the caller can retry the whole flow.\n      await this.kv.removeItem(key);\n      return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n    }\n\n    // Verify PKCE: base64url(sha256(verifier)) === stored challenge.\n    // Don't burn the code on a verifier mismatch — that would let an\n    // attacker who races a network retry brick a legit exchange. The code\n    // is single-use on success and 5-min TTL bounded; brute-forcing the\n    // 256-bit verifier in that window is infeasible.\n    const computed = createHash('sha256').update(verifier).digest();\n    const stored = Buffer.from(record.cc, 'base64url');\n    if (stored.length !== computed.length || !timingSafeEqual(computed, stored)) {\n      return Response.json(invalidArgument('INVALID_DESKTOP_CODE'), { status: 400 });\n    }\n\n    // One-time use: remove BEFORE creating the session so a concurrent\n    // duplicate exchange (e.g. retry after timeout) sees the code as\n    // consumed even if our session save races it.\n    await this.kv.removeItem(key);\n\n    const session = this.repository.createSession();\n    session.setAttribute(PRINCIPAL_NAME_INDEX_NAME, record.name);\n    session.setAttribute('client_type', 'desktop');\n    await this.repository.save(session);\n\n    const response = Response.json({ ok: true });\n    const maxAge = session.getMaxInactiveInterval();\n    setCookie(response, this.cookieName, session.getId(), {\n      ...this.cookieOptions,\n      // Override sameSite/secure for desktop: the renderer's origin\n      // (file:// / app://) is cross-site to the API, so SameSite=Lax/Strict\n      // would block the cookie on subsequent fetches.\n      sameSite: 'none',\n      secure: true,\n      maxAge,\n    });\n    return response;\n  };\n\n  sendPhoneVerificationCode = async (\n    _request: Request,\n    _sender: (data: { phone: string; verificationCode: string }) => Promise<void>\n  ): Promise<Response> => {\n    return Response.json({ error: 'not_implemented' }, { status: 501 });\n  };\n\n  loginPhone = async (_request: Request, _handler: AuthorizedHandler): Promise<Response> => {\n    return Response.json({ error: 'not_implemented' }, { status: 501 });\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 = this.getSessionId(request);\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 = this.getSessionId(request);\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 = this.getSessionId(request);\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","body","session"]}