import objectPath = require("object-path"); import randomstring = require("randomstring"); import BluebirdPromise = require("bluebird"); import util = require("util"); import Exceptions = require("./Exceptions"); import { IUserDataStore } from "./storage/IUserDataStore"; import Express = require("express"); import ErrorReplies = require("./ErrorReplies"); import { AuthenticationSessionHandler } from "./AuthenticationSessionHandler"; import { AuthenticationSession } from "../../types/AuthenticationSession"; import { ServerVariables } from "./ServerVariables"; import { IdentityValidable } from "./IdentityValidable"; import * as Constants from "../../../shared/constants"; import Identity = require("../../types/Identity"); import { IdentityValidationDocument } from "./storage/IdentityValidationDocument"; import { OPERATION_FAILED } from "../../../shared/UserMessages"; import GetHeader from "./utils/GetHeader"; function createAndSaveToken(userid: string, challenge: string, userDataStore: IUserDataStore): BluebirdPromise<string> { const five_minutes = 4 * 60 * 1000; const token = randomstring.generate({ length: 64 }); 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.post(pre_validation_endpoint, post_start_validation(handler, vars)); app.post(post_validation_endpoint, post_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 post_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.token"); vars.logger.debug(req, "Identity token provided is %s", identityToken); return checkIdentityToken(req, identityToken) .then(() => { authSession = AuthenticationSessionHandler.get(req, vars.logger); return handler.postValidationInit(req); }) .then(() => { return consumeToken(identityToken, handler.challenge(), vars.userDataStore); }) .then((doc: IdentityValidationDocument) => { authSession.identity_check = { challenge: handler.challenge(), userid: doc.userId }; handler.postValidationResponse(req, res); return BluebirdPromise.resolve(); }) .catch(ErrorReplies.replyWithError200(req, res, vars.logger, OPERATION_FAILED)); }; } export function post_start_validation(handler: IdentityValidable, vars: ServerVariables) : Express.RequestHandler { return function (req: Express.Request, res: Express.Response) : BluebirdPromise<void> { let identity: Identity.Identity; return handler.preValidationInit(req) .then((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((token: string) => { const scheme = GetHeader(req, Constants.HEADER_X_FORWARDED_PROTO); const host = GetHeader(req, Constants.HEADER_X_FORWARDED_HOST); const link_url = util.format("%s://%s/#%s?token=%s", scheme, host, handler.destinationPath(), token); vars.logger.info(req, "Notification sent to user \"%s\"", identity.userid); return vars.notifier.notify(identity.email, handler.mailSubject(), link_url); }) .then(() => { handler.preValidationResponse(req, res); return BluebirdPromise.resolve(); }) .catch(Exceptions.IdentityError, (err: Error) => { vars.logger.error(req, err.message); handler.preValidationResponse(req, res); return BluebirdPromise.resolve(); }) .catch(ErrorReplies.replyWithError401(req, res, vars.logger)); }; }