30

I'm currently using Cognito User Pools, federated with Google as an identity provider, to handle user sign in for my web app. At the moment, I have only implemented Sign-In with Google. This is currently being done through Cognito's Hosted UI.

When users sign in with Google, I'd like them to always be prompted to select their account, i.e. be presented with this prompt.

However, I have found that when I'm logged in with only one Google account, then this screen is skipped. Although, when I'm logged into no Google account / 2 or more Google accounts, this screen is shown.

Things I've tried to make this screen always show up:

  1. Using AWS Amplify's federatedSignIn({provider: 'Google'}) function. However, I found that this is just a wrapper on Cognito's Hosted UI and just redirects to the same authorization endpoint, as described here.
  2. Adding prompt=select_account to the authorization endpoint as detailed in Google's documentation, however this had no effect. This was unsurprising as the prompt option is not detailed in the AWS documentation for the authorization endpoint.

If anyone has any ideas on how I can always have this account selection screen shown, it would be very much appreciated.

jueno
  • 403
  • 1
  • 4
  • 5

6 Answers6

24

Turns out that at this moment (January 2020) (edit: see below for their proposed solution which is still problematic) AWS Cognito does not support the prompt=select_account (or any of the prompt options Google provides). Went back and forth with their support, and here is the final resulting message with their current plan of action:

(restating the issue) Auth.signOut() only signs out from Cognito, but not from the federated provider (Google in your case). So when you try to login again (in your customers case, using Auth.federatedSignIn({ .provider: 'Google' })) it will automatically bypass Google's account selection/login and directly use the existing session. [which could be a problem if it is the wrong Google session]

One sub optimal solution to this is to also sign out from Google. You can accomplish this by making a GET request to https://accounts.google.com/logout. This way, a subsequent federatedSignIn will need to go through the Google login screen.

I have escalated this case to the Cognito service team in Seattle to get a feature request:

Being able to pass a prompt="select_account" option via the URL query to Google.

Edit to add Cognito Response:

If you're using Cognito Hosted UI, you can clean up the Cognito user pool session by invoking the Logout end point:

    https://<Your-User-Pool-Domain>.auth.<Your-User-Pool-Region>.amazoncognito.com/logout?client_id=<Your-User-Pool-App-Client>&logout_uri=<Your-User-Pool-SignOut-URL>

When I (AWS Congito) tried to reproduce the issue with Cognito Hosted UI, I had to re-sign in with Google after I signed out. I couldn't reproduce this issue one way or another.

See the Cognito documentation for the logout link for more information and various options.

After trying their response: Unfortunately, this fix (using the logout link) does not work as expected. It DOES let the user select a new identity provider (Google, Facebook, etc.), but if the user is logged in with the one they select, it then proceeds to use that user identity rather than giving the user the option to choose among multiple accounts or login with a new one.

LocalPCGuy
  • 6,006
  • 2
  • 32
  • 28
  • 3
    I thought I was going crazy for a while. Thank you for restoring my sanity. Is the "ticket" publicly viewable? Could you share the link? – teddybeard Feb 07 '20 at 17:59
  • 1
    The ticket is not public, but I have shared their response on how to clean up a Cognito session. We haven't had a chance to implement it yet, which is why I hadn't edited this yet. – LocalPCGuy Feb 07 '20 at 21:01
  • Thanks for the info. Have you ever managed to get this fixed?! – Ahmed Fathy Jul 08 '20 at 08:01
  • 1
    @AhmedFathy We've basically hacked around the issue. We were using the Logout and Relogin link that Cognito has but that causes a different problem. So we now open the Logout link, wait for that to complete, and then open the Login link to the appropriate provider. It doesn't specifically solve the "logged into a single Google account" issue, but it helps ensure the Cognito session is not part of the problem. – LocalPCGuy Jul 08 '20 at 16:36
  • 2
    With some digging, I found that upon federated login, a Cognito cookie is stored on the hosted UI domain. When that cookie is manually cleared in the browser, you're able to pick a different account again. However, the only way to clear it is using the LOGOUT endpoint. This means there's no other way to clear it other than to show the hosted UI in your browser. The cookie resides in your provided hosted UI subdomain and cannot be cleared by other subdomains. – Warren Wang Nov 26 '20 at 00:41
  • 1
    Any solution to this problem? I am facing the same issue. – Debotos Das Oct 01 '21 at 10:10
3

The way around this is to use Google as an OpenID authentication provider for your user pool in Cognito.

First, configure Google as a federated Identity provider for AWS.

Most tutorials for adding Google as a federated identity provider will take you through the initial steps. You can refer to this medium article or this AWS blog post to set up Google as a federated identity provider in AWS. This youtube video explains the steps for that as well.

The most important thing is that you get the app client Id and secret from the google console.

Next you need to configure Google as an OpenID connect provider in the AWS IAM service.

  1. In AWS service, Go to the IAM console.
  2. Choose "Identity providers" from the navigation menu.
  3. Click the "Create provider" button.
  4. Choose "OpenID Connect" as the provider type.
  5. In the provider URL write https://accounts.google.com
  6. In the "audience", enter the client ID obtained from Google developer console for your app.

Finally set up google as an openID authentication provider for your user pool.

  1. In the AWS console, search for cognito service.
  2. Choose "Federated Identities" from the navigation menu.
  3. Choose the identity you want to configure or create a new one.
  4. Click "edit Identity pool"
  5. Under Authentication providers, choose OpenId
  6. Enable Google as an identity provider.
  7. After configuring save your changes.

