I have communicate with API using retrofit. When the user is log in, I save account to database and when the user go to app next time, I get data from database (so I skipped login screen). Problem is when user's token expires.
How to handle the situation?
in login fragment
PSEUDOCODE
user = ... //get user from database
if(user != null) {
startActivityAccountActivity();
}
//onButtonClick
emailLogin();
Don't go to your "logged in" activity just because you have a token saved, because as you have correctly noticed, it may be invalid. Try authenticating with the API when you get the token, and only go to the "logged in" activity if it indeed worked. Otherwise proceed as if there was no token saved (and remove the expired one from the database).
You should also note that the token may expire when the user is in the "logged in" activity. For example, the user logged in, used the app and then went to another app from the recents screen. A week later he/she returns to your app with the "logged in" activity open, but in the mean time the token has expired and nothing will work. So you should also check if the token still works there, maybe in the onStart() of the activity.
As indramurari said, you can also handle it on the backend if you control it. But keep in mind that it doesn't solve your inherent problem of handling expired tokens, a refresh token may also expire and you are back to square one. If you make refresh tokens not expire you can just make the login tokens not expire. (This paragraph would be a comment on his answer, but I don't have enough reputation.)
It depends on your back end security level. You have two options to handle this situation.
Build some mechanism on back end side so that your server will send some refresh-token along with the user's token at the time of login to Android device. Whenever user's token get expired then request from Android device to your server to obtain new user's token by sending old user's token along with refresh-token. And you can use that new user's token for future. Repeat this whenever user's token get expired.
Note - refresh-token is for validating if it is valid resource who is requesting for a user's token.
You can delete the user account from database and tell user to Re-Login.
Related
If you're trying to request data from an API/Web Service how do you design the login process?
If it is sensitive data, do you send a login request to the server /w username+password, and receive a session-token or similar, or do you send username+password every request?
Assume you do get back a session-token. How do you get a fresh token, without asking the user to reenter their credentials. Do you save username+password on the device?
It is best to use client credentials flow of auth2:
show web page with login page
user enters username + password
page reloads and you get parameters from new page (auth code)
issue token request with auth code retrieved from previous step
save token with refresh token
use refresh token to obtain new token but remember that refresh tokens will have 'refresh_token'
value set to null so you will have to save refresh token retrieved
at the beginning to issue new tokens after they got too old (usually
3600s)
This: http://bshaffer.github.io/oauth2-server-php-docs/grant-types/client-credentials/ and this http://brentertainment.com/oauth2/ will make it easier to understand and implement
I'm using Parse as the backend.
To check if the user is logged in I use ParseUser.getCurrentUser(). What I don't understand is, if the user changes his password from somewhere else (another device, the web-client), will this ParseUser return with some kind of error?
I don't think it does a check on the server, so I think it just returns the last saved user. This mean that I can continue to use this user (with an old password) or will I get a "wrong credential" response on the first request to the servers?
If I don't get it, will I at least get it when setting an ACL with parseObject.setACL(new ParseACL(ParseUser.getCurrentUser()));?
Try same action on yahoo in 2 open browsers of different types and see what you get?
Each client's been handed a token value by the respective servers and until the token expire will not be prompt for a new logon.
Well IMO Parse work very similar except the lease on Parse token never expire.
Response to the original parse logon contain the token value which the SDK may retain. Details are in the docs section on Rest api / user logon...
So, if a diff client change password but the token lease over on some other client never expire, the other client stays logged in.
Some days ago I asked how to retain Box tokens (Load, save and use of authentication data in Box Android API). Now, when user wants to access his Box account I use this code to configure BoxAndroidClient:
client = new BoxAndroidClient(C, S);
client.authenticate(loadAuth()); //loadAuth() returns BoxAndroidOAuthData object
For short period of time after obtaining authentication data it works good. But after an hour or so I get an AuthFatalFailureException:
07-06 17:21:01.841: W/System.err(3647): com.box.boxjavalibv2.exceptions.AuthFatalFailureException
07-06 17:21:01.841: W/System.err(3647): at com.box.boxjavalibv2.authorization.OAuthDataController.doRefresh(OAuthDataController.java:275)
07-06 17:21:01.841: W/System.err(3647): at com.box.boxjavalibv2.authorization.OAuthDataController.refresh(OAuthDataController.java:191)
07-06 17:21:01.841: W/System.err(3647): at org.redscorpio.cloudtest.network.Box$2$1.run(Box.java:71)
Line 71 is
client.getOAuthDataController().refresh();
but it happens every time I need to access Box:
client.getFoldersManager().getFolderItems(current.getId(), LIST_REQUEST()).getEntries();
client.getFoldersManager().getFolder("0", DEFAULT_REQUEST);
I suspect that my token is invalidated at some point, but I don't know why it can't be renewed and why it happens after such a short period of time.
What I can do to prevent this?
I am not exactly sure what's going on. The sdk does auto refresh the token. However every time the token is refreshed, you actually will get a new refresh token and new access token, the old refresh token will not be valid any more. So basically the easiest way probably is update your stored OAuth token object every time your api call succeeds.
In the meantime, can you double check(maybe add some loggings) whether the stored refresh token and access token are the latest ones?
You can logcat out the access token in the code of token refresh:
com.box.boxjavalibv2.authorization.OAuthDataController class, doRefresh() method.
and in the place where api call is made: com.box.boxjavalibv2.authorization.OAuthAuthorization class, getAuthString() method.
When a user logs in and accepts your app's grant, you exchange the authorization_code for an access_token and a refresh_token (response shown below). The reason your token invalidates is that the access_token expires in one hour. You can exchange the refresh_token, which is valid for 14 days, for another one-hour access token. This is why you'll want the app to store both the access_token and the refresh_token, ensuring that a user will only need to re-authenticate if they return to the app after more than 14 days.
Using the refresh_token for another access_token will always return one more of each (refresh + access).
{
"access_token": "T9cE5asGnuyYCCqIZFoWjFHvNbvVqHjl",
"expires_in": 3600,
"token_type": "bearer",
"refresh_token": "J7rxTiWOHMoSC1isKZKBZWizoRXjkQzig5C6jFgCVJ9bUnsUfGMinKBDLZWP9BgR"
}
I was successfully making an FQL query earlier today. Now when I try to make the same query I am getting the following message.
"error_code":190,"error_msg":"Error validating access token: The session has been invalidated because the user has changed the password."
I am making the query on my own account, and my password has not been changed. Any suggestions for why this may be happening?
Now I am getting this error.
"Error validating access token: Session does not match current stored session. This may be because the user changed the password since the time the session was created or Facebook has changed the session for security reasons."
Based on above comments, this will be this issue.
The Single Sign On (SSO) in Android allows a user to authorise your app, and once they have done that they will be signed straight in on subsequent visits if they've signed in to Facebook for any application on the phone/tablet.
The downside of this is that the SSO remembers the token and if the token becomes invalidated (by a user de-authorising the app or by them changing password or other details), it will try to use it anyway. You MUST check for those two errors specifically as "isSessionValid() will return true, even though it's not. If you catch one of those two errors, you call the authorize method again.
So, in your case, trap that error, and call authorize().
Documentation from Facebook: http://developers.facebook.com/docs/mobile/android/build/#sso
I am writing an Android 2.2 app for my company. The app simply sends http get/put/post requests to perform certain operations. There is no real login procedure as the username and password have to be included in each http request.
I could see that there is AccountManager in Android. But since the username and password (instead of some auth token) are needed for each http request, how can it fit in? Obviously, I want to make it similar to other Android apps so that the user only needs to login once for the very first time and it won't prompt for username/password again when the app is re-launched.
Any suggestion is appreciated. Thanks!
I have developed an application like that, so here is how I solved it, in psuedocode.
But since the username and password
(instead of some auth token) are
needed for each http request, how can
it fit in?
1#: Make a first page, a login page. Let this View include two EditTexts (username and password) and one Button (login button).
2#: Make a login request on the Button click to see if you're getting a correct Cookie with HTTP header names that is corresponding with the values you are getting when you're logged in. Locate valid information via a network tool, like WireShark. For more information about the login procedure, check out my other answer here.
3#: If the username and password resulted in correct Cookie information, save the username and password in a SharedPreferences and make their values available through your application by assigning it into an Application class, read this for more info regarding global variables. If the values were incorrect and you did not get a valid Cookie, show it to the user via a message (Toast?).
4#: When you are trying to reach the authorized information, make a request by using the saved information in the Application class.
5#: Next time you're starting your application, make a check in onCreate() where you are checking if SharedPreferences contains user information, if so: see step 6#, otherwise wait for the user to start entering information.
6#: If the login page has determined user information, assign the SharedPreferences to the global Application state, finish the login Activity and start the authorized Activity instead. This will happen very fast, so the user wouldn't notice that the "login page" was displayed.
7# (extra step): In the authorized Activity, make sure to grab the user information from the Application instance. When doing the first request towards authorized content, validate the task as you did in step #3 in order to control if the user has changed the password on the website. If the user hasn't changed any information, start grabbing the response and you are free to do whatever you want.