Redirect user when he has already validated some factors
Example 1: The user has validated first factor when accessing a service protected by basic auth. When he tries to access another service protected by second factor, he is redirected to second factor step to complete authentication. Example 2: The user has already validated second factor. When he access auth service, he is redirected either to /loggedin page that displays an "already logged in" page or to the URL provided in the "redirect" query parameter.pull/131/head
parent
c061dbfda4
commit
1cf4e57bb1
|
@ -29,6 +29,9 @@ import ResetPasswordRequestPost = require("./routes/password-reset/request/get")
|
||||||
import Error401Get = require("./routes/error/401/get");
|
import Error401Get = require("./routes/error/401/get");
|
||||||
import Error403Get = require("./routes/error/403/get");
|
import Error403Get = require("./routes/error/403/get");
|
||||||
import Error404Get = require("./routes/error/404/get");
|
import Error404Get = require("./routes/error/404/get");
|
||||||
|
|
||||||
|
import LoggedIn = require("./routes/loggedin/get");
|
||||||
|
|
||||||
import { ServerVariablesHandler } from "./ServerVariablesHandler";
|
import { ServerVariablesHandler } from "./ServerVariablesHandler";
|
||||||
|
|
||||||
import Endpoints = require("../../../shared/api");
|
import Endpoints = require("../../../shared/api");
|
||||||
|
@ -72,5 +75,6 @@ export class RestApi {
|
||||||
app.get(Endpoints.ERROR_401_GET, withLog(Error401Get.default));
|
app.get(Endpoints.ERROR_401_GET, withLog(Error401Get.default));
|
||||||
app.get(Endpoints.ERROR_403_GET, withLog(Error403Get.default));
|
app.get(Endpoints.ERROR_403_GET, withLog(Error403Get.default));
|
||||||
app.get(Endpoints.ERROR_404_GET, withLog(Error404Get.default));
|
app.get(Endpoints.ERROR_404_GET, withLog(Error404Get.default));
|
||||||
|
app.get(Endpoints.LOGGED_IN, withLog(LoggedIn.default));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,52 @@ import Endpoints = require("../../../../../shared/api");
|
||||||
import AuthenticationValidator = require("../../AuthenticationValidator");
|
import AuthenticationValidator = require("../../AuthenticationValidator");
|
||||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
|
import AuthenticationSession = require("../../AuthenticationSession");
|
||||||
|
import Constants = require("../../../../../shared/constants");
|
||||||
|
import Util = require("util");
|
||||||
|
|
||||||
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
function getRedirectParam(req: express.Request) {
|
||||||
|
return req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined"
|
||||||
|
? req.query[Constants.REDIRECT_QUERY_PARAM]
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirectToSecondFactorPage(req: express.Request, res: express.Response) {
|
||||||
|
const redirectUrl = getRedirectParam(req);
|
||||||
|
if (!redirectUrl)
|
||||||
|
res.redirect(Endpoints.SECOND_FACTOR_GET);
|
||||||
|
else
|
||||||
|
res.redirect(Util.format("%s?redirect=%s", Endpoints.SECOND_FACTOR_GET,
|
||||||
|
encodeURIComponent(redirectUrl)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirectToService(req: express.Request, res: express.Response) {
|
||||||
|
const redirectUrl = getRedirectParam(req);
|
||||||
|
if (!redirectUrl)
|
||||||
|
res.redirect(Endpoints.LOGGED_IN);
|
||||||
|
else
|
||||||
|
res.redirect(redirectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFirstFactor(res: express.Response) {
|
||||||
res.render("firstfactor", {
|
res.render("firstfactor", {
|
||||||
first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST,
|
first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST,
|
||||||
reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET
|
reset_password_request_endpoint: Endpoints.RESET_PASSWORD_REQUEST_GET
|
||||||
});
|
});
|
||||||
return BluebirdPromise.resolve();
|
}
|
||||||
|
|
||||||
|
export default function (req: express.Request, res: express.Response): BluebirdPromise<void> {
|
||||||
|
return AuthenticationSession.get(req)
|
||||||
|
.then(function (authSession) {
|
||||||
|
if (authSession.first_factor) {
|
||||||
|
if (authSession.second_factor)
|
||||||
|
redirectToService(req, res);
|
||||||
|
else
|
||||||
|
redirectToSecondFactorPage(req, res);
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFirstFactor(res);
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -48,7 +48,10 @@ export default function (req: express.Request, res: express.Response): BluebirdP
|
||||||
JSON.stringify(groupsAndEmails));
|
JSON.stringify(groupsAndEmails));
|
||||||
authSession.userid = username;
|
authSession.userid = username;
|
||||||
authSession.first_factor = true;
|
authSession.first_factor = true;
|
||||||
const redirectUrl = req.query[Constants.REDIRECT_QUERY_PARAM];
|
const redirectUrl = req.query[Constants.REDIRECT_QUERY_PARAM] != "undefined"
|
||||||
|
// Fuck, don't know why it is a string!
|
||||||
|
? req.query[Constants.REDIRECT_QUERY_PARAM]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const emails: string[] = groupsAndEmails.emails;
|
const emails: string[] = groupsAndEmails.emails;
|
||||||
const groups: string[] = groupsAndEmails.groups;
|
const groups: string[] = groupsAndEmails.groups;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import Express = require("express");
|
||||||
|
import Endpoints = require("../../../../../shared/api");
|
||||||
|
|
||||||
|
export default function(req: Express.Request, res: Express.Response) {
|
||||||
|
res.render("already-logged-in", {
|
||||||
|
logout_endpoint: Endpoints.LOGOUT_GET
|
||||||
|
});
|
||||||
|
}
|
|
@ -4,15 +4,24 @@ import Endpoints = require("../../../../../shared/api");
|
||||||
import FirstFactorBlocker = require("../FirstFactorBlocker");
|
import FirstFactorBlocker = require("../FirstFactorBlocker");
|
||||||
import BluebirdPromise = require("bluebird");
|
import BluebirdPromise = require("bluebird");
|
||||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||||
|
import AuthenticationSession = require("../../AuthenticationSession");
|
||||||
|
|
||||||
const TEMPLATE_NAME = "secondfactor";
|
const TEMPLATE_NAME = "secondfactor";
|
||||||
|
|
||||||
export default FirstFactorBlocker.default(handler);
|
export default FirstFactorBlocker.default(handler);
|
||||||
|
|
||||||
function handler(req: Express.Request, res: Express.Response): BluebirdPromise<void> {
|
function handler(req: Express.Request, res: Express.Response): BluebirdPromise<void> {
|
||||||
|
return AuthenticationSession.get(req)
|
||||||
|
.then(function (authSession) {
|
||||||
|
if (authSession.first_factor && authSession.second_factor) {
|
||||||
|
res.redirect(Endpoints.LOGGED_IN);
|
||||||
|
return BluebirdPromise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
res.render(TEMPLATE_NAME, {
|
res.render(TEMPLATE_NAME, {
|
||||||
totp_identity_start_endpoint: Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
totp_identity_start_endpoint: Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
||||||
u2f_identity_start_endpoint: Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET
|
u2f_identity_start_endpoint: Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET
|
||||||
});
|
});
|
||||||
return BluebirdPromise.resolve();
|
return BluebirdPromise.resolve();
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
export class DomainExtractor {
|
export class DomainExtractor {
|
||||||
static fromUrl(url: string): string {
|
static fromUrl(url: string): string {
|
||||||
|
if (!url) return "";
|
||||||
return url.match(/https?:\/\/([^\/:]+).*/)[1];
|
return url.match(/https?:\/\/([^\/:]+).*/)[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromHostHeader(host: string): string {
|
static fromHostHeader(host: string): string {
|
||||||
|
if (!host) return "";
|
||||||
return host.split(":")[0];
|
return host.split(":")[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -297,3 +297,5 @@ export const LOGOUT_GET = "/logout";
|
||||||
export const ERROR_401_GET = "/error/401";
|
export const ERROR_401_GET = "/error/401";
|
||||||
export const ERROR_403_GET = "/error/403";
|
export const ERROR_403_GET = "/error/403";
|
||||||
export const ERROR_404_GET = "/error/404";
|
export const ERROR_404_GET = "/error/404";
|
||||||
|
|
||||||
|
export const LOGGED_IN = "/loggedin";
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
Feature: User is redirected when factors are already validated
|
||||||
|
|
||||||
|
@need-registered-user-john
|
||||||
|
Scenario: User has validated first factor and tries to access service protected by second factor. He is then redirect to second factor step.
|
||||||
|
When I visit "https://basicauth.test.local:8080/secret.html"
|
||||||
|
And I'm redirected to "https://auth.test.local:8080/?redirect=https%3A%2F%2Fbasicauth.test.local%3A8080%2Fsecret.html"
|
||||||
|
And I login with user "john" and password "password"
|
||||||
|
And I'm redirected to "https://basicauth.test.local:8080/secret.html"
|
||||||
|
And I visit "https://public.test.local:8080/secret.html"
|
||||||
|
Then I'm redirected to "https://auth.test.local:8080/secondfactor?redirect=https%3A%2F%2Fpublic.test.local%3A8080%2Fsecret.html"
|
||||||
|
|
||||||
|
@need-registered-user-john
|
||||||
|
Scenario: User who has validated second factor and access auth portal should be redirected to "Already logged in page"
|
||||||
|
When I visit "https://public.test.local:8080/secret.html"
|
||||||
|
And I'm redirected to "https://auth.test.local:8080/?redirect=https%3A%2F%2Fpublic.test.local%3A8080%2Fsecret.html"
|
||||||
|
And I login with user "john" and password "password"
|
||||||
|
And I use "REGISTERED" as TOTP token handle
|
||||||
|
And I click on "TOTP"
|
||||||
|
And I'm redirected to "https://public.test.local:8080/secret.html"
|
||||||
|
And I visit "https://auth.test.local:8080"
|
||||||
|
Then I'm redirected to "https://auth.test.local:8080/loggedin"
|
||||||
|
|
||||||
|
@need-registered-user-john
|
||||||
|
Scenario: User who has validated second factor and access auth portal with rediction param should be redirected to that URL
|
||||||
|
When I visit "https://public.test.local:8080/secret.html"
|
||||||
|
And I'm redirected to "https://auth.test.local:8080/?redirect=https%3A%2F%2Fpublic.test.local%3A8080%2Fsecret.html"
|
||||||
|
And I login with user "john" and password "password"
|
||||||
|
And I use "REGISTERED" as TOTP token handle
|
||||||
|
And I click on "TOTP"
|
||||||
|
And I'm redirected to "https://public.test.local:8080/secret.html"
|
||||||
|
And I visit "https://auth.test.local:8080?redirect=https://public.test.local:8080/secret.html"
|
||||||
|
Then I'm redirected to "https://public.test.local:8080/secret.html"
|
|
@ -1,4 +1,4 @@
|
||||||
Feature: User validate first factor
|
Feature: Authentication scenarii
|
||||||
|
|
||||||
Scenario: User succeeds first factor
|
Scenario: User succeeds first factor
|
||||||
Given I visit "https://auth.test.local:8080/"
|
Given I visit "https://auth.test.local:8080/"
|
||||||
|
|
Loading…
Reference in New Issue