Logout launching Apple Sign In process
hangindan opened this issue Β· 55 comments
I'm testing this on a simulator with iOS 13.2.2
The Apple Sign In process is working (with Firebase). However when I call the Logout process the Apple Sign In dialog opens and must be canceled to complete logout process.
My logout code:
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGOUT
})
.catch((error) => {
console.log("Caught logout error..", error)
})
//Sign out of Firebase
When called the Apple dialog below is shown. The actual Sign out of Firebase code is not executed until user hits cancel on Apple dialog.
Not sure how to logout without opening dialog?
Thanks for quick reply. Noticed that as soon as I hit send. I'll give that a try.
I tried passing in user value (as well as with nonce) but I still get the Apple Sign In dialogue. Though it might have been related to simulator but just got same result on real device. Verified user matched response from login. Guessing it's just something simple I'm doing wrong, but haven't found it yet.
const requestOptions = {
user: appleUserID, //user string
requestedOperation: AppleAuthRequestOperation.LOGOUT
};
//re-launches dialog until user cancels
const appleAuthRequestResponse = await appleAuth.performRequest(requestOptions)
.catch((error) => {
return dispatch(logOutFailure(customError(error)))
})
I confirm this locally - as per the docs https://github.com/invertase/react-native-apple-authentication/blob/master/README.md#4-implement-the-logout-process it pops the sign-in dialogue
Experimenting with sending in the user just to see, but at minimum it's a docs issue
Confirmed even when sending user in:
try {
console.log('UserStore::logout calling apple logout');
if (AppleAuth.isSupported) {
console.log(
'UserStore::logout - user currently looks like: ',
JSON.stringify(firebase.auth().currentUser, null, 2)
);
if (firebase.auth().currentUser?.providerData) {
for (let i = 0; i < firebase.auth().currentUser?.providerData!.length; i++) {
if (firebase.auth().currentUser?.providerData[i].providerId === 'apple.com') {
console.log('UserStore::logout - found apple.com provider, trying logout');
const appleAuthRequestResponse = await AppleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGOUT,
user: firebase.auth().currentUser?.providerData[i].uid,
});
// get current authentication state for user
const credentialState = await AppleAuth.getCredentialStateForUser(
appleAuthRequestResponse.user
);
// use credentialState response to ensure the user credential's have been revoked
if (credentialState === AppleAuthCredentialState.REVOKED) {
console.log('UserStore::logout - apple credential successfully revoked');
}
}
}
}
}
} catch (e) {
console.log('UserStore::logout - error from apple logout', e);
}
I see this in console (redacted) then the login pops:
UserStore::logout calling apple logout
LOG UserStore::logout - user currently looks like: {
"phoneNumber": "+x",
"isAnonymous": false,
"email": "kdev1@xxxxxxx.net",
"refreshToken": "xxxxxxx-xxx-xxxx-xxxxx",
"metadata": {
"creationTime": 1568260253456,
"lastSignInTime": 1576134039839
},
"uid": "xxxxxx",
"photoURL": "https://platform-lookaside.fbsbx.com/platform/profilepic/?asid=xxxxxx&height=100&width=100&ext=1577401961&hash=xxxx",
"providerId": "firebase",
"emailVerified": true,
"providerData": [
{
"email": "kdev1@xxxx.net",
"providerId": "password",
"uid": "kdev1@xxxx.net",
"photoURL": "https://platform-lookaside.fbsbx.com/platform/profilepic/?asid=xxxx&height=100&width=100&ext=1577401961&hash=xxxx",
"displayName": "xxx xxx"
},
{
"email": "mike@xxxx.net",
"providerId": "google.com",
"uid": "xxxx",
"photoURL": "https://lh3.googleusercontent.com/a-/xxxxx=s96-c",
"displayName": "Mike Hardy"
},
{
"providerId": "phone",
"phoneNumber": "+xxxx"
},
{
"providerId": "apple.com",
"uid": "000420.xxxxxx",
"email": "apple@xxxxxx.net"
}
],
"displayName": "xxxx Hardy"
}
LOG UserStore::logout - found apple.com provider, trying logout
So I think I'm doing everything correctly and I know I'm sending the user in, but it's not working for some reason?
Quick question, is it still emitting a credential revoked event? Wondering if it is revoking correctly but maybe because there's default scopes that its trigging the sign-in flow again
Not 100% but I had it on my list to alter the example to show this behavior, and to contrast current implementation with expo implementation to see how things went.
When I do that I'll check event emission as well
I received the credential revoked event.
I attempted to send requestedScopes: []
into the logout operation and that didn't help
Anything else I could try?
The logout operation enum is '3', can be seen here in JS:
https://github.com/invertase/react-native-apple-authentication/blob/master/lib/index.js#L35 and converted here to a native ASAuthorizationOperation
enum in ObjC: https://github.com/invertase/react-native-apple-authentication/blob/master/ios/RNAppleAuthentication/RCTConvert%2BASAuthorizationAppleIDRequest.m#L61
Perhaps double checking those are coming through correctly, but I'm pretty sure they are. Only other thing I can think of is commenting out this block on the logout request and seeing what happens, perhaps if the requested scopes property is mutated it assumes login π€·ββ : https://github.com/invertase/react-native-apple-authentication/blob/master/ios/RNAppleAuthentication/RCTConvert%2BASAuthorizationAppleIDRequest.m#L30-L34
Looked at this today and no matter what I tried it always shows the signin flow, even with a raw obj-c app, so I think this is a bug in Apple Auth, unless we're missing anything obvious
I've change the code and tried to logout with this native Xcode project from apple but it kept prompting the login screen. So I
don't think the issue here related to this library.
Okay, so - anyone find or create an upstream issue :-)? I know I could do it myself but my dance card is pretty full at the moment with guests+travel over the holidays
Something weird is happening here, anyone figured it out? What's happening in my case is that LOGOUT is actually never fired, and here's the snippet:
try {
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGOUT
});
const credentialState = await appleAuth.getCredentialStateForUser(
appleAuthRequestResponse.user
);
console.log("Credential state ", credentialState);
if (credentialState === AppleAuthCredentialState.REVOKED) {
console.log("User is unauthenticated");
}
} catch (appleLogoutError) {
console.warn("Apple logout error: ", appleLogoutError);
}
This code just triggers the sign in process, and a result, the credential state is 1, which is LOGIN.
I hope someone will figure out this issue..
I am having the same issue, sign out always fires sign in process.
Correct there has been no progress here. Anyone that has time to follow next steps would win a pile of internet points
- try the alternative implementation (and check their issue list) at https://github.com/expo/expo/tree/master/packages/expo-apple-authentication to see if they have the same issue
- if they have the same problem, look upstream (to be specific: apple developer support) to see if they have some issue being tracked related to this, and if not, open one to have them check it
I am having the same issue, sign out always fires sign in process. :(
@lucasferreiraestevam great! Matches exactly what we know here. How did it go when you tried:
- try the alternative implementation (and check their issue list) at expo/expo:packages/expo-apple-authentication@master to see if they have the same issue
- if they have the same problem, look upstream (to be specific: apple developer support) to see if they have some issue being tracked related to this, and if not, open one to have them check it
Same issue here with logout, hope it gets addressed soon!
@kevwang19 me too! How did it go when you contacted apple about it?
Well for now I'm trying to work around it by using asyncstorage and my own server to handle the authentication logic. I don't know how the native stuff works, so unfortunately not sure if I can ask a meaningful question to Apple since I don't know what's going on under the hood of this library.
@lucasferreiraestevam great! Matches exactly what we know here. How did it go when you tried:
- try the alternative implementation (and check their issue list) at expo/expo:packages/expo-apple-authentication@master to see if they have the same issue
- if they have the same problem, look upstream (to be specific: apple developer support) to see if they have some issue being tracked related to this, and if not, open one to have them check it
-- implemented but to no avail :(
@lucasferreiraestevam just to be clear - you tried the expo implementation and had the exact same issue? If so, excellent! In the sense that we have likely eliminated implemenation as the problem, and it's an apple sign-in issue, so next step is to contact apple support. I am working on them with about different (after you enable apple sign-in you can't transfer an app) so I'm at my personal π© limit with apple support and will not be civil if I do it, so hopefully someone else has time :-)
Any updates on this? I'm looking to go live here pretty soon and would love to know if anyone has an update on what to expect as far as a timeline goes. Thanks!!
so next step is to contact apple support.
Current status. Please give it a shot and report back
same issue. What about logging out from app without apple request i.e just remove user data?
@Desintegrator great you've found this issue! Have you tried your suggestion? Do you have a request in with Apple for clarification and/or any status on it? That would move this forward
@Desintegrator great you've found this issue! Have you tried your suggestion? Do you have a request in with Apple for clarification and/or any status on it? That would move this forward
I didn't contact apple support. I just make logging out process without appleAuth usage. It seems like it works. But I'm not sure if Apple likes it.
@Desintegrator great you've found this issue! Have you tried your suggestion? Do you have a request in with Apple for clarification and/or any status on it? That would move this forward
I didn't contact apple support. I just make logging out process without appleAuth usage. It seems like it works. But I'm not sure if Apple likes it.
@Desintegrator I was able to pass the review process using this approach.
I can also confirm passing the review process with this, but no one has yet put in the time to get to the bottom of it unfortunately. Hopefully some kind soul will take it up with Apple officially - I'm stretched at the moment with module maintenance and don't have time myself.
I have tried out the example app available on the Implementing User Authentication with Sign in with Apple - Sample Code page, and I also have experienced this issue:
I have submitted an issue on apple's https://feedbackassistant.apple.com site, and will report if I hear back.
@mikehardy let's see if Apple replies to my issue. It's my second "apple sign in" issue, the first one I've yet to hear back from and it was submitted in december 2019.
@trev91 @Desintegrator could you two also submit feedback to apple on their feedback assistant site? Maybe it will help put this on their radar.
Any news on that issue?
They seem to be avoiding this entirely in their demo app.
Their code just clears the keychain and puts you back in the home page.
(You need to test this on an actual device, the simulator works differently)
However, when you go back to the login page, the user's info (name, email) are null, because the user is already signed in.
Edit If anyone is interested in more research
Added the following snippet as a logout function to their test project in signOutButtonPressed
https://gist.github.com/ajw-m/5026597e77524b2d07cfd32446b6f8cc#file-logout-swift
It also pops up the login dialog
No news. If you are invested in a fix, please file a bug in appleβs feedback assistant.
Got a response from Apple.
`Okay. This behavior is by design; Sign in with Apple provides the mechanism to authorize a user, with their Apple ID, for a particular application. To remove access to an application, the user must manually revoke their credentials via the following support pageβ
https://support.apple.com/en-us/HT210426
Additionally, if you are trying to implement Sign in with Apple in your native application, please see the follow sample code project for additional informationβ
Furthermore, you can check user credentials while your application is running, by either of the followingβ
- ASAuthorizationAppleIDProvider.getCredentialState(forUserID:completion:)
- NSNotification.Name.credentialRevokedNotification
Both of which can be used to update your application state based on the current credential state of the user.
Note: If the user is no longer authorized, simply log the user out of your application and/or server. If the user decides to authorize your app in the future, you may reuse the same information as before.
Please let me know if you have any further questions regarding Sign in with Apple.`
π€ did that actually answer the question? I miss things all the time, but it did not seem like it answered the question.
π€ did that actually answer the question? I miss things all the time, but it did not seem like it answered the question.
Didn't answer, I replied to their email with more questions. Just keeping everyone in the loop π
Update:
Their library does not support "logout".
User has to manually "logout" from their settings.
Feel free to send me any specific questions you might have, I'll try to forward them to Apple
We are trying to implement this feature in our app ATM, we have't implemented this yet, so there is good chance I am completely wrong about this. Nonetheless, here are my 2 cents:
TL;DR;
I think, for the most apps, there is no need for "Apple Logout". As couple of you already mentioned above this is fine with Apple - apps have passed review process, and I think this is intended
"Apple Logout" being:
await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGOUT
})
More info
Here is how I understood the process:
- When you do Sign in with Apple, they create Special Apple Account (let's call it SAA) just for your app and this account, most importantly, has:
id
(from reading the docs I think it isuser
),fullName
andemail
- You have to somehow/somewhere store this info (in their example it is stored in
KeyChain
) - now you are "in the app" ... you do some stuff, and decide to Log Out
- Following Apples's example, they simply delete data stored in
KeyChain
// For the purpose of this demo app, delete the user identifier that was previously stored in the keychain.
KeychainItem.deleteUserIdentifierFromKeychain()
// Clear the user interface.
userIdentifierLabel.text = ""
givenNameLabel.text = ""
familyNameLabel.text = ""
emailLabel.text = ""
// Display the login controller again.
DispatchQueue.main.async {
self.showLoginViewController()
}
And that's it :)
What your app should do, is to handle Logout as if it would, if there was no "Sign in with apple" (and if you store SAA id
delete this as well)
In our app, we consider user to be logged in if we have userToken
and userId
so we would delete those from memory / AsyncStorage
, as well as SAA id
- Now let's say, user exits the app, they should again be presented with Login Screen (note in 4. we did not do "Apple logout", but w did Logout user).
How does Apple example do this check ?
let appleIDProvider = ASAuthorizationAppleIDProvider()
appleIDProvider.getCredentialState(forUserID: KeychainItem.currentUserIdentifier) { (credentialState, error) in
switch credentialState {
case .authorized:
break // The Apple ID credential is valid.
case .revoked, .notFound:
// The Apple ID credential is either revoked or was not found, so show the sign-in UI.
DispatchQueue.main.async {
self.window?.rootViewController?.showLoginViewController()
}
default:
break
}
}
So if I am following logic correctly, KeychainItem.currentUserIdentifier
is empty, and appleIDProvider.getCredentialState
will result in case .revoked, .notFound
I suspect that we'll have something similar in our app, meaning that we (already do) check if there is userToken
&& userId
, if not, user is logged out (I think for us, there will be no ned to perform appleIDProvider.getCredentialState
check in this scenario)
Logged in check
This one is simple,
Apple example:
KeychainItem.currentUserIdentifier
won't return empty, and calling appleIDProvider.getCredentialState
returns .authorized
.
For our app we'll probably firstly check for userId
, token
and if appleIDProvider.getCredentialState
returns .authorized
., if all 3 are true, user is logged in :)
And if you "Stop app from using Apple ID" - this would be as if your SAA was destroyed, and next time when you enter the app and try Apple Login, you would be getting new SAA and new user in the app.
What's more, even after "Apple logout", as other have reported I still get AUTHORIZED
status, which goes in favour to my theory that apps should NOT use:
await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGOUT
})
So why even have ASAuthorizationOperationLogout
My GUESS is that Apple wanted aligned interface between:
- Sign in With Apple and
- SSO (single sign-on)
In their AuthenticationServices framework
In the docs about AppleAuthRequestOperation it is stated that this is used both for AppleId and SSO
Use one of these values as the requestedOperation property in an OpenID request that you make with an instance of either ASAuthorizationAppleIDRequest or ASAuthorizationSingleSignOnRequest.
Unfortunately, there is no special info about LOGUT
:
An operation that ends an authenticated session.
Whatever that actually means π€·ββοΈ
If all my speculations & conclusions are correct, I think that it would be best to update this modules docs (point 4, regarding logout), so that using;
await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGOUT
})
Is NOT encouraged, but custom logout solution is
Finally
I cannot stress this enough:
- I am by no stretch of imagination "expert" in this topic
- I haven't implemented this in our app
- I have no knowledge of iOS development
These are just my thoughts/observations after couple of hours docs reading and digging around the net :)
Hope this helps! π€
API not available / upstream "issue" really. Don't do signout, basically. Great information above β¬οΈ
In this scenario we need to update docs. I ll shoot for a PR soon-ish
The only problem with this is that without a "force logout" from the apple's authentication, the next time you log in with apple, the information about the FullName and Email come as null... Until now I had to store the apple id from the first apple login in the server in order for future logins to get the users info. But because of the gdpr I have to delete the users info from the database, that includes the apple id.
In order for the user to create a new account he can't do it through apple authentication since I don't receive the email information of the user (unless the user goes to "Password & Security" from his iphone and "forces" logout).
Is there any update regarding this?
@LMestre14 if you decode the identityToken
JWT you can extract the email out of it. I think this is a recent change. Paste it into JWT.io and you will see the email in the payload.
For the name however there is no solution yet.
The only problem with this is that without a "force logout" from the apple's authentication, the next time you log in with apple, the information about the FullName and Email come as null... Until now I had to store the apple id from the first apple login in the server in order for future logins to get the users info. But because of the gdpr I have to delete the users info from the database, that includes the apple id.
In order for the user to create a new account he can't do it through apple authentication since I don't receive the email information of the user (unless the user goes to "Password & Security" from his iphone and "forces" logout).
Is there any update regarding this?
FWIW there seems to be no solution to this: https://stackoverflow.com/questions/57545635/cannot-get-name-email-with-sign-in-with-apple-on-real-device
Extracting email from JWT only seems to work if it's not a forwarding address: https://developer.apple.com/forums/thread/121496?answerId=421415022#421415022
Good digging - so there will always be the possibility of a new user that is a "returning" user from apple auth perspective that is using a forwarding address so you can't JWT decode. I guess if using apple auth and email is mandatory you'll simply have to have to have a set of input email + confirm email + change email flows.
userToken
Hello @mralj , thanks for the great write up. It is clear to me how to obtain, fullName, email and userId. But what do you mean by userToken? Can do I obtain that to check if the user is logged in as you said? Thanks
userToken
Hello @mralj , thanks for the great write up. It is clear to me how to obtain, fullName, email and userId. But what do you mean by userToken? Can do I obtain that to check if the user is logged in as you said? Thanks
Hi, I phrased that part badly, sorry!
Not sure if anything has changed since we've implemented this (I was shocked to see it was almost 2yrs since my comment on this thread π±), but AFAIK, no you cannot obtain it, at least not from Apple.
The userToken
is simply a token for authentication&authorization. It is generated by our server each time the user registers/logins. If the mobile app doesn't have this token we know a user is not logged in.
On logout, we send a request to the server to delete the token for that particular user from our DB and we delete it from the mobile app's storage.
Hopefully, this answers your question :)
Thank you very much.
Someone implemented working logout on their app?
Anyone having working Logout code on their app?
Apple review need use firstName, lastName, email for auth process. First reg β fullName return, then user Delete Account (it's button need for Apple review), then when i secondary reg, fullName not returned because user not manual delete app from "Apps Using Apple ID".
Not manually, just not implemented by you!