{"version":3,"file":"flow-srp.cjs","sourceRoot":"","sources":["../../../src/sdk/authentication-jwt-bearer/flow-srp.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Eip1193Provider } from 'ethers';\n\nimport {\n  authenticate,\n  authorizeOIDC,\n  getNonce,\n  getUserProfileLineage,\n} from './services';\nimport type {\n  AuthConfig,\n  AuthSigningOptions,\n  AuthStorageOptions,\n  AuthType,\n  IBaseAuth,\n  LoginResponse,\n  UserProfile,\n  UserProfileLineage,\n} from './types';\nimport * as timeUtils from './utils/time';\nimport type { MetaMetricsAuth } from '../../shared/types/services';\nimport { ValidationError, RateLimitedError } from '../errors';\nimport { getMetaMaskProviderEIP6963 } from '../utils/eip-6963-metamask-provider';\nimport {\n  MESSAGE_SIGNING_SNAP,\n  assertMessageStartsWithMetamask,\n  connectSnap,\n  isSnapConnected,\n} from '../utils/messaging-signing-snap-requests';\nimport { validateLoginResponse } from '../utils/validate-login-response';\n\ntype JwtBearerAuth_SRP_Options = {\n  storage: AuthStorageOptions;\n  signing?: AuthSigningOptions;\n  rateLimitRetry?: {\n    cooldownDefaultMs?: number; // default cooldown when 429 has no Retry-After\n    maxLoginRetries?: number; // maximum number of login retries on rate limit\n  };\n};\n\nconst getDefaultEIP6963Provider = async () => {\n  const provider = await getMetaMaskProviderEIP6963();\n  if (!provider) {\n    throw new ValidationError('No MetaMask wallet connected');\n  }\n  return provider;\n};\n\nconst getDefaultEIP6963SigningOptions = (\n  customProvider?: Eip1193Provider,\n): AuthSigningOptions => ({\n  getIdentifier: async (entropySourceId?: string): Promise<string> => {\n    const provider = customProvider ?? (await getDefaultEIP6963Provider());\n    return await MESSAGE_SIGNING_SNAP.getPublicKey(provider, entropySourceId);\n  },\n  signMessage: async (\n    message: string,\n    entropySourceId?: string,\n  ): Promise<string> => {\n    const provider = customProvider ?? (await getDefaultEIP6963Provider());\n    assertMessageStartsWithMetamask(message);\n    return await MESSAGE_SIGNING_SNAP.signMessage(\n      provider,\n      message,\n      entropySourceId,\n    );\n  },\n});\n\nexport class SRPJwtBearerAuth implements IBaseAuth {\n  readonly #config: AuthConfig;\n\n  readonly #options: {\n    storage: AuthStorageOptions;\n    signing: AuthSigningOptions;\n  };\n\n  readonly #metametrics?: MetaMetricsAuth;\n\n  // Map to store ongoing login promises by entropySourceId\n  readonly #ongoingLogins = new Map<\n    string | undefined,\n    Promise<LoginResponse>\n  >();\n\n  // Default cooldown when 429 has no Retry-After header\n  readonly #cooldownDefaultMs: number;\n\n  // Maximum number of login retries on rate limit errors\n  readonly #maxLoginRetries: number;\n\n  #customProvider?: Eip1193Provider;\n\n  constructor(\n    config: AuthConfig & { type: AuthType.SRP },\n    options: JwtBearerAuth_SRP_Options & {\n      customProvider?: Eip1193Provider;\n      metametrics?: MetaMetricsAuth;\n    },\n  ) {\n    this.#config = config;\n    this.#customProvider = options.customProvider;\n    this.#options = {\n      storage: options.storage,\n      signing:\n        options.signing ??\n        getDefaultEIP6963SigningOptions(this.#customProvider),\n    };\n    this.#metametrics = options.metametrics;\n\n    // Apply rate limit retry config if provided\n    this.#cooldownDefaultMs =\n      options.rateLimitRetry?.cooldownDefaultMs ?? 10000;\n    this.#maxLoginRetries = options.rateLimitRetry?.maxLoginRetries ?? 1;\n  }\n\n  setCustomProvider(provider: Eip1193Provider) {\n    this.#customProvider = provider;\n    this.#options.signing = getDefaultEIP6963SigningOptions(provider);\n  }\n\n  // TODO: might be easier to keep entropySourceId as a class param and use multiple SRPJwtBearerAuth instances where needed\n  async getAccessToken(entropySourceId?: string): Promise<string> {\n    const session = await this.#getAuthSession(entropySourceId);\n    if (session) {\n      return session.token.accessToken;\n    }\n\n    const loginResponse = await this.#login(entropySourceId);\n    return loginResponse.token.accessToken;\n  }\n\n  async getUserProfile(entropySourceId?: string): Promise<UserProfile> {\n    const session = await this.#getAuthSession(entropySourceId);\n    if (session) {\n      return session.profile;\n    }\n\n    const loginResponse = await this.#login(entropySourceId);\n    return loginResponse.profile;\n  }\n\n  async getIdentifier(entropySourceId?: string): Promise<string> {\n    return await this.#options.signing.getIdentifier(entropySourceId);\n  }\n\n  async getUserProfileLineage(\n    entropySourceId?: string,\n  ): Promise<UserProfileLineage> {\n    const accessToken = await this.getAccessToken(entropySourceId);\n    return await getUserProfileLineage(this.#config.env, accessToken);\n  }\n\n  async signMessage(\n    message: string,\n    entropySourceId?: string,\n  ): Promise<string> {\n    return await this.#options.signing.signMessage(message, entropySourceId);\n  }\n\n  async isSnapConnected(): Promise<boolean> {\n    const provider =\n      this.#customProvider ?? (await getDefaultEIP6963Provider());\n    if (!provider) {\n      return false;\n    }\n\n    const isConnected = await isSnapConnected(provider);\n    return isConnected;\n  }\n\n  async connectSnap(): Promise<string> {\n    const provider =\n      this.#customProvider ?? (await getDefaultEIP6963Provider());\n\n    const res = await connectSnap(provider);\n    return res;\n  }\n\n  // convert expiresIn from seconds to milliseconds and use 90% of expiresIn\n  async #getAuthSession(\n    entropySourceId?: string,\n  ): Promise<LoginResponse | null> {\n    const auth = await this.#options.storage.getLoginResponse(entropySourceId);\n    if (!validateLoginResponse(auth)) {\n      return null;\n    }\n\n    const currentTime = Date.now();\n    const sessionAge = currentTime - auth.token.obtainedAt;\n    const refreshThreshold = auth.token.expiresIn * 1000 * 0.9;\n\n    if (sessionAge < refreshThreshold) {\n      return auth;\n    }\n    return null;\n  }\n\n  async #login(entropySourceId?: string): Promise<LoginResponse> {\n    // Use a deferred login to avoid race conditions\n    return await this.#deferredLogin(entropySourceId);\n  }\n\n  async #performLogin(entropySourceId?: string): Promise<LoginResponse> {\n    // Nonce\n    const publicKey = await this.getIdentifier(entropySourceId);\n    const nonceRes = await getNonce(publicKey, this.#config.env);\n\n    const rawMessage = this.#createSrpLoginRawMessage(\n      nonceRes.nonce,\n      publicKey,\n    );\n    const signature = await this.signMessage(rawMessage, entropySourceId);\n\n    // Authenticate\n    const authResponse = await authenticate(\n      rawMessage,\n      signature,\n      this.#config.type,\n      this.#config.env,\n      this.#metametrics,\n    );\n\n    // Authorize\n    const tokenResponse = await authorizeOIDC(\n      authResponse.token,\n      this.#config.env,\n      this.#config.platform,\n    );\n\n    // Save\n    const result: LoginResponse = {\n      profile: authResponse.profile,\n      token: tokenResponse,\n    };\n\n    await this.#options.storage.setLoginResponse(result, entropySourceId);\n\n    return result;\n  }\n\n  async #deferredLogin(entropySourceId?: string): Promise<LoginResponse> {\n    // Check if there's already an ongoing login for this entropySourceId\n    const existingLogin = this.#ongoingLogins.get(entropySourceId);\n    if (existingLogin) {\n      return existingLogin;\n    }\n\n    // Create a new login promise\n    const loginPromise = this.#loginWithRetry(entropySourceId);\n\n    // Store the promise in the map\n    this.#ongoingLogins.set(entropySourceId, loginPromise);\n\n    try {\n      // Wait for the login to complete\n      return await loginPromise;\n    } finally {\n      // Always clean up the ongoing login promise when done\n      this.#ongoingLogins.delete(entropySourceId);\n    }\n  }\n\n  async #loginWithRetry(entropySourceId?: string): Promise<LoginResponse> {\n    // Allow max attempts: initial + maxLoginRetries on 429\n    for (let attempt = 0; attempt < 1 + this.#maxLoginRetries; attempt += 1) {\n      try {\n        return await this.#performLogin(entropySourceId);\n      } catch (e) {\n        // Only retry on rate-limit (429) errors\n        if (!RateLimitedError.isRateLimitError(e)) {\n          throw e;\n        }\n\n        // If we've exhausted attempts, rethrow\n        if (attempt >= this.#maxLoginRetries) {\n          throw e;\n        }\n\n        // Wait for Retry-After or default cooldown\n        const waitMs = e.retryAfterMs ?? this.#cooldownDefaultMs;\n        await timeUtils.delay(waitMs);\n\n        // Loop continues to retry\n      }\n    }\n\n    // Should never reach here due to loop logic, but TypeScript needs a return\n    throw new Error('Unexpected: login loop exhausted without result');\n  }\n\n  #createSrpLoginRawMessage(\n    nonce: string,\n    publicKey: string,\n  ): `metamask:${string}:${string}` {\n    return `metamask:${nonce}:${publicKey}` as const;\n  }\n}\n"]}