aws-amplify / amplify-cli

The AWS Amplify CLI is a toolchain for simplifying serverless web and mobile development.
Apache License 2.0
2.81k stars 821 forks source link

[Exact Line Found] Amplify Auth doesn't work for non-trailing slash redirect #7359

Open jgiunta1 opened 3 years ago

jgiunta1 commented 3 years ago

Adding up here for visibility:

Found the bug: https://github.com/aws-amplify/amplify-js/blob/286c9e8fbec3ccdc5bf24edf3cbfb2bae370d751/packages/auth/src/OAuth/OAuth.ts#L128-L130

Please update this to not care about a trailing slash.

example.com/dashboard/?code=xxxxxxxx&state=xxxxxx == example.com/dashboard?code=xxxxxxxx&state=xxxxxx

Amplify shouldn't care about trailing slash, this line needs to be updated to disregard trailing slash from redirect url.

Describe the bug I'm able to sign in with Auth.federatedSignIn({provider: 'Facebook'}); in js, but I can't use that in typescript. Auth.federatedSignIn({provider: CognitoHostedUIIdentityProvider.Facebook}) fails because it calls cognito-identity instead of cognito-idp. My local storage isn't updated with the cognito tokens and I get a 400 from cognito-identity.us-east-1.amazonaws.com. js does not call this endpoint ever, not sure why the ts SDK is calling it, or why there is no user-pool support for this. my export file is the same for both js and ts. The only difference is the federated sign in call above.

To Reproduce Steps to reproduce the behavior:

  1. setup amplify with user pools and facebook identity provider
  2. create a new next ts project
  3. import amplify newest version: import { Auth } from 'aws-amplify';
  4. import { CognitoHostedUIIdentityProvider } from "@aws-amplify/auth/lib/types";
  5. Call Auth.federatedSignIn({provider: CognitoHostedUIIdentityProvider.Facebook});
  6. Open your network tab in chrome and see that cognito-identity is being called. even though the aws-export file should have "federationTarget": "COGNITO_USER_POOLS"

eg:

import React from "react";
import { Button, Heading } from "@chakra-ui/react";

import { CognitoHostedUIIdentityProvider } from "@aws-amplify/auth/lib/types";
import { Auth } from 'aws-amplify';

interface loginProps {}

export const Login: React.FC<loginProps> = ({}) => {

  function continueWithFacebook() {
    return Auth.federatedSignIn({provider: CognitoHostedUIIdentityProvider.Facebook});
  }

  return (
    <div>
      <Button onClick={() => continueWithFacebook()}>Continue with Facebook</Button>
    </div>
  );
};

export default Login;

This is the call from the typescript sdk. Notice its missing the login:

{"IdentityId":"us-east-1:xxx","Logins":{"cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxx":"<THIS IS BLANK AND SHOULDN'T BE>"}}

Expected behavior The vanilla js version of this works. Auth.federatedSignIn({provider: 'Facebook'}); works as expected. You can go to the application tab in chrome and see local storage updated. CognitoIdentityServiceProvider object will be under the local storage tab.

cognito-identity.us-east-1.amazonaws.com is called with a valid login.

{"IdentityId":"us-east-1:xxx","Logins":{"cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxx":"eyJraWQiOiJWTzYrsasfasdf"}}

Code Snippet Please provide a code snippet or a link to sample code of the issue you are experiencing to help us reproduce the issue. (Be sure to remove any sensitive data)

Working: Auth.federatedSignIn({provider: 'Facebook'}); in js Not working: Auth.federatedSignIn({provider: CognitoHostedUIIdentityProvider.Facebook}) in tsx

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

Environment ``` npx envinfo --system --binaries --browsers --npmPackages --npmGlobalPackages ```

Smartphone (please complete the following information):

Additional context Add any other context about the problem here.

_You can turn on the debug mode to provide more info for us by setting window.LOGLEVEL = 'DEBUG'; in your app.

Jeontaeyun commented 3 years ago

How about use @aws-amplify/Auth. The way I see it, there is no export CognitoHostedUIIdentityProvider with aws-amplify library. But It works with @aws-amplify/Auth.

import { CognitoHostedUIIdentityProvider } from '@aws-amplify/auth'
jgiunta1 commented 3 years ago

Sorry I should have been more specific, I'll update my comment. I'm using

import { CognitoHostedUIIdentityProvider } from "@aws-amplify/auth/lib/types";
import { Auth } from 'aws-amplify';
jgiunta1 commented 3 years ago

