@simulacrum/auth0-simulator
Version:
Run local instance of Auth0 API for local development and integration testing
1 lines • 8.32 kB
Source Map (JSON)
{"version":3,"file":"oauth-handlers.mjs","names":["user: Auth0User | undefined","nonce: string | undefined","refreshToken: RefreshToken","context: RuleContext<Partial<AccessTokenPayload>, IdTokenData>","userData: RuleUser","idTokenData: IdTokenData","username: string","password: string | undefined","decodeBase64"],"sources":["../../src/handlers/oauth-handlers.ts"],"sourcesContent":["import { assert } from \"assert-ts\";\nimport { decode, decode as decodeBase64 } from \"base64-url\";\nimport { epochTime, expiresAt } from \"../auth/date.ts\";\nimport { createJsonWebToken } from \"../auth/jwt.ts\";\nimport { createRulesRunner } from \"../rules/rules-runner.ts\";\nimport { deriveScope, createPersonQuery } from \"./utils.ts\";\n\nimport type { Request } from \"express\";\nimport type { RuleContext, RuleUser } from \"../rules/types.ts\";\nimport type {\n ScopeConfig,\n AccessTokenPayload,\n GrantType,\n IdTokenData,\n RefreshToken,\n} from \"../types.ts\";\nimport {\n createRefreshToken,\n issueRefreshToken,\n} from \"../auth/refresh-token.ts\";\nimport { type ExtendedSimulationStore } from \"../store/index.ts\";\nimport { type Auth0User } from \"../store/entities.ts\";\n\nexport const createTokens = async ({\n body,\n iss,\n clientID,\n audience,\n rulesDirectory,\n scope: scopeConfig,\n simulationStore,\n}: {\n body: Request[\"body\"];\n iss: string;\n clientID: string;\n audience: string;\n rulesDirectory: string | undefined;\n scope: ScopeConfig;\n simulationStore: ExtendedSimulationStore;\n}) => {\n let { grant_type }: { grant_type: GrantType } = body;\n let scope = deriveScope({ scopeConfig, clientID, audience });\n\n let accessToken = getBaseAccessToken({ iss, grant_type, scope, audience });\n let user: Auth0User | undefined;\n let nonce: string | undefined;\n\n if (grant_type === \"client_credentials\") {\n return { access_token: createJsonWebToken(accessToken) };\n }\n // TODO: check refresh_token expiry date\n else if (grant_type === \"refresh_token\") {\n let { refresh_token: refreshTokenValue } = body;\n let refreshToken: RefreshToken = JSON.parse(decode(refreshTokenValue));\n\n let findUser = createPersonQuery(simulationStore);\n\n user = findUser((person) => person.id === refreshToken.user.id);\n\n nonce = refreshToken.nonce;\n assert(!!nonce, `400::No nonce in request`);\n } else {\n let result = verifyUserExistsInStore({\n simulationStore,\n body,\n grant_type,\n });\n\n user = result.user;\n nonce = result.nonce;\n }\n\n assert(!!user, \"500::No user found\");\n\n let { idTokenData, userData } = getIdToken({\n body,\n iss,\n user,\n clientID,\n nonce,\n });\n\n let context: RuleContext<Partial<AccessTokenPayload>, IdTokenData> = {\n clientID,\n accessToken: { scope, sub: idTokenData.sub },\n idToken: idTokenData,\n };\n\n let rulesRunner = createRulesRunner(rulesDirectory);\n // the rules mutate the values\n await rulesRunner(userData, context);\n\n return {\n access_token: createJsonWebToken({\n ...accessToken,\n ...context.accessToken,\n ...(scope.split(\" \").includes(\"email\") ? { email: user.email } : {}),\n }),\n id_token: createJsonWebToken({\n ...userData,\n ...context.idToken,\n }),\n refresh_token: issueRefreshToken(scope, grant_type)\n ? createRefreshToken({\n exp: idTokenData.exp,\n rotations: 0,\n scope,\n user,\n nonce,\n })\n : undefined,\n };\n};\n\nexport const getIdToken = ({\n body,\n iss,\n user,\n clientID,\n nonce,\n}: {\n body: Request[\"body\"];\n iss: string;\n user: Auth0User;\n clientID: string;\n nonce: string | undefined;\n}) => {\n let userData: RuleUser = {\n name: body?.name ?? user.name,\n email: body?.email ?? user.email,\n email_verified: true,\n user_id: body?.id ?? user.id,\n nickname: body?.nickname,\n picture: body?.picture ?? user.picture,\n identities: body?.identities,\n };\n\n assert(!!user.email, \"500::User in store requires an email\");\n\n let idTokenData: IdTokenData = {\n alg: \"RS256\",\n typ: \"JWT\",\n iss,\n exp: expiresAt(),\n iat: epochTime(),\n email: user.email,\n aud: clientID,\n sub: user.id,\n };\n\n if (typeof nonce !== \"undefined\") {\n idTokenData.nonce = nonce;\n }\n\n return { userData, idTokenData };\n};\n\nexport const getBaseAccessToken = ({\n iss,\n grant_type,\n scope,\n audience,\n}: {\n iss: string;\n grant_type: string;\n scope: string;\n audience: string;\n}): Partial<AccessTokenPayload> => ({\n iss,\n exp: expiresAt(),\n iat: epochTime(),\n aud: audience,\n gty: grant_type,\n scope,\n});\n\nconst verifyUserExistsInStore = ({\n simulationStore,\n body,\n grant_type,\n}: {\n simulationStore: ExtendedSimulationStore;\n body: Request[\"body\"];\n grant_type: string;\n}) => {\n let { code } = body;\n let personQuery = createPersonQuery(simulationStore);\n let nonce: string | undefined;\n let username: string;\n let password: string | undefined;\n\n if (grant_type === \"http://auth0.com/oauth/grant-type/passwordless/otp\") {\n username = body.username;\n } else if (grant_type === \"password\") {\n username = body.username;\n password = body.password;\n } else {\n // specifically grant_type === 'authorization_code'\n // but naively using it to handle other cases at the moment\n assert(typeof code !== \"undefined\", \"400::no code in /oauth/token\");\n [nonce, username] = decodeBase64(code).split(\":\");\n assert(!!username, `400::no nonce in store for ${code}`);\n }\n\n let user: Auth0User | undefined = personQuery((person) => {\n assert(!!person.email, `500::no email defined on person scenario`);\n\n let valid = person.email.toLowerCase() === username.toLowerCase();\n\n if (typeof password === \"undefined\") {\n return valid;\n } else {\n return valid && password === person.password;\n }\n });\n\n assert(!!user, \"401::Unauthorized\");\n\n return { user, nonce };\n};\n"],"mappings":"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"}