| 1 | import * as cip from "@distilled.cloud/aws/cognito-identity-provider"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; |
| 4 | import * as Binding from "../../Binding.ts"; |
| 5 | import { isBindingHost } from "../Lambda/Function.ts"; |
| 6 | import { cognitoMethods } from "./BindingHttp.ts"; |
| 7 | import { UserPoolAuth, type UserPoolAuthClient } from "./UserPoolAuth.ts"; |
| 8 | import type { UserPoolClient } from "./UserPoolClient.ts"; |
| 9 | |
| 10 | /** The injected identifier field, in the distilled wire type (Cognito marks |
| 11 | * `ClientId` sensitive, so the field accepts `string | Redacted<string>`). */ |
| 12 | type ClientIdField = Pick<cip.SignUpRequest, "ClientId">; |
| 13 | |
| 14 | /** |
| 15 | * HTTP implementation of {@link UserPoolAuth}. The token-based auth-flow |
| 16 | * operations are unauthenticated (Cognito does not evaluate IAM for them), |
| 17 | * so the deploy-time half only records the binding — no policy statements |
| 18 | * are attached. |
| 19 | */ |
| 20 | export const UserPoolAuthHttp = Layer.effect( |
| 21 | UserPoolAuth, |
| 22 | Effect.gen(function* () { |
| 23 | const signUp = yield* cip.signUp; |
| 24 | const confirmSignUp = yield* cip.confirmSignUp; |
| 25 | const resendConfirmationCode = yield* cip.resendConfirmationCode; |
| 26 | const initiateAuth = yield* cip.initiateAuth; |
| 27 | const respondToAuthChallenge = yield* cip.respondToAuthChallenge; |
| 28 | const forgotPassword = yield* cip.forgotPassword; |
| 29 | const confirmForgotPassword = yield* cip.confirmForgotPassword; |
| 30 | const getUser = yield* cip.getUser; |
| 31 | const globalSignOut = yield* cip.globalSignOut; |
| 32 | const revokeToken = yield* cip.revokeToken; |
| 33 | const getTokensFromRefreshToken = yield* cip.getTokensFromRefreshToken; |
| 34 | const changePassword = yield* cip.changePassword; |
| 35 | const updateUserAttributes = yield* cip.updateUserAttributes; |
| 36 | const deleteUserAttributes = yield* cip.deleteUserAttributes; |
| 37 | const getUserAttributeVerificationCode = |
| 38 | yield* cip.getUserAttributeVerificationCode; |
| 39 | const verifyUserAttribute = yield* cip.verifyUserAttribute; |
| 40 | const setUserMFAPreference = yield* cip.setUserMFAPreference; |
| 41 | const associateSoftwareToken = yield* cip.associateSoftwareToken; |
| 42 | const verifySoftwareToken = yield* cip.verifySoftwareToken; |
| 43 | const getUserAuthFactors = yield* cip.getUserAuthFactors; |
| 44 | const deleteUser = yield* cip.deleteUser; |
| 45 | |
| 46 | return Effect.fn(function* <C extends UserPoolClient>(client: C) { |
| 47 | const ClientId = yield* client.clientId; |
| 48 | if (!globalThis.__ALCHEMY_RUNTIME__) { |
| 49 | const host = yield* Binding.Host; |
| 50 | if (isBindingHost(host)) { |
| 51 | // No IAM is required for the public auth flows; the binding is |
| 52 | // recorded so the app client deploys before the function. |
| 53 | yield* host.bind`Allow(${host}, AWS.Cognito.UserPoolAuth(${client}))`( |
| 54 | { policyStatements: [] }, |
| 55 | ); |
| 56 | } |
| 57 | } |
| 58 | const methods = cognitoMethods( |
| 59 | "AWS.Cognito.UserPoolAuth", |
| 60 | client.LogicalId, |
| 61 | ); |
| 62 | const withClient = methods.injecting( |
| 63 | Effect.map(ClientId, (id): ClientIdField => ({ ClientId: id })), |
| 64 | ); |
| 65 | const authClient: UserPoolAuthClient = { |
| 66 | // client-scoped flows — the app client ID is injected |
| 67 | signUp: withClient("signUp", signUp), |
| 68 | confirmSignUp: withClient("confirmSignUp", confirmSignUp), |
| 69 | resendConfirmationCode: withClient( |
| 70 | "resendConfirmationCode", |
| 71 | resendConfirmationCode, |
| 72 | ), |
| 73 | initiateAuth: withClient("initiateAuth", initiateAuth), |
| 74 | respondToAuthChallenge: withClient( |
| 75 | "respondToAuthChallenge", |
| 76 | respondToAuthChallenge, |
| 77 | ), |
| 78 | forgotPassword: withClient("forgotPassword", forgotPassword), |
| 79 | confirmForgotPassword: withClient( |
| 80 | "confirmForgotPassword", |
| 81 | confirmForgotPassword, |
| 82 | ), |
| 83 | revokeToken: withClient("revokeToken", revokeToken), |
| 84 | getTokensFromRefreshToken: withClient( |
| 85 | "getTokensFromRefreshToken", |
| 86 | getTokensFromRefreshToken, |
| 87 | ), |
| 88 | // token-scoped self-service — authorized by the access token itself |
| 89 | getUser: methods.plain("getUser", getUser), |
| 90 | globalSignOut: methods.plain("globalSignOut", globalSignOut), |
| 91 | changePassword: methods.plain("changePassword", changePassword), |
| 92 | updateUserAttributes: methods.plain( |
| 93 | "updateUserAttributes", |
| 94 | updateUserAttributes, |
| 95 | ), |
| 96 | deleteUserAttributes: methods.plain( |
| 97 | "deleteUserAttributes", |
| 98 | deleteUserAttributes, |
| 99 | ), |
| 100 | getUserAttributeVerificationCode: methods.plain( |
| 101 | "getUserAttributeVerificationCode", |
| 102 | getUserAttributeVerificationCode, |
| 103 | ), |
| 104 | verifyUserAttribute: methods.plain( |
| 105 | "verifyUserAttribute", |
| 106 | verifyUserAttribute, |
| 107 | ), |
| 108 | setUserMFAPreference: methods.plain( |
| 109 | "setUserMFAPreference", |
| 110 | setUserMFAPreference, |
| 111 | ), |
| 112 | associateSoftwareToken: methods.plain( |
| 113 | "associateSoftwareToken", |
| 114 | associateSoftwareToken, |
| 115 | ), |
| 116 | verifySoftwareToken: methods.plain( |
| 117 | "verifySoftwareToken", |
| 118 | verifySoftwareToken, |
| 119 | ), |
| 120 | getUserAuthFactors: methods.plain( |
| 121 | "getUserAuthFactors", |
| 122 | getUserAuthFactors, |
| 123 | ), |
| 124 | deleteUser: methods.plain("deleteUser", deleteUser), |
| 125 | }; |
| 126 | return authClient; |
| 127 | }); |
| 128 | }), |
| 129 | ); |