Skip to content

Commit

Permalink
Merge pull request #2 from martinbedouret/master
Browse files Browse the repository at this point in the history
Initial draft for authentication
  • Loading branch information
martinbedouret authored Jan 11, 2018
2 parents d7fdb65 + 65b569a commit 5ab9427
Show file tree
Hide file tree
Showing 7 changed files with 655 additions and 53 deletions.
33 changes: 28 additions & 5 deletions api/controllers/user.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
var User = require('../models/User');
var mailing = require('../mail');
var nev = mailing('en');
var auth = require("../helpers/auth");

module.exports = {
createUser: createUser,
activateUser: activateUser,
listUser: listUser,
removeUser: removeUser,
getUser: getUser,
updateUser: updateUser
updateUser: updateUser,
loginUser: loginUser
};

function createUser(req, res) {
Expand Down Expand Up @@ -91,10 +93,7 @@ function removeUser(req, res) {
message: 'User not found. User Id: ' + id
});
}
return res.status(200).json({
success: 1,
message: 'User Id: ' + id + ' was removed. ' + users
});
return res.status(200).json(users);
});
}
function getUser(req, res) {
Expand Down Expand Up @@ -144,4 +143,28 @@ function updateUser(req, res) {
});
return res.status(200).json(users);
});
}
function loginUser(args, res) {
var role = args.swagger.params.role.value;
var username = args.body.username;
var password = args.body.password;
console.log(role + username + password);

if (role != "user" && role != "admin") {
return res.status(400).json({
message: 'Error: Role must be either "admin" or "user"'
});
}

if (username == "username" && password == "password" && role) {
var tokenString = auth.issueToken(username, role);
res.status(200).json({
token: tokenString,
message: "User successfully authenticated"
});
} else {
res.status(403).json({
message: "Error: Credentials incorrect"
});
}
}
75 changes: 75 additions & 0 deletions api/helpers/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use strict";

var jwt = require("jsonwebtoken");
var sharedSecret = "shh";
var issuer = "my-awesome-website.com";

//Here we setup the security checks for the endpoints
//that need it (in our case, only /protected). This
//function will be called every time a request to a protected
//endpoint is received
exports.verifyToken = function(req, authOrSecDef, token, callback) {
//these are the scopes/roles defined for the current endpoint
var currentScopes = req.swagger.operation["x-security-scopes"];

function sendError() {
return req.res.status(403).json({ message: "Error: Access Denied" });
}

//validate the 'Authorization' header. it should have the following format:
//'Bearer tokenString'
if (token && token.indexOf("Bearer ") == 0) {
var tokenString = token.split(" ")[1];

jwt.verify(tokenString, sharedSecret, function(
verificationError,
decodedToken
) {
//check if the JWT was verified correctly
if (
verificationError == null &&
Array.isArray(currentScopes) &&
decodedToken &&
decodedToken.role
) {
// check if the role is valid for this endpoint
var roleMatch = currentScopes.indexOf(decodedToken.role) !== -1;
// check if the issuer matches
var issuerMatch = decodedToken.iss == issuer;

// you can add more verification checks for the
// token here if necessary, such as checking if
// the username belongs to an active user

if (roleMatch && issuerMatch) {
//add the token to the request so that we
//can access it in the endpoint code if necessary
req.auth = decodedToken;
//if there is no error, just return null in the callback
return callback(null);
} else {
//return the error in the callback if there is one
return callback(sendError());
}
} else {
//return the error in the callback if the JWT was not verified
return callback(sendError());
}
});
} else {
//return the error in the callback if the Authorization header doesn't have the correct format
return callback(sendError());
}
};

