I am currently working on Podio integration and I have stumbled upon articles which do not give clear answer whether refresh_token expire on its own and what is the exact flow of obtaining new refresh token in such case.
The articles:
Podio Refresh token Expiry - it does not expire (answer from someone who has Podio in user name, quite recent)
https://help.podio.com/hc/en-us/community/posts/206669587-Get-new-refresh-token - it expires, you get it as a part of response but not rly? There is some discussion with no conclusion
I am asking this because I worked with a lot of services and OAuth implementations, but its the first time that refresh token is actually getting invalidated. So if 28 days pass then user has to reauthenticate? Or just a token is invalidated but "grant" still exists? I have to say it's quite confusing, because I'm used to approach grant == refresh token, but I understand its withih the OAuth specs.
Also we just want to store refresh token in db.
I'd love to test it, but I don't want to wait 28 days.
The docs do not state clearly what is the lifespan of refresh token.
I'd love someone from Podio to give clear answer about this. Do refresh tokens expire, is it only when there is total inactivity (no api calls) or just fixed amount, and in what cases (inactivity or time passed), what is the exact flow of getting refresh token and does it require user reauthentication?
There are two parts at play here, the access token and the refresh token.
The access token expires after the interval specified via the expires_in attribute.
The refresh token can expire if unused for longer than 28 days. Each time you use the refresh token to request a new access token, the timer resets and you have another 28 days before the refresh token expires.
If you regularly obtain new access tokens within the 28-day period you can hypothetically use the same refresh token indefinitely.
Example HTTP request to obtain an initial access token and refresh token (values redacted with asterisks):
POST /oauth/token HTTP/1.1
Host: podio.com
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
grant_type=password&username=******&password=******&client_id=******&client_secret=******
Response Body:
{
"access_token": "******9c2",
"expires_in": 28800,
"token_type": "bearer",
"scope": "global:all",
"ref": {
"type": "user",
"id": ******
},
"refresh_token": "******04a"
}
Example request to obtain a new access token (uses the same refresh token):
Note: Any extra whitespace characters in the request body can cause issues. This is an issue I bumped into as I was experimenting.
Request:
POST /oauth/token HTTP/1.1
Host: api.podio.com
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
grant_type=refresh_token&client_id=******&client_secret=******&refresh_token=******04a
Response Body:
{
"access_token": "******676",
"expires_in": 28800,
"token_type": "bearer",
"scope": "global:all",
"ref": {
"type": "user",
"id": ******
},
"refresh_token": "******04a"
}
It's important to note that the value of the refresh token doesn't change and can be used repeatedly to obtain new access tokens.
TL;DR - If you don't use the refresh token it expires after 28 days. Whenever you use the refresh token to obtain a new access token the expiration timer for the refresh token resets.
Related
I am trying to use microsoft365 and oauth to get an access and refresh token. According to Microsoft 365 docs, we need to use the "offline_access" scope to get a refresh token along with access token. However, The response I am getting does not contain a refresh token.
Here is the code I used:
url = "https://login.microsoftonline.com/{}/oauth2/v2.0/token".format(tenant_id)
headers = {
"Content-Type": "application/x-www-form-urlencoded",
}
data = {
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "client_credentials",
"scope": "https://graph.microsoft.com/.default offline_access",
}
response = requests.post(url, headers=headers, data=data)
The response contains the access token like usual, but does not contain the refresh token despite using the offline_access scope. Could someone kindly tell me what the issue is?
I agree with #junnas Client Credential Flow doesn’t return refresh token as user interaction is not present.
I tried to reproduce the same in my environment and got the results like below:
To get the refresh token, you need to choose user interactive flows such as Auth-Code Flow.
I created an Azure AD Application and added API permissions like below:
I generated auth-code using below endpoint:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/authorize?
client_id=ClientID
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope=https://graph.microsoft.com/.default offline_access
&state=12345
I generated the access token and refresh token using below parameters:
GET https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
scope:https://graph.microsoft.com/.default offline_access
grant_type:authorization_code
redirect_uri:redirectURi
code:code
By using the above generated refresh token, I refreshed the access token successfully like below:
GET https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
grant_type:refresh_token
refresh_token:xxx
client_secret:xxx
You are using client credentials flow. It does not return a refresh token because you don't need one.
Refresh tokens are involved when a user logs in to your application.
Since doing the authentication again would require that the user does the log in again, instead you get a refresh token to get new tokens.
But in the client credentials case re-authenticating is just a matter of sending the same request again.
Refresh token would add nothing to this.
Send the same request again before your token expires.
I configured AD FS 2016 to support authentication of a "Native Application" via OAuth2/OpenID Connect using Authorization Code Grant with PKCE. I created a relying party and configured (for testing purposes) token lifetimes by setting the following:
Set-AdfsRelyingPartyTrust -TargetName MyRPT -IssueOAuthRefreshTokensTo AllDevices -TokenLifetime 3
Grant-AdfsApplicationPermission -ClientRoleIdentifier MyClient -ServerRoleIdentifier MyRPT -ScopeNames openid,profile,email
Set-AdfsProperty -SSOLifetime 7 -PersistenSsoEnabled $false
...and that gives me Access/ID Tokens that expire after 3 minute and Refresh Tokens that expire after 7 (actually 14, see below) minutes. I also disabled Persistent SSO, so I get no session cookie. All good.
After a successful authentication, my client issues a POST request to the /oauth2/token endpoint, passing the following parameters:
client_id: "MyClient"
code: ...
redirect_uri: ...
code_verifier: ...
grant_type: "authorization_code"
I get a valid response with the following:
{
"access_token": "...",
"token_type": "bearer",
"expires_in": 180,
"resource": "MyRPT",
"refresh_token": "...",
"refresh_token_expires_in": 419,
"scope": "email profile openid",
"id_token": "..."
}
Awesome.
Then about 10 seconds before the access token is due to expire, the client issues another POST request to /oauth2/token, this time with the following parameters:
refresh_token: ...
grant_type: "refresh_token"
client_id: "MyClient"
And the following successful response is returned:
{
"access_token": "...",
"token_type": "bearer",
"expires_in": 180,
"id_token": "..."
}
Note that this time no refresh token is returned. The same happens another FOUR times (about 14 mins in total, so twice the SSOLifetime then?) while the refresh token is still valid, and then finally, at the fourth request for a new access token I get a 400 error with the following body:
{
"error":"invalid_grant",
"error_description":"MSIS9615: The refresh token received in \u0027refresh_token\u0027 parameter has expired."
}
Which kinda makes sense, but... shouldn't a new refresh token be issued when the current refresh token is close to the expiration time?
The Official Docs are somewhat cryptic on the matter:
Although refresh tokens aren't revoked when used to acquire new
access tokens, you are expected to discard the old refresh token. As
per the OAuth 2.0 spec says: "The authorization server MAY issue a new
refresh token, in which case the client MUST discard the old refresh
token and replace it with the new refresh token. The authorization
server MAY revoke the old refresh token after issuing a new refresh
token to the client." AD FS issues refresh token when the new refresh
token lifetime is longer than previous refresh token lifetime. To view
additional information on AD FS refresh token lifetimes, visit AD FS
Single Sign On Settings.
Err, what? Let me write that in pseudo code:
if (newRefreshTokenLifetime > previousRefreshTokenLifetime) {
issueNewRefreshToken();
}
...but that would be always then, wouldn't it?
Any idea on how to configure AD FS so that it issues new refresh tokens as well when needed? Ideally it would be nice to have refresh token rotation, but one thing at a time...
Usually in OAuth the lifetime of a refresh token is set at the time of the delegation, when the user signs in, at which time they may consent to certain permissions being used for a certain time.
So if a user signs in at 09:00 for an 8 hour session, and their app refreshes an access token at 10:00, if there is a new refresh token issued it should then be usable for 7 hours. That is, you cannot override the initial delegation without involving the user again.
As you say, the newer trend is to get a new refresh token on every access token refresh, but this is just a protection mechanism, and ADFS does not support that. So I would proceed as follows:
Set the SSO Lifetime to the desired value, eg 8 hours, and set the access token lifetime to a standard value such as 30 minutes
Write code in a future facing way, to discard the existing refresh token if you get a new one
REFRESH TOKENS IN NATIVE APPS
Based on comments, you are trying to use PKCE and would like to use rotating refresh tokens, but ADFS does not support the latter, so you cannot.
You have a native app where the standard solution has always been to store the refresh token in secure operating system storage only available to the app and user. It should not matter whether the refresh token rotates or not. Examples:
Desktop App
iOS App
Android App
SPAs
Tokens and the browser is a whole different topic, since there is nowhere secure to store refresh tokens. With recent third party cookie browser restrictions the only way to get a Javascript app to work is to store refresh tokens in local storage, which is disastrous from a security viewpoint.
The optimal solution to this tricky problem is to use an API driven solution where a utility API issues SameSite=strict cookies for the SPA. There are a few moving parts to deploy to both developer PCs and your pipeline though. See the below Curity resources for details on the design pattern. This will also work fine with ADFS.
Code
Docs
I am currently using OpenID Connect/Oauth2 Implicit Flow in a mobile app. I am bringing up a Web View for the user to login and obtaining the access token and expiry. However, when the access token expires, do I need to ask the user to log in again? Or is there a way to get a new access token silently using the current one, without bugging the user. I guess another option is to set the token expiry to be a really long time, but I have read that this is a bad idea.
Am I missing something here?
Since Implicit flow does not send a refresh token (as explained in section 9 of RFC6746), usage of refresh tokens is not possible. But as a workaround one can use client credential grant to obtain an access token.
A viable solution is to first follow the implicit flow and authenticate the client. Then client authentication grant can be used to do the required API calls.
Sample request (from RFC6749)
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
rant_type=client_credentials
Sample resposne (from RFC6749)
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}
P.S -
If you are using authorization code flow, you can use refresh_token to get a new access token. How the request should be formed can be obtained from OAuth2 documentation. Note that to do so, your authorization response should contain a `refresh_token.
A refresh token should be protected as valuable as a credential for a user. More can be read from keycloak documentation from here
Sample request and a response (from RFC6749)
Request
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
Response
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "TlBN45jURg",
"token_type": "Bearer",
"refresh_token": "9yNOxJtZa5",
"expires_in": 3600
}
The recommended way is to use an external browser and the Authorization Code Flow. Check the OAuth 2.0 for Native Apps RFC. For Android, there is also a supporting library AppAuth. With this flow, you can use a refresh token to get a new access token, but there is an issue with a client secret (usually needed for accessing /token endpoint), because you cannot keep it safe in a mobile app (it's described in the RFC).
If you decide to stick with the WebView and the Implicit Flow, which is not safe (your app can see the password) you could use the same technique as in JavaScript applications - request a new token with /auth?...&prompt=none URL which will return a new token without asking the user for credentials if he still has an open session there.
I create Backend server, which gets the ID Token from mobile application (iOS). How can I verify that this token is OK and can be used it securely?
Official Google's documentation about validating token:
https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken
It recommends to verify the ID Token locally, without sending verification request to the Google. Is it OK to check some fields from ID Token locally like in documentation or maybe should I send some request to Google to verify token as well?
Google documentation mentions about debugging and verifying ID Token with:
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=XYZ123
But it doesn't recommend to use it in production. I thought also about using Access Token along with the Id Token and verify Access Token first with:
https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=
But does it make the whole process of validating client's credentials (mobile app, web app) more secure?
Fist let me start by saying I don't work for Google. However I have been developing with Google Oauth2 since 2012. A while back I asked a Googler just this question.
His recommendation was if you have a refresh token just request a new access token. If its bad the server will return an error. If you have an access token send a request if its bad the server will return an error.
There isn't really much point in validating it first your just sending two requests to the server for every request you make. All you will be doing is preventing errors on a small percentage of the requests you are making in the long run.
I have never bothered with the id token. Id token is a jwt so you should be able to open it I think.
update
You should consult Verifiy the integrity of the id token.
You can also do some checking on your own. The id token is a jwt if you decrypt it you get or by calling the tokeninfo endpoint
{
"iss": "https://accounts.google.com",
"azp": "407408718192.apps.googleusercontent.com",
"aud": "407408718192.apps.googleusercontent.com",
"sub": "11720055326",
"at_hash": "HQVaIRLqmsjaTt8KoOIQ",
"name": "Linda Lawton",
"picture": "https://lh3.googleusercontent.com/a-/AAuE7mDuIWqXzrrp-65cIhXSD2HjCI8WYsWHR0fDx5_wQPY=s96-c",
"given_name": "Linda",
"family_name": "Lawton",
"locale": "en",
"iat": 1567751,
"exp": 1567755
}
iss should be https://accounts.google.com
aud will be the client id of your app 7408718192.apps.googleusercontent.com
at_hash there may also be some way to validate against this but i haven't bothered
This is a follow up to question Not receiving Google OAuth refresh token
The answer is "The refresh_token is only provided on the first authorization from the user."
After revoking rights for the app and trying to authorize again, refresh token is not returned. What I get is:
{
"access_token" : "XXXX..",
"token_type" : "Bearer",
"expires_in" : 3600,
"id_token" : "XXXX..."
}
Others suggested to use access_type=offline however, according to description offline access is used if:
"application needs to access a Google API when the user is not present
at the browser"
which isn't the case for me.
What is a proper way to get refresh token?
You only get a refresh token if access_type=offline is set. You have two choices of how to handle this:
Don't use access_type=offline. Your access token will be good for 1 hour. After the access token expires, re-prompt the user to authenticate again. They'll need to do the whole OAuth dance again so that you can get a new access token.
Use access_type=offline so that you can get a new access tokens via the refresh token. If you prefer, after the user logs out, you can revoke the tokens.
Every time When you reload you application page , Access Token is Refreshed or you can say the refresh token for this purpose you should use the following but First You Need the authentication
gapi.auth.getToken().access_token;
i am also doing the same thing by the following Way
var accessToken = gapi.auth.getToken().access_token;
var xhr = new XMLHttpRequest();
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
You can use this to avoid your problem "refresh token is not returned".
Thank You!!