CloudFormationでCognito UserPoolを作成すると、以前はドメインやFederationの設定などを手作業で行う必要があったが、 去年の10月に諸々のリソースが追加され、その必要がなくなった。
API GatewayでCognitoの認証をかけて必要ならログイン画面に飛ばす処理をGoで書く - sambaiz-net
今回はCDKの高レベルAPIを用いて、UserPoolとClientを作成し、トリガーやGoogleのFederationの設定を行って、特定のGoogleアカウントでのみ登録されるようにする。 全体のコードはGitHubにある。
Cognito UserPoolのPreSignUp時に呼ばれるLambdaで登録ユーザーを制限する - sambaiz-net
UserPool
事前にdomainPrefix
を決めておき、OAuthのclient_idとsecretをSecretsManagerに置いておく。
standardAttributes
と、ログイン時にusernameの代わりとなるsignInAliases
は後から変更できない。
変更してデプロイしてもreplaceではなくエラーになってしまう。
トリガーのruntimeはCDKと同じNodeにしても良いがバージョンのライフサイクルが早く追従するのが大変なのでGoにしている。
private createUserPool(userPoolName: string, domainPrefix: string, signUpAllowEmails: string[]): UserPool {
const userPool = new UserPool(this, 'UserPool', {
userPoolName,
standardAttributes: {
email: {
required: true,
mutable: true
},
fullname: {
required: true,
mutable: true
},
},
signInAliases: { username: true, email: true },
lambdaTriggers: {
preSignUp: new Function(this, 'PreSignUpFunction', {
runtime: Runtime.GO_1_X,
code: Code.fromAsset(path.join(__dirname, 'userPoolTrigger')),
handler: "preSignUp.main",
environment: {
ALLOW_EMAILS: signUpAllowEmails.join(",")
}
})
},
})
userPool.addDomain("UserPoolDomain", {
cognitoDomain: {
// Create OAuth 2.0 Client ID for web application with
// Authorized JavaScript origins: https://{domainPrefix}.auth.{region}.amazoncognito.com
// Redirect URI: https://{domainPrefix}.auth.{region}.amazoncognito.com/oauth2/idpresponse
// at https://console.developers.google.com/apis/credentials and put client_secret.json on SecretsManager
domainPrefix: "sambaiz-google-auth-test",
}
})
new cdk.CfnOutput(this, 'UserPoolArnOutput', {
value: userPool.userPoolArn
})
new cdk.CfnOutput(this, 'UserPoolDomainOutput', {
value: `${domainPrefix}.auth.${this.region}.amazoncognito.com`
})
return userPool
}
IdentityProviderGoogle
GoogleのFederationの設定を行う。 Googleのコンソールからダウンロードできるjsonの認証情報はネストしているためにうまくSecretsManagerのAPIで値が抽出できなかったので client_idとsecretのみをkey-valueで格納した。
private createIdentityProviderGoogle(userPool: UserPool, secretName: string): UserPoolIdentityProviderGoogle {
const oauthClientSecret = Secret.fromSecretNameV2(this, "GoogleOAuthClientSecret", secretName)
return new UserPoolIdentityProviderGoogle(this, "UserPoolIdentityProviderGoogle", {
userPool,
clientId: oauthClientSecret.secretValueFromJson('client_id').toString(),
clientSecret: oauthClientSecret.secretValueFromJson('client_secret').toString(),
scopes: ["profile", "email", "openid"],
attributeMapping: {
email: ProviderAttribute.GOOGLE_EMAIL,
fullname: ProviderAttribute.GOOGLE_NAME,
}
})
}
Client
supportedIdentityProviders
に含めたIdPの設定が既にないと失敗するので、addDependency()
で先に作られるようにする必要がある。
callbackUrls
にはALBであれば、https://<alb-domain>/oauth2/idpresponse
を入れる。
LambdaとALBでCognito認証をかけて失敗したらログイン画面に飛ばす - sambaiz-net
private createUserPoolClient(userPool: UserPool, userPoolClientName: string, callbackUrls: string[]): UserPoolClient {
const userPoolClient = userPool.addClient("UserPoolClient", {
userPoolClientName,
supportedIdentityProviders: [UserPoolClientIdentityProvider.GOOGLE],
generateSecret: true,
oAuth: {
callbackUrls,
flows: {
authorizationCodeGrant: true,
}
}
})
new cdk.CfnOutput(this, `UserPoolClientIdOutput`, {
value: userPoolClient.userPoolClientId
})
return userPoolClient
}
デプロイし、https://${props.domainPrefix}.auth.${this.region}.amazoncognito.com/authorize?client_id=${client.userPoolClientId}&redirect_uri=${encodeURIComponent(props.callbackUrls[0])}&response_type=code&identity_provider=Google
にアクセスすると、Googleの認証の後、redirect_uriにcode付きで遷移する。