access_token not present in the passport-github2 request - node.js

I have registered an OAuth App via my Github account. I am basically trying to authorize my node requests(by sending access_token as part of request cookies) so I can access few APIs on another server. Hence I am using the github-passport2 package. I have setup the github strategy etc. & it seems to be all according to the doc. The flow works well too.
My Issue
After logging into Github(authorized) & getting redirected back to my /auth/github/callback, I ideally should be authorized and should have an access_token in the req. But I don't have it! Because of this I am not able to authorize my future requests with an access_token.
Important to note is that, this access_token is automatically attached when the request is initiated from a browser/client(using withCredentials: true parameter). The same access_token via node doesn't seem to be retrievable.
passport.use(new GitHubStrategy({
clientID: GITHUB_CLIENT_ID,
clientSecret: GITHUB_CLIENT_SECRET,
callbackURL: "http://localhost:8080/auth/github/callback",
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
return done(null, profile);
});
}
));
app.get('/auth/github', passport.authenticate('github', { scope: [ 'user:email' ] }), function(req, res){
// The request will be redirected to GitHub for authentication, so this
// function will not be called.
});
app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), function(req, res) {
console.log(req); // <- This ideally should have the access_token? but doesn't
});
I have struggling for days on this. Any help is much appreciated.

Related

Authentication using linkedin in a mean stack application

so I've been trying to implement login with linkedin in my application, and I couldn't find anything online that could show me the steps from A to Z
I implemented the backend and the frontend separateley, however, I don't know how to link them together
In the backend, I'm using passportjs
So here's what I've done so far:
FRONTEND
app.component.html
<button click="loginWithLinkedin()">Linkedin</button>
app.component.ts
window.location.href = `https://www.linkedin.com/uas/oauth2/authorization?response_type=code&state=true&client_id=${environment.LINKEDIN_API_KEY}&redirect_uri=${environment.LINKEDIN_REDIRECT_URL}&scope=${environment.LINKEDIN_SCOPE}`;
redirect.component.ts
const linkedInToken = this.route.snapshot.queryParams["code"];
this.http.get('http://localhost:3000/user/auth/linkedin',
{ params: { token: linkedinToken }}).subscribe(res => {
console.log(res);
});
BACKEND
passport.use(new LinkedInStrategy({
clientID: LINKEDIN_CLIENT_ID,
clientSecret: LINKEDIN_CLIENT_SECRET,
callbackURL: "http://127.0.0.1:8000/user/auth/linkedin/callback",
scope: ['r_emailaddress', 'r_basicprofile'],
passReqToCallback: true
},
function (req, accessToken, refreshToken, profile, done) {
req.session.accessToken = accessToken;
process.nextTick(function () {
return done(null, profile);
});
}));
linkedinRouter.route('/auth/linkedin')
.get(passport.authenticate('linkedin', { state: 'SOME STATE' }),
function(req, res){
// The request will be redirected to LinkedIn for authentication, so this
// function will not be called.
});
linkedinRouter.route('/auth/linkedin/callback')
.get( passport.authenticate('linkedin', { failureRedirect: '/' }),
function (req, res) {
return res.send('hello');
});
I don't understand how would passport work and I don't understand how to link backend and frontend.
I don't know if this is the correct way to implement linkedin authentication
If you have any articles that could guide, or if you can correct that would really help, I've been stuck for a couple of days now.
Thank you very much
I apologize if I formulated the question in a bad way or I didn't know how to ask the question. However, I managed to make it work and I wrote a medium article about it.
here's the link:
https://nour-karoui.medium.com/linkedin-authentication-for-a-mean-stack-application-bd8542b2dc7f
I hope it can help somebody out there !

Battle.net API returns 401 Error using OAuth Token

