amplify-js: SSR Cognito Authentication doesn't work with email address usernames

Describe the bug Cognito user authentication doesn’t appear to support url-encoded cookie fields when authenticating during server-side renders using withSSRContext.

To Reproduce Steps to reproduce the behavior:

  1. Configure a cognito instance which allows email addresses to be used as usernames
  2. Connect an amplify application to this cognito user pool
  3. Use a framework like NextJs to perform some server-side rendering similar to the following
export const getServerSideProps = async (context) => {
    const { Auth } = withSSRContext(context)
    await Auth.getCurrentAuthenticatedUser()
}
  1. Note that when trying to server side render this page with a user whose username is formatted like an email address (contains an @), the server side render will fail with a The user is not authenticated message.

Expected behavior So long as Cognito identity pools support usernames formatted like email addresses, I’d expect those usernames to be able to be used for SSR applications.

What is Configured? If applicable, please provide what is configured for Amplify CLI:

  • Which steps did you follow via Amplify CLI when configuring your resources.
  • Which resources do you have configured?
    • Auth and API as follows:
{
  Auth: {
    region: 'us-east-1',
    userPoolId: 'us-east-xxxxxxxx',
    userPoolWebClientId: 'xxxxxxxxxxxxxxxxx',
    oauth: {
      "domain": "vdxauth.auth.us-east-1.amazoncognito.com",
      "redirectSignIn": `http://localhost:3000`,
      "redirectSignOut": `http://localhost:3000`,
      "responseType": "code"
    },
    cookieStorage: {
      domain: "localhost",
      secure: false
    }
  },
  API: {
    graphql_endpoint: `http://localhost:8082/graphql`,
  },
  ssr: true
}

Additional context Here’s the specific stack trace when the SSR request fails

[DEBUG] 55:00.353 AuthClass - getting current authenticated user
[DEBUG] 55:00.353 AuthClass - get current authenticated userpool user
[DEBUG] 55:00.353 AuthClass - Failed to get the user session Error: Local storage is missing an ID Token, Please authenticate
    at CognitoUser.getSession (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/amazon-cognito-identity-js/lib/CognitoUser.js:1353:16)
    at AuthClass.<anonymous> (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:1125:34)
    at step (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:56:23)
    at Object.next (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:37:53)
    at /Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:31:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:27:12)
    at /Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:1086:44
[DEBUG] 55:00.354 AuthClass - The user is not authenticated by the error Error: Local storage is missing an ID Token, Please authenticate
    at CognitoUser.getSession (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/amazon-cognito-identity-js/lib/CognitoUser.js:1353:16)
    at AuthClass.<anonymous> (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:1125:34)
    at step (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:56:23)
    at Object.next (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:37:53)
    at /Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:31:71
    at new Promise (<anonymous>)
    at __awaiter (/Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:27:12)
    at /Users/dcornwell/Documents/projects/kmax/kmax-web/node_modules/aws-amplify/node_modules/@aws-amplify/auth/lib/Auth.js:1086:44
The user is not authenticated

I’ve verified that this seems to be a mismatch between the raw username user@domain.com and the url-encoded cookie name that gets sent with the SSR request e.g. CognitoIdentityServiceProvider.2vphm6diig0n6p3nob5osjdn7j.user%40domain.com.idToken by sticking a debug breakpoint inside CognitoUser.js -> getSession(callback). The username used to compose token keys appears to be a raw user@domain.com but the token storage has the keys saved under {prefix}.user%40domain.com.{field}

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 5
  • Comments: 19 (6 by maintainers)

Most upvoted comments

We also need to validate both use-cases:

  1. Cognito configured with username, but using an email address
  2. Cognito configured with email (and using an email address)

Appreciate your still following up, @cornwe19! There’s lots of improvements inbound with our developer preview of v6 released and the full suite of v6 going General Availability soon. Feel free to check out our announcement and updated documentation to see what has changed.

@ocsross96, thank you for confirming! That’s great to hear and love that you provided some sample code for others that may come across this issue. I’ll close out the issue then for now, but please feel free to comment back if there’s further problems with this the latest version of Amplify.

Hi all, I am running into this issue as well. Do we have any updates?

Thanks a lot @cwomack I have updated the aws-amplify version to 5.3.11 and I can confirm this issue is now fixed for me in NextJS version 13.4.2.

For clarity and to help others here is what the code looks like for this:

// utils/getWithSSRContext.ts

import { withSSRContext } from 'aws-amplify';
import { cookies } from 'next/headers';

type CookieObj = {
  name: string;
  value: string;
};

const config = {
  Auth: {
    region: process.env.AWS_REGION,
    userPoolId: process.env.AWS_COGNITO_USER_POOL_ID,
    userPoolWebClientId: process.env.AWS_COGNITO_USER_POOL_WEB_CLIENT_ID,
    cookieStorage: {
      domain: process.env.APP_DOMAIN,
      path: '/',
      expires: 1,
      sameSite: 'strict',
      secure: false,
    },
  },
  ssr: true,
};

function serializeCookies(cookies: CookieObj[]) {
  return cookies
    .map((c) => `${c.name}=${c.value ? decodeURIComponent(c.value) : ''};`)
    .join('');
}

export default function getWithSSRContext() {
  const allCookies = cookies().getAll();

  const SSR = withSSRContext({
    req: {
      headers: {
        cookie: serializeCookies(allCookies),
      },
    },
  });
  SSR.configure(config);

  return SSR;
}
// app/protected-server/page.tsx

import { redirect } from 'next/navigation';
import getWithSSRContext from 'utils/getWithSSRContext';

export default async function ProtectedServer() {
  const SSR = getWithSSRContext();

  try {
    const res = await SSR.Auth.currentAuthenticatedUser();
    console.log('ProtectedServer user', res); // logs auth user correctly
  } catch (err) {
    console.log('err', err);
    redirect('/login');
  }

  return (
    <>
      <div>If you can see this page, you are logged in.</div>
    </>
  );
}

@cwomack glad to hear this one has likely been resolved and thanks for the heads up. Unfortunately, I’m no longer working on the project which was using amplify for SSO, so I don’t have a simple way to test this out myself.

@ocsross96 created an issue here (https://github.com/aws-amplify/amplify-js/issues/11649), seems to be specific for next 13 for server side.

Recording of the bug in action provided by @Nubtehy in this comment.

Missing cookies?

@cornwe19 I have a hunch why await Auth.getCurrentAuthenticatedUser() could be failing — you might be missing cognito cookies

Verify with:

export const getServerSideProps = async (context) => {
+   console.log("ssr cookies", context.req.cookies);
    const { Auth } = withSSRContext(context)
    await Auth.getCurrentAuthenticatedUser()
}

Bad

if you see {}, that’s what I understand to be the cause.

Good

You should see keys like in your SSR logs

{ 
  'CognitoIdentityServiceProvider.<app_client_id>.<cognito_username_or_sub>.idToken': ey...
  'CognitoIdentityServiceProvider.<app_client_id>.<cognito_username_or_sub>.accessToken': ey...
}

How do you get those cookies?

I dunno? … AWS TEAM PLEASE HELP 🙏

I’ve noticed Auth.federatedSignIn() set them there, but not Auth.sendCustomChallengeAnswer()