Federated OIDC login with Cognito and Amplify
When working on either 2C application or 2B service, the customers do not want to or is not allowed to sign up the new account, they can login the application via existing IdP or enterprise SSO. So, building the application supports the federated OIDC login to address such requirements.
This post extends the capability of Todolist application protected by Amazon Cognito, using Auth0 as the third party OpenID Connect provider introduces the external user pool.
The application also uses the AWS Amplify to build the frontend capabilities(for example, authentication, invoke backend restful api), Amazon Cognito providing both federated OIDC login and self-managed users sign in/sign up, and Amazon API Gateway providing the backend API and validating the token with OIDC provider.
Below is the key procedures to add the federated OIDC login to the existing web application protected by Cognito,
1. Update the authorizer of API Gateway to validate the token issued by OIDC providers.
The previous authorizer is using API Gateway Cognito authorizer, it only can validate the token issued by Cognito user pool. Cognito user pool also complies with the OIDC standard, using Lambda authorizer can implement to validate the tokens issued by either Cognito user pool and third party OIDC provider.
The CDK code creates a lambda function as Lambda Authorizer of API Gateway, which sets the supported OIDC issuers as environment,
1 const authFunc = new NodejsFunction(this, `${resourceName}AuthFunc`, {
2 entry: path.join(__dirname, './lambda.d/authorizer/index.ts'),
3 handler: 'handler',
4 architecture: Architecture.ARM_64,
5 timeout: Duration.seconds(5),
6 memorySize: 128,
7 runtime: Runtime.NODEJS_16_X,
8 tracing: Tracing.ACTIVE,
9 environment: {
10 ISSUERS: issuers,
11 RESOURCE_PREFIX: Arn.format({
12 service: 'execute-api',
13 resource: api.restApiId,
14 }, Stack.of(this)),
15 },
16 });
The custom lambda authorizer uses the Auth0's jwt-decode and AWS JWT Verify to verify the ID tokens issued by OIDC provider. See source for detail implementation.
2. Add the third party OIDC provider to Cognito user pool. It involves the client information with secrets generated by OIDC provider, we use the AWS Secrets Manager to securely store the credentials.
As prerequisites of this step, you must create an application in your OIDC provider. For example, creating an application in Auth0, then configure the allowed callback URLs to the pool domain. The next saving the issuer domain, client id, client secret and name(will be readable string in UI) to a secret in Secrets Manager.
The code snippet of CDK creates the external OIDC provider looks like below,
1 const oidcSecretArn = this.node.tryGetContext('OIDCSerectArn');
2 var oidcProvider: UserPoolIdentityProviderOidc | undefined;
3 if (oidcSecretArn) {
4 const secret = Secret.fromSecretAttributes(this, 'OIDCSecret', {
5 secretCompleteArn: oidcSecretArn,
6 });
7 oidcProvider = new UserPoolIdentityProviderOidc(this, 'FedarationOIDC', {
8 clientId: secret.secretValueFromJson('clientId').toString(),
9 clientSecret: secret.secretValueFromJson('clientSecret').toString(),
10 issuerUrl: secret.secretValueFromJson('issuerUrl').toString(),
11 name: secret.secretValueFromJson('name').toString(),
12 userPool: userpool,
13 scopes: [
14 'profile',
15 'openid',
16 'email',
17 ],
18 });
19 userpool.registerIdentityProvider(oidcProvider);
20 }
3. Update the amplify configuration file with OIDC provider information.
1 const amplifyConfFile = 'aws-exports.json';
2 const body =
3`{
4 "aws_project_region": "${Aws.REGION}",
5 "Auth": {
6 "region": "${Aws.REGION}",
7 "userPoolId": "${poolInfo.userpool.userPoolId}",
8 "userPoolWebClientId": "${poolInfo.client.userPoolClientId}",
9 "authenticationFlowType": "USER_SRP_AUTH",
10 "oauth": {
11 "name": "${poolInfo.oidc.name}",
12 "domain": "${poolInfo.poolDomain.domainName}.auth.${Aws.REGION}.amazoncognito.com",
13 "scope": ["email", "openid", "aws.cognito.signin.user.admin", "profile"],
14 "redirectSignIn": "${poolInfo.oidc.signinUrl}",
15 "redirectSignOut": "${poolInfo.oidc.signinUrl}",
16 "responseType": "code"
17 }
18 },
19 "API": {
20 "endpoints": [
21 {
22 "name": "backend-api",
23 "endpoint": "https://${cloudFrontS3.cloudFrontWebDistribution.distributionDomainName}/prod/"
24 }
25 ]
26 }
27}`;
4. Customize the Amplify's React Authenticator component to add the federated OIDC login entrance.
1 SignIn: {
2 Footer() {
3 const { toResetPassword } = useAuthenticator();
4
5 return (
6 <View textAlign="center">
7 <Divider orientation="horizontal" />
8 <Text>
9 {
10 !isAuthenticated && (
11 <View
12 as="div"
13 backgroundColor="var(--amplify-colors-white)"
14 borderRadius="6px"
15 color="var(--amplify-colors-blue-60)"
16 height="4rem"
17 maxWidth="100%"
18 padding="1rem"
19 >
20 <Button
21 variation="primary"
22 onClick={
23 () => {
24 Auth.federatedSignIn({ customProvider: oidcProviderName });
25 }}
26 >
27 Sign In with {oidcProviderName}
28 </Button>
29 </View>
30 )
31 }
32 </Text>
33 </View>
34 );
35 },
36 },
The new look of Amplify's authoricator component looks like below with both self-managed user pool and federated OIDC login,
As usual, all AWS resources are orchestrated by a AWS CDK project, it's easliy to be deployed to any account and any region of AWS!
Happying logging your website with externl OIDC provider 🔒 😆😆😆
Posts in this series
- Build serverless web application with AWS Lambda web adapter
- Define your API via OpenAPI definition on AWS
- Setup DevOps pipeline with few code
- Federated OIDC login with Cognito and Amplify
- Protect website with Cognito
- Distribute the website globally
- Build no code restful HTTP API with API Gateway and DynamoDB
- Build serverless web application with AWS Serverless
- 无服务器架构的Docker镜像数据分析应用
- 无服务器架构的域名重定向服务
- Spring Cloud Function -- 跨Serverless平台的函数计算框架
- 基于函数计算的钉钉回调函数接口