Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'redirect_mismatch' error with hosted UI #5127

Closed
YangMann opened this issue Jun 22, 2019 · 33 comments
Closed

'redirect_mismatch' error with hosted UI #5127

YangMann opened this issue Jun 22, 2019 · 33 comments
Labels
Auth Related to Auth components/category feature-request Request a new feature

Comments

@YangMann
Copy link

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.<some_id>.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.<some_id>.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!

@attilah
Copy link

attilah commented Jun 26, 2019

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

@YangMann
Copy link
Author

YangMann commented Jun 27, 2019

@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.

@YangMann
Copy link
Author

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

@kaustavghosh06 kaustavghosh06 transferred this issue from aws-amplify/amplify-cli Jul 3, 2019
@sonywijaya
Copy link

@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.

@Avi-Levi
Copy link

Hi Guys, any plans on fixing this issue?

@patleeman
Copy link

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

@arelaxtest
Copy link

arelaxtest commented Aug 25, 2019

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);

@mbalex99
Copy link

@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.

@codercatdev
Copy link

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?

@sammartinez sammartinez transferred this issue from aws-amplify/amplify-js Mar 5, 2020
@kaustavghosh06 kaustavghosh06 transferred this issue from aws-amplify/amplify-hosting Mar 17, 2020
@rasensio
Copy link

rasensio commented Apr 4, 2020

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

@stale
Copy link

stale bot commented May 4, 2020

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.

@stale stale bot added the pending-close-response-required A response is required for this issue to remain open, it will be closed within the next 7 days. label May 4, 2020
@stale
Copy link

stale bot commented May 12, 2020

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

@mauerbac
Copy link
Member

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. #5565.

@stale stale bot removed the pending-close-response-required A response is required for this issue to remain open, it will be closed within the next 7 days. label May 15, 2020
@mauerbac mauerbac added the bug Something isn't working label May 15, 2020
@sammartinez sammartinez added feature-request Request a new feature Auth Related to Auth components/category and removed bug Something isn't working labels May 21, 2020
@jesuscovam
Copy link

could this fix also support an Expo app that has no window object?

@adi518
Copy link

adi518 commented Jul 28, 2021

My Preview build fails with a redirect mismatch error, because it doesn't update auth with the preview url. I don't understand how Amplify doesn't do this automatically. How do you update sign-in/sign-out redirect URIs headlessly?

@NiksanJP
Copy link

NiksanJP commented Oct 2, 2021

Hey guys I searched around and I found myself the solution.

  1. Fix your Sign in and Sign out URIs
  2. Check your Cognito
  3. Fix your JS code

Fix your Sign in and Sign out URIs
So here you wanna enter where the user will be navigated after sign in and after sign out.
For example
image
I want my user to be sent to dashboard after sign
Also when sign out to the '/' or Landing page

Check your Cognito
image
SHould be something like this for web client

Fix your JS code
This works
`
import Amplify, {Auth } from 'aws-amplify';
import aws_exports from './aws-exports';

aws_exports.oauth.redirectSignIn = ${window.location.origin}/dashboard/;
aws_exports.oauth.redirectSignOut = ${window.location.origin}/;

Amplify.configure(aws_exports);
Auth.configure(aws_exports);
`

Thanks

@m98
Copy link
Contributor

m98 commented Apr 11, 2022

I wish the documentation updates soon. I was facing this issue too, and in my case, I had more than 2 domains. I followed this comment and tidy that up a bit to suit my needs. I hope the following solution can help someone:

import awsconfig from '../aws-exports'
import {Amplify} from 'aws-amplify'

const configureAmplify = () => {
  const urlsIn = awsconfig.oauth.redirectSignIn.split(',')
  const urlsOut = awsconfig.oauth.redirectSignOut.split(',')
  const currentHost = location.protocol + '//' + location.host
  const isCurrentHost = (url: string) => url.includes(currentHost)
  const newUrls = {
    redirectSignIn: urlsIn.find(url => isCurrentHost(url)),
    redirectSignOut: urlsOut.find(url => isCurrentHost(url))
  }
  Amplify.configure({
    ...awsconfig,
    oauth: {
      ...awsconfig.oauth,
      ...newUrls
    }
  })
}