I deleted and re-set up my amplify auth, its still calling https://cognito-identity.us-east-1.amazonaws.com/ with an empty login:

See below request that I copied from chrome as a fetch

fetch("https://cognito-identity.us-east-1.amazonaws.com/", {
  "headers": {
    "accept": "*/*",
    "accept-language": "en-US,en;q=0.9",
    "amz-sdk-invocation-id": "0a1356f1-3c72-443f-819c-686149132528",
    "amz-sdk-request": "attempt=1; max=3",
    "content-type": "application/x-amz-json-1.1",
    "sec-ch-ua": "\"Google Chrome\";v=\"87\", \" Not;A Brand\";v=\"99\", \"Chromium\";v=\"87\"",
    "sec-ch-ua-mobile": "?0",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "cross-site",
    "x-amz-target": "AWSCognitoIdentityService.GetId",
    "x-amz-user-agent": "aws-sdk-js-v3-@aws-sdk/client-cognito-identity/1.0.0-rc.4 Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 aws-amplify/3.8.7 js"
  },
  "referrer": "http://localhost:3000/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": "{\"IdentityPoolId\":\"us-east-1:xxxxxxxxxxxxxxxxxxxxxxxxxxx\",\"Logins\":{\"cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxxxx\":\"\"}}",
  "method": "POST",
  "mode": "cors"
});
{"__type":"ValidationException","message":"1 validation error detected: Value '{cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxxxx=}' at 'logins' failed to satisfy constraint: Map value must satisfy constraint: [Member must have length less than or equal to 50000, Member must have length greater than or equal to 1]"}
jgiunta1 commented 3 years ago

Still have this issue, is this because I'm using Next.js? The call to /login works as expected, and I'm redirected to my post-login page. However after this, next removes the trailing slash:

308 Permanent Redirect - example.com/dashboard/?code=xxxxxxxx&state=xxxxxx 200 OK - example.com/dashboard?code=xxxxxxxx&state=xxxxxx

After this amplify should be calling cognito-auth-url.com/oauth2/token to get the auth token. But this call is never made. Instead it calls cognito-identity.us-east-2.amazonaws.com with a botched login because the auth token it was supposed to get from the other call was never made

jgiunta1 commented 3 years ago

Found the bug: https://github.com/aws-amplify/amplify-js/blob/286c9e8fbec3ccdc5bf24edf3cbfb2bae370d751/packages/auth/src/OAuth/OAuth.ts#L128-L130

Please update this to not care about a trailing slash. Or at the very least do not fail silently, makes no sense why this is a blank return. The auth flow half succeeds then just fails randomly.

example.com/dashboard/?code=xxxxxxxx&state=xxxxxx == example.com/dashboard?code=xxxxxxxx&state=xxxxxx
alexciesielski commented 3 years ago

How is this still open

tobiastimm commented 3 years ago

I'm experiencing the same issue with the typescript sdk and nextjs. The auth flow only works, if my redirect sign-url is http://localhost:3000. When I'm changing it to http://localhost:3000/sign-in it fails with the mentioned 400 error

jarrodwatts commented 3 years ago

Any update or workaround on this? Really struggling on this one - Next JS / Typescript / Amplify

jarrodwatts commented 3 years ago

Anyone finding this thread, an easy hack solution if you are using Next JS, is to include: trailingSlash: true, in your next.config.js file. Sucks this has to be done, but easy work around for the time being.

jgiunta1 commented 3 years ago

jarrodwatts@ Thats what I had to do as well.

FaizBShah commented 3 years ago

I am facing this exact issue in my project. Hopefully they fix it fast. @jarrodwatts Thanks for the soln. for now. Saved me a lot of time :D

tlmader commented 3 years ago

I ran into the below error last night, and my redirect did not contain a trailing slash.

validation error detected: Value '{cognito-idp.us-east-1.amazonaws.com/us-east-xxx=}' at 'logins' failed to satisfy constraint: Map value must satisfy constraint: [Member must have length less than or equal to 50000, Member must have length greater than or equal to 1]"}

My issue was that I needed to add responseType: 'code' to the Amplify oauth configuration object.

My Amplify configuration:

const awsconfig = {
  Auth: {
    userPoolId: 'us-east-1_x',xxx
    userPoolWebClientId: 'xxx',
    identityPoolId: 'us-east-1:xxx',
    region: 'us-east-1',
    oauth: {
      domain: 'xxx.auth.us-east-1.amazoncognito.com',
      redirectSignIn: redirectUri,
      redirectSignOut: redirectUri,
      responseType: 'code',
      urlOpener,
    },
  },
};

