Skip to content

Added support for idp_identifier query parameter in cognito authorize… #10505

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ncarvajalc
Copy link
Contributor

… endpoint

Description of changes

Files changed

The following files had changes:

  • packages/auth/src/types/OAuth.ts
  • packages/auth/src/Auth.ts
  • packages/auth/src/OAuth/OAuth.ts
Changes

The packages/auth/src/types/OAuth.ts file had the following changes:

An idpIdentifierfiled was added as an optional field in the FederatedSignInOptions and FederatedSignInOptionsCustom types to enable the idp_identifier query parameter when sending the request URL to the Cognito authorize endpoint. Additionally, a hasIdpIdentifier function was added to evaluate if the provider options have an idpIdentifier key. Also, the provider field was made optional, as either an idp_identifier or a provider is used when requesting for an IdP, as described in the Amazon Cognito documentation.

The packages/auth/src/Auth.ts file had the following changes:

The hasIdpIdentifier function was used to check for the idpIdentifier key. In case it has it, the idpIdentifier value is extracted either from the FederatedSignInOptions type or the FederatedSignInOptions type. After that the idpIdentifier is passed to the this._oAuthHandler.oauthSignIn function.

Finally, the packages/auth/src/OAuth/OAuth.ts file had the following changes:

The function oauthSignIn was modified to make the provider argument optional and added an optional argument idpIdentifier. In case there is a provider present the request URL generated will send the provider as the identity_provider query parameter and ignore any idpIdentifier present, as either an idp_identifier or an identity_provider is used when requesting for an IdP. In case there is no provider, it will check for the idpIdentifier and send it as the idp_identifier query parameter in the Cognito authorize endpoint.

Issue #, if available

Closes #10226

Description of how you validated changes

I created a sample React app with the following component:

import { Auth } from "aws-amplify";
import { useEffect, useState } from "react";

export default function SignInPage() {
  const [user, setUser] = useState(null);

  const signIn = async () => {
    try {
      await Auth.federatedSignIn({
        idpIdentifier: "hotmail.com", // IdP identifier which was set up via amazon cognito
      });
    } catch (error) {
      console.log("error signing in", error);
    }
  };

  useEffect(() => {
    getUser().then((userData) => setUser(userData));
  }, []);

  function getUser() {
    return Auth.currentAuthenticatedUser()
      .then((userData) => userData)
      .catch(() => console.log("Not signed in"));
  }

  return (
    <div>
      <h1>
        Welcome{" "}
        {user ? JSON.stringify(user.attributes.email) : "Ups! Not signed in"}
      </h1>
      {user ? (
        <button onClick={() => Auth.signOut()}>Sign Out</button>
      ) : (
        <button onClick={signIn}>Federated Sign In</button>
      )}
    </div>
  );
}

The federate sign in identity was set up as described in the comment in #10226 and adding an IdPidentifier as shown below:
image

The app successfully logged in the user and added it to the cognito user pool created.

Previous test cases when using default options or no arguments for the federatedSignin function where tested and they worked correctly.

Checklist

  • PR description included
  • yarn test passes (It is not working for @aws-amplify/storage tests in the latest branch)
  • yarn run test --scope @aws-amplify/auth passes
  • Tests are changed or added
  • Relevant documentation is changed or added (and PR referenced)

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@ncarvajalc ncarvajalc requested a review from a team as a code owner October 19, 2022 18:49
@abdallahshaban557 abdallahshaban557 added this to the triage milestone Oct 31, 2022
@ncarvajalc
Copy link
Contributor Author

Behavior for each permutation of idp_identifier + identity_provider

The way it was implemented is the following:

const queryString = provider
			? Object.entries({ // Case a provider is given (with or without an idp_identifier)
					redirect_uri: redirectSignIn,
					response_type: responseType,
					client_id: clientId,
					identity_provider: provider, // Present instead of idp_identifier
					scope: scopesString,
					state,

					...(responseType === 'code' ? { code_challenge } : {}),
					...(responseType === 'code' ? { code_challenge_method } : {}),
			  })
					.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
					.join('&')
			: Object.entries({ // Case a provider is not given but there is an idp_identifier
					redirect_uri: redirectSignIn,
					response_type: responseType,
					idp_identifier: idpIdentifier, // Present instead of identity_provider
					client_id: clientId,
					scope: scopesString,
					state,
					...(responseType === 'code' ? { code_challenge } : {}),
					...(responseType === 'code' ? { code_challenge_method } : {}),
			  })
					.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
					.join('&');

		const URL = `https://${domain}/oauth2/authorize?${queryString}`;
		logger.debug(`Redirecting to ${URL}`);
		this._urlOpener(URL, redirectSignIn);

