React-native-apple-authentication: Authenticating via Firebase on Android

Created on 9 Oct 2020  ·  26Comments  ·  Source: invertase/react-native-apple-authentication

Hello there,

I have successfully set up the Firebase login process on iOS but is there a way to do this on Android as well? I tried passing the id_token and the code to AppleAuthProvider.credential() but that resulted in this error:

Something unexpected happened while signing in with Apple NativeFirebaseError: [auth/invalid-credential] The supplied auth credential is malformed, has expired or is not currently supported.

Am I missing something or is it actually not possible at the moment?

Regards

help wanted

Most helpful comment

Hi, I was stuck with the same problem. I got it working like this:

const getRandomString = length => {
  let randomChars =
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  let result = ''
  for (let i = 0; i < length; i++) {
    result += randomChars.charAt(Math.floor(Math.random() * randomChars.length))
  }
  return result
}

export const appleLogin = async () => {
  return Platform.select({
    ios: iosAppleLogin(),
    android: androidAppleLogin(),
  })
}

const iosAppleLogin = async () => {
  const appleAuthRequestResponse = await appleAuth.performRequest({
    requestedOperation: appleAuth.Operation.LOGIN,
    requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
  })
  if (!appleAuthRequestResponse.identityToken) {
    throw 'Apple Sign-In failed - no identify token returned'
  }
  const { identityToken, nonce } = appleAuthRequestResponse
  const appleCredential = auth.AppleAuthProvider.credential(
    identityToken,
    nonce,
  )
  return auth().signInWithCredential(appleCredential)
}
const androidAppleLogin = async () => {
  // Generate secure, random values for state and nonce
  const rawNonce = getRandomString(20)
  const state = getRandomString(20)

  // Configure the request
  appleAuthAndroid.configure({
    clientId: 'THE SAME SERVICE ID AS APPLE DEV AND FIREBASE CONSOLE',
    redirectUri: 'THIS IS THE SAME AS APPLE DEV CONSOLE',
    responseType: appleAuthAndroid.ResponseType.ALL,
    scope: appleAuthAndroid.Scope.ALL,
    nonce: rawNonce,
    state,
  })
  const response = await appleAuthAndroid.signIn()
  if (response.state === state) {
    const credentials = auth.AppleAuthProvider.credential(
      response.id_token,
      rawNonce, // Passing the rawNonce here do the trick.
    )
    return auth().signInWithCredential(credentials)
  }
}

The idea is to generate the nonce ourselves and just passing it to Firebase to let it check if what give Apple is the same as what we give in the nonce.

All 26 comments

I'm not sure anyone has tried it, you might be first! Unfortunately that means you are the current leading edge of knowledge in the area :sweat_smile:

This is definitely one of those issues in between the two libraries but might be best in the react-native-firebase library,

It is supposed to work thought now that I look more deeply:

https://github.com/invertase/react-native-firebase/blob/b84e718f15b9254cffb0202ea956e47014c181a2/packages/auth/android/src/main/java/io/invertase/firebase/auth/ReactNativeFirebaseAuthModule.java#L1400

@dburdan - did you integrate with react-native-firebase for Android Apple auth or are you using something else? If so, do you have it working and it's just an API usage issue here? If not, we'll keep digging

@Sebastian-Neubert the best I can suggest is going into node_modules around the line that I linked there and logging out all the inputs and outputs to the underlying API call to start tracing the data and comparing it vs the assumptions in the code to see where things go off the rails

@mikehardy My implementation was fully bespoke; I'm unfamiliar with Firebase Auth or the RN library, but I'm happy to lend a hand here.

@Sebastian-Neubert Did you pass your nonce value through to Firebase? If I'm not mistaken, I believe Firebase needs just the id_token and nonce.

@dburdan In the iOS implementation I indeed passed the id_token and nonce to Firebase but on Android I don't get a value named nonce from the library. The only available values in the response are: user, state, code and id_token

