I have an app which requires users to log in. Logging in is handled via a POST request to my back-end REST API, which generates a new access token if the login details are correct and returns it to the app (which is then saved in the keychain). My question is, how do I handle cases when a user logs in from multiple devices at once? The device who logged in first will not realize that their access token has expired, but will continue to try to send requests which will be denied (because the access token no longer matches the one stored in the database). I have thought about checking that the access token matches the database token in viewDidLoad in my AppDelegate, but this would only work when the app is first loaded. Is there best practice already established for this sort of thing? If so what is it?
The best solution I have thought of so far is to just re-issue the same access token each time the user logs in, but this seems insecure to me. Is this a safe way to handle it?
It's a good practice to issue a new token on each sign-in. Tokens are meant to be expendable, and differentiating between user sessions on different devices and browsers is useful (see Facebook's session management for an example).
There are two possible ways to handle the double login scenario:
Maintain multiple active tokens for each user in your database and invalidate each on sign-out
Allow only one token per user, invalidating the existing token on sign-in
The choice depends on the nature of your application. In the second instance, the first client won't know that its session is invalid until the app makes a network call. At that time, your server should return a custom error to indicate that the token has expired. The app should then alert the user and return them to the sign-in screen.
Related
I build an application that must use delegated AAD permissions to interact with the Graph API. App permissions are no option due to secruity restrictions.
This application is a background service which has some minimal UI for configuration of the application. The idea is to have users log into the UI once (via AAD), granting the required permissions. After that users will mostly never need to use this UI as there is nothing vital to configure.
This creates a situation where the user will only once present a usable access token. After that initial onboarding, the application will have to use a refresh token once in a while to keep being able to use the Graph API.
Is my assumption correct that in order for this to work, I must persist refresh tokens of each user? Intuitively this seems like both a security risk (if not implemented properly) and also a kind of complex system which would not only store tokens but also have additional background refreshing tokens and potentially informing users about eventual expired tokens (in case of failing token refreshs).
Yes, the refresh token you receive back is unique for each user, so you really need to keep the refresh and access token separate between each user and store them in a secure way.
Please also note that in some systems you get a new refresh token each time you use it.
Either you use the refresh token with a timer and refresh them each XX minutes. Or you do the refresh when the user receives a 401 Not Authenticated response from the API.
I am working on an a security module for an application, using google's oauth 2.0 access token using what's being suggested in the section "Obtaining OAuth 2.0 access tokens" on this article :https://developers.google.com/identity/protocols/OAuth2WebServer
I am storing the access_token received as part of the step 5 in the article above, "Step 5: Exchange authorization code for refresh and access tokens".
Lets say that the user goes to any google web app in the browser and logs out, I understand that google's session is invalid at that point that's fine and that is the session which the access_token i have stored was generated for, then, lets say that in that moment the user was logged into my app and my app's session expired so the problem is that if my app's session expires and the user goes to my app, my app will try to validate the access token ( i want to maintain my app's session somewhat align with the google session as long as the google session is alive ) but this service https://www.googleapis.com/oauth2/v2/tokeninfo does not intermediately reflect that the access_token i have in my app is invalid, it takes a lot of minutes to reflect that the user has logged out and the access token is not valid.
Please excuse my long explanation and i expect it is clear.
Now my question is, what google rest/service provides a better validation for the access_token?
I think you have a wrong assumption that an access token (and a refresh token) is bound to the session that was used when authenticating the user. I tried to find some info about it, but didn't succeed. There may be multiple sessions - for example in different browsers. If you log out in one browser, the other stays logged in, so there is probably no "global sign out" that would terminate all sessions of the user.
If the tokens were invalidated on the browser session logout, it would make the applications using the tokens fragile. For example mobile or desktop applications that use a browser just for authentication and don't use to keep the browser window open after successful authentication, so they cannot keep the browser session alive. Their tokens would get invalidated with the session expiration.
Google doesn't seem to provide OpenID Connect session management features in their discovery document, so you cannot monitor the session using iframes either (as described in the RFC).
I am trying to figure out how an access token in OAuth 2.0 should be used. To be more precise, I am trying to use the Google Plus API from a web application.
I am now able to get a code and from it an access token. The problem is that this token is only about 3600 seconds valid.
Is there a way to get another token without making the user go again through this process: https://developers.google.com/accounts/images/consent1.png because it seems a bit irritating. I know of the offline access and its refresh token, but it doesn't feel right to have permanent access to a user's account.
Do you have any ideas on how should I proceed?
I'm definitely not an authority, but I believe the answer is 'no'. The offline token allows you access without subsequent user approval, but only to the scopes to which the user already agreed when authenticating for the first time. Also, the user has the option of revoking your application's access at any time, which when combined with their previous consent means they both a.) know what they're allowing; and b.) can stop it at any time. Ostensibly, if a user uses your app enough that they constantly have to get a new token, they already trust it to act on their behalf within the scope you set, and the offline token is a way for you to take your relationship to the next level :)
I realize this is probably more philosophical than you were looking for, so apologies if it isn't pertinent to your situation.
Facebook's deprecation of the offline_access permission is coming May 2012 and the documentation isn't giving us enough information on how to handle it.
We have an iOS app and corresponding service that powers it and integrates with Facebook in a deep way to leverage a user's friend list within out app (so if your FB friends are also using the app you can more easily connect). This is like how all social apps seem to work, so nothing special here.
Client
Our app uses Facebook iOS SDK to allow user to login, which we currently ask for offline_access. The token is persisted in our iOS app, but also sent to our server where it is saved. The client acts on behalf of user to post updates to a user's newsfeed (we also ask for publish_stream permission).
Server
Our server periodically checks to see if user's FB friends are now using our app. Next time user signs in, we expose content and relationships in a certain way to promote that user's friends. The server also acts on behalf of the user to periodically connect to the graph API and get the user's current friends list. This is so we can account for changes in a user's relationships and have them reflected in our app. We do this when the user isn't currently using the app so they have the best experience the next time they do use it. To enable this, our iOS app sends the access token to our server which it uses and why we ask for offline_access.
Note: If user signs out of our app explicitly, we delete the access tokens from both client and server.
Problems
Now that there is no longer a perpetual access token we can use, I'm trying to figure out the best practice for still enabling our scenarios while leveraging facebook's new intended way of handling and extending access tokens. The documentation is unfortunately not totally helpful.
Questions
A. When you authenticate through the newest Facebook iOS SDK, what is the default lifetime of the access token you get? This document says an extended token request will give you one that lasts 60 days. This other document talks about the first access token request and mentions varying validities but it's unclear and does it talk about specific validity times:
(emphasis is mine)
When you obtain an access token from Facebook, it will be valid
immediately and usable in requests to the API for some time period
defined by Facebook. After that period has elapsed, the access token
is considered to have expired and the user will need to be
authenticated again in order for your app to obtain a fresh access
token. The duration for which a given access token is valid depends on
how it was generated.
There are also events which may cause an access token to become
invalid before its expected expiry time. Such events include the user
changing their password, an application refreshing it's App Secret.
Dealing with varying access token expiry times, and handling the case
when an access token becomes invalid before its expected expiry time
is essential for building robust social experiences.
B. For the client, now that the access token isn't necessarily long lived, is the right approach for us to:
Let use login through FB, then detect whenever the access token is expired. If it is, then call into FB iOS SDK to re-authentication/re-authorize? (this should just trigger user to bounce out to FB iOS app, and in most cases come immediately back to our app with a new access token).
C. According to this blog post I found, you can only extend an access token once:
Can I exchange my 60 day access token for a new 60 day access token?
No, sorry you cannot. You can only exchange a valid (meaning current)
user access token for an extended one. You cannot extend an already
extended access token.
On the client, I can just handle this by prompting a re-authentication/re-authorization as I mentioned in Question B. However, this doesn't work on our server. We could certainly have the server renew it once to 60 days, but what happens on the 61st day? The server just stops being able to sync the friend's list?
D. It seems to make sense to check the validity of the FB access token every time the app starts or re-hydrates from sleep. What is the best way for our iOS app to check this? Is there a recommended endpoint to call to validate a token? Should we just call into https://graph.facebook.com/me passing the access token and checking the response?
Note: we can certainly record the expires time when we get the initially extended token, but this isn't reliable since the user could revoke our app's permission anytime which makes the expires time an unreliable data point on validity
Overview
I believe that the root of what facebook is trying to achieve is to prevent an app from having perpetual ever-lasting access to a user's account. So, with the new migration an app can only access an account for 60 days unless the user signs in again.
I don't work for facebook, but here are my findings from playing around with the facebook graph api.
General Solution
Whenever a user signs in, take their access token and immediately extend/refresh it, and save it
Record the expiration date of the access token
When an access token expires (either from the recorded date, or a graph API exception telling you so), then notify the user that you don't have access, and ask them to sign in again.
Answers
A. When you authenticate through the newest Facebook iOS SDK, what is the default lifetime of the access token you get? This document says an extended token request will give you one that lasts 60 days. This other document talks about the first access token request and mentions varying validities but it's unclear and does it talk about specific validity times:
Here's how it works:
The first sign-in grants you approximately two hours
By refreshing the access token, you can get up to 60 days
If the user doesn't sign in to those 60 days, there is no way to get access for longer without having them sign in.
If the user de-authorizes your app, that 60 day windows ends immediately, and you will no longer have access.
B. For the client, now that the access token isn't necessarily long lived, is the right approach for us to: Let use login through FB, then detect whenever the access token is expired. If it is, then call into FB iOS SDK to re-authentication/re-authorize? (this should just trigger user to bounce out to FB iOS app, and in most cases come immediately back to our app with a new access token).
If the users access token is expired, your only option is to have them go through a login loop like you are talking about.
C. According to this blog post I found, you can only extend an access token once. On the client, I can just handle this by prompting a re-authentication/re-authorization as I mentioned in Question B. However, this doesn't work on our server. We could certainly have the server renew it once to 60 days, but what happens on the 61st day? The server just stops being able to sync the friend's list?
You can only extend an access token once. On the 61st day, you are out of luck. Best notify the user and let them know that unless they sign in, you won't be able to do anything.
D. It seems to make sense to check the validity of the FB access token every time the app starts or re-hydrates from sleep. What is the best way for our iOS app to check this? Is there a recommended endpoint to call to validate a token? Should we just call into https://graph.facebook.com/me passing the access token and checking the response?
I haven't be able to find an API equivalent of the Debug Console. This FB blog article talks about invalidated access tokens, but doesn't mention any API methods in particular meant to test the API.
I your suggestion of hitting https://graph.facebook.com/me would work just fine is exactly what they recommend in their example. In fact, I might use this approach in my app as a pro-active way of checking an access token.
Tid Bits
When you "refresh" an access token, a new access token will be returned. The response looks like: access_token=TOKEN&expires=5183912
You can only "refresh" an access token once. If you try to "refresh" the long-lived token returned from a previous call, it will return the same token, but doesn't throw an exception unless the token has expired. (in other words, you can safely try to refresh your token)
The default access token length seems to be around 2 hours
If you "refresh" an access token, that new access tokens seems to be the one that you'll get from the facebook API afterwards (instead of returning the original, short-lived access token)
Also, if you want to play around, these tools make it easy to test out your use case in a browser before burying it in your code:
Graph API Explorer - For creating and getting access tokens
Debug Console - For checking the expiry date of tokens before/after refresh
Refresh Endpoint - For manually testing extending your tokens
Great answer, one important addition : the default token lasts between 1 and 2 hours. You get the remaining of the hour during which the user signs up, plus 1 full hour. For example if a user signs up at 3:45pm, the access token will expire at 5pm. To be safe developers should assume it only lasts 1hour.
Note: Although I raise this issue in the context of an iOS app, I don't think it's confined to an app running on that specific OS.
I'm developing an iOS application that will back up user data to a server and I'm trying to figure out the best way to verify server-side that the user being updated is actually the real user. Each user will have an id (uid). If that's all I depended on server-side, then I imagine the process would go like this:
User runs app for the first time
Creates account in the app, which communicates with the server to both create the account on the server and to get a unique "user id" (uid)
App stores this uid so that it can identify the user in subsequent communications with the server
However, if someone were to hack the app on their iphone, they could change the user id value and then that would instantly give them access to/allow them to modify a different user's data.
The current solution I'm considering is that the user receives 2 unique ids, the uid (just an auto-incremented number) and a longer, more complex key string. All communication with the server will therefore have to send along both the uid and the key. The server will verify that they match in order to make sure that the user truly is who the app says it is.
So, my question is two-fold:
Is this the correct way to achieve this? Or is there some other standard method that I should pursue?
If this is the correct approach, what's the recommended way to generate the unique key?
First of all, you can use the more complex value as the user ID to begin with, if you like (e.g. a UUID). Monotonically increasing IDs get hard to manage as your service scales.
You have the same problem a secure web site does when it leaves secure cookies on the browser to remember a session. Those cookies do include the user ID, but must prevent tampering. This is generally done by signing the cookie on the server before sending it back.
So what you'd do is:
Generate the user ID on the server, and use it to create some sort of "auth token" for the client to have to sign in.
Sign the auth token on the server with a secret key that only your server knows.
Send the auth token to the client, where it is stored for all subsequent logins. Transfer the auth token over HTTPS to prevent someone else from snooping it on the network.
When the app goes to login, send up the auth token to the server. If it's been hacked, the signature validation will fail, and you'll know to reject the client.
Consider including a timestamp in the signed token as well, so it expires after some time, forcing the server to regenerate an auth token periodically, which protects you in case your key is compromised. It's hard to do this all fully unless the user himself has a shared secret/password he can use to authenticate periodically as well. Depends on how far you need to go.
Other considerations: If all you know about a user is their generated UID, you don't have any way for that user to come back later from a different iOS device and restore their account there, right? Generally, if the user will be creating anything "valuable" in their account that they'll want access to later, you'll probably want to create a more traditional user account backed by an email address and password or the like, so they can access the account again after reinstalling your app. (This may or may not be relevant to your case.)
I would recommend going the "standard web browser way" and just letting the user set an email (login) and password.
When the iOS device connects to the server (using HTTPS), it uses regular "basic authentication" to log in, and receives a cookie which is valid for a set period of time. As long as the device keeps requesting data from the server within the cookie's lifetime, the cookie is renewed, and when the cookie is expired the server will automatically challenge the client to log in using its stored information again.
This has some advantages;
The user can log back into his account with a new device with a regular password reset. Easy, straight forward solved problem.
There is no special solution on the server side, any server side script can require authentication just like it would for a browser - built in functionality.
You would not have to invent your own security scheme. This scheme is used by millions of browsers every day to authenticate to web sites.
Not tied to a special phone, if the user has several iOS devices, he can use the same account from all of them by just logging in. No special set up procedures.
In other words; no special solutions for you to develop, generally solved problems how to handle login information, proven security and ease of use.
According to me, you can't really beat that :)