UNPKG

nsfw-api

Version:
322 lines (276 loc) 13.7 kB
import HttpStatus from 'http-status-codes'; import MyTokenHelper from "./MyTokenHelper"; import MyAccessControl from "./../module/MyAccessControl"; import MyExpressRouter from "../module/MyExpressRouter"; import AuthConnector from "./AuthConnector"; /** * The My Express Router which is the core functionality. It setups all controllers and handles routes */ export default class MyAuthMiddlewares { constructor(logger, models, expressApp, routeAuth, authConfig) { this.logger = logger; this.expressApp = expressApp; this.routeAuth = routeAuth; this.authConfig = authConfig; this.models = models; this.routeAuthMethodList = this.routeAuth+"/"+"methods"; this.routeAuthAccessToken = this.routeAuth+"/"+"accessToken"; this.routeAuthRefreshAccessToken = this.routeAuth+"/"+"refresh"; this.routeAuthAuthorize = this.routeAuth+"/"+"authorize"; this.routeAuthLogout = this.routeAuth+"/"+"logout"; this.routeAuthLogoutFromAllDevices = this.routeAuth+"/"+"logoutFromAllDevices"; this.routeAuthCurrentUser = this.routeAuth+"/"+"currentUser"; this.tokenHelper = new MyTokenHelper(logger); //create the token helper this.authConnector = new AuthConnector(authConfig); this.authConnector.configureAuthMethods(); this.configureExpressApp(); } configureExpressApp(){ this.expressApp.use(this.middlewareCookieParse.bind(this)); this.expressApp.use(this.middlewareAuthToken.bind(this)); //always check if there is a access token provided this.expressApp.get(this.routeAuth, this.handleGetRouteInfo.bind(this)); this.expressApp.get(this.routeAuthMethodList, this.handleGetAuthMethods.bind(this)); this.expressApp.post(this.routeAuthRefreshAccessToken, this.middlewareOnlyAuthenticatedViaRefreshToken.bind(this), this.handleRefreshAccessToken.bind(this)); this.expressApp.get(this.routeAuthLogout, this.middlewareOnlyAuthenticatedViaRefreshToken.bind(this), this.handleLogout.bind(this)); this.expressApp.get(this.routeAuthLogoutFromAllDevices, this.middlewareOnlyAuthenticatedViaPlaintextSecret.bind(this), this.handleLogoutFromAllDevices.bind(this)); this.expressApp.post(this.routeAuthLogout, this.middlewareOnlyAuthenticatedViaRefreshToken.bind(this), this.handleLogout.bind(this)); this.expressApp.post(this.routeAuthLogoutFromAllDevices, this.middlewareOnlyAuthenticatedViaPlaintextSecret.bind(this), this.handleLogoutFromAllDevices.bind(this)); this.expressApp.post(this.routeAuthAuthorize, this.middlewareOnlyAuthenticatedViaPlaintextSecret.bind(this), this.handleAuthorize.bind(this)); this.expressApp.get(this.routeAuthCurrentUser, this.handleGetCurrentUser.bind(this)); } middlewareCookieParse(req, res, next){ let cookies = MyAuthMiddlewares.parseCookies(req); req.locals = req.locals || {}; req.locals.cookies = cookies; next(); } static parseCookies (request) { //console.log("parseCookies"); const list = {}; const rc = request.headers.cookie; //console.log("Request Cookies"); //console.log(rc); rc && rc.split(';').forEach(function( cookie ) { const parts = cookie.split('='); list[parts.shift().trim()] = decodeURI(parts.join('=')); }); //Remove Empty Variables in Cookies let cookieKeys = Object.keys(list); for(let i=0; i<cookieKeys.length; i++){ let key = cookieKeys[i]; if(list[key]===""){ delete list[key]; } } return list; } handleGetRouteInfo(req,res){ let answer = { "AuthMethods" : this.routeAuthMethodList, "Refresh": this.routeAuthRefreshAccessToken, "Authorize": this.routeAuthAuthorize, "AccessToken" : this.routeAuthAccessToken, "CurrentUser" : this.routeAuthCurrentUser, "Logout": this.routeAuthLogout, "LogoutFromAllDevices" : this.routeAuthLogoutFromAllDevices } MyExpressRouter.responseWithSuccessJSON(res, answer); //anyway answer normaly } handleGetCurrentUser(req,res){ let currentUser = req.locals.currentUser; MyExpressRouter.responseWithSuccessJSON(res, currentUser); //anyway answer normaly } handleGetAuthMethods(req,res){ let dataJSON = AuthConnector.getAuthMethods(); MyExpressRouter.responseWithSuccessJSON(res, dataJSON); //anyway answer normaly } /** * @apiDefine MyAuthorization * @apiHeader {String="Authorization TOKEN"} authorization Authorization Token. */ middlewareAuthToken(req, res, next) { const logger = this.logger; this.logger.info("[MyExpressRouter] middlewareAuthToken - url: " + req.url); //console.log("URL: "+req.url); req.locals = req.locals || {}; req.locals.currentUser = {}; //create a current user, which will be the user who initiates the request req.locals.currentUser.role = MyAccessControl.roleNameGuest; //define it as anonymous if(!!this.authConfig.disabled){ req.locals.currentUser.role = MyAccessControl.roleNameAdmin; //better make him then admin next(); return; //abort further checks } try { //lets try to find if a token is provided let authorization = req.headers.authorization; //get auth headers if (!!authorization) { //if there are headers const token = authorization.split(" ")[1]; //get the token Header: ACCESSTOKEN TheSuperCoolToken //start verification of headers this.tokenHelper.verifyToken(token, function (err, tokenPayload) { //verify the token if (err != null) { //if there is an error if (err.name === "TokenExpiredError") { //if token is Expired MyExpressRouter.responseWithErrorJSON(res, HttpStatus.UNAUTHORIZED, {message:'AccessTokenExpiredError'}); return; } else { //thats an invalid token boy ! //logger.error("[" + workerID + "][MyExpressRouter] middlewareAuthToken - invalid token ! remoteAddress: " + ip); MyExpressRouter.responseWithErrorJSON(res, HttpStatus.UNAUTHORIZED, {message:'AccessTokenInvalid'}); return; } } if (!!tokenPayload) { // payload was correct so we know which user this is req.locals.currentUser = tokenPayload || {}; if (!req.locals.currentUser.role) { req.locals.currentUser.role = MyAccessControl.roleNameUser; // is nothing provided, you are atleast a user } } next(); }); } else { //well no authoritation provided next(); } } catch (err) { //no headers found or some wrong headers provided console.log(err); logger.error("[MyExpressRouter] middlewareAuthToken - " + err.toString()); next(); } } static cookieRefreshTokenKey = "refreshToken"; resetRefreshTokenCookie(res){ this.setRefreshTokenCookie(res,""); } setRefreshTokenCookie(res,refreshToken){ //HttpOnly - Javascript cant access this //Path since we dont want, that the client ALWAYS sends the refresh token, we specify a path when to send it res.header('Set-Cookie', [MyAuthMiddlewares.cookieRefreshTokenKey+'='+refreshToken+'; HttpOnly; Path=/api/auth;']); } async handleLogout(req,res){ let refreshToken = req.locals.refreshToken; let success = await this.tokenHelper.rejectRefreshToken(refreshToken); this.resetRefreshTokenCookie(res); MyExpressRouter.responseWithSuccessJSON(res, {refreshToken: null}); return; } async handleLogoutFromAllDevices(req,res){ let authObject = req.locals.currentUser; if(!!authObject){ let success = await this.tokenHelper.rejectAllRefreshTokensFromAuthObject(authObject); this.resetRefreshTokenCookie(res); MyExpressRouter.responseWithSuccessJSON(res, {refreshToken: null}); return; } else { MyExpressRouter.responseWithErrorJSON(res, HttpStatus.INTERNAL_SERVER_ERROR, {message:"No Credentials Provided"}); } } async handleAuthorize(req, res){ let authObject = req.locals.currentUser; let currentUser = req.locals.currentUser; let rememberMe = req.body.rememberMe; let accessToken = this.tokenHelper.createAccessToken(authObject); //create token let refreshToken = await this.tokenHelper.createRefreshToken(authObject); //create token if(!!rememberMe){ this.setRefreshTokenCookie(res,refreshToken); } MyExpressRouter.responseWithSuccessJSON(res, {refreshToken: refreshToken, accessToken: accessToken, currentUser: currentUser}); } handleRefreshAccessToken(req, res) { let authObject = req.locals.currentUser; let currentUser = req.locals.currentUser; let accessToken = this.tokenHelper.createAccessToken(authObject); //create token let answer = {accessToken: accessToken, currentUser: currentUser}; //create answer MyExpressRouter.responseWithSuccessJSON(res, answer); } static getRefreshTokenFromReq(req){ req.locals = req.locals || {}; return req.locals.cookies[MyAuthMiddlewares.cookieRefreshTokenKey]; } /** * Middleware which will only Accept Plaintext Passwords, Tokens dont help you here ! * @param req the reuqest object * @param res the response object * @param next the next function * * @apiDefine MyPasswordAuthorization * @apiHeader {Content-Type="application/json"} Content-Type Authorization Token. * @apiParam {Number} user_id User's unique ID. * @apiParam {String} plaintextSecret User's password as plain text. */ async middlewareOnlyAuthenticatedViaRefreshToken(req, res, next) { if(!!this.authConfig.disabled){ console.log("Skip, auth is disabled"); next(); return; } req.locals = req.locals || {}; let refreshToken = null; let cookiesRefreshToken = MyAuthMiddlewares.getRefreshTokenFromReq(req); if(!!cookiesRefreshToken){ refreshToken = cookiesRefreshToken; } let bodyRefreshToken = req.body.refreshToken; if(!!bodyRefreshToken){ refreshToken = bodyRefreshToken; } if(!!refreshToken){ let authObject = await MyTokenHelper.getAuthObjectFromRefreshToken(refreshToken); //console.log(authObject); if(!!authObject){ req.locals.refreshToken = refreshToken; req.locals.currentUser = authObject; next(); return; } else { if(!!cookiesRefreshToken){ this.resetRefreshTokenCookie(res); } MyExpressRouter.responseWithErrorJSON(res, HttpStatus.BAD_REQUEST, {message:"RefreshTokenInvalid"}); return; } } else { if(!!cookiesRefreshToken){ this.resetRefreshTokenCookie(res); } MyExpressRouter.responseWithErrorJSON(res, HttpStatus.BAD_REQUEST, {message:"RefreshTokenMissing"}); return; } } /** * Middleware which will only Accept Plaintext Passwords, Tokens dont help you here ! * @param req the reuqest object * @param res the response object * @param next the next function * * @apiDefine PasswordAuthorization * @apiHeader {Content-Type="application/json"} Content-Type Authorization Token. * @apiParam {Number} user_id User's unique ID. * @apiParam {String} plaintextSecret User's password as plain text. */ async middlewareOnlyAuthenticatedViaPlaintextSecret(req, res, next) { if(!!this.authConfig.disabled){ next(); return; } console.log("middlewareOnlyAuthenticatedViaPlaintextSecret"); //Remove Token Authentication req.locals = req.locals || {}; req.locals.currentUser = {}; req.locals.currentUser.role = MyAccessControl.roleNameGuest; let models = this.models; let auth = req.body.auth; if (!!auth) { //if a plaintext was provided let answer = await AuthConnector.authorize(auth, models); if(!!answer){ if(answer.success){ req.locals.currentUser = answer.auth; next(); } else if(!!answer.error){ MyExpressRouter.responseWithErrorJSON(res, HttpStatus.BAD_REQUEST, {message:"auth not correct", details: answer.error}); } else if(!!answer.success) { } } else { MyExpressRouter.responseWithErrorJSON(res, HttpStatus.INTERNAL_SERVER_ERROR, {message:"Unexpected Error at auth"}); } } else { //no auth given MyExpressRouter.responseWithErrorJSON(res, HttpStatus.BAD_REQUEST, {message:"auth is missing"}); } } }