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

Amplify Gen2 SignInWithApple not adding email attribute to userpool that requires email #14019

Closed
3 tasks done
JustinHaut opened this issue Nov 17, 2024 · 8 comments
Closed
3 tasks done
Assignees
Labels
Auth Related to Auth components/category documentation Related to documentation feature requests feature-request Request a new feature

Comments

@JustinHaut
Copy link

Before opening, please confirm:

JavaScript Framework

React Native

Amplify APIs

Authentication

Amplify Version

v6

Amplify Categories

auth

Backend

Amplify Gen 2

Environment information

System:
    OS: macOS 14.6.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 69.11 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.12.2 - /opt/homebrew/opt/node@20/bin/node
    Yarn: 1.22.22 - ~/.npm-global/bin/yarn
    npm: 10.8.2 - ~/.npm-global/bin/npm
    Watchman: 2024.04.15.00 - /opt/homebrew/bin/watchman
  Browsers:
    Chrome: 130.0.6723.119
    Safari: 17.6
  npmPackages:
    %name%:  0.1.0 
    @aws-amplify/backend: ^1.3.1 => 1.6.2 
    @aws-amplify/backend-cli: ^1.2.9 => 1.4.1 
    @aws-amplify/core: ^6.5.3 => 6.5.3 
    @aws-amplify/core/internals/adapter-core:  undefined ()
    @aws-amplify/core/internals/aws-client-utils:  undefined ()
    @aws-amplify/core/internals/aws-client-utils/composers:  undefined ()
    @aws-amplify/core/internals/aws-clients/cognitoIdentity:  undefined ()
    @aws-amplify/core/internals/aws-clients/pinpoint:  undefined ()
    @aws-amplify/core/internals/providers/pinpoint:  undefined ()
    @aws-amplify/core/internals/utils:  undefined ()
    @aws-amplify/core/server:  undefined ()
    @aws-amplify/react-native: ^1.1.6 => 1.1.6 
    @aws-amplify/rtn-web-browser: ^1.1.1 => 1.1.1 
    @aws-amplify/ui-react-native: ^2.2.13 => 2.2.16 
    @babel/core: ^7.20.0 => 7.26.0 
    @react-native-async-storage/async-storage: 1.23.1 => 1.23.1 
    @react-navigation/bottom-tabs: ^6.5.9 => 6.6.1 
    @react-navigation/native: ^6.1.18 => 6.1.18 
    @react-navigation/native-stack: ^6.11.0 => 6.11.0 
    @react-navigation/stack: ^6.4.1 => 6.4.1 
    @types/react: ~18.2.45 => 18.2.79 
    @types/react-dom: ~18.2.25 => 18.2.25 
    @types/react-navigation: ^3.0.8 => 3.0.8 
    HelloWorld:  0.0.1 
    aws-amplify: ^6.8.2 => 6.8.2 
    aws-amplify/adapter-core:  undefined ()
    aws-amplify/analytics:  undefined ()
    aws-amplify/analytics/kinesis:  undefined ()
    aws-amplify/analytics/kinesis-firehose:  undefined ()
    aws-amplify/analytics/personalize:  undefined ()
    aws-amplify/analytics/pinpoint:  undefined ()
    aws-amplify/api:  undefined ()
    aws-amplify/api/server:  undefined ()
    aws-amplify/auth:  undefined ()
    aws-amplify/auth/cognito:  undefined ()
    aws-amplify/auth/cognito/server:  undefined ()
    aws-amplify/auth/enable-oauth-listener:  undefined ()
    aws-amplify/auth/server:  undefined ()
    aws-amplify/data:  undefined ()
    aws-amplify/data/server:  undefined ()
    aws-amplify/datastore:  undefined ()
    aws-amplify/in-app-messaging:  undefined ()
    aws-amplify/in-app-messaging/pinpoint:  undefined ()
    aws-amplify/push-notifications:  undefined ()
    aws-amplify/push-notifications/pinpoint:  undefined ()
    aws-amplify/storage:  undefined ()
    aws-amplify/storage/s3:  undefined ()
    aws-amplify/storage/s3/server:  undefined ()
    aws-amplify/storage/server:  undefined ()
    aws-amplify/utils:  undefined ()
    aws-cdk: ^2.161.0 => 2.166.0 
    aws-cdk-lib: ^2.161.0 => 2.166.0 
    axios: ^1.7.7 => 1.7.7 
    axios-retry: ^4.5.0 => 4.5.0 
    constructs: ^10.3.0 => 10.4.2 
    esbuild: ^0.24.0 => 0.24.0 (0.23.1)
    expo: ~51.0.39 => 51.0.39 
    expo-build-properties: ~0.12.5 => 0.12.5 
    expo-file-system: ^17.0.1 => 17.0.1 
    expo-local-authentication: ~14.0.1 => 14.0.1 
    expo-secure-store: ~13.0.2 => 13.0.2 
    expo-splash-screen: ~0.27.7 => 0.27.7 
    expo-status-bar: ~1.12.1 => 1.12.1 
    expo-store-review: ^7.0.2 => 7.0.2 
    expo-system-ui: ~3.0.7 => 3.0.7 
    expo-updates: ~0.25.27 => 0.25.27 
    jwt-decode: ^4.0.0 => 4.0.0 
    nanoid: ^5.0.7 => 5.0.8 (3.3.7)
    react: 18.2.0 => 18.2.0 (18.3.1)
    react-dom: 18.2.0 => 18.2.0 (18.3.1)
    react-native: 0.74.5 => 0.74.5 
    react-native-device-info: ^13.0.0 => 13.2.0 
    react-native-gesture-handler: ~2.16.1 => 2.16.2 
    react-native-get-random-values: ^1.11.0 => 1.11.0 
    react-native-paper: 4.12.8 => 4.12.8 
    react-native-progress: ^5.0.1 => 5.0.1 
    react-native-reanimated: ~3.10.1 => 3.10.1 
    react-native-safe-area-context: 4.10.5 => 4.10.5 
    react-native-screens: 3.31.1 => 3.31.1 
    react-native-url-polyfill: ^2.0.0 => 2.0.0 
    react-native-worklets-core: ^1.5.0 => 1.5.0 
    tsx: ^4.19.1 => 4.19.2 
    typescript: ~5.3.3 => 5.3.3 (4.4.4, 4.9.5)
    uuid: ^9.0.0 => 9.0.1 (8.3.2, 7.0.3)
  npmGlobalPackages:
    @aws-amplify/cli: 12.13.0
    aws-cdk: 2.167.1
    eas-cli: 13.2.3
    expo-cli: 6.3.10
    n: 9.2.0
    npm: 10.8.2
    sharp-cli: 4.1.1
    ts-node: 10.9.1
    typescript: 4.7.4
    yarn: 1.22.22