What helped me figure out the exact problem was adding window.LOG_LEVEL = 'DEBUG'; and I was able to get the following message:

[DEBUG] 57:07.955 OAuth - Starting undefined flow with exp://127.0.0.1:19000?code=xxx

Hope this helps someone!

osnodegeoffrey commented 3 years ago

Adding trailing slash seems to cause problem with amplify hosting. Getting a redirect loop and rewrites with %2F instead of slashes. So +1 to get this fixed.

iamdavidmartin commented 3 years ago

I had this same issue, but the root cause was not a trailing slash. There is no problem with the library, I had a misconfiguration on the app and the server.

Problem: {"IdentityId":"us-east-1:xxx","Logins":{"cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxx":"<THIS IS BLANK AND SHOULDN'T BE>"}}

I solved it by including openid in my scopes.

const awsconfig = {
  Auth: {
    ...
    oauth: {
        ...
        scope: ['email', 'aws.cognito.signin.user.admin', 'profile', 'openid'],
        ...
    },
  },
};

When the codeFlow auth event happens on the client, it goes to retrieve tokens from your auth provider https://xxxxxx/oauth2/token. If you don't include openid in your scopes, you won't get an id_token back. These docs say "Note that an ID token is only provided if the openid scope was requested": https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-user-pool-oauth-2-0-grants/

That id_token is a JWT that is then used to fill in Logins in the request. Without that id_token, you get the problem listed above: "THIS IS BLANK AND IT SHOULDN'T BE".

aj120426394 commented 3 years ago

I ran into this problem today. After few hours of try and error, for people who have the same issue maybe you can try this solution.

I crated an OIDC idp via AWS Cognito console manually. It was working when I manually add OAuth config in index.js like this:

Amplify.configure(aws_exports)
Auth.configure({
  ...aws_exports,
  oauth: {
    domain: 'xxxxxx-dev.auth.ap-southeast-2.amazoncognito.com',
    scope: ['openid'],
    redirectSignIn: 'http://localhost:3000/,http://localhost:3000/xxxx/,https://xxxxx/yyyyyyy/',
    redirectSignOut: 'http://localhost:3000/,http://localhost:3000/xxxx/,https://xxxxx/yyyyyyy/',
    responseType: 'code'
  }
})

However, we have multiple backend environment. We decide to setup OAuth config via amplify-cli to allow the domain be dynamic. Then it went into the problem above. The solution I finally came with is:

Amplify.configure({ ...aws_exports, oauth: undefined })
Auth.configure({
  ...aws_exports,
  oauth: {
    ...aws_exports.oauth,
    redirectSignIn: `${window.location.origin}/`,
    redirectSignOut: `${window.location.origin}/`
  }
})

So far, I have no idea why this works. Seems like Amplify.configure() doesn't like oauth, and you need to set if via Auth.configure(). Also redirectSignIn and redirectSignOut only accept single url. It used to (before config via cli) accept multiple url.

Hope this could help someone.

StriderHND commented 3 years ago

Any updates about this? I'm having the same issue as @aj120426394, tried his solution but still the problem persist.

josefaidt commented 2 years ago

note: potentially need to handle trailing slashes in libraries as well

jk171505 commented 2 years ago

Any update on this?

ryanrhee commented 2 years ago

fyi: redirecting to http://localhost:8000/foo resulted in a redirect loop, but redirecting to http://localhost:8000 stopped the redirect loop and made auth work as expected.

I'm using nextjs, using Amplify.configure(), and manually specifying the configuration object.

DanyPell commented 2 years ago

Any solution?

MrBrunoSilva commented 2 years ago

Wasted many hours on this. Turns out there are various things that can break it, trailing slashes, custom domains, etc. aws-exports.js will have oauth configuration generated on amplify push from your cli inputs. That configuration will often have the "wrong" settings (for example, point to a cognito domain when you have a custom one, or have the wrong redirect urls). These things end up with a request to cognito with an incomplete payload (blank login). In my case there were a few things needed:

  1. Be consistent with trailing slashes on my main.js, aws-exports.js and my auth configuration passed on to Auth.configure()
  2. have a completely separate configuration for Auth, not rely on aws-exports.js, something like this:
    import awsconfig from './aws-exports';
    import awsauth  from './aws-auth';
    awsconfig.oauth={} // ignore the aws-exports auth settings
    Amplify.configure(awsconfig);
    awsauth.oauth.redirectSignIn = `${window.location.origin}/`;
    awsauth.oauth.redirectSignOut = `${window.location.origin}/`;
    const currentConfig = Auth.configure(awsauth);