I am using react for my project. Make sure to add the identity_pool_id of your user pool in your AWS configuration object on the front end.

Here is the code written in react for your sign in page. You can customize the UI to your liking. Refer to google identity documentation on how to configure the UI and authentication flow.

import { Auth } from "aws-amplify";
import jwt from "jsonwebtoken";
import {useEffect} from "react";

const SignInPage = ()=> {

useEffect(()=>{

//TODO: You can check using Auth.currentAuthenticatedUser() to navigate to another page if a user is already logged in

 if (!window.google) createGoogleScript();
},[]);

return (
<>
<form>
{/* google will render its sign-in button in this div below */}
  <div id="buttonDiv"> google div</div>

{ /*you can have a form here for signing in the user with email and password */}

</form>
</>

)

}

const decodeJWTResponse = (jwtToken) => {
  return jwt.decode(jwtToken);
};

const getAWSCredentials = async (googleResponse) => {
  const googleUser = decodeJWTResponse(googleResponse.credential);

  let user = {
    email: googleUser.email,
    name: googleUser.name,
  };

  try {
    const credentials = await Auth.federatedSignIn("google", { token: googleResponse.credential, expires_at: googleUser.exp }, user);
    console.log("credentials from federated sign in ", JSON.stringify(credentials));
//TODO: You can navigate away if you want to, user object will now be available on Auth library
  } catch (err) {
    alert(err);
  }
};


const createGoogleScript = () => {
  // load the Google SDK
  const script = document.createElement("script");
  script.src = "https://accounts.google.com/gsi/client";
  script.async = true;
  script.onload = function () {
    window.google.accounts.id.initialize({
      client_id: YOUR_APP_CLIENT_ID_IN_GOOGLE_CONSOLE,
      callback: (result) => {
       getAWSCredentials(result);
      },
    });
    window.google.accounts.id.renderButton(
      document.getElementById("buttonDiv"),
      { theme: "outline", size: "large" } // customization attributes
    );
    window.google.accounts.id.prompt();
  };
  document.body.appendChild(script);
};

Your UI will look this after, allowing you to sign in with different google accounts

Please note that the User object returned from cognito library with this setup is slightly different from the one you get through the regular sign-in methods so you may have to re-write some of the logic to extract the user information and tokens.

Hamza Kyamanywa
  • 417
  • 3
  • 8
0

One workaround for this issue will be to use the Cognito Logout and Prompt the User to Sign In As Another User instead of calling the normal login. This works similar to the login, but clears any existing session and shows the login screen. So instead of calling

https://xxxxxxxxxxx.auth.xxxxxxx.amazoncognito.com/login?client_id=xxxxxxxxxxxxxx&response_type=code&scope=email+openid+profile&redirect_uri=https://example.com/login

for login, use this:

https://xxxxxxxxxxx.auth.xxxxxxx.amazoncognito.com/logout?client_id=xxxxxxxxxxxxxx&response_type=code&redirect_uri=https://example.com/login
Jishnu
  • 441
  • 3
  • 13
  • 1
    This was noted in the answer I supplied, with the caveat that it doesn't fully work to resolve the issue. -- "Unfortunately, this fix (using the logout link) does not work as expected. It DOES let the user select a new identity provider (Google, Facebook, etc.), but if the user is logged in with the one they select, it then proceeds to use that user identity rather than giving the user the option to choose among multiple accounts or login with a new one." – LocalPCGuy Oct 01 '21 at 14:23
0

For anyone else that happens upon this and needs a simple workaround for development, this solution has allowed me to select other google accounts when logging in again after signing out.

The short of it:

  1. In the android emulator/on the android device, log into multiple accounts on the default browser (in my case - and default case - chrome).
  2. In the pubspec.yaml, use the preview packages:
    dependencies:
      amplify_flutter: 1.0.0-next.0
      amplify_auth_cognito: 1.0.0-next.0
    
  3. Perform your login and logout and you should be prompted to select one of the google accounts on each login.

Source here: https://github.com/aws-amplify/amplify-flutter/issues/1528#issuecomment-1205936858

Jay
  • 3,373
  • 6
  • 38
  • 55
  • What about the users of the app? Correct me if I'm wrong, but should one tell them to do that workaround too? It's not practical if that's the case. – Abdelalim Hassouna Nov 30 '22 at 15:29
0

The issue with signing in using Google has been resolved. Now, the prompt is displayed every time to select the Gmail account. This was resolved by calling the URL https://you_cognito_domain/logout?client_id=your_client_id&logout_uri=your_logout_url

front end team has to redirect them by calling this url after cicking on logout

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 28 '23 at 09:48
-2

According to the Google documentation, prompt=consent requests re-authorization of all scopes that might have been previously granted, and therefore should be used only when necessary (perhaps when migrating user data).

Otherwise, if a user is already signed in to their Google account, this is expected OAuth2.0 behavior. This is the same behavior as using Github for Oauth2.0 authentication with StackOverflow. This thread might shed more light, even if it entails signing out rather than signing in: How to Logout of an Application Where I Used OAuth2 To Login With Google?.

Of course, your application should not sign users out of their Google accounts from your application. You may find yourself with a lot of angry users when they learn that they need to sign back into Google over and over again each time they sign out of your application.

I had a similar situation where users signing out of their Google accounts would not prompt an account selection from my application (bypassing the HostedUI). Upgrading aws-amplify-react to version 3.1.5 seems to have fixed it, probably due to the Authenticator component we're using. I haven't been able to dig into the source code to find what might have been the relevant change, however.