Get invalid_grant error when attempting to refresh a token - node.js

I am developing a node.js application which uses outlook rest API to fetch the mails. I am using this API.
I am trying to refresh the token using the following request. I am using request npm to call the API
{
url: 'https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token',
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
formData:
{
scope: 'offline_access User.Read Mail.Read',
client_id: 'c251b61b-c6db-4f64-89bd-7009444d1bc8',
grant_type: 'refresh_token',
redirect_uri: 'http://localhost:3000/myurl',
refresh_token: 'refresh-token',
client_secret: 'cli-secret'
}
}
but getting the following error
{
"error": "invalid_grant",
"error_description": "AADSTS9002313: Invalid request. Request is malformed or invalid.745ec0500",
"correlation_id": "a2d87f11-0671-41f1-a5e7-654f1796c3d1"
}
I have also tried with adding Content-length in headers and appending all variables into a string using & and = and sending that in the body, but I got the same error. I am getting an access-token successfully.

So far I know you are trying to get refresh token in wrong way!
As the error said ,you are trying in incorrect grant_type.
As per your given document reference the grant_type should be authorization_code. Once you would get your Code then you need to use it for achieving access tokenand refresh token.
When your access token would expired then you have to Use the refresh token to get a new access token as document explains
In that case try with response_type=code format. I hope it would resolve your problem.
Request For Code:
Get Code In Postman Console:
Request For Access And Refresh Token With Code:
Get Access And Refresh Token By Code:
Get Refresh Token When Access Token Expired:
Note: This this the exact way how you would get authorization code and with this code how to get access token and refresh
token finally how to renew token with the refresh token when the
access token expired!
Thank you and happy coding!

