Microsoft Graph API token expiring after 3600 seconds - NodeJS - node.js

I am using the Microsoft Graph API to generate oAuth 2.0 access token, which expires after 1 Hour. Route being used is: https://login.microsoftonline.com/[Tenant ID]/oauth2/v2.0/token
I am passing these values in the Query string.
{
grant_type: "client_credentials",
client_id: APP_ID,
client_secret: APP_PASSWORD,
scope: "https://graph.microsoft.com/.default",
}
Now, my current implementation I have added recursion in which I added retries before calling a graph api to extract user details from AD.
Is there a way I can get/fetch a refresh token, because refresh tokens last longer?

You need to follow the below steps to enable Refresh Tokens:
Request the scope 'offline_access'. This will tell the endpoint to provide a refresh_token along with the access_token and associated metadata.
You need to request a new access_token (and refresh_token as they come together) by repeating the same POST to /common/oauth2/v2.0/token with a slightly different body - grant_type is set to refresh_token and instead of a code, you supply a refresh_token property and value
For e.g.
POST /{tenant}/oauth2/v2.0/token
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=[client Id]
&scope=[scope]
&refresh_token=[refresh token]
&grant_type=refresh_token
&client_secret=[secret]
Please refer to the documentation here for refresh token request details.

In my view, use of refresh token along with client crendential flow is of no use. Why would you want to have an additional step added to get access token when you can get that straight away.
Flow with the client_credentials grant type:
Step 1: client authentication
OK access token is issued
Flow for obtaining Refresh_token
Step 1: client authentication
Step 2: Refresh token validation and use it to obtain access token
OK access token is issued.

Related

How to get access token using refresh token AzureAD

I want to get access token with the help of refresh token that I got previously.
I got tokens using scope: user.read offline_access openid in oauth2 endpoint:
https://login.microsoftonline.com/tenant.com/v2.0/token
After a couple of hours, access token expired. Now I am trying to get this using refresh token.
But I'm unsuccessful in it, any help is much needed.
I tried to reproduce the same in my environment and got below results:
I created one Azure AD application and added API permissions as below:
With below parameters, I got the tokens via Postman:
POST https://login.microsoftonline.com/tenantID/oauth2/v2.0/token
client_id:appID
grant_type:authorization_code
scope:https://graph.microsoft.com/.default
client_secret:client_secret
code:code
redirect_uri:https://jwt.ms
Response:
In order to get access token using above refresh token, change grant type to refresh_token.
I got the access token successfully using refresh token with parameters like below:
POST https://login.microsoftonline.com/tenantID/oauth2/v2.0/token
client_id:appID
grant_type:refresh_token
refresh_token: 0.AVYA_in0zaI3eUqOQHrbrD-FUv //paste the refresh token that you got above
client_secret:client_secret //Mandatory if client is web app
Response:

Why is rate limit similar with authorized and anonymous access to GitHub API?

When I send request with Postman and check response header I can see this:
When I try with old PAT I created, or with OAuth token after validation (I created app and validated user with OAuth flow from my DB, so I used this token in Postman just to check) to call GitHub REST API like this: https://api.github.com/repos/djordjeviclazar/rep/branches
and set access_token in header like in documentation, I can see in headers X-RateLimit-Limit is 60, and I could see that X-RateLimit-Remaining is less than 60.
From documentation:
For API requests using Basic Authentication or OAuth, you can make up to 5,000 requests per hour.
Authenticated requests are associated with the authenticated user, regardless of whether Basic Authentication or an OAuth token was used. This means that all OAuth applications authorized by a user share the same quota of 5,000 requests per hour when they authenticate with different tokens owned by the same user.
So I guess that means I can't make more tokens and expect more than 5000 requests per hour, but why only 60, why API treats my requests as anonymous? Also I think that Search API is more limiting. What is the right way to access GitHub REST API?
The issue is that this call is not authenticated since you've specified:
Add To Headers
Key: access_token
Value: {{PAT}}
It will add an HTTP header with the following value: access_token: [PAT value] which is not processed by Github.
Checkout the headers sent in the headers tab it should print Authorization: Token YOUR_TOKEN or Authorization: Bearer YOUR_TOKEN
The following configuration will work correctly:
Add To Headers
Key: Authorization
Value: Token {{PAT}} (also Bearer {{PAT}} works)
You can also use Authorization of Type Bearer Token which is the same as Bearer XXXX:
Also, you can also disable Authorization (to the value No), and in the headers, just append the Authorization header:
Note that the usage of access_token in the url query parameters has been deprecated since end 2019

