How to use the Spotify Access Token outside of an Express Route - node.js

I am currently learning Node.js and Express and wanted to build a simple app that queries the Spotify Web API without using an external library such as spotify-web-api-node. My code is available at https://pastebin.com/Jwe8sckJ
My "/callback" route looks like this;
app.get("/callback", function(req, res){
res.send("OK!")
var authCode = req.query.code
var options = { method: 'POST',
url: 'https://accounts.spotify.com/api/token',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
form:
{ grant_type: 'authorization_code',
code: authCode,
redirect_uri: 'https://example.com/callback',
client_id: clientId,
client_secret: clientSecret } };
request(options, function (error, response, body) {
if (error) throw new Error(error);
var accessToken = JSON.parse(body).access_token;
var refreshToken = JSON.parse(body).refresh_token
console.log("Access Token: " + accessToken);
console.log("Refresh Token: " + refreshToken);
});
});
So far this is all working fine, I am able to get an Access Token and a Refresh Token. The part that I am confused about is how do I now use these tokens outside of the "/callback" route? Or maybe another way of asking would be how do I save the variables globally. If I do that, would it have any impact on new sessions to script?

The most common solution would be to store these tokens in a database, i.e. MongoDB or MySQL and retrieve them when needed. You'll probably want to use sessions to set the received tokens per client.
You could even save the tokens in the session, but please note that sessions are volatile.

If I got you right then you want to save access token for later use. You can declare global variable and can use it later.
global.spotify_access_token = '';
Declare this variable at the start of any file. Now you can use this variable to save access token in callback.like below :
spotify_access_token = JSON.parse(body).access_token;
Hope this will help you.
Thanks.

Related

Node.js back-end application on Firebase to access Azure Active Directory users

I have a Node.js back which currently is running on Firebase in the form of cloud functions. Except for that, I also have an Azure Active Directory with some users that I have invited
So, I want to be able to access them from the Node.js get a list of their emails and names. From what I understood, I can achieve that by making a reference to Microsoft's Graph API and more specifically to their Users API. As every request to Azure AD needs to be OAuth2 authenticated, I was wondering what is the best way of achieving that in my situation. What client flow do I need to implement? I am currently focused on the one which is based on client credentials.
Thanks in advance and whatever general suggestion are more than welcome!
This issue gets Access token and calls Microsoft Graph API using node.js.
The user API of Azure Active Directory Graph API is no longer updating. This MS graph API is newer.
Get access token using client credentials flow:
const request = require("request");
const endpoint = "https://login.microsoftonline.com/[Tenant]/oauth2/v2.0/token";
const requestParams = {
grant_type: "client_credentials",
client_id: "[ApplicationID]",
client_secret: "[Key]",
scope: "https://graph.microsoft.com/.default"
};
request.post({ url:endpoint, form: requestParams }, function (err, response, body) {
if (err) {
console.log("error");
}
else {
console.log("Body=" + body);
let parsedBody = JSON.parse(body);
if (parsedBody.error_description) {
console.log("Error=" + parsedBody.error_description);
}
else {
console.log("Access Token=" + parsedBody.access_token);
}
}
});
Call MS Graph API:
function testListGroupGraphAPI(accessToken) {
request.get({
url:"https://graph.microsoft.com/v1.0/users",
headers: {
"Authorization": "Bearer " + accessToken
}
}, function(err, response, body) {
console.log(body);
});
}

Why do I get a 404 response when I try to post to github API whilst authenticated with oauth access tokens?

