X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fauthenticator.js;h=c69ada91c6ed6a5234942b1e6ae8eb92a420d7e6;hb=bf14939f630dac71d286d499d6c096a003232148;hp=15089a020f5a977f09239537e210c40c6ca61173;hpb=dd173e6b450cbba8100883514610c9fde83d050a;p=squeep-authentication-module diff --git a/lib/authenticator.js b/lib/authenticator.js index 15089a0..c69ada9 100644 --- a/lib/authenticator.js +++ b/lib/authenticator.js @@ -20,6 +20,7 @@ class Authenticator { * @param {Boolean} options.authenticator.secureAuthOnly * @param {String[]} options.authenticator.forbiddenPAMIdentifiers * @param {String[]} options.authenticator.authnEnabled + * @param {Number=} options.authenticator.inactiveSessionLifespanSeconds * @param {String[]=} options.authenticator.loginBlurb * @param {String[]=} options.authenticator.indieAuthBlurb * @param {String[]=} options.authenticator.userBlurb @@ -51,6 +52,8 @@ class Authenticator { } this.mysteryBox = new MysteryBox(logger, options); + + this.cookieLifespan = options.authenticator.inactiveSessionLifespanSeconds || 60 * 60 * 24 * 32; } @@ -63,10 +66,14 @@ class Authenticator { */ async isValidIdentifierCredential(identifier, credential, ctx) { const _scope = _fileScope('isValidIdentifierCredential'); - this.logger.debug(_scope, 'called', { identifier, credential: '*'.repeat(credential.length), ctx }); + this.logger.debug(_scope, 'called', { identifier, credential: '*'.repeat((credential || '').length), ctx }); let isValid = false; + if (typeof credential === 'undefined') { + return isValid; + } + await this.db.context(async (dbCtx) => { const authData = await this.db.authenticationGet(dbCtx, identifier); if (!authData) { @@ -259,6 +266,18 @@ class Authenticator { && (ctx.session.authenticatedIdentifier || (profilesAllowed && ctx.session.authenticatedProfile))) { this.logger.debug(_scope, 'valid session cookie', { ctx }); + // Refresh timeout on valid session. + const cookieParts = [ + sessionCookie, + 'HttpOnly', + `Max-Age=${this.cookieLifespan}`, + 'SameSite=Lax', + `Path=${this.options.dingus.proxyPrefix}/`, + ]; + if (this.secureAuthOnly) { + cookieParts.push('Secure'); + } + res.setHeader(Enum.Header.SetCookie, cookieParts.join('; ')); return true; } @@ -268,6 +287,7 @@ class Authenticator { `${Enum.SessionCookie}=""`, 'HttpOnly', 'Max-Age=0', + 'SameSite=Lax', `Path=${this.options.dingus.proxyPrefix}/`, ]; if (this.options.authenticator.secureAuthOnly) { @@ -335,6 +355,40 @@ class Authenticator { return this.sessionCheck(req, res, ctx, undefined, false); } + + /** + * Require auth for an API endpoint. + * Check for valid local identifier in Authorization header; optionally + * fall back to session cookie if no header provided. + * Prompts for Basic auth if not valid. + * @param {http.ClientRequest} req + * @param {http.ServerResponse} res + * @param {Object} ctx + * @param {Boolean} sessionAlsoValid + */ + async apiRequiredLocal(req, res, ctx, sessionAlsoValid = true) { + const _scope = _fileScope('apiRequiredLocal'); + this.logger.debug(_scope, 'called', { ctx, sessionAlsoValid }); + + // If a Authorization header was provided, never consider session as a fallback. + const authorizationHeader = req.getHeader(Enum.Header.Authorization); + if (authorizationHeader) { + if (await this.isValidAuthorization(authorizationHeader, ctx)) { + this.logger.debug(_scope, 'valid authorization', { ctx, sessionAlsoValid }); + return true; + } + } else { + if (sessionAlsoValid + && await this.sessionCheck(req, res, ctx, undefined, false, false)) { + this.logger.debug(_scope, 'valid session', { ctx, sessionAlsoValid }); + return true; + } + } + + this.logger.debug(_scope, 'invalid authorization', { ctx, sessionAlsoValid }); + this.requestBasic(res); + } + } -module.exports = Authenticator; \ No newline at end of file +module.exports = Authenticator;