I'm new to NodeJS and developing an API using it,
I want the API to be authenticated with an API token method (only people with a token stored in DB, created through a specific encryption should be able to access the API resource.)
I'm using an SQL server, NodeJS, and the Express framework.
Please guide me in what I should use to authenticate the API request.
Thanks in advance.
You could use passport.js with JwtStrategy. This is the idea:
mypassport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const opts = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'yourSecret'
};
passport.use(new JwtStrategy(opts, (payload, done) => {
const user = findUserById(payload.id);
if (!user) {
return done('user not exists', null);
}
return done(null, user);
}));
server.js (using express)
require('./mypassport'); // <- initialize passport strategies
//you could also use passport with local strategy for this
app.post('login', (req, res) => {
const username = req.query.username;
const password = req.query.password;
if (validLogin(username, password)) {
const user = findUserByUsername(username);
const jwt = createTokenWithSecret(user, 'yourSecret'); // You can use jwt-simple for this
res.json({ token: jwt });
} else {
//send unauthorized
}
});
const requireLogin = passport.authenticate('jwt');
app.get('/something', requireLogin, (req, res) => {
//here, user is authenticated and available in 'req.user'
});
First, you must login with POST /login { username: 'john', password: '1234' }. That will return a JSON with the jwt token like this:
{ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' }
In subsequent requests, you must send a header Authorization with value: Bearer {token} so passportjs and JwtStrategy can authorize the request.
Hope it helps!
NOTE: I have not tested code above, it just shows the approach.
For API Authenication use Passport JS
You can use json web token (jwt) for API authorization. There are node modules available which provides authentication functionality and you can simply use.
Please have a look at this article: https://medium.freecodecamp.org/securing-node-js-restful-apis-with-json-web-tokens-9f811a92bb52?gi=89f3f4d89dfd
Related
My goal is to create an authentication backend and I would like to implement google's Oauth2, and for that I decided to follow the passport documentation. My only issue is, how can I test this on my Postman? I'm just developing a backend and I don't know if it's working, I know it sounds a little silly but for a beginner like me it's a lot. Thanks
const dotenv = require('dotenv').config();
const express = require('express');
const passport = require('passport');
var GoogleStrategy = require('passport-google-oauth20').Strategy;
const app = express();
const port = process.env.PORT;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile'] }));
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
try {
app.listen(port);
console.log(`Server starting on port ${port}`);
} catch (err) {
console.log(err);
}
Whichever OAuth2 service is pretty simply, if you step into it ;)
In first step you send client_id and client_secret to authorization uri.
In second step, the user authenticates with his credentials and you receive the code which you exchange with authorization server for token.
You make request with Bearer token.
Postman can handle some of the things for you. So:
In Google Dev Console create your credentials.
As callback uri fill in:
https://www.getpostman.com/oauth2/callback
https://oauth.pstmn.io/v1/callback
In Postman create new Collection and define following variables:
Define following Authorization:
When you click Get New Access Token you will be asked to login to Google and get back with token.
With this token you can use whichever service you have in scope and your project on Developer Console includes (look for Enabled APIs and services).
Example request:
Create new GET request and save it in your collection, so the variables and tokens are inherited.
URL is: https://www.googleapis.com/oauth2/v1/userinfo?alt=json
Auth in Postman: Inherit auth from parent.
And Send...
Took me months to understand, so hope you'll be swifter :)
I'm having trouble getting the token once the user has logged in. I am supposed to get the token that is generated when I login through req.header so that when I ask for a route in the api (thunder api client) it should be able to give me access
My auth middleware:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
const token = req.headers['auth-token']
console.log(token)
res.json({
token: token
})
}
My routes:
const { Router } = require('express');
const router = Router();
const {
getAllUsers,
signupUser,
signinUser
} = require('../controllers/user.controller');
const auth = require('../middleware/auth');
router.get('/users', auth, getAllUsers);
router.post('/users/signup', signupUser);
router.post('/users/signin', signinUser);
module.exports = router;
when I user users/signin the response header:
Well well well, Apparently my problem was the following:
When I consumed by (thunder client) the route /users/signin -> it returned the object with the token. What I don't realize is that when I request the route that I want to be restricted to logged in users (only with the token) I must pass the same token that I get back from the signin in the headers of the new request (XD !) as seen in the photo.
I'm using passport to authenticate using Google API, I'm sending a token by URL to the client (React app) which saves it in the localStorage.
I want to use that token : With every API call (get, post, put) I want to send that token to the server , but I didn't know how to verify that token on the server side.
Passport Startegy :
app.use(passport.initialize()); // Used to initialize passport
app.use(passport.session()); // Used to persist login sessions
passport.use(new GoogleStrategy({
clientID: 'IDxxxxx',
clientSecret: 'SecreXXX',
callbackURL: 'http://localhost:3000/callback'
},
(accessToken, refreshToken, profile, done) => {
// Directory API here
var userData = {
name: profile.displayName,
token: accessToken
};
done(null, userData);
Authentication :
app.get('/auth/google', passport.authenticate('google', {
scope: ['profile'] // Used to specify the required data
}));
// The middleware receives the data from Google and runs the function on Strategy config
app.get('/callback', passport.authenticate('google'), (req, res) => {
var token = req.user.token;
res.redirect("http://localhost:8000?token=" + token);
});
API in express (which contains CRUD methods) :
app.use('/api', movieRouter)
In react side : Getting the token
componentWillMount() {
var query = queryString.parse(this.props.location.search);
if (query.token) {
window.localStorage.setItem("jwt", query.token);
// appel a directory api (avec token) puis sauvergarder dans redux puis redirection vers liste demandes
this.props.history.push("/");
}
}
Doing API calls :
import axios from 'axios'
const api = axios.create({
baseURL: 'http://localhost:3000/api',
})
export const insertMovie = payload => api.post(`/movie`, payload)
I just need to send the token in every call and check it in the server side.
Thanks
You want to set the token in a header most likely, try changing your axios client to something like
const api = axios.create({
baseURL: 'http://localhost:3000/api',
headers: {
Authorization: `Bearer ${your_token_here}`
}
})
I'm not 100% sure if this is the correct header form that passport will be expecting, but it's the general idea you need to do.
If the token is correctly set in the header, session, or cookie by the client as noted by Bill Metcalf, then express is able to authenticate a route/endpoint by adding the passport.authenticate middleware function to the route, like so
app.use('/api', passport.authenticate('google', {failureRedirect:'/login'}), movieRouter)
Refer to http://www.passportjs.org/docs/google/ for more information
For every API that you want to verify the token, you can pass a verify token function (which I call 'isCorrectToken') before taking action like this:
router.get("/api", isCorrectToken, (req, res) => {
// your api content })
And then, this is our isCorrectToken function:
const isCorrectToken = (req, res, next) => {
const token = req.headers.authorization;
if(token){
const onlyToken = token.slice(7, token.length);
jwt.verify(onlyToken, accessToken, (err, decode) => {
if(err) return res.status(401).send({ msg: 'Invalid Token' });
req.user = decode;
next();
return;
});
}
else return res.status(401).send({ msg: 'Token is not supplied'});}
The number 7 is the length of 'Bearer ' (from Bill Metcalf's answer above).
I'm trying to figure out how to do the identity server 4 authentication below using NodeJS - way out of my comfort zone here.
services.AddAuthentication(IdentityServerAuthenticationDefaults
.AuthenticationScheme)
.AddIdentityServerAuthentication(
options =>
{
options.Authority = "<authority-url>";
options.ApiName = "<api-url>";
});
I'm missing something in the flow here as the C# implementation isn't provided a secret or similar - so the token is probably verified via identity server? How would I verify the token using NodeJS if I don't have a 'secret' to verify it with?
I've stumbled on introspection endpoint - am I heading in the right direction?
I was able to solve this using the jwks -endpoint and it's public keys to verify tokens and then I also found a nice package that I used to prepare the middleware:
private issuer: string = process.env.idsrv;
auth = jwt({
secret: jwksClient.expressJwtSecret({
cache: true, // see https://github.com/auth0/node-jwks-rsa#caching,
cacheMaxAge: ms('24h'),
rateLimit: true, // see https://github.com/auth0/node-jwks-rsa#rate-limiting
jwksRequestsPerMinute: 100,
jwksUri: `${this.issuer}/.well-known/jwks`
}),
// validate the audience & issuer from received token vs JWKS endpoint
audience: `${this.issuer}/resources`,
issuer: this.issuer,
algorithms: ["RS256"]
});
The accepted question is right. but i wanted to fix some of it's trial errors.
you can easily (it took me 4 days) add authentication to your express api with ids4.
it's how its work:
creating a middleware:
const jwt = require("express-jwt"),
jwksClient = require("jwks-rsa");
const auth = jwt({
secret: jwksClient.expressJwtSecret({
cache: true, // see https://github.com/auth0/node-jwks-rsa#caching
rateLimit: true, // see https://github.com/auth0/node-jwks-rsa#rate-limiting
jwksRequestsPerMinute: 2,
jwksUri: `${issuer}/.well-known/openid-configuration/jwks`,
}),
audience: "api1.resource", // <---- its your api resource.
issuer: issuer, // <----- address of identityserver4.
algorithms: ["RS256"], //<----- its needed algorithm to handle secret.
});
The following auth middleware is like following code in .net:
services.AddAuthentication(IdentityServerAuthenticationDefaults
.AuthenticationScheme)
.AddIdentityServerAuthentication(
options =>
{
options.Authority = "<authority-url>";
options.ApiName = "<api-url>";
});
to secure a nodejs route you can use following example:
// this is the secured route by identityserver4
// the jwt module set the ids4 auth result in req.user object
// so you can use it to access logged in user claims and stuff.
// for example testing with client credentials it return some info about the jwt token sent to the /me endpoint.
app.get("/me", auth, (req, res) => {
const user = req.user;
debug("req.user: %O", user);
return res.json(user);
});
// this a unsecured route. so anyone can call this route without any restrictions.
app.get("/", (req, res) => {
return res.send("Hello");
});
It takes me so long to find how to secure nodejs or express with identityserver4 but it was as simple as it look like.
The code here is from the following identityserver4 community example with full ids4 and node api and javascript client and .net console client:
https://github.com/lyphtec/idsvr4-node-jwks
There is a good documentation about this example in its own github repository too.
You can download the node api and run it with yarn start or npm run start.
I tested it with client credentials flow and it worked like a charm.
Wish all of you luck.
If you want only to validate your token you can use the following package:
npm install token-introspection --save
This package is configured with endpoint and client credentials, and a function is returned.
Calling that function with token, and optional token_type_hint will return a Promise.
const tokenIntrospection = require('token-introspection')({
endpoint: 'https://example.com/introspect',
client_id: '<Client ID>',
client_secret: '<Client Secret>',
});
tokenIntrospection(token).then(console.log).catch(console.warn);
Example :
Here is a middleware to validate the token :
module.exports = (req, res, next) => {
const token = "wEvxS0y2TkvCjLpKP33oGTK0BcKUb6MHt1u3AeMu8h4"; // get your token from your request
const tokenIntrospection = require('token-introspection')({
endpoint: 'http://localhost:5000/connect/introspect',
client_id: 'api1',
client_secret: 'apisecret',
});
tokenIntrospection(token).then(result => {
console.log(result);
next();
}).catch(console.warn);
}
then you can use it as below :
const auth = require('./atuh')
app.get('/', auth, (req, res, next) => {
res.send("Hi");
})
Auth0 documentation describes how to set up express-jwt middleware to protect endpoints. The trouble is that the documentation doesn't seem to cover how you get a valid JWT in the first place.
On the angular side, there's documentation on using angular plugins to implement a login page, that's fine. How would one implement a route using express that would take a username/password and return to the client the appropriate JWT such that subsequent requests would be authorized?
I think I may be missing a basic concept about JWT here; via Auth0, when using Username-Password-Authentication, my guess is that Auth0 acts as the repo for those credentials. There's documentation out there about wiring passport to auth0 and JWT, the problem with those is that this documentation assumes that the username/password database is some MongoDB instance locally...I want to avoid that type of setup which was an initial attraction with auth0.
Are there sample projects that cover this, showing how to get a valid JWT on a back-end, without some separate front-end angular app requesting it first?
I use passport.js built in local strategy for authentication and store user information in a JWT that I read on routes that require authorization.
User id's can be serialized/deserialized into and out of the express sessionto obtain the user identifier using the auth token (JWT) in the request. This is in my opinion the best approach since it limits the amount of data stored on the client and provides better security than storing any user information. Here's an example of this in express:
//Set a session secret
var secrets = { sessionSecret: process.env.secret || 'my secret string'};
//Require express-jwt and set a secret for the cookie
var expressJwt = require('express-jwt');
var validateJwt = expressJwt({ secret: secrets.sessionSecret });
//Returns a jwt token signed by the app secret
var signToken = function(id) {
return jwt.sign({
id: id
}, secrets.sessionSecret, {
expiresInMinutes: 60 * 24 // 24 hours
});
};
//Set token cookie directly
var setTokenCookie = function(req, res) {
if (!req.user) {
return res.status(404).json({
message: 'Error during user validation'
});
}
var token = signToken(req.user.id, req.user.role);
res.cookie('token', JSON.stringify(token));
};
//Check to see if user is authenticated (call this when a route is requested)
var isAuthenticated = function(req, res, next) {
// allow access_token to be passed through query parameter as well
if (req.body && req.body.hasOwnProperty('access_token')) {
req.headers.authorization = 'Bearer ' + req.body.access_token;
}
// Validate jwt token
return validateJwt(req, res, next);
};
You can use these methods as middleware in express. Say the above code was token.js, you can force it to execute on each request to a route like this:
app.get('/employee', token.isAuthenticated, employeeController.getEmployees);
I haven't worked with angular but it works great on the backbone projects i've worked on and this process should work with any browser based client that can supply a X-auth cookie on each request. You can do this by using the ajax setup:
$(document).ajaxSend(function(event, request) {
var token = readCookie('token');
if (token) {
request.setRequestHeader('authorization', 'Bearer ' + token);
}
});
Here is an example of middleware that validates a users login and returns a token to the client that can be used on subsequent requests:
var validateLogin = function (req, res, next) {
var username = req.params.username;
// Authenticate using local strategy
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.status(404).json({
info: [{
msg: info.message
}]
});
}
// Send user and authentication token
var token = token.signToken(user.id, user.role);
res.cookie('token', token);
res.render('index', {token: token, user: user});
})(req, res, next);
};
#FrobberOfBits
This is to answer the follow-up Q posted by FrobberOfBits on Feb 6, 2016 at 3:04
I use auth0 for local + social media authentication.
The way auth0 works is, you hand over the approach to authenticate to auth0 ...either it be local with db or social media.
It is a bundled approach where local db and social media authentication is all bundled and provided as a service to you by auth0.
Hope this helps.