ADB2C refresh_token always expires in one day - azure

I've been struggling with adb2c for a while now. In particular the refresh flow. I'm using the latest version of msal-browser and everthing works fine, refreshing the token works well. The only problem is that the token endpoint returns a refresh_token that will always expire in one day. In this case, a user can only be logged in for a day, after that, the user will always have to re-authorize. Here is an example of the endpoint and what it returns directly after logging in. (note that I have set the access_token expire time on 5 mins for testing purposes)
Endpoint:
https://{b2c_domain.onmicrosoft.com/{b2c_policy}/oauth2/v2.0/token
Response:
{
"access_token": "{access_token_hidden}",
"id_token": "{id_token_hidden}",
"token_type": "Bearer",
"not_before": 1610023338,
"expires_in": 300,
"expires_on": 1610023638,
"resource": "{resource_hidden}",
"client_info": "{client_info}",
"scope": "https://{adb2c_domain_hidden}.onmicrosoft.com/api/user_impersonation",
"refresh_token": "{refresh_token_hidden}",
"refresh_token_expires_in": 86400
}
When, at some point, the application will try to refresh a token, it will call the token endpoint again. This is what a second response looks like:
{
"access_token": "{access_token_hidden}",
"id_token": "{id_token_hidden}",
"token_type": "Bearer",
"not_before": 1610023891,
"expires_in": 300,
"expires_on": 1610024191,
"resource": "{resource_hidden}",
"client_info": "{client_info}",
"scope": "https://{adb2c_domain_hidden}.onmicrosoft.com/api/user_impersonation",
"refresh_token": "{refresh_token_hidden}",
"refresh_token_expires_in": 85846
}
The refresh_token_expires_in is not rolling. But that is understandable, the user should not always stay logged in. But, in my adb2c policy the following settings are active:
I would assume, as I have configured in the settings, the refresh token should at least be active for 14 days. If not, even up to 90 days? I can play with the settings, but it will always give me a refresh_token that lasts for 1 day. Does anyone has any experience with this or has a possible solution? Thanks!

If you are using the Msal-Browser which implements the code grant with PKCE in SPA application. For this case, you will get the refresh token which will have a expiry of 24 hours and that is not rolling. After 24 hours you need to go to /authorization endpoint of azure ad to get the new access and refresh token. This can also be also non-interactive flow if the browser has the valid login session.
In the Msal-browser library, If you have configured the session more than 24 hours then you can perform the Silent login with ssoSilent(), it require you to send the login_hint.

Yes, as you think, the lifetime of the refresh token can be up to 90 days. If you need to configure the lifetime of the refresh token, you should use powershell to create a token lifetime policy, and then assign the policy to your service principal to set the token lifetime. See: here.
Update:
I just used the Azure AD B2C portal to set the lifetime of the refresh token to 14 days, and then tested it with the ROPC user flow, and the result was that it did take effect. The refresh token I got was 14 days.
So, please make sure that the user flow that you set the lifetime of for the refresh token is the user flow you are using, which is very important!
By the way, your endpoint is wrong, it should be:
https://<tenant-name>.b2clogin.com/<tenant-name>.onmicrosoft.com/{b2c_policy}/oauth2/v2.0/token
2.
3.

Related

Oauth2 : how and where to store client id and secret

I have an authorization server and my client is an angular app.I'm not a third party app.
I used this symfony bundle https://github.com/trikoder/oauth2-bundle
I'm using grant type password.
{ "grant_type":"password", "client_id":"myclientid", "username":"john#doe.com", "password":"foo", "client_secret":"3d4a940.....3c1ea38b5" }
And the response is :
{ "token_type": "Bearer", "expires_in": 60, "access_token": "eyJ0eXAi....nK0Ag", "refresh_token": "def50200dfce4da3....fdc689e5" }
The access_token is only valid for 1 min, afterward the client need to use the refresh token to be able to talk to my api :
{ "grant_type":"refresh_token", "client_id":"myclientid", "client_secret":"3d4a940.....3c1ea38b5" "refresh_token": "def50200dfce4da3....fdc689e5" }
client_id and client_secret are stored in a table.
My question is :
Is it safe/recommended to store the client_id and the client_secret in the local storage of the front angular app ? Because it basically represents the user credentials and if some one steal them they would have access to the api. But without them the client can't send request to the api.
I crawled the web but I can't find a real answer, even on the oauth 2 documentation
Thanks
A few points here:
Your angular app should use the authorization code flow + open id connect
Consider using a more standard authorization server and more standard access token lifetimes
Session storage has fewer issues around Safari / Firefox and SPA restarts
Whether to use session storage for tokens depends on data sensitivity + risk analysis
Consider that the session time for which access tokens are valid sometimes maps to a user consent
I have a write-up, which is based around trade offs between usability and security: https://authguidance.com/2019/09/08/ui-token-management/

Handle directline Tokens and their "expires_in"