I am using the passportjs library to authenticate users into the application. An access token is usually generated when users authenticate successfully with passportjs. I am attempting to create a branch with the github API with this access token but without much success, both using the octokit wrapper as well as posting with super-agent.
I first attempted to authenticate the octokit by providing it username and password, in this fashion.
let octokit=new Octokit({
auth:{
username:username,
password:password
}
});
I was then able to create a ref/branch without much issue. However, when I did the same but with accesstokens generated by github SSO, like this
passport.use(new GitHubStrategy({
clientID: keys.clientId,
clientSecret: keys.clientSecret,
callbackURL: "/auth/github/callback"
},
async (accessToken, refreshToken, profile, done) => {
let octokit = new Octokit(auth: `token ${accessToken}`);
const branchName = 'refs/heads/vue' + Math.random();
let masterHash = '123x123x1231232';
octokit.git.createRef({
owner: owner,
repo: 'gitDemo',
ref: branchName,
sha: masterHash
}).then((data) => {
console.log('data ', data);
});
}
I receive an HttpError: Not found error. Another method that I tried is to post directly to the end point with superagent, putting the acccess code in the authorization header.
const data={
ref:'refs/heads/FooBranch',
sha:masterHash
};
const res2=await request.post('https://api.github.com/repos/SomeOwner/SomeRepo/git/refs')
.set('Authorization','token '+accessToken)
.send(data);
However, I still receive an HttpError :not found issue. I am quite confused as to what I may have done wrong. Thank you and any help would be greatly appreciated!
I found the anwser here
Basically you don't send data using JSON but rather FormData.
So the post should look like this (copied from link):
let data = new FormData()
data.append('client_id', options.client_id)
data.append('client_secret', options.client_secret)
data.append('code', code)
fetch(`https://github.com/login/oauth/access_token`, {
method: 'POST',
body: data
})
In case anyone else comes across this in the future, you have to specify the Content-Type and Accept when making the request. Without specifying it in the headers you will have to send and receive FormData.
Kudos #Github for not mentioning this at all in their docs.
Using node's built in fetch:
const githubRes = await fetch(githubUrl, {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
code,
client_id: config.github.clientId,
client_secret: config.github.clientSecret,
redirect_uri: config.github.redirectUri,
}),
});
const githubJson = await githubRes.json();
const token = githubJson.access_token;

Auth0 + JWT + NodeJS + Express End-user authentication (Login)