exports.issueToken = function(username, role) {
var token = jwt.sign(
{
sub: username,
iss: issuer,
role: role
},
sharedSecret
);
return token;
};
62 changes: 56 additions & 6 deletions api/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@ schemes:
# tip: remove http to make production-grade
- http
- https
securityDefinitions:
Bearer:
type: apiKey
name: Authorization
in: header
description: |
For accessing the API a valid JWT token must be passed in all the queries in
the 'Authorization' header.
A valid JWT token is generated by the API and retourned as answer of a call
to the route /login giving a valid user & password.
The following syntax must be used in the 'Authorization' header :
Bearer xxxxxx.yyyyyyy.zzzzzz
# format of bodies a client can send (Content-Type)
consumes:
- application/json
Expand Down Expand Up @@ -68,6 +81,11 @@ paths:
get:
operationId: listUser
description: Return users list
security:
- Bearer: []
x-security-scopes:
- admin
- user
responses:
"200":
description: Success
Expand Down Expand Up @@ -100,14 +118,13 @@ paths:
put:
operationId: updateUser
description: update a user
# define the parameters
parameters:
- name: id
description: User id
description: User Id
type: string
in: path
required: true
mlinimum: 1
minimum: 1
- name: info
description: User properties
in: body
Expand Down Expand Up @@ -137,7 +154,7 @@ paths:
"200":
description: Success
schema:
$ref: "#/definitions/GeneralResponse"
$ref: "#/definitions/GetUserResponse"
default:
description: Error
schema:
Expand All @@ -147,7 +164,6 @@ paths:
post:
operationId: activateUser
description: Create a new user
# define the type of response for Success "200" and Error
parameters:
- in: path
name: url
Expand All @@ -164,6 +180,32 @@ paths:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
/user/login/{role}:
x-swagger-router-controller: user
post:
operationId: loginUser
description: Authenticate user by email and password
parameters:
- name: role
description: user role
in: path
required: true
type: string
- name: info
description: User properties
in: body
required: true
schema:
$ref: "#/definitions/User"
responses:
"200":
description: Success
schema:
$ref: "#/definitions/LoginResponse"
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
# complex objects have schema definitions
definitions:
User:
Expand Down Expand Up @@ -231,4 +273,12 @@ definitions:
type: string
locale:
type: string

LoginResponse:
required:
- token
- message
properties:
token:
type: string
message:
type: string
55 changes: 43 additions & 12 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,57 @@

var SwaggerExpress = require('swagger-express-mw');
var app = require('express')();
var swaggerTools = require('swagger-tools');
var YAML = require('yamljs');
var auth = require("./api/helpers/auth");

const config = require('./config');

var swaggerConfig = YAML.load("./api/swagger/swagger.yaml");

module.exports = app; // for testing

var swaggerConfig = {
appRoot: __dirname // required config,
}
//var swaggerConfig = {
// appRoot: __dirname // required config,
//};

/*bbdd configuration in its own file*/
require('./db');

SwaggerExpress.create(swaggerConfig, function(err, swaggerExpress) {
if (err) { throw err; }
swaggerTools.initializeMiddleware(swaggerConfig, function(middleware) {
//Serves the Swagger UI on /docs
app.use(middleware.swaggerMetadata()); // needs to go BEFORE swaggerSecurity

app.use(
middleware.swaggerSecurity({
//manage token function in the 'auth' module
Bearer: auth.verifyToken
})
);

var routerConfig = {
controllers: "./api/controllers",
useStubs: false
};

// install middleware
swaggerExpress.register(app);
app.use(middleware.swaggerRouter(routerConfig));

app.use(middleware.swaggerUi());

var port = process.env.PORT || 10010;
app.listen(port);

if (swaggerExpress.runner.swagger.paths['/hello']) {
console.log('try this:\ncurl http://127.0.0.1:' + port + '/hello?name=Scott');
}
app.listen(port, function() {
console.log("Started server on port 10010");
});
});

//
//SwaggerExpress.create(swaggerConfig, function(err, swaggerExpress) {
// if (err) { throw err; }
//
// // install middleware
// swaggerExpress.register(app);
//
// var port = process.env.PORT || 10010;
// app.listen(port);
//
//});
Loading

0 comments on commit 5ab9427

Please sign in to comment.