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 Error403Get = require("./routes/error/403/get");
|
||||
import Error404Get = require("./routes/error/404/get");
|
||||
|
||||
import LoggedIn = require("./routes/loggedin/get");
|
||||
|
||||
import { ServerVariablesHandler } from "./ServerVariablesHandler";
|
||||
|
||||
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_403_GET, withLog(Error403Get.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 { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||
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", {
|
||||
first_factor_post_endpoint: Endpoints.FIRST_FACTOR_POST,
|
||||
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));
|
||||
authSession.userid = username;
|
||||
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 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 BluebirdPromise = require("bluebird");
|
||||
import { ServerVariablesHandler } from "../../ServerVariablesHandler";
|
||||
import AuthenticationSession = require("../../AuthenticationSession");
|
||||
|
||||
const TEMPLATE_NAME = "secondfactor";
|
||||
|
||||
export default FirstFactorBlocker.default(handler);
|
||||
|
||||
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, {
|
||||
totp_identity_start_endpoint: Endpoints.SECOND_FACTOR_TOTP_IDENTITY_START_GET,
|
||||
u2f_identity_start_endpoint: Endpoints.SECOND_FACTOR_U2F_IDENTITY_START_GET
|
||||
});
|
||||
return BluebirdPromise.resolve();
|
||||
});
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
export class DomainExtractor {
|
||||
static fromUrl(url: string): string {
|
||||
if (!url) return "";
|
||||
return url.match(/https?:\/\/([^\/:]+).*/)[1];
|
||||
}
|
||||
|
||||
static fromHostHeader(host: string): string {
|
||||
if (!host) return "";
|
||||
return host.split(":")[0];
|
||||
}
|
||||
}
|
|
@ -297,3 +297,5 @@ export const LOGOUT_GET = "/logout";
|
|||
export const ERROR_401_GET = "/error/401";
|
||||
export const ERROR_403_GET = "/error/403";
|
||||
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
|
||||
Given I visit "https://auth.test.local:8080/"
|
||||
|
|
Loading…
Reference in New Issue