Have you tried generating the nonce before hand and providing it to both libraries as seen in the Android example? The nonce can be any unique, random set of characters, like a UUID.

I will take a deeper look at the return values on Android to make sure they match iOS.

No, I haven't generated the nonce before hand. The iOS-part of the library does that somehow automatically and provides it for me.

Hi, I was stuck with the same problem. I got it working like this:

const getRandomString = length => {
  let randomChars =
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  let result = ''
  for (let i = 0; i < length; i++) {
    result += randomChars.charAt(Math.floor(Math.random() * randomChars.length))
  }
  return result
}

export const appleLogin = async () => {
  return Platform.select({
    ios: iosAppleLogin(),
    android: androidAppleLogin(),
  })
}

const iosAppleLogin = async () => {
  const appleAuthRequestResponse = await appleAuth.performRequest({
    requestedOperation: appleAuth.Operation.LOGIN,
    requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
  })
  if (!appleAuthRequestResponse.identityToken) {
    throw 'Apple Sign-In failed - no identify token returned'
  }
  const { identityToken, nonce } = appleAuthRequestResponse
  const appleCredential = auth.AppleAuthProvider.credential(
    identityToken,
    nonce,
  )
  return auth().signInWithCredential(appleCredential)
}
const androidAppleLogin = async () => {
  // Generate secure, random values for state and nonce
  const rawNonce = getRandomString(20)
  const state = getRandomString(20)

  // Configure the request
  appleAuthAndroid.configure({
    clientId: 'THE SAME SERVICE ID AS APPLE DEV AND FIREBASE CONSOLE',
    redirectUri: 'THIS IS THE SAME AS APPLE DEV CONSOLE',
    responseType: appleAuthAndroid.ResponseType.ALL,
    scope: appleAuthAndroid.Scope.ALL,
    nonce: rawNonce,
    state,
  })
  const response = await appleAuthAndroid.signIn()
  if (response.state === state) {
    const credentials = auth.AppleAuthProvider.credential(
      response.id_token,
      rawNonce, // Passing the rawNonce here do the trick.
    )
    return auth().signInWithCredential(credentials)
  }
}

The idea is to generate the nonce ourselves and just passing it to Firebase to let it check if what give Apple is the same as what we give in the nonce.

Appears this might a documentation / example issue then, or we could decide to handle it internally for Android. I'd take PRs for either

for iOS we do handle it internally (with the ability to disable) - here's a PR that added the ability to disable nonce for ios if people are interested in seeing all the places nonce is touched https://github.com/invertase/react-native-apple-authentication/pull/52