With Postman I send a POST to https://directline.botframework.com/v3/directline/tokens/generate with the Header Authorization: Bearer MySecret
The response is
{
"conversationId": "MyConversationID",
"token": "MyToken",
"expires_in": 3600
}
Now I can go to this URL with the token
https://directline.botframework.com/embed/MyName?t=MyToken
and I have a chat opened. After (I assume) 3600 seconds I cannot reach the URL anymore as it is expired.
I can "refresh" my token by sending a POST via Postman to https://directline.botframework.com/v3/directline/tokens/refresh with this Header Authorization: Bearer MyToken and it gives me this response
{
"conversationId": "MyConversationID",
"token": "MySecondToken",
"expires_in": 3600
}
I assume that https://directline.botframework.com/embed/MyName?t=MyToken will still run out after 3600 seconds but I can then continue the chat with https://directline.botframework.com/embed/MyName?t=MySecondToken as they have the same conversationId. Yet it also will expire after 3600 seconds.
Is it possible to set my own expires_in value?
Is it possible to remove expires_in so the token is permanent?
No, it is not possible to change the expires_in value. It is also not possible to remove it, either. Doing so would defeat the purpose behind issuing and using tokens which is to provide a level of security and obfuscation. By using tokens coupled with the conversationId, you are making it harder for unsavory individuals to gain access to your application and data.
It is not absolutely necessary or required to use a token (it depends on your application needs). You can always use your secret, just do so carefully. You can read more about the use of tokens and secrets in the BotFramework docs here.
If you plan on using tokens, then automate the call to refresh the token before it expires. Then swap the token out for the new.
Hope of help!

Azure AD OAuth2.0: I dont get a refresh token

when a client application (such as a webpage using our api) is connecting to a Azure AD OAuth2.0-protected web api
To get the access token, the client applications make a POST to this
https://login.microsoftonline.com/{tenant}/oauth2/token
But the client applcation does not get a refresh token.Is that not needed in the "client application" scenario?
This is what they get
"token_type": "Bearer",
"expires_in": "3600",
"ext_expires_in": "0",
"expires_on": "1531906803",
"not_before": "1531902903",
"resource": "https://our-api.azurewebsites.net",
"access_token": "YtNGEzZi1hZGYyLTExNjU4N......rdFqQ"
The token works fine and it SEEMS that it never expires.
If you got the token with client credentials (client id + client secret or certificate), then you don't get a refresh token.
In this scenario, you can always get a new access token with the application's credentials alone, so you do not need refresh tokens.
In the case of flows which have user context, you get a refresh token since you cannot repeat the user login at will, and must use the refresh token to get a fresh token.
You need add a special scope offline_access when you request authorization_code to receive refresh_token as the result. Check it out, may be it is you case :)

What is the expiry time of refresh token issued by Microsoft Azure OAuth2.0?

What is the expiry time of the refresh_token issued by Azure OAuth2.0 using the following link :
POST /{tenant}/oauth2/v2.0/token HTTP/1.1
Host: https://login.microsoftonline.com
Sample response :
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
"token_type": "Bearer",
"expires_in": 3599,
"scope": "https%3A%2F%2Fgraph.microsoft.com%2Fmail.read",
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD...",
}
This is described in the documentation for v2: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-tokens#token-lifetimes
For Azure AD users, 14 days, personal accounts 1 year.
But of course if you get a new token with the refresh token, you also get a new refresh token there.
But also like the docs say, you must not rely on these. Refresh tokens can become invalid for various reasons, for example if the user's password is reset.

Security - JWT and Oauth2 (refresh token)

I have a angular client app and a .net web api server.
I'm trying to understand how to implement security in the best way using tokens.
I thought about few options, and I don't know which one is the best, or if there is a better way.
JWT with expiration
1.User login with credentials -> Server returns a JWT with expiration (for example 60 minutes from login time). Each valid request to the server within this 60 minutes the server returns a new JWT token with a new expiration of 60 minutes. If user didn't send server request for 60 minutes he must login again.
This solution is very similiar to sessions.
Oauth2 - I don't think I understand this protocol correctly, so I apologize if what I'm saying is not correct.
2.User login with credentials -> user gets a refresh_token and access_token.
For every request, the client attaches the access_token. If server returns 401 (unauthorized) the client uses the refresh_token to create a new access_token and re-send the failed request with the new token.
The problem in this flow, for me, is that I don't know if I got unauthorized because the token was invalid or the user tried to access unauthorized resources.
This led me to a third solution.
3.User login with credentials -> user gets a refresh_token, access_token and access_token_expiration. When the user wants to create a request to the server, he checks if access_token has expired. If expired, the client will request a new access_token with new expiration and only then perfroms the request.
And 2 more little question about auth2:
1.Why do I have a refresh_token and access_token? Both of them are stored in client local storage. Why not to always use the refresh_token? For the server it makes sense to have a refresh_token and access_token since the refresh_token is secured.
2.Does the refresh token have an expiration date?
If it does, how can I create a new refresh token?
If it doesn't, isn't it a little unsave to give the ability to stay connected forever?
Thank you very much for your help
Option 3, login with credentials and getting an access token, refresh token and expiration time is the usual way.
eg.:
{
"access_token": "eyJ0eXA....CqVJcc",
"token_type": "bearer",
"expires_in": 3599,
"refresh_token": "AQAAAN...H2JXjIUAQ"
}
the expiration time of the JWT access token can also be found in the token itself in the payload field exp, in form of a UNIX timestamp:
{
...
"exp": 1500547257,
"nbf": 1500543657
...
}
With that information you can implement your own mechanism to check if your access token is still valid and refresh it when necessary.
The refresh token usually also expires. When the refresh token is expired, you need to start again with the credentials.
Additionally you can read this for further information about the topic: https://auth0.com/learn/refresh-tokens/
And here is a tutorial that talks about handling of refresh tokens in Angular:
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/

Resources