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.

Todolist app in Auth0
Todolist app in Auth0

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,

Todolist federated OIDC login
Todolist 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