Then you can call the configureAmplify in your App

@contractorwolf
Copy link

contractorwolf commented Oct 29, 2022

@NiksanJP's solution worked for me, with a slight modification (my example is for Vuejs 3 with Pinia):

import { Amplify, Auth } from 'aws-amplify';
import { createApp } from 'vue';
import AmplifyVue from '@aws-amplify/ui-vue';
import { createPinia } from 'pinia';
import awsExports from './aws-exports';
import App from './App.vue';
import router from './router';

const pinia = createPinia();

awsExports.oauth.redirectSignIn = `${window.location.origin}/`;
awsExports.oauth.redirectSignOut = `${window.location.origin}/`;

Amplify.configure(awsExports);
Auth.configure(awsExports);

createApp(App)
  .use(pinia)
  .use(router)
  .use(AmplifyVue)
  .mount('#app');

I can now do multiple urls (as long as they are added using Amplify so that they are in the Cognito "App Client Settings") and I can use localhost for testing and development. This should be handled in the Amplify.configure() code. Its makes no sense to have this issue, I was limping along with another hack that meant that I could either run my prod service or my localhost but not both before this fix. Why isn't this the default?

Thanks @NiksanJP!

@tannerabread
Copy link
Contributor

Hi 👋 Closing this as resolved. The docs now clearly show how to handle multiple redirect URIs. If you are still experiencing this issue and in need of assistance, please feel free to comment and provide us with additional information so we can re-open this issue and be better able to assist you.

Thank you!

@contractorwolf
Copy link

I have to ask @tannerabread why is the newly documented answer better than mine above? I feel like mine is cleaner and assumes that the current window.location is in the cognito config and will fail otherwise. What is my solutions missing?

@tannerabread
Copy link
Contributor

Sorry @contractorwolf, I wasn't trying to imply the answer in the docs is better, only that a solution does exist. I prefer the succinct nature of your answer as well.

I can speak with the team to make sure I'm not missing anything that could be wrong about that change, and if not I will change the docs for it myself.

I will say though outside of setting the redirectSignIn/Out, you shouldn't be configuring both Amplify and Auth packages individually and instead just use Amplify.configure for all of your configuration. Using both can lead to multiple instances of Amplify and unintended problems when using Amplify services.

@contractorwolf
Copy link

cool @tannerabread, I was working off of an earlier answer that included the Auth.configure(). I will test using my code without that and get back to this thread on whether or not it works. If so I would suggest that Amplify should assume these values and not require them in awsExports configuration:

awsExports.oauth.redirectSignIn = `${window.location.origin}/`;
awsExports.oauth.redirectSignOut = `${window.location.origin}/`;

unless there is something else that I am not understanding? Ill update my answer after I have tested without the Auth configuration code.

@tannerabread
Copy link
Contributor

@contractorwolf
Just to make sure we are on the same page, I was referring to removing the need for setting a new variable isLocalHost from arelaxtest's answer and then setting the object directly how you are doing.

Cognito/Hosted UI would still need to know all of the possible redirects for your app (I think the limit is 99 in and 99 out).

Amplify currently doesn't handle that inside of the exports because using the window object doesn't work the same way for mobile apps AFAIK, which Amplify supports. So this would just be a docs change to show the cleaner way of finding the url on app load on the JS side.

@contractorwolf
Copy link

contractorwolf commented Nov 14, 2022

CORRECTION
based on @tannerabread's suggestion that the Auth.configure() was not needed (and removing would be cleaner) I tested and found that he was correct. This code no longer does the Auth.configure() step (that I had above), which @tannerabread said was redundant. This is my current implementation (which works):

import { Amplify } from 'aws-amplify';
import { createApp } from 'vue';
import AmplifyVue from '@aws-amplify/ui-vue';
import { createPinia } from 'pinia';
import awsExports from './aws-exports';
import App from './App.vue';
import router from './router';

const pinia = createPinia();

awsExports.oauth.redirectSignIn = `${window.location.origin}/`;
awsExports.oauth.redirectSignOut = `${window.location.origin}/`;

Amplify.configure(awsExports);

createApp(App)
  .use(pinia)
  .use(router)
  .use(AmplifyVue)
  .mount('#app');

