Amplify-js: 'redirect_mismatch' error with hosted UI

Created on 22 Jun 2019  路  13Comments  路  Source: aws-amplify/amplify-js

Note: If your question is regarding the AWS Amplify Console service, please log it in the
official AWS Amplify Console forum

* Which Category is your question related to? *
Auth / Hosted UI

* What AWS Services are you utilizing? *
Cognito, Amplify Console

* Provide additional details e.g. code snippets *
Hi,

My auth work flow worked fine with the hosted UI when I was only using http://localhost:3000/ as the sign in/sign out url. When amplify console created a dev build, it hosted the frontend on a different url (https://dev..amplifyapp.com/ in my case). When opening the hosted UI from this url, it complained "redirect_mismatch", which is understandable since I only have localhost configured in cognito at this point.

I ran amplify update auth to add the console provided app url to the sign in/sign out urls, amplify push then git commit & git push to make the amplify console pick up the changes. However, the console hosted app still gave me "redirect_mismatch" error. I checked the aws-export.js file and cognito console, the redirect urls are exactly the same ("http://localhost:3000/,https://dev..amplifyapp.com/"). I used Chrome's inspection tool to check the actual redirect_uri string in the HTTP request, also exactly the same. I have no idea why the hosted UI is complaining mismatch.

Then I found out the app stopped working on my localhost, too. Same "redirect_mismatch" error. :(

image

Any help will be appreciated!

Auth feature-request

Most helpful comment

Sorry guys but in fact, this is a normal behavior.

Setup amplify add auth with two URLs: "http://localhost:3000/,https://master.xxx.amplifyapp.com/" to add the sign in/sign out URLs, amplify push then git commit & git push to make the amplify console pick up the changes.

Then you get a redirect_mismatch error locally and online https://master.xxx.amplifyapp.com/

Why ?
There is no way for the react app. to know by default which URLs to use when you have two or more URLs. You must inform the app. to use one of these URLs. You can do it like that:

What can you do ?
After looking in the doc., you find pretty much a solution here: https://aws-amplify.github.io/docs/js/authentication#react-components

import Amplify, { Auth } from 'aws-amplify'
import config from './aws-exports'

and,

// copied from serviceWorker.js to know if it is localhost or not
const isLocalhost = Boolean(
  window.location.hostname === 'localhost' ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === '[::1]' ||
    // 127.0.0.1/8 is considered localhost for IPv4.
    window.location.hostname.match(
      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

// by default, say it's localhost
const oauth = {
  domain: 'xxx.auth.us-east-2.amazoncognito.com',
  scope: ['phone', 'email', 'profile', 'openid', 'aws.cognito.signin.user.admin'],
  redirectSignIn: 'http://localhost:3000/',
  redirectSignOut: 'http://localhost:3000/',
  responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
};

// if not, update the URLs
if (!isLocalhost) {
  oauth.redirectSignIn = 'https://master.xxx.amplifyapp.com/';
  oauth.redirectSignOut = 'https://master.xxx.amplifyapp.com/';
}

// copy the constant config (aws-exports.js) because config is read only.
var configUpdate = config;
// update the configUpdate constant with the good URLs
configUpdate.oauth = oauth;
// Configure Amplify with configUpdate
Amplify.configure(configUpdate);

Full code & example
You can find a demo here that also work in localhost: https://master.d3h5j4begww46c.amplifyapp.com/
And a github fork (from dabit3): https://github.com/arelaxtest/amplify-auth-demo

Personnally, I do something like

var urlsIn = config.oauth.redirectSignIn.split(",");
var urlsOut = config.oauth.redirectSignOut.split(",");
const oauth = {
  domain: config.oauth.domain,
  scope: config.oauth.scope,
  redirectSignIn: config.oauth.redirectSignIn,
  redirectSignOut: config.oauth.redirectSignOut,
  responseType: config.oauth.responseType
};
var hasLocalhost  = (hostname) => Boolean(hostname.match(/localhost/) || hostname.match(/127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/));
var hasHostname   = (hostname) => Boolean(hostname.includes(window.location.hostname));
var isLocalhost   = hasLocalhost(window.location.hostname);
if (isLocalhost) {
  urlsIn.forEach((e) =>   { if (hasLocalhost(e)) { oauth.redirectSignIn = e; }});
  urlsOut.forEach((e) =>  { if (hasLocalhost(e)) { oauth.redirectSignOut = e; }});
}
else {
  urlsIn.forEach((e) =>   { if (hasHostname(e)) { oauth.redirectSignIn = e; }});
  urlsOut.forEach((e) =>  { if (hasHostname(e)) { oauth.redirectSignOut = e; }});
}
var configUpdate = config;
configUpdate.oauth = oauth;
Amplify.configure(configUpdate);

All 13 comments

@YangMann do you've a repro project you could share with us?

@attilah sorry the repo I'm working on is private. But I can share the details about redirect URLs.

After having two sign-in/sign-out redirect URLs in amplify auth, amplify push, the generated "redirect link" to the hosted UI page is

https://<app_id>.auth.us-east-1.amazoncognito.com/login?
redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F%2Chttps%3A%2F%2Fdev.<app_id>.amplifyapp.com%2F
&response_type=code&client_id=<client_id>
&identity_provider=COGNITO
&scopes=phone%2Cemail%2Copenid%2Cprofile%2Caws.cognito.signin.user.admin
&state=<state_string>
&code_challenge=<challenge>
&code_challenge_method=S256

I think the problem is that the redirect_uri parameter here in the generated link contains two URLs. I kinda walked around this by

  1. removing localhost from the sign-in/sign-out urls via amplify CLI, keeping only the amplifyapp.com one
  2. amplify push
  3. modify /amplify/backend/auth/cognito<some_id>/parameters.json, add http://localhost:3000/ back to the CallbackURLS and LogoutURLs in oAuthMetadata
  4. add http://localhost:3000/ to the callback URLs and logout URLs on Cognito Console

At this point, when I run on localhost, the generated link will only contain localhost in the redirect_uri parameter. I haven't tested my app on amplify domain yet, but I assume it should work.

@attilah I have a question: how is the redirect uri populated when calling Auth.federatedSignIn()?

@attilah I have a question: how is the redirect uri populated when calling Auth.federatedSignIn()?

redirect uri populated from oauth.redirectSignIn at aws-export.js or Auth.configure({ oauth: {redirectSignIn: "..."} }).

i got the problem when using two redirect uris, change it to one uri fix my problem.

Hi Guys, any plans on fixing this issue?

Same issue. I get redirect_mismatch when using more than one redirect url

Sorry guys but in fact, this is a normal behavior.

Setup amplify add auth with two URLs: "http://localhost:3000/,https://master.xxx.amplifyapp.com/" to add the sign in/sign out URLs, amplify push then git commit & git push to make the amplify console pick up the changes.

Then you get a redirect_mismatch error locally and online https://master.xxx.amplifyapp.com/

Why ?
There is no way for the react app. to know by default which URLs to use when you have two or more URLs. You must inform the app. to use one of these URLs. You can do it like that:

What can you do ?
After looking in the doc., you find pretty much a solution here: https://aws-amplify.github.io/docs/js/authentication#react-components

import Amplify, { Auth } from 'aws-amplify'
import config from './aws-exports'

and,

// copied from serviceWorker.js to know if it is localhost or not
const isLocalhost = Boolean(
  window.location.hostname === 'localhost' ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === '[::1]' ||
    // 127.0.0.1/8 is considered localhost for IPv4.
    window.location.hostname.match(
      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

// by default, say it's localhost
const oauth = {
  domain: 'xxx.auth.us-east-2.amazoncognito.com',
  scope: ['phone', 'email', 'profile', 'openid', 'aws.cognito.signin.user.admin'],
  redirectSignIn: 'http://localhost:3000/',
  redirectSignOut: 'http://localhost:3000/',
  responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
};

// if not, update the URLs
if (!isLocalhost) {
  oauth.redirectSignIn = 'https://master.xxx.amplifyapp.com/';
  oauth.redirectSignOut = 'https://master.xxx.amplifyapp.com/';
}

// copy the constant config (aws-exports.js) because config is read only.
var configUpdate = config;
// update the configUpdate constant with the good URLs
configUpdate.oauth = oauth;
// Configure Amplify with configUpdate
Amplify.configure(configUpdate);

Full code & example
You can find a demo here that also work in localhost: https://master.d3h5j4begww46c.amplifyapp.com/
And a github fork (from dabit3): https://github.com/arelaxtest/amplify-auth-demo

Personnally, I do something like

var urlsIn = config.oauth.redirectSignIn.split(",");
var urlsOut = config.oauth.redirectSignOut.split(",");
const oauth = {
  domain: config.oauth.domain,
  scope: config.oauth.scope,
  redirectSignIn: config.oauth.redirectSignIn,
  redirectSignOut: config.oauth.redirectSignOut,
  responseType: config.oauth.responseType
};
var hasLocalhost  = (hostname) => Boolean(hostname.match(/localhost/) || hostname.match(/127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}/));
var hasHostname   = (hostname) => Boolean(hostname.includes(window.location.hostname));
var isLocalhost   = hasLocalhost(window.location.hostname);
if (isLocalhost) {
  urlsIn.forEach((e) =>   { if (hasLocalhost(e)) { oauth.redirectSignIn = e; }});
  urlsOut.forEach((e) =>  { if (hasLocalhost(e)) { oauth.redirectSignOut = e; }});
}
else {
  urlsIn.forEach((e) =>   { if (hasHostname(e)) { oauth.redirectSignIn = e; }});
  urlsOut.forEach((e) =>  { if (hasHostname(e)) { oauth.redirectSignOut = e; }});
}
var configUpdate = config;
configUpdate.oauth = oauth;
Amplify.configure(configUpdate);

@jordanranz 's fix totally worked for me!

The correct solution is either that the SDK should handle this or the documentation should make a disclaimer about this snippet of code.

Is it possible to change the oAuthMetadata based on each environment?

I don't want my production add being allowed for localhost or my other callback URI's.

Is there a way to override this value in the team-provider-info.json?

Remember to add your redirect url to the facebook developer dashboard
https://YOUR-DOMAIN.auth.us-east-1.amazoncognito.com/oauth2/idpresponse

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.

For folks on this thread -- I recently experienced this issue myself and used the fix listed above. It works but I agree that our docs need to be updated and this should be fixed in the way the Amplify config state is created. This is now marked as a bug and will be addressed soon. https://github.com/aws-amplify/amplify-js/issues/5565.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

simon998yang picture simon998yang  路  3Comments

TheRealRed7 picture TheRealRed7  路  3Comments

romainquellec picture romainquellec  路  3Comments

ldgarcia picture ldgarcia  路  3Comments

shinnapatthesix picture shinnapatthesix  路  3Comments