{"version":3,"sources":["../src/auth-auth0.ts","../src/auth.ts"],"sourcesContent":["import { createAuth0Client } from '@auth0/auth0-spa-js'\nimport { registerAuthProvider, assertProviderConfig } from './auth'\nimport type { AuthClient, AuthProviderContext } from './types'\n\n/** Auth0 auth provider factory — pass to createFoundation({ auth: auth0Auth }) or import to auto-register */\nexport const auth0Auth = async (config: Record<string, unknown>, ctx: AuthProviderContext): Promise<AuthClient> => {\n  const auth0 = assertProviderConfig(config, 'auth0', ['domain', 'clientId']) as\n    { domain: string; clientId: string; audience?: string; scope?: string }\n\n  const client = await createAuth0Client({\n    domain: auth0.domain,\n    clientId: auth0.clientId,\n    authorizationParams: {\n      audience: auth0.audience,\n      scope: auth0.scope || 'openid profile email',\n      redirect_uri: window.location.origin\n    },\n    useRefreshTokens: true,\n    cacheLocation: 'localstorage'\n  })\n\n  const domain = auth0.domain\n  const clientId = auth0.clientId\n\n  return {\n    login: (options) => client.loginWithRedirect(options),\n    logout: (options) => client.logout({ logoutParams: { returnTo: window.location.origin }, ...options }),\n    getUser: async () => {\n      const user = await client.getUser()\n      if (!user) return undefined\n      return { id: user.sub || '', email: user.email || '', name: user.name, picture: user.picture }\n    },\n    getTokenSilently: (options) => client.getTokenSilently(options),\n    isAuthenticated: () => client.isAuthenticated(),\n\n    async handleCallback(url?: string) {\n      await client.handleRedirectCallback(url)\n    },\n\n    async confirmSignUp() {\n      // Auth0 confirms via email link, not code — nothing to do on the client\n      // The user clicks the verification link in their email\n    },\n\n    async resendSignUpCode() {\n      // Auth0 verification resend routes through the backend (requires management API access)\n      const token = await client.getTokenSilently().catch(() => null)\n      if (!token) throw new Error('Must be authenticated to resend verification email')\n      const response = await fetch(`${ctx.accountBaseUrl}/api/v1/accounts/account/resend-verification`, {\n        method: 'POST',\n        headers: {\n          'Authorization': `Bearer ${token}`,\n          'Content-Type': 'application/json',\n          'X-Foundation-Mvp-Application-Id': ctx.appId,\n          'X-Foundation-Mvp-Tenant-Id': ctx.tenantId,\n          'X-Foundation-Mvp-Application-Version': ctx.version\n        }\n      })\n      if (!response.ok) {\n        throw new Error('Failed to resend verification email')\n      }\n    },\n\n    async signIn(email: string, password: string) {\n      const response = await fetch(`https://${domain}/oauth/token`, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify({\n          grant_type: 'password',\n          client_id: clientId,\n          username: email,\n          password,\n          audience: auth0.audience,\n          scope: auth0.scope || 'openid profile email'\n        })\n      })\n      if (!response.ok) {\n        const err = await response.json().catch(() => ({}))\n        throw new Error(err.error_description || err.message || 'Sign in failed')\n      }\n      // Auth0 doesn't have multi-step sign-in. Email verification is tracked via\n      // user.email_verified and handled by the backend, not as a blocking step.\n      return { isSignedIn: true, nextStep: { signInStep: 'DONE' } }\n    },\n\n    async signUp(email: string, password: string, metadata?: Record<string, unknown>) {\n      const response = await fetch(`https://${domain}/dbconnections/signup`, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify({\n          client_id: clientId,\n          email,\n          password,\n          connection: 'Username-Password-Authentication',\n          ...metadata\n        })\n      })\n      if (!response.ok) {\n        const err = await response.json().catch(() => ({}))\n        throw new Error(err.description || err.message || 'Sign up failed')\n      }\n      const data = await response.json().catch(() => ({}))\n      // Auth0 signup completes immediately. Verification is via email link, handled out-of-band.\n      return {\n        isSignUpComplete: true,\n        userId: data._id || data.user_id,\n        nextStep: { signUpStep: 'DONE' }\n      }\n    },\n\n    async forgotPassword(email: string) {\n      const response = await fetch(`https://${domain}/dbconnections/change_password`, {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify({\n          client_id: clientId,\n          email,\n          connection: 'Username-Password-Authentication'\n        })\n      })\n      if (!response.ok) {\n        const err = await response.json().catch(() => ({}))\n        throw new Error(err.error_description || err.message || 'Password reset request failed')\n      }\n    },\n\n    async resetPassword() {\n      throw new Error('Auth0 password reset is completed via the email link, not a code')\n    }\n  }\n}\n\n// Auto-register when imported\nregisterAuthProvider('auth0', auth0Auth)\n","/**\n * Auth core — provider registry and AuthService wrapper.\n * Provider implementations live in auth-auth0.ts and auth-cognito.ts.\n */\nimport type { AuthClient, AuthService, AuthProvider, AuthProviderContext, SignInResult, User } from './types'\n\nconst MAX_LISTENERS = 100\n\n/**\n * How long the password given to `signUp` stays in memory so `confirmSignUp` can\n * complete the sign-in itself. Long enough to cover reading a code out of an email,\n * short enough that an abandoned signup does not leave a password sitting in a tab\n * for the rest of the session.\n */\nconst PENDING_SIGN_UP_TTL_MS = 15 * 60 * 1000\n\n// --- Provider registry ---\n\nconst providers = new Map<string, AuthProvider>()\n\nexport function registerAuthProvider(name: string, factory: AuthProvider) {\n  providers.set(name, factory)\n}\n\n// Built-in \"none\" provider\nregisterAuthProvider('none', async () => ({\n  login: async () => {},\n  logout: async () => {},\n  getUser: async () => undefined,\n  getTokenSilently: async () => 'none',\n  isAuthenticated: async () => true\n}))\n\nexport interface AuthProviderConfig {\n  provider: string\n  [key: string]: unknown\n}\n\n/**\n * Validate the auth config block handed to a provider factory before it\n * touches the underlying auth library. The block comes from the backend's\n * public init response, not from local options — so a mismatch means the\n * backend isn't configured for this provider, and configuring the library\n * with empty values would only fail later with an unrelated-looking error.\n */\nexport function assertProviderConfig(\n  config: Record<string, unknown>,\n  provider: string,\n  requiredKeys: readonly string[]\n): Record<string, unknown> {\n  const reported = typeof config.provider === 'string' && config.provider ? config.provider : undefined\n  if (reported !== provider) {\n    throw new Error(\n      `[foundation-sdk] createFoundation was given the \"${provider}\" auth provider, but the backend ` +\n      `config endpoint reports provider \"${reported ?? 'none'}\". Auth factories are configured from ` +\n      `the init response, not local options. Set authenticationProvider: \"${provider}\" in the backend ` +\n      `config.json and redeploy (app:deploy) so /api/v1/public/init returns the ${provider} auth config.`\n    )\n  }\n  const block = config[provider]\n  const record = block && typeof block === 'object' ? block as Record<string, unknown> : undefined\n  const missing = requiredKeys.filter(key => {\n    const value = record?.[key]\n    return typeof value !== 'string' || value.trim() === ''\n  })\n  if (!record || missing.length > 0) {\n    throw new Error(\n      `[foundation-sdk] The backend reports provider \"${provider}\" but its config block is incomplete ` +\n      `(missing or empty: ${missing.join(', ')}). Check the ${provider} settings in the backend ` +\n      `config.json and redeploy (app:deploy) so /api/v1/public/init returns the full ${provider} config.`\n    )\n  }\n  return record\n}\n\nexport async function createAuthClient(config: AuthProviderConfig, ctx: AuthProviderContext): Promise<AuthClient> {\n  const factory = providers.get(config.provider)\n  if (!factory) {\n    throw new Error(\n      `Auth provider \"${config.provider}\" not registered. ` +\n      `Import \"foundation-sdk/${config.provider}\" to register it.`\n    )\n  }\n  return factory(config, ctx)\n}\n\n// --- AuthService wrapper ---\n\nexport interface AuthServiceOptions {\n  /** Sign in automatically after a successful `confirmSignUp`. Default true. */\n  autoSignInAfterConfirm?: boolean\n}\n\nexport function createAuthService(\n  client: AuthClient,\n  options: AuthServiceOptions = {}\n): AuthService & { _initUser(): Promise<void> } {\n  const autoSignInAfterConfirm = options.autoSignInAfterConfirm !== false\n  let user: User | null = null\n  const listeners: Array<(user: User | null) => void> = []\n\n  /**\n   * A confirmation code proves the user owns the address, but confirming does not\n   * create a session — so without this the caller has to bounce them to a login form\n   * to retype the password they entered a minute earlier. The credentials live in this\n   * closure only: never localStorage, never sessionStorage, never a cookie. They are\n   * dropped the moment they are used, superseded by another auth call, or stale.\n   */\n  let pendingSignUp: { email: string; password: string; expiresAt: number } | null = null\n  let pendingSignUpTimer: ReturnType<typeof setTimeout> | null = null\n\n  function clearPendingSignUp(): void {\n    pendingSignUp = null\n    if (pendingSignUpTimer !== null) {\n      clearTimeout(pendingSignUpTimer)\n      pendingSignUpTimer = null\n    }\n  }\n\n  function rememberPendingSignUp(email: string, password: string): void {\n    clearPendingSignUp()\n    if (!autoSignInAfterConfirm || !email || !password) return\n    pendingSignUp = { email, password, expiresAt: Date.now() + PENDING_SIGN_UP_TTL_MS }\n    pendingSignUpTimer = setTimeout(clearPendingSignUp, PENDING_SIGN_UP_TTL_MS)\n    // Background tabs throttle timers and Node would hold the process open for one,\n    // so `expiresAt` is the guard that actually decides; the timer only scrubs early.\n    const handle = pendingSignUpTimer as unknown as { unref?: () => void }\n    if (typeof handle.unref === 'function') handle.unref()\n  }\n\n  /** Returns the held credentials for `email` exactly once, then forgets them. */\n  function takePendingSignUp(email: string): { email: string; password: string } | null {\n    const pending = pendingSignUp\n    clearPendingSignUp()\n    if (!pending || pending.expiresAt <= Date.now()) return null\n    // Compare loosely (Cognito treats email aliases case-insensitively) but sign in with\n    // the username signUp actually used, which is the one the pool has a record for.\n    if (pending.email.trim().toLowerCase() !== email.trim().toLowerCase()) return null\n    return { email: pending.email, password: pending.password }\n  }\n\n  function notifyListeners() {\n    listeners.forEach(fn => { try { fn(user) } catch { /* */ } })\n  }\n\n  async function refreshUser() {\n    const authUser = await client.getUser()\n    user = authUser ? { id: authUser.id, email: authUser.email, name: authUser.name, picture: authUser.picture } : null\n  }\n\n  return {\n    get user() { return user },\n    get isAuthenticated() { return !!user },\n\n    async getToken() {\n      return client.getTokenSilently()\n    },\n\n    async login(options) {\n      await client.login(options)\n      await refreshUser()\n      notifyListeners()\n    },\n\n    async logout(options) {\n      clearPendingSignUp()\n      await client.logout(options)\n      user = null\n      notifyListeners()\n    },\n\n    async handleCallback(url?) {\n      if (!client.handleCallback) throw new Error('handleCallback not supported by this auth provider')\n      await client.handleCallback(url)\n      await refreshUser()\n      notifyListeners()\n    },\n\n    async signIn(email, password) {\n      if (!client.signIn) throw new Error('signIn not supported by this auth provider')\n      // An explicit sign-in supersedes whatever signUp left behind, success or not.\n      clearPendingSignUp()\n      const result = await client.signIn(email, password)\n      // Only refresh user state if the sign-in actually completed.\n      // If a provider returns isSignedIn: false with a next step (e.g. CONFIRM_SIGN_UP),\n      // the user isn't really signed in and we shouldn't populate the user ref.\n      if (result.isSignedIn) {\n        await refreshUser()\n        notifyListeners()\n      }\n      return result\n    },\n\n    async signUp(email, password, metadata) {\n      if (!client.signUp) throw new Error('signUp not supported by this auth provider')\n      const result = await client.signUp(email, password, metadata)\n      rememberPendingSignUp(email, password)\n      return result\n    },\n\n    async confirmSignUp(email, code) {\n      if (!client.confirmSignUp) throw new Error('confirmSignUp not supported by this auth provider')\n      await client.confirmSignUp(email, code)\n\n      const pending = takePendingSignUp(email)\n      if (!pending || !client.signIn) return { isSignedIn: false }\n\n      let result: SignInResult\n      try {\n        result = await client.signIn(pending.email, pending.password)\n      } catch {\n        // The account is confirmed — that is what this call promised, and turning a\n        // failed convenience sign-in into a thrown confirmation would make the caller\n        // replay a code that is already spent. Report no session and let them show\n        // the sign-in form.\n        return { isSignedIn: false }\n      }\n      if (result.isSignedIn) {\n        await refreshUser()\n        notifyListeners()\n      }\n      return result\n    },\n\n    async resendSignUpCode(email) {\n      if (!client.resendSignUpCode) throw new Error('resendSignUpCode not supported by this auth provider')\n      await client.resendSignUpCode(email)\n    },\n\n    async forgotPassword(email) {\n      if (!client.forgotPassword) throw new Error('forgotPassword not supported by this auth provider')\n      await client.forgotPassword(email)\n    },\n\n    async resetPassword(email, code, newPassword) {\n      if (!client.resetPassword) throw new Error('resetPassword not supported by this auth provider')\n      // Whatever signUp handed us is stale the moment the password changes.\n      clearPendingSignUp()\n      await client.resetPassword(email, code, newPassword)\n    },\n\n    onChange(callback) {\n      if (listeners.length >= MAX_LISTENERS) {\n        console.warn('[Foundation SDK] Auth listener limit reached.')\n        return () => {}\n      }\n      listeners.push(callback)\n      return () => {\n        const idx = listeners.indexOf(callback)\n        if (idx > -1) listeners.splice(idx, 1)\n      }\n    },\n\n    async _initUser() {\n      const authenticated = await client.isAuthenticated()\n      if (authenticated) await refreshUser()\n    }\n  }\n}\n"],"mappings":"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","names":["auth_auth0_exports","__export","auth0Auth","__toCommonJS","import_auth0_spa_js","providers","registerAuthProvider","name","factory","assertProviderConfig","config","provider","requiredKeys","reported","block","record","missing","key","value","auth0Auth","config","ctx","auth0","assertProviderConfig","client","domain","clientId","options","user","url","token","email","password","response","err","metadata","data","registerAuthProvider"]}