authelia/server/src/lib/IdentityCheckMiddleware.ts
Clement Michaud 563e2da323 Add default_redirection_url as configuration option
This URL is used when user access the authentication domain without providing
the 'redirect' query parameter. In that case, Authelia does not know
where to redirect the user.
If the parameter is defined, Authelia can redirect the user to a default page
when no redirect parameter is provided.

When user is already authenticated and tries to access the authentication
domain, the "already logged in" page is rendered and it now tells the user he
is to be redirected in few seconds and uses this URL to redirect.

This parameter is optional. If it is not provided, there is only a notification
message at the end of the authentication process, as before, and the user is
not redirected when visiting the authentication domain while already
authenticated.
2017-10-31 07:27:23 +01:00

136 lines
5.3 KiB
TypeScript

import objectPath = require("object-path");
import randomstring = require("randomstring");
import BluebirdPromise = require("bluebird");
import util = require("util");
import Exceptions = require("./Exceptions");
import fs = require("fs");
import ejs = require("ejs");
import { IUserDataStore } from "./storage/IUserDataStore";
import { Winston } from "../../types/Dependencies";
import express = require("express");
import ErrorReplies = require("./ErrorReplies");
import AuthenticationSessionHandler = require("./AuthenticationSession");
import { AuthenticationSession } from "../../types/AuthenticationSession";
import { ServerVariables } from "./ServerVariables";
import Identity = require("../../types/Identity");
import { IdentityValidationDocument } from "./storage/IdentityValidationDocument";
const filePath = __dirname + "/../resources/email-template.ejs";
const email_template = fs.readFileSync(filePath, "utf8");
// IdentityValidator allows user to go through a identity validation process in two steps:
// - Request an operation to be performed (password reset, registration).
// - Confirm operation with email.
export interface IdentityValidable {
challenge(): string;
preValidationInit(req: express.Request): BluebirdPromise<Identity.Identity>;
postValidationInit(req: express.Request): BluebirdPromise<void>;
// Serves a page after identity check request
preValidationResponse(req: express.Request, res: express.Response): void;
// Serves the page if identity validated
postValidationResponse(req: express.Request, res: express.Response): void;
mailSubject(): string;
}
function createAndSaveToken(userid: string, challenge: string, userDataStore: IUserDataStore)
: BluebirdPromise<string> {
const five_minutes = 4 * 60 * 1000;
const token = randomstring.generate({ length: 64 });
const that = this;
return userDataStore.produceIdentityValidationToken(userid, token, challenge, five_minutes)
.then(function () {
return BluebirdPromise.resolve(token);
});
}
function consumeToken(token: string, challenge: string, userDataStore: IUserDataStore)
: BluebirdPromise<IdentityValidationDocument> {
return userDataStore.consumeIdentityValidationToken(token, challenge);
}
export function register(app: express.Application, pre_validation_endpoint: string,
post_validation_endpoint: string, handler: IdentityValidable, vars: ServerVariables) {
app.get(pre_validation_endpoint, get_start_validation(handler, post_validation_endpoint, vars));
app.get(post_validation_endpoint, get_finish_validation(handler, vars));
}
function checkIdentityToken(req: express.Request, identityToken: string): BluebirdPromise<void> {
if (!identityToken)
return BluebirdPromise.reject(new Exceptions.AccessDeniedError("No identity token provided"));
return BluebirdPromise.resolve();
}
export function get_finish_validation(handler: IdentityValidable,
vars: ServerVariables)
: express.RequestHandler {
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
let authSession: AuthenticationSession;
const identityToken = objectPath.get<express.Request, string>(req, "query.identity_token");
vars.logger.debug(req, "Identity token provided is %s", identityToken);
return checkIdentityToken(req, identityToken)
.then(function () {
return handler.postValidationInit(req);
})
.then(function () {
return AuthenticationSessionHandler.get(req, vars.logger);
})
.then(function (_authSession) {
authSession = _authSession;
})
.then(function () {
return consumeToken(identityToken, handler.challenge(), vars.userDataStore);
})
.then(function (doc: IdentityValidationDocument) {
authSession.identity_check = {
challenge: handler.challenge(),
userid: doc.userId
};
handler.postValidationResponse(req, res);
return BluebirdPromise.resolve();
})
.catch(ErrorReplies.replyWithError401(req, res, vars.logger));
};
}
export function get_start_validation(handler: IdentityValidable,
postValidationEndpoint: string,
vars: ServerVariables)
: express.RequestHandler {
return function (req: express.Request, res: express.Response): BluebirdPromise<void> {
let identity: Identity.Identity;
return handler.preValidationInit(req)
.then(function (id: Identity.Identity) {
identity = id;
const email = identity.email;
const userid = identity.userid;
vars.logger.info(req, "Start identity validation of user \"%s\"", userid);
if (!(email && userid))
return BluebirdPromise.reject(new Exceptions.IdentityError(
"Missing user id or email address"));
return createAndSaveToken(userid, handler.challenge(), vars.userDataStore);
})
.then(function (token: string) {
const host = req.get("Host");
const link_url = util.format("https://%s%s?identity_token=%s", host,
postValidationEndpoint, token);
vars.logger.info(req, "Notification sent to user \"%s\"", identity.userid);
return vars.notifier.notify(identity.email, handler.mailSubject(), link_url);
})
.then(function () {
handler.preValidationResponse(req, res);
return BluebirdPromise.resolve();
})
.catch(ErrorReplies.replyWithError401(req, res, vars.logger));
};
}