After going through multiple steps of OAuth2, what should be done with the access_token once it is received?
app.get('/oauth2', function(req, res) {
var code = req.query.code;
var url = "https://.../oauth/access_token";
var options = {
url: url,
method: "POST",
form: {
client_id: '...',
client_secret: '...',
grant_type: 'authorization_code',
redirect_uri: 'http://localhost:8080/oauth2',
code: code,
},
json: true
}
request(options, function(err, response, body) {
// I need to save the user in database if she doesn't exist
// Then redirect, but should I pass the access_token to the redirect?
res.redirect('/'); // or res.redirect('/?access_token=zzz')
}
// Also, should the access_token be encrypted
// Does it need to be saved in database?
// Does it go in local storage?
});
I will want some of the information that I receive in the reponse and so it needs to be stored in the database. But what specifically do I do with the access_token? Does it get saved to the database? Should it be encrypted? When I redirect, do I add it as a query string? Do I store it in local storage? If so, how?
First, your code includes json: true, but RFC 6749 4.1.3. Access Token Request states that parameters should be sent using the application/x-www-form-urlencoded format.
Second, the format of a response from a token endpoint is JSON. See 4.1.4. Access Token Response for details.
Third, after you get an access token, you should save it for later use. The place to save an access token in is up to you. Datebase, on-memory, and wherever you like. If you want to encrypt an access token when you save it, do it as you like.
Finally, an access token is used to call Web APIs of a resource server. In normal cases, implementations of Web APIs accept an access token in the ways defined in RFC 6750. In the specification, the following three ways are defined.
In Authorization header
As a Form parameter
As a Query parameter
Related
I am creating React Native app using Expo and used its inbuilt Facebook.useAuthRequest to generate a response when a user logs in. When I create a response type of Token I am able to take this token and send it to my backend API that successfully uses it to get the user details.
However I had hoped to implement a response type of code and use this on the backend API generate the access Token and then request the user details - as I believe this is the most secure option when sending the code to my server.
The issue that I'm facing is that I keep getting an error when trying to formulate the requst to Graph API and I dont understand why:
error: {
message: 'Missing client_id parameter.',
type: 'OAuthException',
code: 101,
fbtrace_id: 'ARHcoh260kBwj7l9yDHjU-n'
}
I just want to confirm that I believe I have inserted all the correct information into the request, so I am unsure of why this error is saying its missing the cliend_id. Here is my request from my API server:
const { data } = await axios({
url: https://graph.facebook.com/v12.0/oauth/access_token? client_id=${appId} &redirect_uri=${redirectUri} &client_secret=${appSecret} &code=${code},
method: 'get',
});
I just want to confirm that the client_id I have taken from app id I created on the facebook developer page, as well as the client_secret, redirect is the https:// used in the initial request and the code is the code initially received in my client side request.
Thanks in advance for any suggestions :)
Just a quick update on this, I was ablel to reformat the request as I believe it had some errors in the spacing and I moved to using the .env files so now my request looks like this:
const redirectUri = {MY_REDIRECT URL};
const appId = process.env.FACEBOOK_CLIENT_ID;
const appSecret = process.env.FACEBOOK_CLIENT_SECRET;
const { data } = await axios({
url: `https://graph.facebook.com/v12.0/oauth/access_token?client_id=${appId}&redirect_uri=${redirectUri}&client_secret=${appSecret}&code=${code}`,
method: 'get',
});
It seems I have moved onto a new error with the following:
error: {
message: 'Invalid code verifier. Code verifier should be a cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long.',
type: 'OAuthException',
code: 1,
fbtrace_id: 'AQKIUad5RRCitb6m977fnFW'
}
I'm a bit stumped for what this means as I have checked and all my values appear correct. My only thought is if I need to do something with the code initially received on the client side?
Ok so I finally figures it out - the issue was the I wasn't sending the code_verifier along with my request to exchange the Auth Code for a token. I ended up sending this code_verifier to my API server then adding this to the request so it looked something like this:
FB.api(
'oauth/access_token',
{
client_id: appId,
client_secret: appSecret,
redirect_uri: redirectUri,
code_verifier: code_verifier,
code: code,
},
function (response) {
if (!response || response.error) {
console.log(!response ? 'error occurred' : response.error);
return;
}
var accessToken = response.access_token;
This then finally gave me the accessToken I was looking for that I can then use to exchange for user details server side.
... and the code_verifier is obtained from request.codeVerifier.
const [request, response, promptAsync] = Facebook.useAuthRequest(...
This question builds on How to get Microsoft Graph API Access token from Node Script?, however, as a first-time user of, I don't have the required reputation for commenting on the accepted answer in that thread.
The thing is, I tried to implement the approach suggested by the accepted answer, but somewhere it goes wrong. The below code is part of an async function, and I can already tell you that the ONEDRIVE_TENANT_URI is of the format XXX.onmicrosoft.com.
const endpoint = `https://login.microsoftonline.com/${process.env.ONEDRIVE_TENTANT_URI}/oauth2/token`;
const requestParams = {
grant_type: "client_credentials",
client_id: process.env.ONEDRIVE_APP_ID,
client_secret: process.env.ONEDRIVE_CLIENT_SECRET,
resource: "https://graph.windows.net"
};
const authResponse = await request.post({
url: endpoint,
form: requestParams
});
authResponse gets, as its body, just a string with the requestParams as defined above filled out.
If I submit the post request via Postman, with the same parameters as x-www-form-urlencoded, I DO get an access_token in the response body.
So... What do I do wrong? Maybe - but I don't think so - it's because this function is invoked by a (for testing purposes) POSTMAN GET request with a json-formatted body?
You can download the sample here. And fill in the credentials in config.js. You can find them from Azure portal.
This is the code to get access token.
auth.getAccessToken = function () {
var deferred = Q.defer();
// These are the parameters necessary for the OAuth 2.0 Client Credentials Grant Flow.
// For more information, see Service to Service Calls Using Client Credentials (https://msdn.microsoft.com/library/azure/dn645543.aspx).
var requestParams = {
grant_type: 'client_credentials',
client_id: config.clientId,
client_secret: config.clientSecret,
resource: 'https://graph.microsoft.com'
};
// Make a request to the token issuing endpoint.
request.post({ url: config.tokenEndpoint, form: requestParams }, function (err, response, body) {
var parsedBody = JSON.parse(body);
console.log(parsedBody);
if (err) {
deferred.reject(err);
} else if (parsedBody.error) {
deferred.reject(parsedBody.error_description);
} else {
// If successful, return the access token.
deferred.resolve(parsedBody.access_token);
}
});
return deferred.promise;
};
You will get the access token successfully.
You've got two issues going on.
The first isn't an issue yet, but it will be once you try to call Microsoft Graph. The resource should be graph.microsoft.net, not graph.windows.net. The graph.windows.net refers to the legacy Azure AD Graph API, not Microsoft Graph.
The other issue, which is the root cause of this error, is await request.post. Request doesn't natively support promises. From the Request the documentation:
request supports both streaming and callback interfaces natively. If you'd like request to return a Promise instead, you can use an alternative interface wrapper for request. These wrappers can be useful if you prefer to work with Promises, or if you'd like to use async/await in ES2017.
Several alternative interfaces are provided by the request team, including:
request-promise (uses Bluebird Promises)
request-promise-native (uses native Promises)
request-promise-any (uses any-promise Promises)
I am implementing JWT tokens for authentication.
I am not using any client-site framework like Angular or React. It is just EJS.
Step 1. I have an API developed that on successful login returns the token as shown on the picture below(I am using Postman for testing API):
API response with JSON
Step 2. I am then accessing the restricted route and passing along the Authorization header with token value, by inputing it manually in the Postman and it works just fine.
My question is WHERE and HOW do I save the returned token from the step 1 on the client, so that it is sent in the header on step 2.
I am novice to web-development and following the tutorials, but all the tutorials I found about implementing the JWT token are written for Angular or React when it comes to the client site.
Any help would be greatly appreciated.
Thank you!
First you must create the token with JWT :
const token=jwt.sign({
userId: user._id
},config.secret, {expiresIn: '24h'});
res.json({success:true,message:'Success', token:token, user: {username:user.username}});
Then in your front youcan save it into the localStorage
This will generate a unique key that you can implement in your header
After that in your routes when you want to check if there's a JWT in the header just make :
router.use((req,res,next)=>{
const token=req.headers['authorization'];
if(!token){
res.json({success:false, message:"No token provided"});
}
else{
jwt.verify(token, config.secret, (err,decoded)=>{
if(err){
res.json({success:false,message:"Token invalid: " + err});
}
else{
req.decoded=decoded;
next();
}
});
}
});
This is a middleware that will check if there's a JWT key in "authorization" header
Note that every route coming after this one are going to run this middleware.
Here You 'll find every details about JSON Web Tokens
EDIT
Here's how you could do with an AJAX request:
$("submit").click(function(){
$.ajax({
url : 'api/login,',
type : 'POST',
data : {login: $('#login').val(),password:$('#password').val()}
dataType : 'JSON',
success : function(data, statut){
localStorage.setItem('token',data.token) // assuming you send a json token
},
error : function(resultat, statut, erreur){
// whatever code you want
},
complete : function(resultat, statut){
}
});
});
Have an object where you store the token, if you want to keep the token after the user leaves so that he will remain connected as long as the token is valid, you can save it in the localStorage.
And when doing AJAX requests on client side, retrieve the token from your object of from localStorage and set the token in the Authorization header.
I have a 3-legged authentication token, and for some reason when I access the API, I get the following error:
"reason":"Only 2 legged service tokens are allowed to access this api."
This is how I access the API (using the npm package curlrequest, and replacing with my token):
var options = {
url: 'https://developer.api.autodesk.com/project/v1/hubs',
method: 'GET',
headers: {
'Authorization': 'Bearer <Token>'
}
};
curl.request(options, function (err, parts) {
parts = parts.split('\r\n');
var data = parts.pop()
, head = parts.pop();
console.log(data);
});
Am I doing something wrong? Is it only possible to access the Data Management API with a 2-legged token?
Thanks.
So, yes, you can only use 2-legged tokens on the Data Management API. You cannot use 3-legged tokens instead of 2-legged ones, they are meant to do different things.
I'm writing a rule in Auth0 to trigger a verification email if a certain condition is met. To make the example small I have included the code which I am using to send the verification mail (I have removed out the unwanted code).
var url = 'https://myname.au.auth0.com/api/v2/jobs/verification-email';
var token = 'Bearer {{token}}'; //This is where the problem is how do I get the token
var userId = user.user_id;
request.post({
url: url,
headers: {
Authorization: 'Bearer {{token}}',
},
json: {
"user_id": user.user_ID
},
timeout: 5000
},
function(err, res, body) {
console.log(err);
console.log(res);
});
In the body I get the following error
{ statusCode: 400,
error: 'Bad Request',
message: 'Bad HTTP authentication header format',
errorCode: 'Bearer' }
I guess I need to pass in the access token or something like that in the header. How do I get this done?
I also saw the following article (https://auth0.com/docs/email/custom), however I'm not sure what secretToken is?
Starting from the bottom, the article (https://auth0.com/docs/email/custom) is aimed at users that want additional flexibility and use their own custom email handling. The secretToken on that example it's just to illustrate a possible - and very simple - way that their own custom email API could validate that they were being called from Auth0; in conclusion it would work almost as an API key.
If you only need to trigger a verification email through the system provided by Auth0 you're using the correct approach (Management API v2). You have more than one way to obtain a token that allows you to call this API:
Using the client credentials grant
Using the Auth0 Management API v2 Explorer
The second option would be the easiest to get started, but do take in consideration that there's a deprecation notice for that one.
Once you obtain the token, you also need to correctly pass it to the API. The code you showed may be only sample code, but make sure that you don't end up including the Bearer scheme twice, more specifically var token = 'Bearer {{token}}'; should instead just be var token = '{{token}}'; and then you would use the token variable when creating the HTTP header.
Just created the below empty rule that will get called when user tries to login and email is not yet verified and it works like a charm :D
function (user, context, callback) {
if (!user.email_verified) {
console.log("User is: " + user.user_id);
var ManagementClient = require('auth0#2.6.0').ManagementClient;
var management = new ManagementClient({
token: auth0.accessToken,
domain: auth0.domain
});
var new_userobj = {user_id:user.user_id};
management.sendEmailVerification(new_userobj,callback(new UnauthorizedError('Please click on the link in the email we have sent you to continue to login.')));
} else {
return callback(null, user, context);
}
}
I received the same error when using the wrong token, though for a different api call. I recreated your issue by using a user's access_token obtained by calling {{api-audience}}users/{{user_id}}. That token should look something like this: A1bCd2efg34IJkl5
Try using a client's access_token obtained by making this call:
curl --request POST \
--url https://{{domain}}/oauth/token \
--header 'content-type: application/json' \
--data '{
"client_id":"{{client_id}}",
"client_secret":"{{client_secret}}",
"audience":"{{audience}}",
"grant_type":"client_credentials"
}'
That token will be a full JWT.