I'm using Nodejs with express on the Battle.net API to generate an Oauth Token. https://develop.battle.net/documentation/guides/using-oauth
Generating the token itself works, as it returns me the token. But when I use the code to make a request to their API, for example:
https://eu.api.blizzard.com/wow/guild/Malfurion/The%20new%20Dimension?fields=members&locale=de_DE&access_token=HEREISMYTOKEN
I get a 401 Unauthorized Error Response, debug log:
{ url:
'https://eu.api.blizzard.com/wow/guild/Malfurion/The%20new%20Dimension?fields=members&locale=de_DE&access_token=HEREISMYTOKEN',
status: 401,
statusText: 'Unauthorized',
headers: Headers { [Symbol(map)]: [Object] } }
I'm trying to fetch the members of a guild via fetch().
I already tried:
Creating a new Application (with new Client Secret and ID)
setting every possible callback url in the battle.net settings:
https://localhost/
http://localhost/
https://localhost:443/
http://localhost:443/
https://localhost/auth/bnet/callback
http://localhost/auth/bnet/callback
https://localhost:443/auth/bnet/callback
http://localhost:443/auth/bnet/callback
creating a token manually by "trying out the api" (https://develop.battle.net/documentation/api-reference/world-of-warcraft-community-api), where you put in your Client ID and Secret and then get a temporary Token. THAT ONE works, also in my application.
You can compare response of those two urls (just use your browser):
First (Generated in my application): https://eu.api.blizzard.com/wow/guild/Blackmoore/The%20new%20Dimension?fields=members&locale=de_DE&access_token=EU7XD8E4K9IAJKBGJSP3MDBLAVCIU2BYXS
Second (Generated trying out the API on battle.net website where you fill out clientid and secret to test out the api): https://eu.api.blizzard.com/wow/guild/Blackmoore/The%20new%20Dimension?fields=members&locale=de_DE&access_token=US23su4g0hAeS5w3EUCkKA9MJPgJ8k8bzV
CODE
server.js, simple express app
var BNET_ID = "MYID";
var BNET_SECRET = "MYSECRET";
...
// Use the BnetStrategy within Passport.
passport.use(
new BnetStrategy(
{ clientID: BNET_ID,
clientSecret: BNET_SECRET,
scope: "wow.profile sc2.profile",
callbackURL: "https://localhost/",
region: "eu" },
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
return done(null, profile);
});
})
);
// bnet auth routes
app.get('/auth/bnet', passport.authenticate('bnet'));
app.get('/auth/bnet/callback',
passport.authenticate('bnet', { failureRedirect: '/' }),
function(req, res){
res.redirect('/');
});
controller.js
...
const res = await fetch(`https://eu.api.blizzard.com/wow/guild/${servers[iterator]}/The new Dimension?fields=members&locale=de_DE&access_token=${thetoken}`).then((res) => {
res.json();
// for debugging, shows 401 Error
console.log(res);
});
...
I actually expect a response such as this, because it works using a temporary Token:
status: 200 OK
body: {
"lastModified": 1546676373000,
"name": "The new Dimension",
"realm": "Blackmoore",
"battlegroup": "Glutsturm / Emberstorm",
"level": 25,
"side": 0,
"achievementPoints": 1005,
"members":
(......)
}
I managed to resolve the issue!
Very, very hacky but I managed to resolve the issue by hacking the oauth callback middleware like this:
set my used API token to the req.user.token.
app.get('/auth/bnet/callback',
passport.authenticate('bnet', { failureRedirect: '/?error' }),
function(req, res) {
req.session.bnettoken = req.user.token;
res.redirect('/');
}
);
I suspect that "code" or "token" is also used in my SessionStorage (express-session) to store the current session in my database. So I just hack the user.token out of the request and use that. Phew.. Hours of work.
From the documentation I can see that you need to pass the token into the Authorization header with value of : Bearer HEREISMYTOKEN.
More info on Authorization Header and Headers here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Headers
Example on how to use it can be found in this SO answer

Handling authentication in Nodejs with passport-facebook-token, request coming from frontend Facebook SDK