When you generate the access token the first time that time you also get the refresh token. you have to store that token anywhere you can also store it in a database or a txt file.
$post_params_refresh = array(
"grant_type" => "refresh_token",
"client_id" => 'ReplaceYourClientId',
"refresh_token" => 'ReplaceYourOldRefreshToken',
"client_secret" => 'ReplaceYourClientSecretKey',
'scope' => 'https://graph.microsoft.com/User.ReadWrite.All',
);
$refreshTokenUrl = "https://login.windows.net/common/oauth2/v2.0/token";
$curl_refresh = curl_init($refreshTokenUrl);
curl_setopt($curl_refresh, CURLOPT_POST, true);
curl_setopt($curl_refresh, CURLOPT_POSTFIELDS, $post_params_refresh);
curl_setopt($curl_refresh, CURLOPT_HTTPHEADER, array("application/x-www-form-urlencoded"));
curl_setopt($curl_refresh, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($curl_refresh, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl_refresh, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl_refresh, CURLOPT_RETURNTRANSFER, 1);
$response_refresh = curl_exec($curl_refresh);
$arrResponseRefresh = json_decode($response_refresh);
$accessToken = $arrResponseRefresh->access_token;
$refreshToken = $arrResponseRefresh->refresh_token;
You get a new refresh token using this curl method and update this refresh token to the old token which you stored previously.

Related

Auth0 Only receiving access token from token end point

I am following the guide to retrieving the id, access, and refresh token for a nodejs project. I am utilizing authorization_code flow, where the user logs in via the default auth0 account login(non-3rd party login).
When I make the request successfully I only receive the users access token, but not the id token.
I am making the request to the /oauth/token with the authorization code present.
Here is the guide I am following: Call Your API Using the Authorization Code Flow
Here is my server code:
const getAuth0Tokens = async(code)=>{
console.log(`code => here ${code}`)
var options = {
method: 'POST',
url: 'https://********.us.auth0.com/oauth/token',
headers: {'content-type': 'application/x-www-form-urlencoded'},
data: new URLSearchParams({
client_id: '*************clientId**********',
client_secret: '*************clientSecret**********',
audience: 'https://localhost:3000/login.html',
grant_type: 'authorization_code',
redirect_uri:"https://localhost:3000/login.html",
code:`${code}`
})
};
return await axios.request(options).then(function (response) {
console.log("data from auth0 token call " + JSON.stringify(response.data));
const {id_token,access_token, refresh_token, token_type, expires_in} = response.data;
return {id_token, access_token, refresh_token, token_type, expires_in}
}).catch(function (error) {
console.error(error);
});
Here is the response:
The request is returning successfully with 200 status response. For more context I am on the free subscription account tier.
Could the error be due to mu auth0 account configuration? or maybe something else.
You need to specify the scope with offline_access in order to retrieve the refresh_token. It is also mentioned in the same guide which you have linked above
Include offline_access to get a refresh token (make sure that the Allow Offline Access field is enabled in the Application Settings).
So you can just add new parameter scope: offline_access along with your other params
It looks like your scope is empty. You might need to add openid and profile to the scope param to get the id token.
It would help if you could show your authorize request too (step 1 in that guide).
Then, ya, what Umakanth said about the refresh token. Need to add offline_access to the scope.
Include offline_access to get a refresh token (make sure that the Allow Offline Access field is enabled in the Application Settings).

How to get access_token using token endpoint in the node OIDC provider

I tried to get access_token and refresh_token using authorization code flow using node oidc provider. I got auth_code. but I could not get access token and refresh token How to fix this Issue. I referred many documentation but I could not get it.
OIDC Configuration
const oidc = new Provider('http://localhost:3000', {
clients: [
{
client_id: 'foo',
client_secret: 'bar',
redirect_uris: ['https://jwt.io'], // using jwt.io as redirect_uri to show the ID Token contents
response_types: ['code'],
grant_types: ['authorization_code'],
token_endpoint_auth_method: 'none',
},
],
cookies: {
keys: 'secretkey'
},
pkce: {
required: true
},
});
// Heroku has a proxy in front that terminates ssl, you should trust the proxy.
oidc.proxy = true;
app.use(oidc.callback())
I got auth_code also
How to get access token and refresh token using node-oidc provider
Your access token request is missing the PKCE code_verifier parameter.
your client's authentication method is set to none, so you're not supposed to pass any authorization header.
you can start your provider process with DEBUG=oidc-provider:* to get more details for these errors.
Invalid Client but you have input "client_id", it mean you are enabling features:
{
clientCredentials: {
enabled: true
}
}
So you must provide client_secret
and in oidc-provider source I see it always check code_verifier so you should provide it

How to get access token from Azure Active Directory with certificate when service is behind proxy

I need to create service that calls graph api to access company data. In order to authenticate I need JWT token from Azure Active Directory. The authentication will be using application mode with signing certificate. I tried to use MSAL node ConfidentialClientApplication but the service needs to use http proxy to connect to internet. To my knowledge MSAL node does not support this and calls result in library being unable to resolve the address of "https://login.microsoftonline.com". How can I make MSAL node use the proxy or get JWT token without use od MSAL?
In order to get JWT token from azure active directory without MSAL node, one have to generate proper JWT token on its own and then sign it with certificate private key. The header of the token consists of following fields:
{
typ: "JWT",
alg: "RS256",
kid: "156E...",
x5t: "iTYVn..."
}
"kid" is the thumbprint of the certificate used to sign the request - here is a good example how to obtain it for pfx file with powershell https://stackoverflow.com/a/32980899/3588432
"x5t" is base64 encoded and sanitized certificate thumbprint.
Sanitization of base64 encoded string means:
trimming "=" signs at the end
replace "/" with "_"
replace "+" with "-"
Exemplary C# code for the sanitization:
var sanitized = s.Split('=')[0].Replace('+', '-').Replace('/', '_');
and JS code:
var sanitized = s.split('=')[0].replace('+', '-').replace('/', '_');
The payload of the token consists of the following fields:
{
aud: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token",
iss: "{clientId}",
nbf: 1617952610,
exp: 1617953210,
sub: "{clientId}",
jti: "e13efcf..."
}
{tenantId} and {clientId} are Azure AD data of application we are authenticating to
"nbf" is the time when the token will began to be valid, normally it is time the token got generated. It has unix epoch format https://en.wikipedia.org/wiki/Unix_time and is an integer.
"exp" - the time the token expires in unix epoch format.
"jti" - a unique token identifier. It may be random generated guid. Should be different for every request.
An example how to get "nbf" value in JavaScript:
var nbf = Math.floor(new Date().getTime() / 1000);
When ready header and payload should be serialized (with sanitization) on concatenated with ".":
var token = JSON.stringify(header) + "." + JSON.stringify(payload);
Then we need to sign it with certificate private key, encode it with base 64 (with sanitization) and prepare a clientAssertion value:
var clientAssertion = token + "." + signedToken;
As a last step can send request to get JWT token:
const body = new URLSearchParams();
const token = await fetch("https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", {
agent: new HttpsProxyAgent("http://..."),
body: new URLSearchParams({
"client_assertion": clientAssertion,
"client_id": "{clientId}",
"scope": "https://graph.microsoft.com/.default"
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
"grant_type": "client_credentials"
}),
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded"
}
})
.then(response => response.json().access_token);

Refresh Token is not returned in #azure/msal-node using Authorization code flow?

In example project provided by Microsoft here which uses Authorization code flow the acquireTokenByCode method does not return refresh tokens.
From #azure/msal-node here refresh token is not mentioned.
Result returned from the authority's token endpoint.
uniqueId - oid or sub claim from ID token
tenantId - tid claim from ID token
scopes - Scopes that are validated for the respective token
account - An account object representation of the currently signed-in user
idToken - Id token received as part of the response
idTokenClaims - MSAL-relevant ID token claims
accessToken - Access token received as part of the response
fromCache - Boolean denoting whether token came from cache
expiresOn - Javascript Date object representing relative expiration of access token
extExpiresOn - Javascript Date object representing extended relative expiration of access token in case of server outage
state - Value passed in by user in request
familyId - Family ID identifier, usually only used for refresh tokens
please ensure your MSAL authorization code request includes the offline_access scope.
You could use MSAL.js to get token in this case, there is acquireTokenSilent method, it can perform silent renewal of tokens, which means you are no need to get the refresh token by yourself.
Popup
var request = {
scopes: ["Mail.Read"]
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(async (error) => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenPopup(request);
}
}).catch(error => {
handleError(error);
});
Redirect
var request = {
scopes: ["Mail.Read"]
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(error => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenRedirect(request)
}
});
It's designed to not return the refresh token if you are using #azure/msal-node.
As they stated in the discussion, the refresh token is handled background, inside the library itself for better security, which I also disagree with.
However, if you insist to have the token, you can manually call the API to the AzureAD endpoint.

