Once a user's token expires (either because of the expiration date or because the app was removed from the FB apps list) any graph request generates an error (in my case error 190) because of the invalid token.
If the user tries to log in again I get a new token but that token does not get stored in the parse User table. For the login I use:
PFFacebookUtils.logInInBackground(withReadPermissions: permissions) {(returnedUser, error) in
I'm expecting Parse SDK to update the AuthData entry for that user with the new token and expiration date but that's not happening. Any idea why is that?
Related
Scenario :
User should not be logged out once tokens expired .
Apple sign up steps :
Successfully validated the authorization code and got a successful response
{ "access_token" : "",,"refresh_token" : "",expires_in: ""}
Successfully validated the refresh_token obtained from above step and generated a new access token using POST call to https://appleid.apple.com/auth/token
Problem:
How generate user data,id_token from the new access token ?
There is no UserInfo API in Apple's ecosystem now.
Their access tokens are useless at all.
The only way to get user's display name is receiving "user" json object at callback url at the first time authorization.
For email, you can get it in id_token too.
I got the temporary access token, serverAuthCode, web client id and its secret, i am trying to get refresh token from oathplayground but i am getting invalid grant error. I have rechecked the tokens many times but still getting same error. Here are the screenshots :
What can i do to get the refresh tokens ? For more information, i am using react-native-google-sign-in library, from this library, i got the 1. access_token, ServerAuthCode. From Google Developers console, i got the 1.Web Client Id 2.Client Secret , I am using these credentials inside the react native app and i am getting the tokens successfully, but the problem is sometimes they work and most of the times i get "invalid credentials" error when i try to use the access token, it is because the access token is expired, that's why i need the refresh token. If you have any idea why i am not able to get refresh token or how to get refresh token using another method, please let me know.
I understood what i was doing wrong, i was using the serverAuthCode that was not new, it should be the latest one + the first time when you allowed the app for permissions, and it will work only first time, after you have exchanged it for a refresh token, it will not work again and will always give you grant_error. So after getting the refresh token you can get a new refreshed access token. I was using react-native-google-sign-in and was in need of refreshed token, read below to know more about the same.
For React-native-google-sign-in :
The access token you will receive from GoogleSignin.getTokens(); will only last for about an hour, to get a new access_token we need to send serverAuthCode to https://oauth2.googleapis.com/token with fields : client_id, client_secret, code(this is serverAuthCode), grant_type(its value should be authorization_code), redirect_uri(can set it from developers console). Remember to only use the serverAuthCode that you get on your first attempt when you just allowed your app for the permissions FIRST TIME otherwise you will get grant_error every time. After getting the refresh_token, we need to get the new access_token using refresh token that we just got, so now just replace the value of grant_type from authorization_code to refresh_token and also replace the value of code field to refresh_token , fill its value and send a post request to the same url, you will get a fresh access_tokenthat will be valid for 1 hour.
I followed this tutorial EXACTLY, however
#app.route('/test')
def test_api_request():
if 'credentials' not in flask.session:
return flask.redirect('authorize')
# Load credentials from the session.
credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials'])
drive = googleapiclient.discovery.build(
API_SERVICE_NAME, API_VERSION, credentials=credentials)
files = drive.files().list().execute()
# Save credentials back to session in case access token was refreshed.
# ACTION ITEM: In a production app, you likely want to save these
# credentials in a persistent database instead.
flask.session['credentials'] = credentials_to_dict(credentials)
return flask.jsonify(**files)
However in this part:
credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials'])
The refresh token expires after an hour, with this error:
The credentials do not contain the necessary fields need to refresh the access token. You must specify refresh_token, token_uri, client_id, and client_secret.
But clearly in the flask session the dict object is there:
{'client_id': '<COMMENTED_OUT>.apps.googleusercontent.com',
'client_secret': '<COMMENTED_OUT>',
'refresh_token': None,
'scopes': ['https://spreadsheets.google.com/feeds',
'https://www.googleapis.com/auth/drive',
'https://mail.google.com/'],
'token': '<COMMENTED_OUT>',
'token_uri': 'https://oauth2.googleapis.com/token'}
I believe the google tutorial auto-refreshes the token
Two questions
1) Do i need to manually "refresh" the refresh token? The comment in the tutorial says "Save credentials back to session in case access token was refreshed".. which implies that it's refreshed automatically
2) Is this because the app is still in unverified status?
Looking at the dict, the refresh token is missing:
'refresh_token': None,
You need this token in order to refresh your access token after it expires. The refresh token is only provided in the JSON response if the user saw a consent screen (the one that lists the scopes being requested). If the user has previously approved access, and the scopes haven't changed, the OAuth flow will skip that screen if the user is sent back into the flow, and therefore not return the refresh token.
What likely happened is that during your testing you approved access once, but didn't store the refresh token correctly. Further attempts to approve access didn't return the refresh token, hence your inability to refresh the access token.
To ensure a refresh token is always returned, set the URL parameter prompt=consent in the authorization URL:
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true'
prompt='consent')
(It's documented in the "HTTP/REST" tab here).
Alternatively, visit and revoke access to your application. The next time you go through the OAuth flow you should see the consent screen again, and get a new refresh token.
I am getting the FCM first time and save it to my userDefaults. Now When user logs out, how can I refresh the FCM token again? I have searched the docs and many other asked questions, but didn't find a better solution.
Thanks in advance.
The FCM Token is an Instance ID token, it represents the installed app and not the signed in user. Generally once the app remains installed it will have the same token no matter what user is signed in.
You would have to manage what user is associated to the token yourself. When the user signs in you should associate the token with the user's ID and when the user signs out you should remove that association.
To get a new refreshed FCM token (forcefully), first you have to delete it and then request for FCM token again. It will always provide a new token after once deletion.
To delete a saved token:
FirebaseMessaging.getInstance().deleteToken()
To request a FCM token:
Firebase.messaging.isAutoInitEnabled = true
// Get token
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
//On token fetch fail
if (!task.isSuccessful) {
//msg_token_failed
Log.e("Token failed", task.exception)
return#OnCompleteListener
}
// Get new Instance ID token
val newDeviceToken = task.result
Log.e("newDeviceToken", newDeviceToken)
})
So, in your case you can delete FCM token on logout and request it again on login. It will work.
Check this post:
Firebase Cloud Messaging - Handling logout
Mentioned security issue in #Arthur's comment is solved!
Quick summary... Here's a pseudo-flow to show what I'm trying to accomplish...
Client (angular) -> Server (webapi/WIF)
Log in with username and pwd -> Validate login credentials and return bearer token with claims
Change selected client -> Send new bearer token with updated claims based on selected client
When changing selected client, the existing bearer token will of course be sent, so the server can use that to know who I am and verify I can change to the selected client before generating a new bearer token to return
More detail...
I am using the default code generated by WIF to log users in and return a bearer token that is then sent with every request from my app (angular). I call the TOKEN endpoint with my username and password, GrantResourceOwnerCredentials is called, the user is authenticated, the claims are set, and the access_token is returned. All well and good.
Here's the scenario I don't know how to handle. When the user is logged in, they have the claims for the default client to which they are assigned. However, they can change the client they're accessing at any time while logged in, but each client comes with its own set of claims, meaning that the bearer token needs to be replaced with one for the selected client.
I keep thinking there's a "public string ReturnNewBearerToken(AuthenticationTicket ticket)" function somewhere that would let me get a new token and return it as a string. But I've scoured the web and can't find anything to do this. Everything I've seen suggests that you have to log out and log back in to update your token, which I can't believe is the case. I can't know what client the user wants to access the app as until the log in, but once they're logged in I can't update the token? That would be a catch 22.
So, does anyone know how to do this with WIF?
The answer is in my comments above. The jist is that the line...
var tokenStr = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
is what I needed to be able to return an encoded ticket as a bearer token to my angular app.