I am working on a Unity App. For login, there are two methods, one using Email and another using Facebook. In case of login separately, I do not have any problem. Registration and Login with Email works perfectly. And Login with Facebook works perfectly as well. Here's the workflow, I created just to make you clear.
tl;dr [read update]
There's another schema for account, which is used for login.
var Account = new Schema({
email: String,
password: String,
facebookId: String
});
Things to know about the backend API.
Passport is used for Authentication
Successful login returns email and token to the client through API.
On client, token is most to play game and use the overall features.
As I said, I have already covered the part when if a client registers and login using email, then client can use the app. But my confusion is handling the logins with Facebook. Facebook SDK is already integrated with the Unity App, and Login is success.
Now, how can I use the Facebook login information that is generated by the Facebook SDK onto my back end, so that I can authorize the user throughout the system, as done in email login.
Going through other questions in SO and Google, I came across passport-facebook-token, I also tried using the plugin but could not came up with the logic and flow for handling the data from SDK into the Nodejs API. Can someone me help understand how it is done?
Update 1: Using passport-facebook-token
Strategy on index.js
passport.use(new FacebookTokenStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET
}, function(accessToken, refreshToken, profile, done) {
Account.findOrCreate({facebookId: profile.id}, function (error, user) {
return done(error, user);
});
}
));
Controller API
api.post('/auth/facebook/token',
passport.authenticate('facebook-token'),
function (req, res) {
console.log(req.user);
// do something with req.user
res.sendStatus(req.user? 200 : 401);
}
);
Now, there is no error shown, but the data is not inserted into Account Schema, I have this findOrCreate() function in Model.
Account.statics.findOrCreate = function findOrCreate(profile, cb){
var userObj = new this();
this.findOne({facebookId : profile.id},function(err,result){
if(!result){
userObj.facebookId = profile.id;
//....
userObj.save(cb);
}else{
cb(err,result);
}
});
};
you can use facebook-passport for that, you can check the documentation here: https://github.com/jaredhanson/passport-facebook but basically, after you have already set up your developer account and got your keys from the developer site of facebook you can implement a FacebookStrategy object like following where you have to specify your credential and also a callback that in the documentation example is an http request to another resource of an express server where you can then save the data to mongo
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ facebookId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));

Using PassportJS google authentication with the nodejs google api library

I'm currently looking at implementing a google api, using the nodejs client:
https://github.com/google/google-api-nodejs-client/
I'm trying to use passport in order to authenticate, which seems to be working#
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/callback"
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
var user = {
id: profile.id,
email: profile.email,
firstName: profile.given_name,
lastName: profile.family_name,
accessToken: accessToken
};
return done(null, user);
});
}
));
In my google auth callback:
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
//do something here with the google api
});
I can get hold of req.user, and this has the accessToken
However, the docs for the Google api nodejs client aren't clear on how to use an accessToken.
The example included shows the OAauth2Client retrieving a token, but I guess that part has already been covered using Passport?
I'm not familiar with google-api-nodejs-client, but this is in their documentation:
oauth2Client.credentials = {
access_token: 'ACCESS TOKEN HERE',
refresh_token: 'REFRESH TOKEN HERE'
};
client
.plus.people.get({ userId: 'me' })
.withAuthClient(oauth2Client)
.execute(callback);
I assume you can just set the credentials to those provided by Passport, and things will work fine.
Honestly, though, I find these API wrappers really contrived, and recommend just using request. That way you can access any API service from any provider using a familiar module.

passport: different redirect for login and account registration

i'm using the passport module (github authentication) in my app and i want to redirect depending on the action ... i check if it's just a normal login or if the user logs in for the first time.
passport.use(new GitHubStrategy({
clientID: conf.github.app_id,
clientSecret: conf.github.app_secret,
callbackURL: conf.github.callback_url
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's GitHub profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the GitHub account with a user record in your database,
// and return that user instead.
Models_User.findOrCreateUser(profile, function(msg){
console.log("auth type:" + msg);
});
return done(null, profile);
});
}
));
in my findOrCreateUser function i check if it's a new user and do all the db action ... for testing i let the function return a msg variable which is only a string that says "login" or "new_registration".
so my question is how to "transport" that variable that i get from findOrCreateUser so that i can redirect accordingly ("/welcome" or "/back_again") after the passport auth is finished.
the other passport code in my app:
// GET /auth/github
// Use passport.authenticate() as route middleware to authenticate the
// request. The first step in GitHub authentication will involve redirecting
// the user to github.com. After authorization, GitHubwill redirect the user
// back to this application at /auth/github/callback
app.get('/auth/github',
passport.authenticate('github'),
//passport.authenticate('github', { scope: ['user', 'public_repo', 'gist'] }),
function(req, res){
// The request will be redirected to GitHub for authentication, so this
// function will not be called.
});
// GET /auth/github/callback
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
app.get('/auth/github/callback',
passport.authenticate('github', { successRedirect: '/', failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
In your verify callback, I would change things up so that the findOrCreateUser function supplies the actual record to the callback, and then pass that through to done(), like so:
Models_User.findOrCreateUser(profile, function(user){
console.log("auth type:" + msg);
return done(null, user);
});
// take this out, use the actual model above
//return done(null, profile);
Now, when handling the callback URL after authentication, you can check this user record and see if it was new (I'm assuming it has an isNew property here):
app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/login' }),
function(req, res) {
// successful auth, user is set at req.user. redirect as necessary.
if (req.user.isNew) { return res.redirect('/back_again'); }
res.redirect('/welcome');
});

Resources