aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.42k stars 2.12k forks source link

Successful OAuth2 sign-in replaces location with callback url #3391

Open hotgazpacho opened 5 years ago

hotgazpacho commented 5 years ago

Describe the bug In #3005, when a successful OAuth2 sign in occurs, the browser's location is replaced with the callback url:

https://github.com/aws-amplify/amplify-js/blob/76cde598c30ee8049798e5988261db9165cc775e/packages/auth/src/Auth.ts#L1552-L1554

This is, at best, extraneous, as the user should already be on the callback URL after a successful federated sign in. In my case, however, my callback url is distinct from the main application URL, and performing a history.replaceState actively subverts my application's use of a client-side router (react-router). That is, I have the component that handles the callback perform a redirect (possibly to a previously saved location) after authentication occurs. However, the timing of events causes this to occur before the call to history.replaceState completes. The end result is that my application renders the content for one url, while the browser displays a different URL.

Basically, the idea of the OAuth/OIDC callback url is conflated with the idea of the url that I want to redirect a user to on successful sign in.

Expected behavior I would expect that the location would not be replaced on successful federated sign in, OR, allow consumers to configure (preferably at call time) the URL to redirect to after successful sign in, keeping it separate from the callback URL.

Sample code Include additional sample code or a sample repository to help us reproduce the issue. (Be sure to remove any sensitive data)

My Amplify configuration:

const config = {
  Auth: {
    region: process.env.REACT_APP_AWS_REGION,
    identityPoolId: process.env.REACT_APP_IDENTITY_POOL_ID,
    userPoolId: process.env.REACT_APP_USER_POOL_ID,
    userPoolWebClientId: process.env.REACT_APP_USER_POOL_CLIENT_ID,
    oauth: {
      domain: process.env.REACT_APP_AUTH_DOMAIN,
      scope: ['openid', 'profile', 'phone', 'email', 'aws.cognito.signin.user.admin'],
      redirectSignIn: window.location.origin + '/implicit/callback',
      redirectSignOut: window.location.origin,
      responseType: 'code',
      identityProvider: process.env.REACT_APP_IDP,
      options: {
        // Indicates if the data collection is enabled to support Cognito advanced security features. By default, this flag is set to true.
        AdvancedSecurityDataCollectionFlag: true
      }
    }
  }
};

Amplify.configure(config);
stale[bot] commented 4 years ago

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.

easyandme commented 4 years ago

Exact same problem. Is this fixed?

Currently I did a workaround for this by adding a replaceState event listener and force it to replace back to the url I wanted it to redirect to.

AntonioAngelino commented 3 years ago

Exact same problem. Is this fixed?

Currently I did a workaround for this by adding a replaceState event listener and force it to replace back to the url I wanted it to redirect to.

I've opened a Pull Request to add more config options for the redirect, but it hasn't been merged yet.

elorzafe commented 3 years ago

@hotgazpacho @AntonioAngelino @maxim-xu

Have you tried using customState events?. You could listen for the Hub event that ack that federatedSignIn process was completed successfully.

That event is triggered after Amplify handles the redirect from HostedUI (e.g. exchange a code for Cognito tokens), I wont recommend to do anything before that event happens.

Here is a sample code of what I did with react-router

import logo from './logo.svg';
import './App.css';
import Amplify, { Auth, Hub } from 'aws-amplify';
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

Hub.listen("auth", ({ payload: { event, data } }) => {
  if (event === "customOAuthState") {
      window.history.replaceState(
        {},
        null,
        `http://localhost:3000${data}`
      );
      window.history.go();
  }
});

Amplify.configure({
  Auth: {
    // OPTIONAL - Amazon Cognito User Pool ID
    userPoolId: 'us-west-2_xxxxxxxxxxxX',

    // OPTIONAL - Amazon Cognito Web Client ID (26-char alphanumeric string)
    userPoolWebClientId: 'xxxxxxxxxxxxxxxxxxxxxxxxx',
    oauth: {
      domain: 'userpooltest123.auth.us-west-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
    }
  }
});

export default function App() {
  function hostedUISignIn() {
    Auth.federatedSignIn({ customState: window.location.pathname })
  }

  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/users">Users</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/users">
            <Users />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
        <button onClick={hostedUISignIn}>hosted ui sign in</button>
      </div>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}
alanbo commented 2 years ago

What about if somebody only uses @aws-amplify/auth without the whole aws-amplify library, to reduce the bundle size? There is not hub so it's not possible to listen for events. Ideally, amplify library would allow to configure the final redirect or prevent it altogether, just removing the code from the url.