Examples for different test cases

Case when no options are passed

const signIn = async () => {
    try {
      await Auth.federatedSignIn();
    } catch (error) {
      console.log("error signing in", error);
    }
  };

In this case if signIn is executed the user is redirected to the following:
Pasted image 20221110105606

Case empty options are passed

const signIn = async () => {
    try {
      await Auth.federatedSignIn({});
    } catch (error) {
      console.log("error signing in", error);
    }
  };

The following error is raised:
Pasted image 20221110105927

Case only valid provider is present

const signIn = async () => {
    try {
      await Auth.federatedSignIn({ provider: "Azure" });
    } catch (error) {
      console.log("error signing in", error);
    }
  };

The user is redirected to the given provider:
Pasted image 20221110110158

Case only invalid provider is present

const signIn = async () => {
    try {
      await Auth.federatedSignIn({ provider: "NonExistentProvider" });
    } catch (error) {
      console.log("error signing in", error);
    }
  };

The user is redirected to:
Pasted image 20221110110345

Case only valid idp_identifier is present

const signIn = async () => {
    try {
      await Auth.federatedSignIn({ idpIdentifier: "hotmail.com" });
    } catch (error) {
      console.log("error signing in", error);
    }
  };

User is redirected to the corresponding identity provider associated with the idp_identifier:
Pasted image 20221110110627

Case only invalid idp_identifier is present

 const signIn = async () => {
    try {
      await Auth.federatedSignIn({ idpIdentifier: "invalid.com" });
    } catch (error) {
      console.log("error signing in", error);
    }
  };

The user is redirected to the following:
Pasted image 20221110110834

Case valid provider and invalid or valid idp_identifier are used

const signIn = async () => {
    try {
      await Auth.federatedSignIn({
        provider: "Azure",
        idpIdentifier: "hotmail.com",
      });
    } catch (error) {
      console.log("error signing in", error);
    }
  };

Same case as when only a valid provider is passed as only that query parameter is inserted in the request.

Case invalid provider and invalid or valid idp_identifier are used

const signIn = async () => {
    try {
      await Auth.federatedSignIn({
        provider: "NonExistentProvider",
        idpIdentifier: "hotmail.com",
      });
    } catch (error) {
      console.log("error signing in", error);
    }
  };

Same case as when an invalid provider is used as only that query parameter is inserted in the request.

Are there any data/auth rule implications or differences between a user using other categories (ex. API, DataStore, etc) authenticated via identifier and provider?

No, as the redirect to the external provider and the registration in Cognito using idp_identifier is done the same way as using a custom provider (which is already implemented in Amplify).

Same as with custom providers – e.g { provider: "Azure" } – developers have to be aware that Cognito assigns user names and creates groups automatically when using an external provider. Here are 3 users, 2 were registered using an idp_identifier ({ idpIdentifier: "hotmail.com" }) and other using a custom provider ({ provider: "Azure" }). All of them were redirected to the same AzureAD application and registered in Cognito as follows:
Pasted image 20221110113001
Also, a group was created automatically and all of the users are in it:
Pasted image 20221110113155
Pasted image 20221110113210

With this in mind, developers have to be aware about the group name generated to use auth rules for schemas and keep in mind the prefix usernames have with the corresponding identity provider name used when registering the Federated identity provider. This same behavior is expected both when using a provider or an idp_identifier.

@cwomack cwomack added the Auth Related to Auth components/category label Feb 20, 2024
@jon-armen
Copy link
Contributor

jon-armen commented Jun 9, 2025

I have submitted #14423 as an alternative PR to this one, as the auth library has changed significantly since this was authored.

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 external-contributor
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for idp_identifier query parameter when going through OAuth
4 participants