How to private swagger document? - node.js

I'm using Swagger (on NodeJS, swagger-ui-express) to document my API.
This is how I build swagger server and setup my document on server
app.use('/apidocs', swaggerUI.serve, swaggerUI.setup(getSwaggerSpec()));
But now I need to private or set permission for someone in the team can access to document, and I expect all outside person can't access to my document
I tried with adding a middleware, but I didn't work.
app.use('/apidocs', (req, res, next) => {
if (req.query.userName === 'admin' && req.query.password === '12345678') {
console.log('-=-=-')
next();
} else {
res.send('Unauthenticated');
}
}, swaggerUI.serve, swaggerUI.setup(getSwaggerSpec()));
Code is fine. But when I access with exactly userName and password. It show a white page.

Why don't try to use an access token ?
const express = require('express');
const app = express();
const authToken = '123456';
app.use('/apidocs', (req, res) => {
const authHeader = req.headers.authorization || '';
const match = authHeader.match(/Bearer (.+)/);
const token = match[1];
if (token !== authToken) {
return res.status(401).send('Unauthorized');
} else {
return HERE YOUR RESPONSE
}
});
also you could to use express basics auth [https://github.com/LionC/express-basic-auth]
const basicAuth = require('express-basic-auth');
app.use("/api-docs",basicAuth({
users: {'yourUser': 'yourPassword'},
challenge: true,
}), swaggerUi.serve, swaggerUi.setup(swaggerDocument));

Related

How to authenticate keycloak token using node js that calls postgraphile?

I'm new on node js, and the company that i work for needs a proof of concept about postgraphile, the situation is this:
I created a node js mini server that uses postgraphile to access the data on postgres
The mini server works fine and can return data and also can use mutations.
I used keycloak-connect to try to access keycloak to authenticate the token from the request that is sent by postman but there is a problem.
If the token is valid or not it does not matter for the mini server, the only thing that seems to matter is that is a bearer token.
I tried to use other plugins (like keycloak-nodejs-connect, keycloak-verify, etc) but the result is the same, i also changed my code to use the examples in the documentation of those plugins but nothing.
This is my code: (keycloak-config.js file)
var session = require('express-session');
var Keycloak = require('keycloak-connect');
let _keycloak;
var keycloakConfig = {
clientId: 'type credential',
bearerOnly: true,
serverUrl: 'our company server',
realm: 'the test realm',
grantType: "client_credentials",
credentials: {
secret: 'our secret'
}
};
function initKeycloak(){
if(_keycloak){
console.warn("Trying to init Keycloak again!");
return _keycloak;
}
else{
console.log("Initializing Keycloak...");
var memoryStore = new session.MemoryStore();
_keycloak = new Keycloak({store: memoryStore}, keycloakConfig);
return _keycloak;
}
}
function getKeycloak(){
if(!_keycloak){
console.error('Keycloak has not been initialized. Please called init first');
}
return _keycloak;
}
module.exports = {
initKeycloak,
getKeycloak
};
My Index.js file:
const express = require('express')
const bodyParser = require('body-parser')
const postgraphile = require('./postgraphile')
const app = express()
const keycloak = require('../config/keycloak-config').initKeycloak()
var router = express.Router();
app.set( 'trust proxy', true );
app.use(keycloak.middleware());
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(postgraphile);
app.get('/', keycloak.checkSso(), (req, res) => {
res.send('success');
} );
var server = app.listen(8080, () => console.log(`Server running on port ${8080}`));
Also I used this code to get the token and use the keycloak-verify plugin but got nothing:
router.get('/',keycloak.protect(),function(req, res, next) {
var token=req.headers['authorization'];
console.log(token);
try {
let user = keycloak.jwt.verify(token);
console.log(user.isExpired());
} catch (error) {
console.error(error);
}
})
I know that I lack the knowledge because I am a backend (C#) developer, can somebody help me with this?, thanks in advance.
I found the answer to my problem:
const express = require("express");
const request = require("request");
var keycloakConfig = require('../AuthOnly/config/keycloak-config').keycloakConfig;
const postgraphile = require('./postgraphile');
const app = express();
const keycloakHost = keycloakConfig.serverUrl;
const realmName = keycloakConfig.realm;
// check each request for a valid bearer token
app.use((req, res, next) => {
// assumes bearer token is passed as an authorization header
if (req.headers.authorization) {
// configure the request to your keycloak server
const options = {
method: 'GET',
url: `${keycloakHost}/auth/realms/${realmName}/protocol/openid-connect/userinfo`,
headers: {
// add the token you received to the userinfo request, sent to keycloak
Authorization: req.headers.authorization,
},
};
// send a request to the userinfo endpoint on keycloak
request(options, (error, response, body) => {
if (error) throw new Error(error);
// if the request status isn't "OK", the token is invalid
if (response.statusCode !== 200) {
res.status(401).json({
error: `unauthorized`,
});
}
// the token is valid pass request onto your next function
else {
next();
}
});
} else {
// there is no token, don't process request further
res.status(401).json({
error: `unauthorized`,
});
}});
app.use(postgraphile);
app.listen(8080);

JWT-Node authentication - req.headers[''authorization"] is Undefined

I try to set Token then get token from header but it always shows "undefined" when i console.log(req.headers['authorization'])
Here is some code
const express = require('express');
const app = express();
const jwt = require('jsonwebtoken');
const accessTokenSecret = 'youraccesstokensecret';
app.listen(8080, () => {
console.log('Authentication service started on port 8080');
});
I set token
app.get("/createJWT",function(req,res){
const accessToken = jwt.sign({ username: "myName"}, accessTokenSecret);
res.json({
accessToken
});
})
the middleware (I show req.headers['authorization'] is undefined)
const authenticateJWT = (req, res, next) => {
const authHeader = req.headers['authorization'];
console.log(authHeader)
if (authHeader) {
const token = authHeader.split(' ')[1];
jwt.verify(token, accessTokenSecret, (err, user) => {
if (err) {
return res.sendStatus(403);
}
req.user = user;
next();
});
} else {
res.sendStatus(401);
}
};
And finally, I access a route to test, req.headers['authorization'] does not have values.
app.get("/check",authenticateJWT,function(req, res){
res.send("ok")
})
please help me, thanks
So when you are calling the API form the frontend or say you are checking the API with postman are you setting up the header while requesting /check? (I am not talking about your /createJWT which creates the token and sends it as a response)
In your frontend code/postman you need to explicitly add the header authorization JWTtoken while creating an HTTP request and after that your backend will receive it. Kindly check if this is not being done.

Route not being directed correctly

I have set up express to use the following paths:
const profile = require("./api/profile")
const events = require("./api/events")
app.use("/api/events", events)
app.use("/api/profile", profile)
Inside the events and profile index.js files I have the following:
const router = require('./../../modules/router.js')
router.get('/', (req, res) => {
})
module.exports = router
My router.js file:
const express = require("express")
const cookieParser = require('cookie-parser')()
const cors = require('cors')({origin: true})
const router = express.Router()
const firebase = require("./firebase.js")
// https://github.com/firebase/functions-samples/tree/master/authorized-https-endpoint
// Must have header 'Authorization: Bearer <Firebase ID Token>'
const validateFirebaseIdToken = (req, res, next) => {
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!req.cookies.__session) {
res.status(403).send({ "error": 'Unauthorized'})
return
}
let idToken
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1]
} else {
// Read the ID Token from cookie.
idToken = req.cookies.__session
}
firebase.admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
req.user = decodedIdToken
return next()
}).catch(error => {
res.status(403).send({"error": 'Unauthorized'})
})
}
router.use(cors)
router.use(cookieParser)
router.use(validateFirebaseIdToken)
module.exports = router
For some reason, the router mixes up the paths /api/events/ and /api/profile/ whenever I call them. For all other paths it works fine. How can I stop this from happening?
If you are using the same router for both events and profile, it could be the source of your issue.
Have you tested to create one router for each module?
Maybe try something like this for both events and profile:
const router = require('express').Router()
router.get('/', (req, res) => {
})
module.exports = router

