import * as cdk from "aws-cdk-lib"; import * as cognito from "aws-cdk-lib/aws-cognito"; import { Construct } from "constructs"; import { AppConfig } from "./utils/config-manager"; export interface CognitoStackProps extends cdk.NestedStackProps { config: AppConfig; callbackUrls?: string[]; } export class CognitoStack extends cdk.NestedStack { public userPoolId: string; public userPoolClientId: string; public userPoolDomain: cognito.UserPoolDomain; constructor(scope: Construct, id: string, props: CognitoStackProps) { super(scope, id, props); this.createCognitoUserPool(props.config, props.callbackUrls); } private createCognitoUserPool( config: AppConfig, callbackUrls?: string[], ): void { // Use provided callback URLs or defaults const defaultCallbackUrls = [ "http://localhost:3000", "https://localhost:3000", ]; const finalCallbackUrls = callbackUrls || defaultCallbackUrls; const userPool = new cognito.UserPool(this, "UserPool", { userPoolName: `${config.stack_name_base}-user-pool`, selfSignUpEnabled: false, signInAliases: { email: true, }, autoVerify: { email: true, }, standardAttributes: { email: { required: true, mutable: false, }, }, passwordPolicy: { minLength: 8, requireLowercase: true, requireUppercase: true, requireDigits: true, requireSymbols: true, }, accountRecovery: cognito.AccountRecovery.EMAIL_ONLY, removalPolicy: cdk.RemovalPolicy.DESTROY, userInvitation: { emailSubject: `Welcome to ${config.stack_name_base}!`, emailBody: `

Hello {username},

Welcome to ${config.stack_name_base}! Your username is {username} and your temporary password is: {####}

Please use this temporary password to log in and set your permanent password.

The CloudFront URL to your application is stored as an output in the "${config.stack_name_base}" stack, and will be printed to your terminal once the deployment process completes.

Thanks,

Fullstack AgentCore Solution Template Team

`, }, }); const userPoolClient = new cognito.UserPoolClient(this, "UserPoolClient", { userPool: userPool, userPoolClientName: `${config.stack_name_base}-client`, generateSecret: false, authFlows: { userPassword: true, userSrp: true, }, oAuth: { flows: { authorizationCodeGrant: true, }, scopes: [ cognito.OAuthScope.OPENID, cognito.OAuthScope.EMAIL, cognito.OAuthScope.PROFILE, ], // Support both localhost development and production URLs callbackUrls: finalCallbackUrls, logoutUrls: finalCallbackUrls, }, preventUserExistenceErrors: true, }); this.userPoolDomain = new cognito.UserPoolDomain(this, "UserPoolDomain", { userPool: userPool, cognitoDomain: { domainPrefix: `${config.stack_name_base.toLowerCase()}-${cdk.Aws.ACCOUNT_ID}-${ cdk.Aws.REGION }`, }, // Enable the newer managed login UI (v2) with the branding designer. Comment or remove this // if you'd like to use the old classic UI. managedLoginVersion: cognito.ManagedLoginVersion.NEWER_MANAGED_LOGIN, }); // Create managed login branding with Cognito's default styles // This is required for the v2 managed login to display properly const managedLoginBranding = new cognito.CfnManagedLoginBranding( this, "ManagedLoginBranding", { userPoolId: userPool.userPoolId, clientId: userPoolClient.userPoolClientId, useCognitoProvidedValues: true, }, ); managedLoginBranding.node.addDependency(this.userPoolDomain); // Store the IDs for export this.userPoolId = userPool.userPoolId; this.userPoolClientId = userPoolClient.userPoolClientId; // Create admin user if email is provided in config if (config.admin_user_email) { new cognito.CfnUserPoolUser(this, "AdminUser", { userPoolId: userPool.userPoolId, username: config.admin_user_email, userAttributes: [ { name: "email", value: config.admin_user_email, }, ], desiredDeliveryMediums: ["EMAIL"], }); // Output admin user creation status new cdk.CfnOutput(this, "AdminUserCreated", { description: "Admin user created and credentials emailed", value: `Admin user created: ${config.admin_user_email}`, }); } } }