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.44k stars 2.13k forks source link

userName for new user via federatedSignIn() is all lowercase #13844

Closed jburn7 closed 1 month ago

jburn7 commented 1 month ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

Authentication

Amplify Version

v5

Amplify Categories

auth

Backend

Amplify CLI

Environment information

``` # Put output below this line ```

Describe the bug

When calling Auth.federatedSignIn() for a user that does not yet exist in the user pool, the userName property for that user in the pre signup lambda trigger is all lowercase. According to https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation-consolidate-users.html userName should take the format of [providerName]_[userId] which in my case looks like this: Test_eolm9evlerpvogleoikbpvabsw6ewax8qypqdltv_c4

The problem comes when initiating the AdminLinkProviderForUser command from the pre sign up trigger using that all lowercase user id. The new cognito user created for the external provider has its userId stores with caps like so: [{"userId":"eOLm9EvLERpVogLeOikBpvAbsw6EwAx8qyPqDLtV_c4","providerName":"Test","providerType":"SAML","issuer":null,"primary":false,"dateCreated":1727191602989}]

So calling AdminLinkProviderForUser will fail due to case sensitivity on that user id

Expected behavior

When initiating a federatedSignIn, userName is received with case sensitivity

Reproduction steps

  1. Create an external SAML SSO tenant
  2. In a user pool, add a Federated Identity Provider linked to that tenant with name 'Test'
  3. Create pre-sign-up lambda which logs the event and connect it to the user pool
  4. From Amplify frontend, initiate Auth.federatedSignIn({customProvider: 'Test'})
  5. View lambda logs, looking for userName on the object, and see that it is all lowercase
  6. View the user pool, looking for a user with the status of External Provider
  7. Viewing that user, see the identities user attribute
  8. Observe that userId in the identities attribute includes capitialization

Code Snippet

# pre sign up lambda
export async function handler(event) {
  console.log(event);
  return event;
}

Log output

2024-09-24T15:13:33.831Z    3a9520e6-486b-4357-9847-5cb46eacfba3    INFO    {
  version: '1',
  region: 'us-east-2',
  userPoolId: 'us-east-2_***',
  userName: 'Test_eolm9evlerpvogleoikbpvabsw6ewax8qypqdltv_c4',
  callerContext: {
    awsSdkVersion: 'aws-sdk-unknown-unknown',
    clientId: '***'
  },
  triggerSource: 'PreSignUp_ExternalProvider',
  request: {
    userAttributes: {
      email_verified: 'false',
      'cognito:email_alias': '',
      'cognito:phone_number_alias': '',
      email: '***'
    },
    validationData: {}
  },
  response: {
    autoConfirmUser: false,
    autoVerifyEmail: false,
    autoVerifyPhone: false
  }
}

aws-exports.js

No response

Manual configuration

No response

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

No response

cwomack commented 1 month ago

Hello, @jburn7 👋. I'll work on reproducing this in a v5 app and follow up after, but have a couple questions on how you've configured Auth. Are you doing a scoped config for your Amplify.configure() call? And can you share that current config that you're using (with things like your ID's redacted along with any other sensitive info removed)?

jburn7 commented 1 month ago

Sure thing, here's our entire Amplify.configure():

Amplify.configure({
  Auth: {
    region: process.env.REACT_APP_AWS_REGION,
    userPoolId: process.env.REACT_APP_AWS_COGNITO_USER_POOL_ID,
    userPoolWebClientId: process.env.REACT_APP_AWS_COGNITO_CLIENT_ID,
    oauth: {
      domain: `auth.${process.env.REACT_APP_DOMAIN}`,
      scopes: ['openid', 'email', 'phone', 'profile', 'aws.cognito.signin.user.admin'],
      redirectSignIn: [`https://${process.env.REACT_APP_DOMAIN}/sign-in/sso`],
      redirectSignOut: [`https://${process.env.REACT_APP_DOMAIN}`],
      responseType: 'token'
    },
    username: 'true',
    email: 'true',
    phone: 'false'
  }
});
cwomack commented 1 month ago

@jburn7, thanks for the quick response! Can you confirm then for the 3rd party SAML provider where the users are authenticated/federated through, that the user names have the same casing that you're observing?

jburn7 commented 1 month ago

Yes I've viewed the SAML response that Amplify receives from the SSO provider and can confirm that the NameID attribute has the uppercase letters. I removed a ton of stuff from the SAML response but I'm happy to provide more of if needed

<samlp:Response
>
  <Assertion>
    <Subject>
      <NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">
        eOLm9EvLERpVogLeOikBpvAbsw6EwAx8qyPqDLtV_c4
      </NameID>
    </Subject>
  </Assertion>
</samlp:Response>

Viewing this ID on the external users themselves is a little trickier, since the provider (Microsoft Entra ID) generates persistent NameIDs for each user/connected application pair, and thus does not store those IDs as viewable properties on the user. But I feel like the ID having caps in the SAML response itself should suffice

cwomack commented 1 month ago

@jburn7, we've contacted the Cognito team for clarity on this and it appears that there's a discrepancy in the documentation. They'll be updating it to properly describe the behavior detailed in this issue, but essentially what's you've described here is expected for SAML federated Auth flows. The username will end up being all lowercase (not honoring any case sensitivity).

We'll keep this issue open for the time being until we can confirm the Cognito documentation gets updated.

jburn7 commented 1 month ago

@cwomack Thank you for reaching out to them to get the documentation updated at least. But why is that the expected behavior? There are multiple raised issues all over the web that match this one, such as https://stackoverflow.com/questions/68017906/amazon-cognito-openid-connect-invalidprovidername-usernamecombination and https://repost.aws/questions/QUi-m1rf2ZQt-zqrfrIXvQbg/cognito-saml-error-invalid-providername-username-combination

If case sensitivity is required for proper linking of external SAML users to existing Cognito users, then why would Cognito ditch the case sensitivity upon creating the external user? It sounds like that process would set itself up for failure every single time a user is registered, so what is the expected workaround here?

ashika112 commented 1 month ago

@jburn7 we have raised of the ticket to Cognito team but they would be the right folks to answer the question. But the cognito documentation have been updated here. Meanwhile i would suggest creating a case with AWS Cognito and we will reach out if they get back on the above.

cwomack commented 1 month ago

@jburn7, just wanted circle back and make sure you saw the updated documentation on Cognito's side (linked above). Since this is expected behavior for the authentication flows with Cognito (and the docs now align), we'll close this issue on our side.

Thank you for opening it and helping us improve our overall documentation.