Search code examples
cookiesamazon-cognitoamazon-cloudfront

AWS Cognito sets cookie for its own domain, not the target's domain


I have set up AWS Cognito to authenticate a website served through CloudFront. I can direct users to the login and get the sign-in/sign-up pages in Cognito to appear. However, Cognito sets a cookie for its domain, not the website's domain. Therefore the cookie is not sent back to the website on subsequent requests. My domains are similar to this.

mywebsite.com = Cloudfront distribution
auth.mywebsite.com = Cognito custom domain 

My cookie is for auth.mywebsite.com, which is invalid for the parent domain. I don't think I can use auth.mywebsite.com as an alternate domain in Cloudfront because that domain is assigned as my custom Cognito domain. I'm not sure how this is supposed to work in any case.


Solution

  • You cannot use the cookie generated by Cognito Hosted UI.

    Flow is Initial user action => www.my-domain.com Cloudfront Lambda Viewer Request checks auth (not authenticated) 302 redirect to Cognito Hosted UI Signin/Signup page www.auth.my-domain.com/login?client_id=${CLIENT_ID}&response_type=(code or token)&scope=${SCOPE}&redirect_uri=${REDIRECT_URI}

    user authenticates with Cognito Hosted UI.

    Cognito 302 redirects with either tokens or code depending on your configuration. Let's assume auth code flow. Cognito returns to your redirect www.my-domain.com/my-login/?code=123authcodeflow

    Now, there are several ways to use the CODE in the url.

    1. Your app should load from say an S3 bucket, So use app to grab code from url, set a page spinner and then call Cognito token route to get refresh, id, and access tokens.

    https://auth.my-domain.com/oauth2/token?grant_type=authorization_code&code=${code}&client_id=${client_id}&redirect_uri=${REDIRECT_URI} NOTE: same redirect_uri as before.

    You'll now have all your tokens.

    1. handle Cognito redirect in your cloudfront origin request to get the tokens as above, then send them back to your client as payload or a cookie that is created by you.

    excellent article here: https://cloudonaut.io/authentication-at-the-edge-with-lambda-edge-cognito/

    UPDATE

    I was looking for a way to leverage CloudFront and Lambda@Edge functions to authenticate user via Cognito Hosted UI.

    Found this LIB by AWS LABS and it works perfect with Cookie Auth.

    https://github.com/awslabs/aws-jwt-verify

    You can also copy the code and put it in your Lambda Index file and change anything you need to get different cookie names.

    Works with just a Lambda@Edge Viewer request.

    If you are also using AWS Gateway, you'll have to create another lambda that uses this LIB as well with a simple if/else like this one.

    import { CognitoJwtVerifier } from 'aws-jwt-verify';
    
    import { parseCookies } from './utilities/parse-cookie.mjs';
    
    const verifier = CognitoJwtVerifier.create({
      userPoolId: process.env.COGNITO_POOL_ID,
      tokenUse: "access",
      clientId: process.env.COGNITO_CLIENT_ID
    });
    
    const TOKEN_NAME = process.env.COGNITO_TOKEN_NAME;
    
    export const handler = async (event) =>
    {
      if (event.cookies === null)
      {
        return {
          isAuthorized: false,
        };
      }
    
      const TOKEN = parseCookies(event.cookies, TOKEN_NAME);
    
      if (TOKEN === null)
      {
        return {
          isAuthorized: false,
        };
      }
    
      try
      {
        await verifier.verify(TOKEN);
    
        return {
          isAuthorized: true,
        };
      }
      catch(e)
      {
        return {
          isAuthorized: false,
        };
      }
    };