|
- const config = require('../setup/config');
- const crypto = require('crypto');
- const debug = require('debug')('server-connect:auth');
-
- class AuthProvider {
-
- constructor(app, opts, name) {
- this.app = app;
- this.name = name;
- this.identity = this.app.getSession(this.name + 'Id') || false;
- this.secret = opts.secret || config.secret;
- this.basicAuth = opts.basicAuth;
- this.basicRealm = opts.basicRealm;
- this.passwordVerify = opts.passwordVerify || false;
-
- this.cookieOpts = {
- domain: opts.domain || undefined,
- httpOnly: true,
- maxAge: (opts.expires || 30) * 24 * 60 * 60 * 1000, // from days to ms
- path: opts.path || '/',
- secure: !!opts.secure,
- sameSite: opts.sameSite || 'Strict',
- signed: true
- };
- }
-
- async autoLogin() {
- if (this.basicAuth) {
- const auth = require('../core/basicauth')(this.app.req);
- debug(`Basic auth credentials received: %o`, auth);
- if (auth) await this.login(auth.username, auth.password, false, true);
- }
-
- const cookie = this.app.getCookie(this.name + '.auth', true);
- if (cookie) {
- const auth = this.decrypt(cookie);
- debug(`Login with cookie: %o`, auth);
- if (auth) await this.login(auth.username, auth.password, true, true);
- } else {
- debug(`No login cookie found`);
- }
- }
-
- async login(username, password, remember, autoLogin) {
- const identity = await this.validate(username, password, this.passwordVerify);
-
- if (!identity) {
- await this.logout();
-
- if (!autoLogin) {
- this.unauthorized();
- return false;
- }
- } else {
- this.app.setSession(this.name + 'Id', identity);
- this.app.set('identity', identity);
-
- if (remember) {
- debug('setCookie', identity, username, password);
- this.app.setCookie(this.name + '.auth', this.encrypt({ username, password }), this.cookieOpts);
- }
-
- this.identity = identity;
- }
-
- return identity;
- }
-
- async logout() {
- this.app.removeSession(this.name + 'Id');
- this.app.removeCookie(this.name + '.auth', this.cookieOpts);
- this.app.remove('identity');
- this.identity = false;
- }
-
- async restrict(opts) {
- if (this.identity === false) {
- if (opts.loginUrl) {
- if (this.app.req.fragment) {
- this.app.res.status(222).send(opts.loginUrl);
- } else {
- this.app.res.redirect(opts.loginUrl);
- }
- } else {
- this.unauthorized();
- }
-
- return;
- }
-
- if (opts.permissions) {
- const allowed = await this.permissions(this.identity, opts.permissions);
- if (!allowed) {
- if (opts.forbiddenUrl) {
- if (this.app.req.fragment) {
- this.app.res.status(222).send(opts.forbiddenUrl);
- } else {
- this.app.res.redirect(opts.forbiddenUrl);
- }
- } else {
- this.forbidden();
- }
- }
- }
- }
-
- encrypt(data) {
- const iv = crypto.randomBytes(16);
- const key = crypto.scryptSync(this.secret, iv, 32);
- const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
- const encrypted = cipher.update(JSON.stringify(data), 'utf8', 'base64');
- return iv.toString('base64') + '.' + encrypted + cipher.final('base64');
- }
-
- decrypt(data) {
- // try/catch to prevent errors on currupt cookies
- try {
- const iv = Buffer.from(data.split('.')[0], 'base64');
- const key = crypto.scryptSync(this.secret, iv, 32);
- const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
- const decrypted = decipher.update(data.split('.')[1], 'base64', 'utf8');
- return JSON.parse(decrypted + decipher.final('utf8'));
- } catch (err) {
- return undefined;
- }
- }
-
- unauthorized() {
- if (this.basicAuth) {
- this.app.res.set('WWW-Authenticate', `Basic Realm="${this.basicRealm}"`);
- }
- this.app.res.sendStatus(401);
- }
-
- forbidden() {
- this.app.res.sendStatus(403);
- }
-
- async validate(username, password) {
- throw new Error('auth.validate needs to be extended.');
- }
-
- async permissions(identity, permissions) {
- throw new Error('auth.permissions needs to be extended.');
- }
-
- }
-
- module.exports = AuthProvider;
|