Access Token vs Refresh Token NodeJS

I'm new to JWT which stands for Json Web Token. I've confused with couple of its terms: Access Token and Refresh Token.
purpose: I wanna implement a user authorization which logs the user out after two hours of being idle (don't request the site or exit from the browser).
To reach that goal I'm trying to follow the below items:
After the user registers/logs-in in the site, I create Access Token and Refresh Token.
Save the refresh token in the DB or cookie.
After 15 minutes the users token the access token expired.
In case of a user being idle for 2 hours, I remove the refresh token from the cookie or DB, else I renew the access token using refresh token.
Is there any optimized way to reach that purpose?
First of all u need to understand the principle of JWT's and how they are passed between server and client and matched server-side against a secret - here's the doc
The payload can be any arbitrary user data - i.E.: just a usrname or id
Basically you need a service that generates a token on successful authentication (when the user logs in with the proper credentials, i.E.: usr & pwd) and create an additional header with the token to be used in further requests to the server.
// INFO: Function to create headers, add token, to be used in HTTP requests
createAuthenticationHeaders() {
this.loadToken(); // INFO: Get token so it can be attached to headers
// INFO: Headers configuration options
this.options = new RequestOptions({
headers: new Headers({
'Content-Type': 'application/json', // INFO: Format set to JSON
'authorization': this.authToken // INFO: Attach token
})
});
}
// INFO: Function to get token from client local storage
loadToken() {
this.authToken = localStorage.getItem('token');; // Get token and asssign to
variable to be used elsewhere
}
and some functionality to store the user-status i.E.:
// INFO: Function to store user's data in client local storage
storeUserData(token, user) {
localStorage.setItem('token', token); // INFO: Set token in local storage
localStorage.setItem('user', JSON.stringify(user)); // INFO: Set user in local
storage as string
this.authToken = token; // INFO: Assign token to be used elsewhere
this.user = user; // INFO: Set user to be used elsewhere
}
and a logout function to destroy the token in the local storage, i.E.:
// INFO: Function for logging out
logout() {
this.authToken = null; // INFO: Set token to null
this.user = null; // INFO: Set user to null
localStorage.clear(); // INFO: Clear local storage
}
In case you use npm's jsonwebtoken, you can set the ttl of the token when generating it:
const token = jwt.sign({ id: idDB }, "secret", { expiresIn: '24h' });
or whatever ttl you desire, the string "secret" refers to the secret that's matched against the server.
btw: If I understand you correctly, your points number 3 and 4 contradict each other..
After 15 minutes the users token the access token expired.
In case of a user being idle for 2 hours, I remove the refresh token from the cookie or DB, else I renew the access token using refresh token.
in case 4 it will be destroyed anyways in 15 mins if you implemented the logic of number 3 correctly

Resources