@contractorwolf
Copy link

contractorwolf commented Nov 14, 2022

@tannerabread but if the Amplify.configure() method were to just assume that the redirectSignIn/redirectSignOut values were the current location origin (window.location.origin for web and however mobile determines it) then the values would never have to be stored in the awsExports file and then edited as above. It could just check the current location.origin url on that call of configure() against the list in cognito and validate.

@tannerabread
Copy link
Contributor

@contractorwolf
I'm going to discuss this with the rest of the team and I will update here when we have a resolution. I don't see a problem with changing the docs to handle the frontend the way you did, but I need to check with a few people internally to know if it's feasible to handle it like you said.

@contractorwolf
Copy link

right on @tannerabread, I just would just like to keep as simple as possible but I want to confirm that I am still within basic recommended guidelines. Just let us know what you find.

@tannerabread
Copy link
Contributor

I can give an update but the discussion isn't complete yet.

For Amplify to handle it, it would have to be in a major version release sometime in the future because that would introduce some breaking changes and how it would be implemented would need to be discussed as Amplify would need to know if the user is on dev or prod while configuring. I will open up a separate feature request for this or reword the original post to be more clear what would be expected from this feature. I might be more inclined to leave this one because of all the context and just reword the title/OP.

For the docs change, we are trying to ensure there are no security implications with using window.location.origin.

Second point about the docs change, we do allow redirects to specific paths, so if I would change the docs it would need to have 2 examples, one like you have shown above and one that shows ${window.location.origin}/some/path/here/. This also brings about the 2nd point of change if someone were to modify the path they were using, they would need to modify the config as well as their frontend code to handle it, whereas the original solution wouldn't require that because it pulls the values from the config.

Lastly, the solution you posted above modifies the original and maybe that's not desirable in some circumstances, so we might need to keep it as making a copy (e.g. updatedAwsConfig) and setting it through that.

The discussion is still going but I wanted to update you with where we are at. The main points being Amplify handling it would need to go in a future major version and for the docs update we are just trying to make sure we put the best solution forward. I think we are in agreement the localhost check with the RegEx and other things is probably not the best solution, but want to find it before we change it.

@m98
Copy link
Contributor

m98 commented Nov 16, 2022

Cognito & Amplify should either not allow setting more than one URL, or they should handle it properly.

When you set more than one domain/URL, it could link to some documentation and this can have consequences.
Imagine this happening for an app on production when they add another URL, and debugging this can be hard as this is not documented in the right way.

@krishnu9
Copy link

krishnu9 commented Feb 16, 2023

This is how i made the application handle two redirectURIs.
Amplify CLI updates redirect URIs in src/aws-exports.js when you run amplify push.
Before amplify.configure() Set redirectSignIn and redirectSignOut URLs conditionally based on whether app is running on localhost

import awsConfig from './aws-exports';
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}$/
    )
);
const [
  productionRedirectSignIn,
  localRedirectSignIn,
] = awsConfig.oauth.redirectSignIn.split(",");

const [
  productionRedirectSignOut,
  localRedirectSignOut,
] = awsConfig.oauth.redirectSignOut.split(",");

const updatedAwsConfig = {
  ...awsConfig,
  oauth: {
    ...awsConfig.oauth,
    redirectSignIn: isLocalhost ? localRedirectSignIn : productionRedirectSignIn,
    redirectSignOut: isLocalhost ? localRedirectSignOut : productionRedirectSignOut,
  }
}

@dagnym
Copy link

dagnym commented Jun 15, 2023

these docs are insufficient. still having issues getting this to work properly.

@abdallahshaban557
Copy link
Contributor

Hi @dagnym - can you please help us by creating a new GitHub issue to help us get your set up and reproduce the issue?

@contractorwolf
Copy link

contractorwolf commented Jun 18, 2023

are we still in the holding pattern @tannerabread? I don't love my solution because it seems counter-intuitive but it does solve my specific issue. I just want to be sure that in solving this (ultimately configuration issue) isn't unknowingly leaving a door open for some security manipulation. What did the discussions reveal?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auth Related to Auth components/category feature-request Request a new feature
Projects
None yet
Development

No branches or pull requests