I'm building a REST-api that uses facebook for authentication, I think the best solution to be platform agnostic is to let the client deal with retrieving an auth-token from facebook and then use that to authenticate, to keep the API as clean as possible.
Is this at all possible?
You can use passport-facebook-token strategy instead of passport-facebook.
In this way you can get the token on the client-side and send it to the application using:
app.post('/auth/facebook/token',
passport.authenticate('facebook-token'),
function(req, res) {
// do something with req.user
res.send(req.user ? 200 : 401);
}
);
With the code above you can pass the token using a query parameter like GET /auth/facebook/token?access_token=<TOKEN_HERE>, putting on the HTTP header access_token or in the request body.
Related
I'm working on an Ionic application.
On the one hand I have an auth basic form in which people fill in their username and password. On the other hand I'd like to implement authentification with JSON Web Tokens and Node JS.
The workflow would be this one : as soon as a user fills in his credentials, they will be sent with a POST request. If these credentials are correct, the user can access to the application and gets an access token as a response.
The thing is that I'm a little bit lost with all that concepts. I built a form and sent informations with a POST request. I managed to create some APIs with Node JS and that's ok. I see how to build a authentified webservice too (e.g : https://github.com/jkasun/stack-abuse-express-jwt/blob/master/auth.js).
But I concretely don't understand the links between the html form and the authorisation check part..
To be clearer, how is it possible to make the html part and the Node JS scripts communicate together ?
Before posting that question I made many researches and found many stuff on building an authentified API. But there was very few advice on how to make it communicate with the client part (I mean the form), which is what I have to do.
If anyone has any ressources (document, Github examples..) on that, I'll greatly appreciate. But I would be very happy too if someone try to make me understand these concepts. I guess I have to improve my knowledge on all that so that I could test some POCs.
Many thanks in advance !
JWT General flow:
1- Authenticate using a strategy (You done it)
2- Deliver an accessToken along with response (You done it)
3- The client MUST store this accessToken (LocalStorage is the best place, not cookies: They are vulnerable to csrf attacks)
4- On every request you are going to make to a protected area (where user is supposed to be authenticated and authorized), make sure to send you accessToken along with it, you can put it on Authorization header, a custom header, directly in body of the request... Basicaly just make sure to send it properly.
5- On the server receiving client requests, you NEED to verify that token (You verify it by checking the signature of the accessToken).
6- If he is authorized, great, if not, send back an HTTP Unauthorized Error.
Here is my implementation using an accessToken on a header + passportjs-jwt:
Client code
To store token:
localStorage.setItem('accessToken', myAccessToken);
To send it:
const myAccessToken = localStorage.getItem('accessToken');
{
headers: {'Authorization', `Bearer ${myAccessToken}`}
}
Server code
1- Configure passport
passport.use('jwt', new JwtStrategy({
jwtFromRequest: jwtPassport.ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: myAccessTokenSecret,
passReqToCallback: true
}, (req, payload, done: (err?, user?) => void): void {
User
.findOne({where: {id: req.params.id}})
.then((user: User) => {
if (!user) {
return done(new Error(`No user found with id: ${req.params.id}`), null);
}
return done(null, user);
})
.catch((e: Error) => done(e, null));
}));
Pay attention to callback: If your callback is called, it means that passport has successfuly verified the token (It is valid). In my example, i get the user details in database and this is the user that will be returned and put in req.user object passed to my controller below:
2- Finally, the controller route (protected area):
.get('/users/:id', passport.authenticate('jwt'), (req, res, next) => {
// do stuff in protected area.
}
And that's it. If you want more security, check refreshTokens implementation.
I used passport because i found it relevant in my case, but you can write your own handler, by using jsonwebtoken and just calling its "verify" function.
You can find documentation of passport jwt strategy here => http://www.passportjs.org/packages/passport-jwt/
I am so confused,
all the Firebase authentication tutorial online are teaching how to login in frontend,
of course you can get the token and send it to server for verification if its a post request,
but what about normal get request like a page request? I have installed firebase-admin already but i didnt find any method for getting current user........
i am using node and express
for example
app.get('/', function(req, res) {
const idToken = (where can i get the token??)
console.log(idToken);
res.render('index.ejs');
});
You still have to arrange for the auth token to be sent in the HTTP request. You can do that with a header in the request. Sample code showing exactly this case can be found in the official samples. This will work for any HTTP method, and is a lot better than trying to use a POST body.
The sample uses the Authorization header to transmit the token and verifyIdToken() to make sure it's valid.
I really don't understand, why it is so complex to build authentication and persisting in session with Node.js.
I'm having trouble with session persistance, that is described here.
Maybe, I something don't understand...
So, in an SPA, when a browser making fetch with POST method from UI, Passport authenticates and saves session in DB (as I've setup).
What's next?
How to tell React front-end (browser, server…), that It should apply newly created cookie and use it for all subsequent requests for HMR, GraphQL and other stuff?
What I have is all subsequent requests to server referring old cookie (not created one on successful authentication) and that correct one will never looked up…
Some explanation will be greatly appreciated.
Thank You.
PS: Still looking for simple working examples of authentication with latest Next.js, Express and Passport. I'm stuck with this problem on a week…
You can make a request to the endpoint of express which is going to return you the information... for this you can use Axios, when it response you can set the cookie with something like this:
document.cookie = `id_token=${token}; expires=Thu, 18 Dec 2020 12:00:00 UTC`
In my case I set a token because I use JWT, when the cookie is set, you can request it on the server side using cookie-parser, so, when you are going to verify is the user is logged you can check if the cookie exists on the server (Next.js) and render the template, otherwise you can redirect to other view... something like this:
server.get('/profile', (req, res) => {
const actualPage = '/profile';
const logged = req.cookies['id_token']
if (logged) {
return app.render(req, res, actualPage)
}
return res.redirect('/')
})
If you want to see the complete example, check this repo
I have a node.js server which authenticates using google-passport-oauth2. My server-side code looks like that from the documentation:
app.get('/auth/google',
passport.authenticate('google', { scope:
[ 'https://www.googleapis.com/auth/plus.login',
, 'https://www.googleapis.com/auth/plus.profile.emails.read' ] }
));
app.get( '/auth/google/callback',
passport.authenticate( 'google', {
successRedirect: '/auth/google/success',
failureRedirect: '/auth/google/failure'
}));
I figure that /auth/google redirects to google's login, and when permissions are recieved, the google profile and token are sent to the callback /auth/google/callback.
Now I am making an android app which wants to authenticate with this API. I'm using the directions for integrating Google Sign-In to do the authentication on google's end. Now my android app has the profile and token and wants to verify it with my server.
I've tried doing this with passport-google-token and passport-google-id-token (not sure the difference...), but it didn't work for whatever reason. Now I'm looking at other possibilities, like a Google Client API library for node.js, but it seems bulky. Then there's the tokeninfo endpoint, which involves an extra request and more latency. Or maybe I should look at express-jwt?
And suddenly, I wonder... couldn't I just pass the token from my android app to the server at auth/google/callback? That would make things a little simpler. I think this must be a pipe dream, because I haven't found any information about doing it. But if it's possible, how should I format the token/profile data in the request so the passport.authenticate() method recognizes it? (JSON, form data, headers)
If this can't be done, I'm taking suggestions for well-documented token verification libraries for node...
I still don't know about reusing the google-passport-oauth2 route, but I did figure out how to validate Google's idToken using passport-google-id-token.
The documentation says:
The post request to this route should include a JSON object with the
key id_token set to the one the client received from Google (e.g.
after successful Google+ sign-in).
But it only works if it's sent as a query string (GET or POST works).
https://localhost:8888/auth/googletoken?id_token=xxxxxxxxxx
I have a feeling this is not the most secure method, but I'll have to deal with that later.
EDIT: It turns out, the token is useless without the client ID (in your app), so it's OK to send it by querystring.
EDIT 2: One of the google-id-token devs has reminded me that the JSON will only be received if body-parser has been installed.
I was looking for a way to let my client authorize with the facebook JS SDK and then somehow transfer this authorization to my node server (so it can verify requests with the fb graph api)
I stumbled across:
https://github.com/jaredhanson/passport-facebook/issues/26
&
https://github.com/drudge/passport-facebook-token
what seems to be an entirely different strategy from passport-facebook.
Am I correct when assuming that:
One logs in with the fb JS SDK, and then the facebook-token strategy somehow extracts the token and fb id from the document or body object?
Or is there any other decent way to achieve this? I'm namely trying to avoid the redirects enforced by the server SDKs
I've spent a couple of days this week trying to figure out the best way to use Facebook Authentication for a private API, using passport.js — passport-facebook-token is perfect for this.
You are correct in assuming these are two separate authentication strategies. You don't need passport-facebook installed to use passport-facebook-token.
If you have Facebook authentication implemented in the client-side JS (or iOS etc.), and are looking for a way to then authenticate API requests using your user's Facebook authToken, passport-facebook-token is a really elegant solution.
passport-facebook-token works totally independently of passport-facebook, and basically handles the redirects required by Facebook internally, before passing the request along to your controller.
So to authenticate an API route using passport-facebook-token, you'll need to set up a passport strategy like so:
passport.use('facebook-token', new FacebookTokenStrategy({
clientID : "123-your-app-id",
clientSecret : "ssshhhhhhhhh"
},
function(accessToken, refreshToken, profile, done) {
// console.log(profile);
var user = {
'email': profile.emails[0].value,
'name' : profile.name.givenName + ' ' + profile.name.familyName,
'id' : profile.id,
'token': accessToken
}
// You can perform any necessary actions with your user at this point,
// e.g. internal verification against a users table,
// creating new user entries, etc.
return done(null, user); // the user object we just made gets passed to the route's controller as `req.user`
}
));
It's worth noting that the User.findOrCreate method used in the passport-facebook-token Readme is not a default mongo/mongoose method, but a plugin that you'll have to install if you want it.
To use this auth strategy as middleware for any of your routes you'll need to pass it an access_token object either as a URL parameter, or as a property of the request body.
app.get('/my/api/:access_token/endpoint',
passport.authenticate(['facebook-token','other-strategies']),
function (req, res) {
if (req.user){
//you're authenticated! return sensitive secret information here.
res.send(200, {'secrets':['array','of','top','secret','information']});
} else {
// not authenticated. go away.
res.send(401)
}
}
NB. the access_token property is case-sensitive and uses an underscore.
The documentation for passport-facebook-token isn't extensive, but the source is really well commented and pretty easy to read, so I'd encourage you to take a look under the hood there. It certainly helped me wrap my head around some of the more general ways that passport works.