When a user logs out of Azure B2C using the MSAL library on a mobile device this only clears the local cache. The remote session on the server still exists which means any existing refresh tokens could still be used.
From searching I know that the Microsoft Graph API can be used to revoke the current user's sign in session, and therefore invalidate all current refresh tokens. I believe I am doing this, but the refresh tokens keep remaining active.
Here is my flow:
I get a token for user A (I tried this with auth code flow and ROPC but I don't believe that should make a differnce).
I confirmed that I can get a new access token by using the current refresh token that is returned in a Postman call -
{{b2c_login_url}}/B2C_1_ROPC_SignIn/oauth2/v2.0/token?grant_type=refresh_token&client_id={{b2c_ropc_client_id}}&refresh_token=xxxxx&scope={{b2c_scopes}}&redirect_uri={{b2c_api_redirect_uri}}
This returns a new access token as expected.
I then take the azure userId value ("oid" property in the access token) and pass that through to my API that then runs the following code.
var graphClient = GetGraphClient();
var result = await graphClient.Users["{" + userId + "}"]
.RevokeSignInSessions()
.Request()
.PostAsync();
return result.GetValueOrDefault();
I can see that the result of this expression is true. I can also go onto the Azure B2C user details and see that "StsRefreshTokensValidFrom" has been updated to the current date time as expected.
Now, I run the exact same http request I ran previously using the refresh token to get another access token, but this time, it should fail. However, I continue to get new access tokens.
The strange thing is that I am sure I tested this previously, tried to get a new token, and it failed as I'd expect. But now it will always return me new tokens.
I feel I am missing something here. Any advice?
I tried to reproduce the same in my environment and got below results:
I generated token for a B2C user using ROPC flow via Postman with parameters as below:
POST https://<tenant_name>.b2clogin.com/<tenant_name>.onmicrosoft.com/<policy>/oauth2/v2.0/token
client_id : xxxxx-xxxx-xxxx-xxxxxxxx
grant_type : password
scope : https://<tenant_name>.onmicrosoft.com/web_api/api.read offline_access
username : b2c_username
password : password
Response:
Using the above refresh token, I'm able to generate access token successfully like below:
POST https://<tenant_name>.b2clogin.com/<tenant_name>.onmicrosoft.com/<policy>/oauth2/v2.0/token
client_id : xxxxx-xxxx-xxxx-xxxxxxxx
grant_type:refresh_token
scope:https://<tenant_name>.onmicrosoft.com/web_api/api.read
redirect_uri:https://jwt.ms
refresh_token:paste_refresh_token
Response:
To revoke refresh tokens, I ran below query via Graph Explorer like this:
POST https://graph.microsoft.com/beta/users/<user_id>/invalidateAllRefreshTokens
Response:
Code Sample in C#:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
await graphClient.Users["userid"]
.InvalidateAllRefreshTokens()
.Request()
.PostAsync();
To confirm that, I checked user's details in Portal like below:
When I tried to get access token with same refresh token, I got error saying token is revoked like below:
After revoking the tokens from Graph API, it may take up to 5 minutes to work.
If you run the query for access token as soon as you revoked the refresh tokens, you may still get access token.
So, wait for 5-10 minutes and try to get the access token with same query. Then, you won't be getting access token as the refresh token will be revoked at that time.
Related
I'm writing a program that automates some actions with my QuickBooks account (using Node js).But expires refresh token also, what can i did?I have this error message.
Error: The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
You can refer the official node-oauth2.0 lib.
https://www.npmjs.com/package/intuit-oauth#refresh-access_token
You need to generate new accessToken using the latest refreshToken.
Ref docs - https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0
I am using the Easy Auth feature of Azure App Service and I am trying to refresh a token with the Google provider.
I followed the Chris Gillum article and correctly called .auth/login/google with the access_type=offline parameter. Then I called .auth/refreshwhich return me a 200 OK with a new authenticationToken. However, when I check the claims of this ZUMO token by calling .auth/me, I can see that the Google token is in fact not refreshed despite the previous successful response. The exp claims (corresponding to Expiration Time) is the same as the previous token.
I tried several scenario : refresh the token immediately after receiving it, 10 minutes before the expiration time and after the expiration time (when the token is no longer valid) but in every scenario, Easy Auth return me a new ZUMO token but the Google token associated is always the same.
Is it normal for the .auth/refresh endpoint to always return the same token (same exp claims) with the Google provider ?
As Exchange authorization code for refresh and access tokens states about the refresh_token:
A token that you can use to obtain a new access token. Refresh tokens are valid until the user revokes access. Note that refresh tokens are always returned for installed applications.
And the response from Refreshing an access token only contains the access_token,expires_in (The remaining lifetime of the access token in seconds),token_type.
Is it normal for the .auth/refresh endpoint to always return the same token (same exp claims) with the Google provider ?
Using the Log stream under the MONITORING section of your app service, you could find the detailed log when calling .auth/refresh as follows:
Moreover, the exp claim when calling .auth/me represents the expire time for the authenticationToken instead of the refresh_token.
And you could leverage jwt.io to decode your authenticationToken and compare it with the exp user claim.
I am developing an app on Xamarin. I am trying to implement a single sign on across multiple apps. For this I authenticate the user first time with Azure AD using ADAL and user enters the credentials in the O365 page displayed by ADAL in the AcquireTokenAsync().
var authContext = new AuthenticationContext(ServiceConstants.AUTHORITY);
var authResult = await authContext.AcquireTokenAsync(resourceUri, ServiceConstants.CLIENTID, ServiceConstants.RETURNURI, param);
var apiAccessToken = authResult.AccessToken;
When successfully authenticated, I save the received token to a file which does persists even if the user uninstalls the app.
Now next time the user starts the app, I pass the saved token to the
AcquireTokenAsync() as UserAssertion
//Pass old accessToken
var userAssertion = new UserAssertion(accessToken);
var authResult = await authContext.AcquireTokenAsync(resourceUri, ServiceConstants.CLIENTID, userAssertion);
This works as expected and the user is authenticated without showing the O365 login screen.
Now I want this same behavior when the user uninstalls the app and reinstalls it. In my case, when the user uninstalls and reinstalls the app, I try to get a fresh token in the same way by adding the saved token as UserAssertion to the AquireTokenAsync(). However I get an error like so :-
Invalid JWT token. Token format not valid.
I cross checked the Token via Postman and it is a valid token.
EDIT
Any idea on how I can resolve this? Or a better way I can implement the same? Any help is highly appreciated.
If you have got the refresh_token, we can use this token to acquire the access_token silently directly via the RETS.
Here is a example for your reference:
POST /{tenant}/oauth2/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
&grant_type=refresh_token
&resource=https%3A%2F%2Fservice.contoso.com%2F
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for web apps
More detail please refer the link below.
Refreshing the access tokens
I have registered a multitenant app at https://apps.dev.microsoft.com since the "admin consent" prompt wasn't available in the Azure AD apps. Admin consent is required for our app to retrieve info about users and their calendars.
I can provide admin consent from a completely different tenant than what this app is registered from and use the provided access token to retrieve all necessary information, however that obviously expires after an hour and we need offline access.
I have tried using the tenantId instead of 'common' in the https://login.windows.net/common/oauth2/token endpoint, however receive the same message as below.
The following is the data being submitted to the token endpoint in json format (converted within node to form encoded format before submitting):
{
grant_type: 'refresh_token',
client_id: 'e5c0d59d-b2c8-4916-99ac-3c06d942b3e3',
client_secret: '(redacted)',
refresh_token: '(redacted)',
scope: 'openid offline_access calendars.read user.read.all'
}
When I try to refresh the access token I receive an error:
{
"error":"invalid_grant",
"error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID 'e5c0d59d-b2c8-4916-99ac-3c06d942b3e3'. Send an interactive authorization request for this user and resource.\r\nTrace ID: 2bffaa08-8c56-4872-8f9c-985417402e00\r\nCorrelation ID: c7653601-bf96-46c3-b1ff-4857fb25b7dc\r\nTimestamp: 2017-03-22 02:17:13Z",
"error_codes":[65001],
"timestamp":"2017-03-22 02:17:13Z",
"trace_id":"2bffaa08-8c56-4872-8f9c-985417402e00",
"correlation_id":"c7653601-bf96-46c3-b1ff-4857fb25b7dc"
}
This error occurs even when standard consent is used. I have also tried using the node-adal library instead of raw http requests which produces the exact same result.
I note that "offline_access" isn't a permission I am able to set within the MS apps portal, however I would guess the fact that I am getting a refresh token back means that I can refresh the access token?
For the record, the following is the node-adal code I used to see if I was doing something wrong:
var self = this;
var authenticationContext = new AuthenticationContext('https://login.windows.net/common');
authenticationContext.acquireTokenWithRefreshToken(
self.refreshToken,
self.clientId,
self.clientSecret,
'https://graph.microsoft.com/',
function(a) {
console.log(a);
}
);
Any help in getting this refresh process working is appreciated!
Please ensure that the tenant that you using for refreshing token is same as the tenant that you requesting for the access_token.
The refresh token request works well for me unless in the scenario of below:
register the app from protal using Microsoft account
user1 is in tenant1
add user1 as the external users to tenant2
request the access_token/refresh_token from tenant1(OK)
try to refresh the token using tenant1 in the request(OK)
try to refresh the token using tenant2 in the request(same error message)
I am using the following code, along with my refresh token, to request a new access token:
exports.getTokenFromRefreshToken = function (user, callback) {
request.post({
url:'https://login.microsoftonline.com/12345678-1234-1234-1234-2f189712345/oauth2/token',
form: {
grant_type: 'refresh_token',
refresh_token: refresh_token,
client_id: client_id,
client_secret: client_secret,
resource: 'https://graph.microsoft.com'
}
}, function(err, httpResponse, body) {
if (!err) {
var tokens = JSON.parse(httpResponse.body);
console.log('getTokenFromRefreshToken() tokens = ' + JSON.stringify(tokens));
callback(null, tokens);
}
})
};
The httpResponse includes everything that I get when I make the original token request (from the code), but without a new refresh token. I was under the impression that I would also receive a new refresh token. Is that not the case?
You get a new refresh token only when you are including the offline_access scope.
ref.: https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-scopes/
The offline_access scope gives your app access to resources on behalf of the user for an extended time. On the work account consent page, this scope appears as the "Access your data anytime" permission. On the personal Microsoft account consent page, it appears as the "Access your info anytime" permission. When a user approves the offline_access scope, your app can receive refresh tokens from the v2.0 token endpoint. Refresh tokens are long-lived. Your app can get new access tokens as older ones expire.
Refresh tokens aren't refreshed the same way you can get a new access token using the refresh token. When a refresh token expires, you will need to need to get the credentials and do the initial token acquisition again.
More info here: Refreshing an Access Token
It looks like it should work except you seem to be missing the redirect URI. I have a working version of this call but it includes this redirect_uri. It produces for me both a new access and refresh token.
--
http://graph.microsoft.io/en-us/docs/authorization/app_authorization
Renew expiring access token using refresh token
The redirect URL that the browser is sent to when authentication is complete. This should match the redirect_uri value used in the first request.
I had the exact same issue, caused a headache for a while until the problem was found.
Seems like you are probably logging in with a Guest MS account (previously known as Live) and thus getting a 12hr expiry refresh token with no rolling window.
You need to use a full MS account to get the refresh token back in the response body (which is a token that will last 14 days), with a rolling window of 90 days.
For as long as you use a Guest MSA you will not get a refresh token back. Your code is correctly structured as far as I can see, and I don't believe you need a redirect_uri header as stated above, they are optional fields according to doco.
For more information on the types of refresh tokens, see this blog post:
Azure tokens