I'm using express and passport-linkedin. The authentication flow works fine, I can get the user's basic profile, and now I want to make an API request. /v1/people/~/connections, for example.
I've done some reading and trial and error, but all I get is Wrong Authentication Scheme or Internal Server Error.
I have the token and tokenSecret given to the verify callback of the LinkedInStrategy instance. How do I go from those to an authorized API request?
I use a 'isLegit' function running as middleware.
Example :
app.get('/api/mysecuredpage',isLegit,function(req,res){
res.render('mysecurepage');
});
function isLegit(req, res, next) {
// if user is authenticated in the session, next
if (req.isAuthenticated())
return next();
// if they aren't send an error
res.json({error:'You must be logged in to view information'});
}
EDIT :
To make a request to the linkedIn api, just set up your request with the following options :
var options = {
url: 'https://api.linkedin.com/v1/people/~/connections',
headers: { 'x-li-format': 'json' },
qs: { oauth2_access_token: user.access_token }
};
request(options,function(err,res,body){
console.log(body);
});
access_token is the name of the variable in your passport strategy, it could be different depending how you've set it up. basically, it's one of the fields of your authenticated user ;-)
Related
Question appeared while integrating Spotify API into Nodejs Express web application using spotify-web-api-node. How multiple simultaneous user requests should be handled? After passing the authentication step, user receives access_token, which is different for each user. Each request can have a session, for example using express-session since access_token is unique for each authenticated user. The weird thing is that I can't find an example with proper session usage in the description and samples https://www.npmjs.com/package/spotify-web-api-node where spotify-web-api-node is used. How is that possible to use global variable without session? Would it make full mess among separate user requests or I'm missing something? I guess that the access_token would be always replaced with latest authenticated user. Another usage example is here https://github.com/thelinmichael/spotify-web-api-node, though it also suggests to use one global instance.
the solution is to store the access_token and refresh_token after successful authentication in the session storage, than before calling Spotify API endpoints set both tokens for the current user from the present session:
saving tokens in the session after successful authentication:
app.get('/login', (req,res) => {
var scopes = [ ... ]
var authUrl = spotifyApi.createAuthorizeURL(scopes)
res.redirect(authUrl+"&show_dialog=true")
})
app.get('/callback', async (req, res) => {
const { code } = req.query
try {
var data = await spotifyApi.authorizationCodeGrant(code)
const { access_token, refresh_token } = data.body
spotifyApi.setAccessToken(access_token)
spotifyApi.setRefreshToken(refresh_token)
req.session.spotifyAccount = { access_token, refresh_token }
res.redirect('...')
} catch(err) {
res.send(`error ${err}`)
}
});
app.get('/userinfo', async (req,res) => {
try {
spotifyApi.setAccessToken(req.session.spotifyAccount["access_token"])
spotifyApi.setRefreshToken(req.session.spotifyAccount["refresh_token"])
var result = await spotifyApi.getMe()
console.log(result.body);
res.status(200).send(result.body)
} catch (err) {
res.status(400).send(err)
}
});
since access_token is only identification key which identifies any API request, that ensures that API endpoints are called for the current user. This technique prevents mess and confusion, so that each user can see and manipulate his data only.
I created an app on sandbox.pipedrive.com in Marketplace Manager and then I created a callback which asked user to install the app which I setup in pipedrive.
If user allow to install they get redirected to my callback url in controller, my controller the code is :-
app.get('/pipedrive-callback', function(req, res) {
console.log('Success')
});
Now in function I want to exchange the auth token. Can anyone help me to solve this.
Can you try this?
You need to send another post request to their server after user is redirected to your callback. After the redirection you will get the authorization_code from the request params. You have to send that code in this post request to get the actual tokens that will allow you to do magic.
app.get('/pipedrive-callback', function (req, res) {
console.log('Success');
const authorization_code_from_service = req.query.code; // This will extract the authorization_code from the call back url.
//Here goes your step 4 + 5. You need to make a post request to their server now. For this, there is a library aka "request" in npm.
// Here is the link for that https://www.npmjs.com/package/request
const request = require("request");
const formData = {
"grant_type": "authorization_code",
"redirect_uri": "rediect url that you have set for your app",
"code": authorization_code_from_service
}
request.post({
url: 'https://oauth.pipedrive.com/oauth/token',
form: formData
},
function (err, httpResponse, body) {
//This will be the data that you need for further steps. Actual token, expiry time etc
console.log(body);
}
);
});
Npm link : https://www.npmjs.com/package/request
I want to write a stateless node api with passport.js for authentication.
I want to create an oauth google api endpoint that returns the api key on success and other details on success.
Google API route
router.get('/',passport.authenticate('google', { scope : ['profile', 'email']}));
router.get('/callback', function(req, res, next) {
passport.authenticate('google', { session: false, failureRedirect: '/#/login'}, function(err, user, info) {
console.log('------------------in google callback-----------------');
if (err) res.status(500).json({status_code: 500, message: "Authentication error", data : err});
if (!user) {
res.status(404).json({status_code: 404, message : "User not found/created"});
}
var token = 'JWT KEY TO USE AS API TOKEN';
res.status(200).json({status_code: 200, message : "Success", data : {token : token, user:user }});
})(req, res, next);
});
When i open my api endpoint in browser on successfull authentication the json response is just printed on the screen and i have no way to capture the same in a variable. Calling the same api endpoint via ajax doesn't work.
This is how i recieve the json response
How do i create a google login api endpoint that works without
sessions and returns a token?
How can i run this via ajax? Is there any way to call this api end point and recieve the response in a success callback?
The Google authentication process needs to happen in the client (so the browser will always open that new page) and then the Google server needs to contact your server directly (at your callback endpoint), otherwise the client could just pretend they were authenticated.
So if you are trying to do this in a single request to your server or without a server, that is not possible.
I am trying to build a token system to allow authentication via an email link. The flow I am thinking might work is...
click email link (of the form site.com/login?token=hf74hf64&email=m#email.com) -> server checks the token is valid and the email is registered -> server redirects to '/' with a session cookie -> client acknowledges session cookie and authenticates the user
The last step is where I'm having trouble. How do I detect from within my component that a session cookie is present?
I was thinking of something like this in my React auth component:
class AuthenticatedComponent extends Component {
componentWillMount() {
if (cookie) {
this.props.dispatch(authenticateUser())//.....
}
}
}
Might this work, or do I need to make a separate fetch to the server and trigger the dispatch depending on the response?
We've implemented a very similar approach for our app. For this to work, we handle all the login in Node and not in the actual components.
Check if token is provided in query string
Pass token to server to validate
If token is valid, create the cookie as you would for a normal user/pass login
Redirect call to original url, sans the token
server.js
// I abstracted the login functionality into one call since it's the same for us
var handleAuthRequest = function handleAuthRequest(auth_body, req, res, next) {
request ({
method: 'POST',
uri: Constants.API_LOGIN_URL,
body: auth_body,
json: true
}, (error, response, body) => {
if (response.statusCode === 200) {
// this makes a cookie with the response of the body (auth token)
ssoUtils.generateCookies(body, res)
// this redirects to the initial url, with the provided cookie.
// Assuming your router already doesn't allow certain components to be accessed
// without a cookie, it would work the same for the login_token auth too.
res.redirect(req.url)
}
else {
next();
}
})
}
// this needs to come before any other routes
app.use((req, res, next) => {
// check if login_token query string was provided
if (req.query.hasOwnProperty('login_token')) {
var {login_token} = req.query
// API call to server to validate token
var jwtToken = jwt.sign({
sub: login_token
}, Constants.API_JWT_SECRET)
// modify the redirect url to remove the token
let parsed = url.parse(req.url)
delete req.query['login_token']
let newUrl = parsed.pathname + '?' + qs.stringify(req.query)
req.url = newUrl
// call the generic login handler
return handleAuthRequest({link_token: jwtToken}, req, res, next)
}
Assuming your server will return the same response from logging in or a valid link token, this would just redirect the call back to whatever your existing process is so no separate functionality client side is needed. As you can see, we also sign the token in a JWT to ensure it's only accepted by the server if sent from our app.
We use React Router to handle our client side routing. Your onEnter check for the initial route would look like this.
routes.js
// token is passed in from our cookie by both the client and server
module.exports = function (token, userAgent, originalUrl) {
function isToken() {
return token !== undefined && token !== null;
}
function ifNoTokenRedirect(nextState, replaceState) {
// check if token is set from cookie
if (!isToken()) {
replaceState({ nextPathname: nextState.location.pathname}, '/signup? redirect=' + originalUrl.pathname);
}
}
return (
// the actual routes
)
}
I am using Express 3.x and connect-mongo and the request module
My App has some middleware that ensures the external request has an access_token.
The access_token is checked and a some data is stored in the session.
I then want to make an internal call to a url within the application, but the internal call gets issued a new session (as its a separate request from the users browser request).
What I want to do is somehow copy the Express signed cookies across into the internal request() so that the middleware performs actions based on the original external session id.
I have tried passing a cookie jar into the request object but it doesnt seem to support signed cookies very well. Any ideas how I can do this?
/* Middleware to check access tokens */
app.all("/*", requireAuth, function(req, res, next) {
next();
});
function requireAuth(req,res,next) {
if ( req.query.access_token && !req.session ) {
// Check the access token and popualte stuff in the session
req.session.somedata = 'test';
// Then call a url internall to do something
// My issue is that this INTERNAL request below gets a new session Id
// so it itself returns Not Authorised as its hits the same code
request({
url: 'someurlinside-this-node-app',
method: "GET"
}, function _callback(err, serviceres, body) {
next();
});
}else{
res.send('Not Authorised');
}
}
Cookies are just another header so if you want to pass it along you should be able to do this:
var cookies = req.header('Set-Cookie');
request({
url: 'someurlinside-this-node-app',
method: "GET",
headers: { 'Set-Cookie' : cookies}
}