How to make all express routers use validation code

When I create a router I need to add the following code in each module:
const express = require("express")
const cookieParser = require('cookie-parser')()
const cors = require('cors')({origin: true})
const router = express.Router()
const firebase = require("./firebase.js")
// https://github.com/firebase/functions-samples/tree/master/authorized-https-endpoint
// Must have header 'Authorization: Bearer <Firebase ID Token>'
const validateFirebaseIdToken = (req, res, next) => {
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!req.cookies.__session) {
res.status(403).send({ "error": 'Unauthorized'})
return
}
let idToken
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1]
} else {
// Read the ID Token from cookie.
idToken = req.cookies.__session
}
firebase.admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
req.user = decodedIdToken
return next()
}).catch(error => {
res.status(403).send({"error": 'Unauthorized'})
})
}
router.use(cors)
router.use(cookieParser)
router.use(validateFirebaseIdToken)
// api functions go here
module.exports = router
Initially I had this in a separate file and was sharing the router across modules, however, sharing caused problems. I don't want to copy and paste this code in each module file... How can I make sure that all routers are created like this with out having to copy and paste?
It's difficult to see exactly which part you want to share, but if you pull the validateFirebaseIdToken function into its own file, then you can import it like anything else. This is important because you can easily use it wherever you want. You may have routes which don't require authentication... in which case you wouldn't use this middleware.
validate-firebase-token.js
const firebase = require("./firebase.js")
const validateFirebaseIdToken = (req, res, next) => {
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!req.cookies.__session) {
res.status(403).send({ "error": 'Unauthorized'})
return
}
let idToken
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1]
} else {
// Read the ID Token from cookie.
idToken = req.cookies.__session
}
firebase.admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
req.user = decodedIdToken
return next()
}).catch(error => {
res.status(403).send({"error": 'Unauthorized'})
})
}
module.exports = validateFirebaseIdToken
... and now your router code looks like this:
const express = require("express")
const cookieParser = require('cookie-parser')()
const cors = require('cors')({origin: true})
const router = express.Router()
const validateFirebaseIdToken = require('./validate-firebase-token')
router.use(cors)
router.use(cookieParser)
router.use(validateFirebaseIdToken)
// api functions go here
module.exports = router
As stated by someone in the answers to my other question, you can apply middleware on to the app rather than a specific router. That way the middleware applies to all requests!