I submitted a PR (#153) that adds support for automatic nonce generation on Android. While I ran it through various tests, I don't personally use this feature, so additional checks would be appreciated.

Hopefully this provides more parity with the iOS half of the library.

Hi, i'm facing this issue too. I try to go by documentation and by @ghivert answer but without success. Is there someone with working solution for Firebase? 🙏

web login is open, user fill login info and function await appleAuthAndroid.signIn() return object with some data (user, state, code and id_token). But when i try to set this data into auth().signInWithCredential() function then it return error: The supplied auth credential is malformed, has expired or is not currently supported.

I know this is not stackoverflow but...

there is my code and Firebase config:

import { appleAuthAndroid } from '@invertase/react-native-apple-authentication'
import uuid from 'uuid'

const rawNonce = uuid()
const state = uuid()

appleAuthAndroid.configure({
    clientId: 'APPLE_CLIENT_ID',
    redirectUri: 'REDIRECT_CALLBACK',
    responseType: appleAuthAndroid.ResponseType.ALL,
    scope: appleAuthAndroid.Scope.ALL,
    nonce: rawNonce,
    state,
})

const response = await appleAuthAndroid.signIn()

if (response.state === state && response.id_token) {
    const providerData = auth.AppleAuthProvider.credential(
                    response.id_token,
                    rawNonce // or response.code -> without success
    )

    await auth().signInWithCredential(providerData)
} else {
    throw new Error('Apple sign-in error')
}

Screenshot 2020-10-20 at 15 46 45

Packages:
...
"@invertase/react-native-apple-authentication": "^2.0.2",
"@firebase/auth": "^0.13.5",
"react-native": "0.63.2"
...

Hi @alesmraz,

It looks like an issue with Firebase. Are you sure you provide the Apple Service ID in Firebase Dashboard ? You can find it on Apple Developers Dashboard under service section. I had this error at first because I was providing bundle app ID and not service ID.

Edit : are your OAuth settings completed too ?

It still does not work for me as well. I have everything set up correctly (I guess), even the OAuth settings, but I always get the error:

AppleAuth is not supported on the device. Currently Apple Authentication works on iOS devices running iOS 13 or later. Use 'AppleAuth.isSupported' to check...

@mikehardy But why is it not supported in my case? What could be the reasons?

@Sebastian-Neubert it is, that line runs and executes correctly. Note it is on a different imported type

@mikehardy You are right, it is supported. But the login with Firebase does still not work. Even the solution from @ghivert does not work for me.

@ghivert Thanks for advice! I double checked configuration in Firebase and Apple Developers and it's same. I also try hash (SHA256) nonce before go to apple service server but without success. Also function isSupported return true.

OAuth setting is completed. We already has Apple auth on web (not React native) connected to same firebase without any issues. I assume that is work same (please correct me if its wrong) on web and on non-apple devices?

Hum, that’s weird… It should work in the same for web and Android because it uses OAuth to authenticate in a web-browser. In Android, it’s just a matter of handling the redirect URL correctly.

The error The supplied auth credential is malformed, has expired or is not currently supported. seems like an issue with Firebase itself if await appleAuthAndroid.signIn() is working. I think Apple is sending correct informations, but Firebase is not handling them as needed. I had this error when Firebase Auth wasn’t properly configured. I would say that you should double-check Firebase Auth with Apple IDs and Key IDs, and if it really doesn’t work, try to ask the Firebase team directly? If appleAuthAndroid.signIn() is working, then it’s not a bug with react-native-apple-authentication.

@alesmraz any luck? I'm stuck on the same workflow.

@alesmraz
I think it is not a firebase issue. The Apple sign in on iOS also uses firebase's signInWithCredential and it works.
I tried using a predefined nonce like the example above but having the same problem.
Does it have to do something with the id_token we get back as response from await appleAuthAndroid.signIn() ?

Also having the same issue with Android Apple Sign in. (Works with iOS I get the UID from Firebase just fine)

const response = await appleAuthAndroid.signIn();
const { id_token } = response;

const androidCredential = firebase.auth.OAuthProvider.credential(
  id_token,
  rawNonce,
);

const userCredential = await firebase
  .auth()
  .signInWithCredential(androidCredential);

Looks like I get everything I need with androidCredential the:

providerId: string;
token: string;
secret: string;

but the userCredential doesn't return a UID from Firebase like it does Apple, it doesn't return anything nor does it register the user in the Firebase console.

Followed this https://github.com/invertase/react-native-apple-authentication/blob/master/example/app.android.js#L61

This seems like an issue with firebase.auth().signInWithCredential() not with react-native-apple-authentication

I think you have to decode the idtoken - see #160

@mikehardy decode the id_token how? That comment in the issue you shared does not mention that.

Did you try with firebase.auth.AppleAuthProvider.credential instead of firebase.auth.OAuthProvider.credential? I think the AppleAuthProvider does the decoding work for you.

I was stuck with the same problem.

For me adding correct Services ID to firebase apple auth settings solved the issue, it is optional for iOS so I had it empty. I am using auth.AppleAuthProvider.credential and generate nonce manually. Can confirm that it works, if you see apple login page but get the The supplied auth credential is malformed, has expired or is not currently supported. then it is probably some issue with your configs.

Was this page helpful?
0 / 5 - 0 ratings