Describe the bug

My userpool is setup to have users login with their email. When I add apple as an external provider, scope email, map email, I can login fine the first time; however, the email does not get stored in cognito userpool so when I log out I cannot log back in.

Google as an external provider works as expected.
Regular sign up and login with email works as expected.

In summary, sub gets created and Apple adds identities attribute to the userpool, but no email.

export const auth = defineAuth({
  loginWith: {
    email: true,
    externalProviders: {
      google: {
        clientId: secret('GOOGLE_CLIENT_ID'),
        clientSecret: secret('GOOGLE_CLIENT_SECRET'),
        scopes: ['email'],
        attributeMapping: {
          email: 'email'
        }
      },
      signInWithApple: {
        clientId: secret('SIWA_CLIENT_ID'),
        keyId: secret('SIWA_KEY_ID'),
        privateKey: secret('SIWA_PRIVATE_KEY'),
        teamId: secret('SIWA_TEAM_ID'),
        scopes: ['email'],
        attributeMapping: {
          email: 'email'
        }
      },
...
}
)

Expected behavior

Expected signInWithApple to add the scoped email or at least a placeholder to cognito email attribute -or, to have the initial signup/login fail if email truly isn't available.

Reproduction steps

  1. Create an expo project add latest amplify Gen2,
  2. Add auth with external providers and make email required,
  3. Create respective apple identifier and key,
  4. Add button to mobile app "Continue with Apple" that executes the signInWithRedirect("Apple")
  5. Check your userpool. The user gets created, but with no email.
  6. Log out
  7. Try logging back in.

Code Snippet

Screenshot 2024-11-17 at 7 59 10 AM

Log output

{"code": undefined, "fullError": [OAuthSignInException: attributes required: [email]], "message": "attributes required: [email]", "name": "OAuthSignInException"}

aws-exports.js

{
  "auth": {
    "user_pool_id": "us-east-2_XXXXXXXXX",
    "aws_region": "us-east-2",
    "user_pool_client_id": "XXXXXXXXXXXXXX",
    "identity_pool_id": "us-east-2:XXXXXXXXX",
    "mfa_methods": [],
    "standard_required_attributes": [
      "email"
    ],
    "username_attributes": [
      "email"
    ],
    "user_verification_types": [
      "email"
    ],
    "groups": [],
    "mfa_configuration": "NONE",
    "password_policy": {
      "min_length": 8,
      "require_lowercase": true,
      "require_numbers": true,
      "require_symbols": true,
      "require_uppercase": true
    },
    "oauth": {
      "identity_providers": [
        "GOOGLE",
        "SIGN_IN_WITH_APPLE"
      ],
      "redirect_sign_in_uri": [
        "myapp://profile"
      ],
      "redirect_sign_out_uri": [
        "myapp://"
      ],
      "response_type": "code",
      "scopes": [
        "phone",
        "email",
        "openid",
        "profile",
        "aws.cognito.signin.user.admin"
      ],
      "domain": "mydomain.auth.us-east-2.amazoncognito.com"
    },
    "unauthenticated_identities_enabled": true
  },
  "version": "1.3"
}

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

@github-actions github-actions bot added pending-triage Issue is pending triage pending-maintainer-response Issue is pending a response from the Amplify team. labels Nov 17, 2024
@cwomack cwomack added the Auth Related to Auth components/category label Nov 18, 2024
@cwomack cwomack self-assigned this Nov 18, 2024
@cwomack cwomack added the React Native React Native related issue label Nov 18, 2024
@cwomack
Copy link
Member

cwomack commented Nov 18, 2024

Hello, @JustinHaut 👋 and thanks for opening this issue. It looks like this might be the result of making the email attribute required within the user pool for your authentication flow (see here). This can cause an error to be thrown when attempting to sign in with an external provider like Apple. There's more context on some of these differences between social providers here.

Unfortunately, this also means that you may need to create a new user pool that does NOT have the email attribute set to be required (since required attributes cannot be updated in Cognito at this time). Was this a new app that had a small amount of users or (if you had to start over) would you need to migrate the existing users?

@cwomack cwomack added question General question pending-community-response Issue is pending a response from the author or community. and removed pending-triage Issue is pending triage pending-maintainer-response Issue is pending a response from the Amplify team. labels Nov 18, 2024
@JustinHaut
Copy link
Author

Hi @cwomack! 👋 thanks for the quick response! Small amount of users but if I create a new userpool and switch to username then there would be no verify email which is a nice filter. 😕 On the flip side I could eliminate email entirely, but that would exclude non gmail, apple, amazon, facebook users. What would you suggest here? Easiest option would be eliminate apple as an external provider.

@github-actions github-actions bot added pending-maintainer-response Issue is pending a response from the Amplify team. and removed pending-community-response Issue is pending a response from the author or community. labels Nov 18, 2024
@JustinHaut
Copy link
Author

Any chance AWS Amplify can add a placeholder when Apple keeps a user's email private? Particularly since Cognito doesn't require a verified email when using external providers it seems like that could be a work around.

I tried to add a second userpool and then would have conditionally configured Amplify based on login type, but only one userpool is allowed.

Here's the guideline Apple sent in the review which basically says they want you to use Apple; while recommended it isn't mandatory at this time 😬 Concerning for future releases though.

Guideline 4.8 - Design - Login Services

The app uses a third-party login service, but does not appear to offer an equivalent login option with the following features:

  • The login option limits data collection to the user’s name and email address.

  • The login option allows users to keep their email address private as part of setting up their account.

  • The login option does not collect interactions with the app for advertising purposes without consent.

@cwomack
Copy link
Member

cwomack commented Nov 20, 2024

@JustinHaut after reviewing this issue further, I think we need to update the docs to be more clear about this edge case when using Apple as a federated auth provider with Amplify. The suggestion to use a placeholder would likely be a breaking change to the Auth flows here, and I think the better way to ensure future developers don't run into this is to be more clear in our docs.

As for how you handle the Cognito User Pool from here, we'd suggest creating a new one (which is frustrating, sorry) to ensure the email attribute is NOT required to ensure there's no issues with signInWithApple as an externalProvider. If you don't mind eliminating Apple as a provider, then you can simply keep the existing User Pool.

@github-actions github-actions bot removed the pending-maintainer-response Issue is pending a response from the Amplify team. label Nov 20, 2024
@cwomack cwomack added feature-request Request a new feature documentation Related to documentation feature requests and removed question General question React Native React Native related issue labels Nov 20, 2024
@JustinHaut
Copy link
Author

@cwomack thanks, the docs state that we can't just use a username, which wouldn't be ideal anyway, so that leaves phone..
...think you can get Apple to send a placeholder email when they keep a user's email private?

Otherwise if that's not possible, are you at AWS able to do a regex check on email before creating a user in the userpool and then we could have the Apple signup fail so we can at least have users signup/login another way? Right now signUpWithApple simply logs the user in for the first time with no problems... until the user logs out and can't get back in.

@github-actions github-actions bot added the pending-maintainer-response Issue is pending a response from the Amplify team. label Nov 20, 2024
@JustinHaut
Copy link
Author

@cwomack when there's a will there's a way 🚀

Attach this post confirmation lambda trigger and make sure it has proper permissions.

import { CognitoIdentityProviderClient, AdminUpdateUserAttributesCommand } from "@aws-sdk/client-cognito-identity-provider";

export const handler = async (event) => {
  console.log('PostConfirmation event:', JSON.stringify(event, null, 2));

  try {
    // Check if it's an Apple Sign In by checking the username prefix
    if (event.triggerSource === "PostConfirmation_ConfirmSignUp" && 
        event.userName.startsWith('signinwithapple_')) {
      
      // Parse identities to check if it's Apple and no email
      const identities = JSON.parse(event.request.userAttributes.identities);
      const appleIdentity = identities.find(id => id.providerName === "SignInWithApple");
      
      if (appleIdentity && !event.request.userAttributes.email) {
        // Extract Apple sub ID from the identity
        const appleSubId = appleIdentity.userId;
        const placeholderDomain = 'apple-placeholder.com';
        
        // Generate placeholder email using their Apple sub
        const placeholderEmail = `apple_${appleSubId}@${placeholderDomain}`;

        // Initialize the Cognito client
        const client = new CognitoIdentityProviderClient({ region: event.region });
        
        const command = new AdminUpdateUserAttributesCommand({
          UserPoolId: event.userPoolId,
          Username: event.userName,
          UserAttributes: [
            {
              Name: 'email',
              Value: placeholderEmail
            },
            {
              Name: 'email_verified',
              Value: 'true'
            }
          ]
        });

        await client.send(command);
        
        console.log('Added placeholder email:', placeholderEmail);
      }
    }

    return event;
  } catch (error) {
    console.error('Error in PostConfirmation Lambda:', error);
    throw error;
  }
};

@github-actions github-actions bot added pending-maintainer-response Issue is pending a response from the Amplify team. and removed pending-maintainer-response Issue is pending a response from the Amplify team. labels Nov 21, 2024
@JustinHaut
Copy link
Author

Looks like they added privaterelay as a placeholder! No post confirmation trigger needed.

@cwomack
Copy link
Member

cwomack commented Nov 25, 2024

@JustinHaut, appreciate you following up here with the additional code and context. Glad you're unblocked!

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 documentation Related to documentation feature requests feature-request Request a new feature
Projects
None yet
Development

No branches or pull requests

2 participants