Node.js - How do I set up separate authentication for different routes?

I'm working on Node.js a project that's using basic-auth for password protection. Currently, the auth.js file provides the same username/password for all routes. How would I adjust this to use a different username/password for each route?
auth.js file:
const auth = require('basic-auth');
const username = 'admin';
const password = 'supersecret';
const internalIp = 'xxx.xx.xxx.xxx';
module.exports = function(app) {
app.use((req, res, next) => {
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
// whitelist internal IP
if (ip === internalIp) {
next();
} else {
const user = auth(req);
if (user === undefined || user.name !== username || user.pass !== password) {
// Return 401 error if user/pass is incorrect or empty
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="Research tool"');
res.end('Unauthorized');
} else {
next();
}
}
});
};
app.js file:
var express = require('express');
var app = express();
var auth = require('./sources/auth.js');
// Run auth around app first
auth(app);
app.get('/route1', function(req, res) {
res.render('pages/route1');
}
app.get('/route2', function(req, res) {
res.render('pages/route2');
}
app.listen(port, function() {
console.log('App listening on port: ' + port);
});
Running: node v6.11.1, express 4.13.4, basic-auth 1.1.0
It's very unusual that you have usernames and passwords hard-coded in. What is more typical, is that usernames and (hashed) passwords are stored in a database. Then when an authorization request comes in, you use the username to fetch the password and just compare the two passwords to each other. In this way, one piece of auth middleware could service any number of username/password combinations.
That said, if you really need two separate pieces of auth middleware, the better way is to insert the needed middleware into each route. Something like this:
auth.js
const auth = require('basic-auth')
const getAuthorizer = (name, pass) => (req, res, next) => {
const user = auth(req)
if (!user || user.name !== name || user.pass !== pass) {
res.status(401).send('Unauthorized')
} else {
next()
}
}
const admin = getAuthorizer('admin', 'supersecret')
const user = getAuthorizer('user', '12345')
module.exports = { admin, user }
app.js
const express = require('express')
const app = express()
const auth = require('./sources/auth')
app.get('/route1', auth.admin, (req, res) => {
res.render('pages/route1')
})
app.get('/route2', auth.user, (req, res) => {
res.render('pages/route2')
})
app.listen(port, () => {
console.log('App listening on port: ' + port)
})
You can get url from request object in app.js so you could include route => credentials map either from file or internal code. Then you will be able to iterate over map to check if route matches passed credential data.

Resources