Office365: Refreshing access token results with "AADSTS9002313" invalid_grant execption

From last couple of weeks we have few clients complaining that our app is auto revoking Office365 oauth every 1 hour. This is the typical behiviour as access token have validity of 1 hour, so our app is designed to auto refresh the access token using refresh token captured during oauth.
This seems to be not working for atleast few customers from last few weeks. Below exception is thrown back by Office365 token api - https://login.windows.net/common/oauth2/token
{"error":"invalid_grant","error_description":"AADSTS9002313: Invalid request. Request is malformed or invalid.\r\nTrace ID: 7f80c2c3-41bc-41bd-8304-b56969c83a00\r\nCorrelation ID: 5a08714a-1e7d-4f32-814d-146bc721e8ab\r\nTimestamp: 2020-10-12 05:42:11Z","error_codes":[9002313],"timestamp":"2020-10-12 05:42:11Z","trace_id":"7f80c2c3-41bc-41bd-8304-b56969c83a00","correlation_id":"5a08714a-1e7d-4f32-814d-146bc721e8ab","error_uri":"https://login.windows.net/error?code=9002313"}
Here, the error code '9002313' states there is some issue related to auth parameters especially 'client_id' of our azure app. (reference)
Below data is sent to fetch new access token
client_id (related to azure app)
client_secret (related to azure app)
grant_type = 'refresh_token'
refresh_token
Edit 1: Update token endpoint to v2.0
Request URI
POST https://login.microsoftonline.com/common/oauth2/v2.0/token
Request Body
client_id=<client-id> &scope=https://outlook.office365.com/Calendars.ReadWrite https://outlook.office365.com/Contacts.ReadWrite https://outlook.office365.com/Mail.ReadWrite &refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq... &grant_type=refresh_token &client_secret=<client-secret>
Reponse Body
{"error":"invalid_grant","error_description":"AADSTS9002313: Invalid request. Request is malformed or invalid.\r\nTrace ID: 4447c69e-09d6-4a00-8dfe-735106d71200\r\nCorrelation ID: 1820e135-a511-4516-99d9-b6cebb342eb2\r\nTimestamp: 2020-10-13 03:39:37Z","error_codes":[9002313],"timestamp":"2020-10-13 03:39:37Z","trace_id":"4447c69e-09d6-4a00-8dfe-735106d71200","correlation_id":"1820e135-a511-4516-99d9-b6cebb342eb2","error_uri":"https://login.microsoftonline.com/error?code=9002313"}
Your authority is old and you missed the scope in the request body(if use the v2.0 endpoint), if you want to get a new access token for O365 with the refresh token, use the sample request below.
Request url:
POST https://login.microsoftonline.com/common/oauth2/v2.0/token
Request body:
client_id=<client-id>
&scope=https://outlook.office365.com/.default
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
&grant_type=refresh_token
&client_secret=<client-secret>
For more details, refer to the doc - Refresh the access token.
Update:
I test it for you, it works on my side. Make sure you also get the refresh token with the v2.0 endpoint, see here.
The permissions for my app:
Test to get a new access token in the postman after getting the refresh token.

Why is my request for a new access token not returning a new refresh token?

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

How do I handle OAuth refresh token?

When I authorize on my OAuth server it returns me access / refresh tokens:
access_token: "ZjJlMGM2MDcxNDg5MDQ1NzA4ZjkyNzRiOTIwM2E5MWI4N2M0MWU0ZD..."
expires_in: 3600
refresh_token: "NWZjMzQ3YjNjMmY5YTEzYzMxMDYzNGVhNzRiNjAxZTdmZTdjNzE3z..."
scope: null
token_type: "bearer"
How do I use them in my client side javascript application?
Is it okay to save access token and refresh token in the cookies?
(is it safe? - but anyway I dont see any other place where I can
store them...)
I can request protected resources like this: /api/user?access_token=TOKEN . And when I access them I really get my protected data successful. But what will happen when this access token expired? Will it be automatically refreshed, or do I need to handle it manually?
Why do I need refresh token and when I should send it to the server?
three-legged ( User---client ---- Oauthserver)
1)In 3 legged authentication access Token is stored at the client side and is never transferred to the user.
two legged (user ----Oauthserver)
In 2 legged authentication the token is stored at the user side. Probably in the cookie.
2)When the token expires user explicitly has to use the refresh token to get a new auth token.
3) Each Auth token has an expiry and instead of reauthenticating itself with a username/password,User can present refresh Token to get a new valid Auth token.

Resources