{"version":3,"file":"idpHelpers.mjs","sources":["../../../../../../../web/src/utils/idpHelpers.ts"],"sourcesContent":["/* eslint-disable lingui/no-unlocalized-strings -- user-facing strings in this file can be overridden using different APIs */\n\nimport { RBACPolicyRaw } from '@stytch/core';\nimport { IDPConsentItem } from '@stytch/core/public';\n\nconst OPENID_CONSENT_ITEM: IDPConsentItem = {\n  text: 'Verify your identity',\n  details: ['View information stored within your account'],\n};\n\n// Full Access didn't really make the cut\n// const FULL_ACCESS_CONSENT_ITEM: IDPConsentItem = {\n//   text: 'Act on behalf of you',\n//   details: [],\n// };\n\nconst PROFILE_CONSENT_ITEM: IDPConsentItem = {\n  text: 'View your personal profile information',\n  details: [],\n};\n\nconst OFFLINE_ACCESS_CONSENT_ITEM: IDPConsentItem = {\n  text: \"Maintain access to your data even when you're not actively using the app\",\n  details: [\n    'Access your data even when you are offline.',\n    'Synchronize data and process background tasks on your behalf.',\n  ],\n};\n\nconst OPENID_SCOPE = 'openid';\nconst PROFILE_SCOPE = 'profile';\nconst EMAIL_SCOPE = 'email';\nconst PHONE_SCOPE = 'phone';\nconst FULL_ACCESS_SCOPE = 'full_access';\nconst OFFLINE_ACCESS_SCOPE = 'offline_access';\n\nconst DEFAULT_SCOPES = new Set([\n  OPENID_SCOPE,\n  PROFILE_SCOPE,\n  EMAIL_SCOPE,\n  PHONE_SCOPE,\n  FULL_ACCESS_SCOPE,\n  OFFLINE_ACCESS_SCOPE,\n]);\n\n// Scope is an optional param in OAuth2.1\n// When no scope is present, this is what we default to\nconst FALLBACK_SCOPES = [OPENID_SCOPE, EMAIL_SCOPE, PROFILE_SCOPE];\n\nexport const containsCustomScopes = (scope: string): boolean => {\n  // set.difference is not in ES2015\n  return scope.split(' ').some((sc) => !DEFAULT_SCOPES.has(sc));\n};\n\nexport const createBuiltinScopeDescriptions = (scopes: string[]): IDPConsentItem[] => {\n  const descriptions: IDPConsentItem[] = [];\n\n  if (scopes.includes(OPENID_SCOPE)) {\n    descriptions.push(OPENID_CONSENT_ITEM);\n  }\n\n  if (scopes.includes(EMAIL_SCOPE) || scopes.includes(PROFILE_SCOPE) || scopes.includes(PHONE_SCOPE)) {\n    descriptions.push(\n      createProfileScopeDescription({\n        containsEmail: scopes.includes(EMAIL_SCOPE),\n        containsPhoneNumber: scopes.includes(PHONE_SCOPE),\n        containsProfile: scopes.includes(PROFILE_SCOPE),\n      }),\n    );\n  }\n\n  // if (scopes.has(FULL_ACCESS_SCOPE)) {\n  //   descriptions.push();\n  // }\n\n  if (scopes.includes(OFFLINE_ACCESS_SCOPE)) {\n    descriptions.push(OFFLINE_ACCESS_CONSENT_ITEM);\n  }\n\n  return descriptions;\n};\n\nconst createProfileScopeDescription = ({\n  containsProfile,\n  containsEmail,\n  containsPhoneNumber,\n}: {\n  containsProfile: boolean;\n  containsEmail: boolean;\n  containsPhoneNumber: boolean;\n}): IDPConsentItem => {\n  const details = [];\n  if (containsProfile) {\n    details.push('Your name, profile picture, and language preferences');\n  }\n  if (containsEmail) {\n    details.push('Your email address');\n  }\n  if (containsPhoneNumber) {\n    details.push('Your phone number');\n  }\n\n  return {\n    text: PROFILE_CONSENT_ITEM.text,\n    details: details,\n  };\n};\n\nexport const createCustomScopeDescriptions = (scopes: string[], rbacPolicy: RBACPolicyRaw | null): string[] => {\n  if (!rbacPolicy) return [];\n\n  const descriptions: string[] = [];\n\n  for (const scope of scopes) {\n    if (DEFAULT_SCOPES.has(scope)) continue;\n    const found = rbacPolicy.scopes.find((policyScope) => policyScope.scope === scope);\n    if (found && found.description) {\n      descriptions.push(found.description);\n    } else {\n      descriptions.push(`Use the ${scope} scope`);\n    }\n  }\n\n  return descriptions;\n};\n\nexport const fallbackConsentManifestGenerator = ({\n  scopes,\n  clientName,\n  rbacPolicy,\n}: {\n  scopes: string[];\n  clientName: string;\n  rbacPolicy: RBACPolicyRaw | null;\n}) => {\n  return [\n    {\n      header: `${clientName} is requesting to:`,\n      items: createBuiltinScopeDescriptions(scopes).concat(createCustomScopeDescriptions(scopes, rbacPolicy)),\n    },\n  ];\n};\n\nexport type OAuthAuthorizeParams = {\n  // Required.\n  client_id: string;\n  redirect_uri: string;\n  // Required, but has default\n  response_type: string;\n  scopes: string[];\n  // Optional.\n  code_challenge?: string;\n  state?: string;\n  nonce?: string;\n  prompt?: string;\n  resources?: string[];\n};\n\nexport type OAuthLogoutParams = {\n  // Required.\n  client_id: string;\n  post_logout_redirect_uri: string;\n  // Optional.\n  id_token_hint?: string;\n  state?: string;\n};\n\nexport type IDPFlowParams =\n  | { type: 'Authorize'; params: OAuthAuthorizeParams }\n  | { type: 'Logout'; params: OAuthLogoutParams };\n\n/**\n * Parse the OAuth Authorize params from the URL search parameters to pass in to subsequent OAuthAuthorize calls.\n *\n * @param params - The URL search parameters to parse.\n * @returns The parsed OAuth Authorize parameters.\n */\nexport const parseOAuthAuthorizeParams = (\n  params: URLSearchParams,\n): { error: string | null; result: OAuthAuthorizeParams } => {\n  const authorizeParams: OAuthAuthorizeParams = {\n    client_id: '',\n    redirect_uri: '',\n    // As of writing, ChatGPT isn't sending `response_type` when making calls to our\n    // authorization endpoint, even though it's technically a required field in the spec.\n    // We default it to 'code' here and server-side.\n    // See: https://stytchio.slack.com/archives/C07U0MHAH7G/p1749075544763149.\n    response_type: 'code',\n    // Default to this initial set of scopes when the client does not provide this param\n    scopes: [...FALLBACK_SCOPES],\n  };\n\n  const requiredFields = ['client_id', 'redirect_uri'] as const;\n  for (const field of requiredFields) {\n    const value = params.get(field);\n    if (!value) {\n      return {\n        error: `Required parameter is missing: ${field}. Please reach out to the application developer.`,\n        result: authorizeParams,\n      };\n    }\n    authorizeParams[field] = value;\n  }\n\n  const optionalStringFields = ['response_type', 'scope', 'code_challenge', 'state', 'nonce', 'prompt'] as const;\n  for (const field of optionalStringFields) {\n    const value = params.get(field);\n    if (value) {\n      if (field === 'scope') {\n        authorizeParams.scopes = value.split(' ').filter(Boolean);\n      } else {\n        authorizeParams[field] = value;\n      }\n    }\n  }\n\n  if (params.has('resource')) {\n    authorizeParams.resources = params.getAll('resource');\n  }\n\n  return { error: null, result: authorizeParams };\n};\n\nexport const parseOAuthLogoutParams = (\n  params: URLSearchParams,\n): { error: string | null; result: OAuthLogoutParams } => {\n  const logoutParams: OAuthLogoutParams = {\n    client_id: '',\n    post_logout_redirect_uri: '',\n  };\n\n  const requiredFields = ['client_id', 'post_logout_redirect_uri'] as const;\n  for (const field of requiredFields) {\n    const value = params.get(field);\n    if (!value) {\n      return {\n        error: `Required parameter is missing: ${field}. Please reach out to the application developer.`,\n        result: logoutParams,\n      };\n    }\n    logoutParams[field] = value;\n  }\n\n  logoutParams.id_token_hint = params.get('id_token_hint') || undefined;\n  logoutParams.state = params.get('state') || undefined;\n\n  return { error: null, result: logoutParams };\n};\n\n/**\n * Parse generic IDP parameters and determine if it is an Authorize or Logout request.\n */\nexport const parseIDPParams = (searchParams: string): { error: string | null; flow: IDPFlowParams } => {\n  const params = new URLSearchParams(searchParams);\n\n  // Check if `post_logout_redirect_uri` exists to determine if it's a Logout request.\n  if (params.has('post_logout_redirect_uri')) {\n    const logoutResult = parseOAuthLogoutParams(params);\n    return { error: logoutResult.error, flow: { type: 'Logout', params: logoutResult.result } };\n  }\n\n  // Otherwise, assume it's an Authorize request.\n  const authorizeResult = parseOAuthAuthorizeParams(params);\n  return { error: authorizeResult.error, flow: { type: 'Authorize', params: authorizeResult.result! } };\n};\n"],"names":["OPENID_CONSENT_ITEM","text","details","PROFILE_CONSENT_ITEM","OFFLINE_ACCESS_CONSENT_ITEM","OPENID_SCOPE","PROFILE_SCOPE","EMAIL_SCOPE","PHONE_SCOPE","FULL_ACCESS_SCOPE","OFFLINE_ACCESS_SCOPE","DEFAULT_SCOPES","Set","FALLBACK_SCOPES","containsCustomScopes","scope","split","some","sc","has","createBuiltinScopeDescriptions","scopes","descriptions","includes","push","createProfileScopeDescription","containsEmail","containsPhoneNumber","containsProfile","createCustomScopeDescriptions","rbacPolicy","found","find","policyScope","description","fallbackConsentManifestGenerator","clientName","header","items","concat","parseOAuthAuthorizeParams","params","authorizeParams","client_id","redirect_uri","response_type","requiredFields","field","value","get","error","result","optionalStringFields","filter","Boolean","resources","getAll","parseOAuthLogoutParams","logoutParams","post_logout_redirect_uri","id_token_hint","undefined","state","parseIDPParams","searchParams","URLSearchParams","logoutResult","flow","type","authorizeResult"],"mappings":"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"}