supabase / supabase-js

An isomorphic Javascript client for Supabase. Query your Supabase database, subscribe to realtime events, upload and download files, browse typescript examples, invoke postgres functions via rpc, invoke supabase edge functions, query pgvector.
https://supabase.com
MIT License
3.26k stars 270 forks source link

Missing User Info (Email & Display Name) in Supabase Auth with Apple Sign-In via signInWithIdToken #1309

Open dhruvbhatia opened 3 days ago

dhruvbhatia commented 3 days ago

Bug report

Apple Authentication via supabase.auth.signInWithIdToken creates a record without the Email or Display name fields in Supabase Auth (despite the user providing these during the Auth process).

This used to work properly a couple of weeks ago, so I believe it's a recent problem with Supabase / Supabase JS / Apple's endpoint.

Describe the bug flow

An iOS user authenticates via Apple Auth and chooses to share their name and email:

Apple Auth Flow

expo-apple-authentication correctly returns this credential object:

{
  "authorizationCode": "<some auth code>",
  "email": "<my email address>",
  "fullName": {
    "familyName": "Doe",
    "givenName": "John",
    "middleName": null,
    "namePrefix": null,
    "nameSuffix": null,
    "nickname": null
  },
  "identityToken": "<JWT token>",
  "realUserStatus": 1,
  "state": null,
  "user": "<user identifier string>"
}

Which is then sent to to Supabase via signInWithIdToken:

supabase.auth.signInWithIdToken({
                            provider: "apple",
                            token: credential.identityToken,
                          })

Supabase signs the user in and the SIGNED_IN auth state is set, as expected.

However, looking at the Supabase Auth UI, the Email and Display name fields are not set: Supabase Auth UI

This is obviously problematic as the scoped values that the user shared are not being populated. To reiterate, this used to work properly in the past so something must have broken it recently. Assistance appreciated.

To Reproduce

  1. Follow the Login with Apple guide for React Native
  2. Attempt to authenticate using an Apple account
  3. Check the Supabase Auth UI and see that the Email and Display name fields are not populated

Here's the relevant code for this:

 try {
            const credential = await AppleAuthentication.signInAsync({
              requestedScopes: [
                AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
                AppleAuthentication.AppleAuthenticationScope.EMAIL,
              ],
            })
            // Sign in via Supabase Auth.
            if (credential.identityToken) {
              const {
                error,
                data: { user },
              } = await supabase.auth.signInWithIdToken({
                provider: 'apple',
                token: credential.identityToken,
              })
              console.log(JSON.stringify({ error, user }, null, 2))
              if (!error) {
                // User is signed in.
              }
            } else {
              throw new Error('No identityToken.')
            }
          } catch (e) {
            if (e.code === 'ERR_REQUEST_CANCELED') {
              // handle that the user canceled the sign-in flow
            } else {
              // handle other errors
            }
          }

Expected behavior

Upon creating a Supabase record for a user authenticating via Apple, the Email and Display name fields should be set (if shared).

System information

Additional context

This may be related to #899, though I didn't see anybody have issues with the Email field not populating there.

dhruvbhatia commented 3 days ago

Looking into this further, I decoded the credential.identityToken JWT token and found it no longer contains an email key (instead, the email is stored as a top level key within the credential object itself - as shown in my original post). Hence when credential.identityToken gets passed to Supabase's signInWithIdToken, it doesn't see any email address (but creates the auth record regardless).

This is what credential.identityToken looks like - note the absence of the email field:

{
  "iss": "https://appleid.apple.com",
  "aud": "com.company.appname",
  "exp": 1731569098,
  "iat": 1731482698,
  "sub": "<some uid of the user>",
  "c_hash": "<some hash>",
  "auth_time": 1731482698,
  "nonce_supported": true
}

So I'm guessing Apple has changed the structure of what the identity token JWT returns (though their documentation still says the email field should be present).

Perhaps Supabase should take the entire credential object rather than just the credential.identityToken JWT token? That way it'll have access to the top level email field (as well as fullName and any other user info shared during auth)...