Has been almost a year that I switch to Auth0 in order to manage my customer's access to the dashboard of my application. Nowadays I need to implement access for a RESTFULL API.
If I follow the instructions in order to secure the NodeJS app using JWT it works like a charm. The issue is that I am not properly sure on the implementation for the end user in order to get the token needed for access this API.
I thought of creating the tokens on the dashboard or just use a server side implementation for the login/authentication. I did the last using the access to my own database before and worker amazingly. My issue is that I am not completely sure on how to do it for the end user using Auth0.
Anyone implemented a RESTfull API that has login using Auth0 before in order to get the JWT token ? Would my great to hear your thoughts.
The solution was to use a different approach.
There is an Auth0 endpoint that uses the user and password for the user in order to login with the service. This way I can get the id of the authenticated user and a JWT token that I can use to validate future requests.
https://auth0.com/docs/api/authentication#resource-owner-password
This flow should only be used from highly trusted applications that cannot do redirects. If you can use redirect-based flows from your apps we recommend using the Authorization Code Grant instead.
router.post('/login', function (req, res, next) {
var options = {
method: 'POST',
url: process.env.AUTH0_URL_OAUTH,
headers: {
'Cache-Control': 'no-cache',
'Content-Type': 'application/json'
},
body: {
grant_type: 'password',
username: req.body.username,
password: req.body.password,
audience: process.env.AUTH0_AUDIENCE,
scope: process.env.AUTH0_SCOPE,
client_id: process.env.AUTH0_CLIENT_ID,
client_secret: process.env.AUTH0_CLIENT_SECRET
},
json: true
};
request(options, function (error, response, body) {
if (error) {
res.sendStatus(500); //We could not connect to the service
} else {
if (body.error) {
console.log(body);
res.status(400);
res.send({
error: body.error_description // There was an error with the user or password
});
} else {
console.log(body);
/**
* Everything went well. We return the JWT
*/
res.send({
access_token: body.access_token,
expires_in: body.expires_in,
token_type: body.token_type
});
}
};

Node/react best practice: How do I keep track on the current client/user in OAuth2 flow?

I'm a beginner with Node and React, and web programming in general. I want to import user credentials from LinkedIn's API and for that I need to authenticate using OAuth2.
My approach so far is to make an API-call from the client side to the LinkedIn oauth API with the relevant parameters, including a redirect URI which leads to an API endpoint on my node server. When the user has been redirected and approved LinkedIn's authentication dialog box, they will be redirected to the node server with an access token.
My question is as follows: I now want to update the user in my database with their corresponding access token, but how do I know which user to update when I can't get any information about the client in my function that handles the last redirect and fetches the access token?
Here's my node function that handles the redirect from LinkedIn:
router.get('/redirect', (req, res) => {
// Handle cancel by user
if(req.query.error){
console.log(req.query.error_description)
return
}
// Extract variables
const code = req.query.code
const state = req.query.state
// Check that state matches
if (state !== testState) {
console.log("State doesnt match")
return
}
// Exchange Authorization Code for an Access Token
var options = {
method: 'POST',
url: 'https://www.linkedin.com/oauth/v2/accessToken',
form: {
client_id: 'theClientID',
client_secret: 'theClienSecret',
grant_type: 'authorization_code',
code: code,
redirect_uri: 'http://localhost:3000/api/linkedin/redirect'
},
headers:
{ 'cache-control': 'no-cache',
"content-type": "application/json",
'user-agent': 'node.js' },
json: true };
// make the actual request
request(options, (error, response, body) => {
if (error) {
res.status(500).json({
message: error
})
return
}
// Extract access token
const token = body.access_token;
// Here I want to save access token to DB with the corresponding
// user, but I don't know which user to update
})
// Redirect user to profile
res.writeHead(301, {
Location: 'http://localhost:3000/profile'
})
res.end()
})
I had a really hard time formulating this question but I hope that my message gets through.

Using dropboxjs to authenticate the client with oauth 2. What about the server?

I'm new to Oauth and server-side stuff, so please be patient with me.
I have a web application that authenticates users with dropbox-js. Everything is pretty straightforward. The application uses dropbox-js' client.authenticate function, and if the user is authenticated, the application gets automatically redirected to the initial page, where it executes the authenticate callback. From that moment on, I know I'm happily authenticated with Dropbox, and I can do stuff with the app's Dropbox directory.
I got a public node.js server that currently does nothing. What I would like to do is:
As soon as the client is authenticated, call my server and tell it that the user is authenticated
If the user doesn't exist on the server database, create an entry for it him/her the user database (I don't need detailed instructions to do this). If it exists, send back the user's associated data.
How can I do that in a secure way? I mean, how can the server tell that the user is a valid Dropbox user? Should the server authenticate to Dropbox on its side with the user credentials? What is the workflow in these cases?
At the end of the authentication process, you have an access token, which is what's used to make calls to the API. If both the client and the server need to make calls to the API, then both will need to have the access token.
If you're doing the authentication client-side today, you could pull the access token out somehow (not sure if/how it's exposed from the library, but it's in there somewhere and also storaged in local storage) and pass it to the server. The server can then use it to call /account/info and get the Dropbox user ID of the authenticated user.
An alternative is to do it the other way around. Authenticate the user with the "code flow" (rather than "token flow") and get the access token on the server in the first place. Then you could pass it down to the client and pass it as an option in the Dropbox.Client constructor. I think that dropbox-js supports this itself, but it's also not hard to do yourself. Here's some raw Express code that logs in a user and displays his or her name:
var crypto = require('crypto'),
express = require('express'),
request = require('request'),
url = require('url');
var app = express();
app.use(express.cookieParser());
// insert your app key and secret here
var appkey = '<your app key>';
var appsecret = '<your app secret>';
function generateCSRFToken() {
return crypto.randomBytes(18).toString('base64')
.replace(/\//g, '-').replace(/\+/g, '_');
}
function generateRedirectURI(req) {
return url.format({
protocol: req.protocol,
host: req.headers.host,
pathname: app.path() + '/callback'
});
}
app.get('/', function (req, res) {
var csrfToken = generateCSRFToken();
res.cookie('csrf', csrfToken);
res.redirect(url.format({
protocol: 'https',
hostname: 'www.dropbox.com',
pathname: '1/oauth2/authorize',
query: {
client_id: appkey,
response_type: 'code',
state: csrfToken,
redirect_uri: generateRedirectURI(req)
}
}));
});
app.get('/callback', function (req, res) {
if (req.query.error) {
return res.send('ERROR ' + req.query.error + ': ' + req.query.error_description);
}
// check CSRF token
if (req.query.state !== req.cookies.csrf) {
return res.status(401).send(
'CSRF token mismatch, possible cross-site request forgery attempt.'
);
} else {
// exchange access code for bearer token
request.post('https://api.dropbox.com/1/oauth2/token', {
form: {
code: req.query.code,
grant_type: 'authorization_code',
redirect_uri: generateRedirectURI(req)
},
auth: {
user: appkey,
pass: appsecret
}
}, function (error, response, body) {
var data = JSON.parse(body);
if (data.error) {
return res.send('ERROR: ' + data.error);
}
// extract bearer token
var token = data.access_token;
// use the bearer token to make API calls
request.get('https://api.dropbox.com/1/account/info', {
headers: { Authorization: 'Bearer ' + token }
}, function (error, response, body) {
res.send('Logged in successfully as ' + JSON.parse(body).display_name + '.');
});
// write a file
// request.put('https://api-content.dropbox.com/1/files_put/auto/hello.txt', {
// body: 'Hello, World!',
// headers: { Authorization: 'Bearer ' + token }
// });
});
}
});
app.listen(8000);

Resources