summaryrefslogtreecommitdiffstats
path: root/server/lib/authentication.js
diff options
context:
space:
mode:
authorJannik Schönartz2019-02-22 02:59:26 +0100
committerJannik Schönartz2019-02-22 02:59:26 +0100
commit892a048d072d05886951bcb92e6b61c2094a6463 (patch)
tree2ff89b4d69c829304f55d529203eed985aaac413 /server/lib/authentication.js
parentrework user api to rest (diff)
downloadbas-892a048d072d05886951bcb92e6b61c2094a6463.tar.gz
bas-892a048d072d05886951bcb92e6b61c2094a6463.tar.xz
bas-892a048d072d05886951bcb92e6b61c2094a6463.zip
[authentication] Implement initial root account setup
[backend] Reworked authentication library to the api structure Add authentication api to remove the login routes from the router.js [webapp] Split login Page in StartPage + Login/Setup Add Setup Page for the initial root creation
Diffstat (limited to 'server/lib/authentication.js')
-rw-r--r--server/lib/authentication.js206
1 files changed, 109 insertions, 97 deletions
diff --git a/server/lib/authentication.js b/server/lib/authentication.js
index 58d5e10..76e8b60 100644
--- a/server/lib/authentication.js
+++ b/server/lib/authentication.js
@@ -6,110 +6,121 @@ var db = require(path.join(__appdir, 'lib', 'sequelize'))
var securePassword = require('secure-password')
var pwd = securePassword()
-module.exports = {
- // Authentifivation method for the frontend using secure httpOnly cookies. (POST)
- login: function (req, res) {
- var params = req.body
-
- verifyUser(res, params.username, params.password, function (token) {
- // The token has the form header.payload.signature
- // We split the cookie in header.payload and signature in two seperate cookies.
- // The signature cookie is httpOnly so JavaScript never has access to the full cookie.
- // Read more at: https://medium.com/lightrail/getting-token-authentication-right-in-a-stateless-single-page-application-57d0c6474e3
- const split = token.split('.')
- const headerPayload = split[0] + '.' + split[1]
- const signature = split[2]
- res.cookie('jwt_hp', headerPayload, { secure: true, httpOnly: false, sameSite: 'strict' })
- res.cookie('jwt_s', signature, { secure: true, httpOnly: true, sameSite: 'strict' })
- return res.status(200).send({ auth: true, status: 'VALID' })
- })
- },
- // Authentification method for the API using the authorization header. (GET)
- auth: function (req, res) {
- var query = req.query
+module.exports = { loginCookie, loginToken, logout, verifyToken, signup, changePassword }
+
+// Authentifivation method for the frontend using secure httpOnly cookies. (POST)
+function loginCookie (req, res) {
+ var params = req.body
+ verifyUser(res, params.username, params.password, function (token) {
+ // The token has the form header.payload.signature
+ // We split the cookie in header.payload and signature in two seperate cookies.
+ // The signature cookie is httpOnly so JavaScript never has access to the full cookie.
+ // Read more at: https://medium.com/lightrail/getting-token-authentication-right-in-a-stateless-single-page-application-57d0c6474e3
+ const split = token.split('.')
+ const headerPayload = split[0] + '.' + split[1]
+ const signature = split[2]
+ res.cookie('jwt_hp', headerPayload, { secure: true, httpOnly: false, sameSite: 'strict' })
+ res.cookie('jwt_s', signature, { secure: true, httpOnly: true, sameSite: 'strict' })
+ return res.status(200).send({ auth: true, status: 'VALID' })
+ })
+}
- verifyUser(res, query.username, query.password, function (token) {
- return res.status(200).send({ auth: true, token })
- })
- },
-
- signup: function (req, res) {
- // TODO: Implement some security stuff. Not every user who call this request should be able to sign up.
-
- var params = req.body
- if (!params.username) return res.status(500).send({ auth: false, status: 'USER_MISSING', error_message: 'This service requires an username.' })
- if (!params.password) return res.status(500).send({ auth: false, status: 'PASSWORD_MISSING', error_message: 'This services requires a password.' })
- if (!params.email) return res.status(500).send({ auth: false, status: 'EMAIL_MISSING', error_message: 'This services requires an email.' })
- // Database and user validation.
- db.user.findOne({ where: { username: params.username } }).then(userDb => {
- // User exists validation.
- if (userDb) return res.status(500).send({ auth: false, status: 'USER_ALREADY_EXISTS', error_message: 'The provided username already exists.' })
-
- // Password requirements validation.
- if (!validatePassword(params.password)) return res.status(500).send({ auth: false, status: 'PASSWORD_REQUIREMENTS', error_message: 'The password requirements are not fullfilled.' })
- // Email validation.
- if (!validateEmail(params.email)) return res.status(500).send({ auth: false, status: 'EMAIL_INVALID', error_message: 'The provided email is invalid.' })
- var userPassword = Buffer.from(params.password)
- // Register user
- pwd.hash(userPassword, function (err, hash) {
- if (err) return res.status(500).send({ auth: false, status: 'PASSWORD_HASH_ERROR', error_message: 'Hashing the password failed.' })
- // Saving the non improved hash and creating the user in the db.
- db.user.create({ username: params.username, password: hash, email: params.email, name: params.name }).then((userDb) => {
- // TODO: Username could also be used because those are unique as well.
- var userId = userDb.id
-
- // Verify & improving the hash.
- verifyHash(res, userPassword, hash, userId, function () {
- return res.status(200).send({ auth: true, status: 'VALID' })
- })
+// Authentification method for the API using the authorization header. (GET)
+function loginToken (req, res) {
+ var body = req.body
+ verifyUser(res, body.username, body.password, function (token) {
+ return res.status(200).send({ auth: true, token })
+ })
+}
+
+// Method for creating a new user.
+function signup (req, res) {
+ // TODO: Implement some security stuff. Not every user who call this request should be able to sign up.
+ var params = req.body
+ if (!params.username) return res.status(500).send({ auth: false, status: 'USER_MISSING', error_message: 'This service requires an username.' })
+ if (!params.password) return res.status(500).send({ auth: false, status: 'PASSWORD_MISSING', error_message: 'This services requires a password.' })
+ if (!params.email) return res.status(500).send({ auth: false, status: 'EMAIL_MISSING', error_message: 'This services requires an email.' })
+
+ // Database and user validation.
+ db.user.findOne({ where: { username: params.username } }).then(userDb => {
+ // User exists validation.
+ if (userDb) return res.status(500).send({ auth: false, status: 'USER_ALREADY_EXISTS', error_message: 'The provided username already exists.' })
+ // Password requirements validation.
+ if (!validatePassword(params.password)) return res.status(500).send({ auth: false, status: 'PASSWORD_REQUIREMENTS', error_message: 'The password requirements are not fullfilled.' })
+ // Email validation.
+ if (!validateEmail(params.email)) return res.status(500).send({ auth: false, status: 'EMAIL_INVALID', error_message: 'The provided email is invalid.' })
+ var userPassword = Buffer.from(params.password)
+
+ // Register user
+ pwd.hash(userPassword, function (err, hash) {
+ if (err) return res.status(500).send({ auth: false, status: 'PASSWORD_HASH_ERROR', error_message: 'Hashing the password failed.' })
+ // Saving the non improved hash and creating the user in the db.
+ db.user.create({ username: params.username, password: hash, email: params.email, name: params.name }).then((userDb) => {
+ // TODO: Username could also be used because those are unique as well.
+ var userId = userDb.id
+ // Verify & improving the hash.
+ verifyHash(res, userPassword, hash, userId, function () {
+ return res.status(200).send({ auth: true, status: 'VALID' })
})
})
})
- },
-
- // Logout method for the frontend. Deleting the cookies by overwriting them.
- logout: function (req, res) {
- // End session properly.
- res.clearCookie('jwt_hp')
- res.clearCookie('jwt_s')
- return res.status(200).send()
- // TODO: Implement.. blacklisting for jwt's and destroy the cookies..
- // Maybe use express-jwt and use the rewoke function.
- },
-
- changePassword: function (req, res) {
- // TODO: IMPLEMENT
- },
- verifyToken: function (req, res, next) {
- var token = ''
- // Check for the token in the authorization header or in the cookies. Else return with auth: false.
- if (req.headers['authorization']) {
- var authorization = req.headers['authorization']
- // Authorization: Bearer <token>
- // Split the bearer token.
- const bearer = authorization.split(' ')
- token = bearer[1]
- } else if (req.cookies.jwt_hp && req.cookies.jwt_s) {
- token = req.cookies.jwt_hp + '.' + req.cookies.jwt_s
- } else {
- if (res) return res.status(403).send({ auth: false, status: 'TOKEN_MISSING', error_message: 'This service requires a token.' })
- else return next(new Error('TOKEN_MISSING'))
+ })
+}
+
+// Logout method for the frontend. Deleting the cookies by overwriting them.
+function logout (req, res) {
+ // End session properly.
+ res.clearCookie('jwt_hp')
+ res.clearCookie('jwt_s')
+ return res.status(200).send()
+ // TODO: Implement.. blacklisting for jwt's and destroy the cookies..
+ // Maybe use express-jwt and use the rewoke function.
+}
+
+function changePassword (req, res) {
+ // TODO: IMPLEMENT
+}
+
+// Middleware function.
+// Verifies the token given in the request either by the authorization header or jwt cookies.
+function verifyToken (req, res, next) {
+ var token = ''
+ // Check for the token in the authorization header or in the cookies. Else return with auth: false.
+ if (req.headers['authorization']) {
+ var authorization = req.headers['authorization']
+ // Authorization: Bearer <token>
+ // Split the bearer token.
+ const bearer = authorization.split(' ')
+ token = bearer[1]
+ } else if (req.cookies.jwt_hp && req.cookies.jwt_s) {
+ token = req.cookies.jwt_hp + '.' + req.cookies.jwt_s
+ } else {
+ if (res) return res.status(403).send({ auth: false, status: 'TOKEN_MISSING', error_message: 'This service requires a token.' })
+ else return next(new Error('TOKEN_MISSING'))
+ }
+
+ // Verify the token with the secret.
+ jwt.verify(token, config.secret, function (err) {
+ if (err) {
+ if (res) return res.status(500).send({ auth: false, status: 'TOKEN_INVALID', error_message: 'The provided token is invalid.' })
+ else return next(new Error('TOKEN_INVALID'))
}
- // Verify the token with the secret.
- jwt.verify(token, config.secret, function (err) {
- if (err) {
- if (res) return res.status(500).send({ auth: false, status: 'TOKEN_INVALID', error_message: 'The provided token is invalid.' })
- else return next(new Error('TOKEN_INVALID'))
- }
- req.token = token
- const decodedToken = jwt.decode(token, { complete: true })
- req.user = { id: decodedToken.payload.user.id }
-
- next()
+ req.token = token
+ const decodedToken = jwt.decode(token, { complete: true })
+ req.user = { id: decodedToken.payload.user.id }
+
+ // Check weather the user exists.
+ db.user.findOne({ where: { id: req.user.id } }).then(user => {
+ if (user) next()
+ else return res.status(500).send({ auth: false, status: 'TOKEN_INVALID', error_message: 'The token is from an invalid userid.' })
})
- }
+ })
}
+// ################################################
+// ############## Helper function #################
+// ################################################
+
// The function for verifying a user. Callback only gets called if the user gets verified.
function verifyUser (res, username, password, callback) {
if (!username) return res.status(500).send({ auth: false, status: 'USER_MISSING', error_message: 'This service requires an username.' })
@@ -175,6 +186,7 @@ function validateEmail (email) {
// Function for validating the password. Password requirements are implemented here.
function validatePassword (password) {
- // TODO: implement pw requirements like in the frontend.
+ // TODO: implement pw requirements like in the frontend. (SetupPage)
+ if (password.length < 8) return false
return true
}