PGuimarais commented 1 year ago

Any updates on this?

I was able to resolve this issue with Next.js using trailingSlash: true config as suggested by @jarrodwatts

This seemed great until we realized trailing slashes broke our blog.

The blog is a separate Next.js codebase which we link to the main webpage with Next.js rewrites - but we can't override the trailingSlash behavior local to the blog itself - it is enforced across the entire domain.

It looks like we have to choose between OAuth or our blog. Unless someone pushes a fix :)

It seems like Vercel has been sitting on this issue for a couple years now.

Has anyone else run into a similar situation and worked around it?

Neuroforge commented 1 year ago

I had this same issue, but the root cause was not a trailing slash. There is no problem with the library, I had a misconfiguration on the app and the server.

Problem: {"IdentityId":"us-east-1:xxx","Logins":{"cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxx":"<THIS IS BLANK AND SHOULDN'T BE>"}}

I solved it by including openid in my scopes.

const awsconfig = {
  Auth: {
    ...
    oauth: {
        ...
        scope: ['email', 'aws.cognito.signin.user.admin', 'profile', 'openid'],
        ...
    },
  },
};

When the codeFlow auth event happens on the client, it goes to retrieve tokens from your auth provider https://xxxxxx/oauth2/token. If you don't include openid in your scopes, you won't get an id_token back. These docs say "Note that an ID token is only provided if the openid scope was requested": https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-user-pool-oauth-2-0-grants/

That id_token is a JWT that is then used to fill in Logins in the request. Without that id_token, you get the problem listed above: "THIS IS BLANK AND IT SHOULDN'T BE".

You are a hero.

eelior commented 1 year ago

+1

Neo-Ciber94 commented 1 year ago

Anyone finding this thread, an easy hack solution if you are using Next JS, is to include: trailingSlash: true, in your next.config.js file. Sucks this has to be done, but easy work around for the time being.

This worked for me locally

dannyk08 commented 1 year ago

Anyone finding this thread, an easy hack solution if you are using Next JS, is to include: trailingSlash: true, in your next.config.js file. Sucks this has to be done, but easy work around for the time being.

@jarrodwatts This is what was missing for me 🙏🏽. Thank you for offering this solution Can't believe this is still an issue

kevoj7 commented 1 year ago

This issue is still persistent, insane how this is not fixed...... devs do something please, or at least give a heads up about this issue.

Spent 1 week troubleshooting this issue to figure out it doesn't accept "/"

SNK47 commented 10 months ago

while logging sso (Google) that was successful. but after redirecting to my URI it throws an error. that error was ( Uncaught (in promise) Error: No current user ). and in a network, the error was --->

[ {"__type":"ValidationException","message":"1 validation error detected: Value '{cognito-idp.ap-south-1.amazonaws.com/ap-south-1_ibkjGZP3L=}' at 'logins' failed to satisfy constraint: Map value must satisfy constraint: [Member must have length less than or equal to 50000, Member must have length greater than or equal to 1]"} ]

how do resolve this @Neuroforge ?

DaiLoc5698 commented 10 months ago

Hi guys, I got the same issue, I think the reason for this error is that the URL redirect and the url (redirectSignIn, redirectSignOut) we configured in Amplify.configure do not match, even the '/' character. yes, trailingSlash: true can be useful, but it may not be necessary if we have configured the redirect url as aws user pool and ours env are same even the '/'. In my case, my website supports internationalization, but I configure redirectSignIn and redirectSignOut in Amplify.configure incorrect.

Here's my solution: add this code in RootLayout or AppProvider or some where like that

const updatedAwsConfig = {
  ...awsConfig,
  oauth: {
    ...awsConfig.oauth,
    redirectSignIn:
      locale === DEFAULT_LOCALE
        ? `${process.env.BASE_URL}/${your-page-name}`
        : `${process.env.BASE_URL}/${locale}/${your-page-name}`,
    redirectSignOut:
      locale === DEFAULT_LOCALE
        ? `${process.env.BASE_URL}`
        : `${process.env.BASE_URL}/${locale}`,
  },
}
Amplify.configure(updatedAwsConfig)

I hope this helps someone

redjonzaci commented 8 months ago

I still have this issue.