mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
Fix failing second factor when no default redirection url set.
When no default redirection url was set, Duo push second factor was shown as failing even if authentication was successful.
This commit is contained in:
parent
e3b6410e79
commit
81207b49ad
|
@ -5,14 +5,11 @@ import { triggerDuoPushAuth, triggerDuoPushAuthSuccess, triggerDuoPushAuthFailur
|
||||||
export default async function(dispatch: Dispatch, redirectionUrl: string | null) {
|
export default async function(dispatch: Dispatch, redirectionUrl: string | null) {
|
||||||
dispatch(triggerDuoPushAuth());
|
dispatch(triggerDuoPushAuth());
|
||||||
try {
|
try {
|
||||||
const res = await AutheliaService.triggerDuoPush(redirectionUrl);
|
const body = await AutheliaService.triggerDuoPush(redirectionUrl);
|
||||||
const body = await res.json();
|
|
||||||
if ('error' in body) {
|
|
||||||
throw new Error(body['error']);
|
|
||||||
}
|
|
||||||
dispatch(triggerDuoPushAuthSuccess());
|
dispatch(triggerDuoPushAuthSuccess());
|
||||||
return body;
|
return body;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
dispatch(triggerDuoPushAuthFailure(err.message))
|
dispatch(triggerDuoPushAuthFailure(err.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import { Dispatch } from 'redux';
|
||||||
import SecondFactorDuoPush, { StateProps, OwnProps, DispatchProps } from '../../../components/SecondFactorDuoPush/SecondFactorDuoPush';
|
import SecondFactorDuoPush, { StateProps, OwnProps, DispatchProps } from '../../../components/SecondFactorDuoPush/SecondFactorDuoPush';
|
||||||
import FetchStateBehavior from '../../../behaviors/FetchStateBehavior';
|
import FetchStateBehavior from '../../../behaviors/FetchStateBehavior';
|
||||||
import TriggerDuoPushAuth from '../../../behaviors/TriggerDuoPushAuth';
|
import TriggerDuoPushAuth from '../../../behaviors/TriggerDuoPushAuth';
|
||||||
|
import RedirectionResponse from '../../../services/RedirectResponse';
|
||||||
|
|
||||||
|
|
||||||
const mapStateToProps = (state: RootState): StateProps => ({
|
const mapStateToProps = (state: RootState): StateProps => ({
|
||||||
|
@ -12,16 +13,16 @@ const mapStateToProps = (state: RootState): StateProps => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
async function redirectIfPossible(body: any) {
|
async function redirectIfPossible(body: any) {
|
||||||
if ('redirect' in body) {
|
if (body && 'redirect' in body) {
|
||||||
window.location.href = body['redirect'];
|
window.location.href = body['redirect'];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSuccess(dispatch: Dispatch, res: Response, duration?: number) {
|
async function handleSuccess(dispatch: Dispatch, body: RedirectionResponse | undefined, duration?: number) {
|
||||||
async function handle() {
|
async function handle() {
|
||||||
const redirected = await redirectIfPossible(res);
|
const redirected = await redirectIfPossible(body);
|
||||||
if (!redirected) {
|
if (!redirected) {
|
||||||
await FetchStateBehavior(dispatch);
|
await FetchStateBehavior(dispatch);
|
||||||
}
|
}
|
||||||
|
@ -35,9 +36,8 @@ async function handleSuccess(dispatch: Dispatch, res: Response, duration?: numbe
|
||||||
}
|
}
|
||||||
|
|
||||||
async function triggerDuoPushAuth(dispatch: Dispatch, redirectionUrl: string | null) {
|
async function triggerDuoPushAuth(dispatch: Dispatch, redirectionUrl: string | null) {
|
||||||
const res = await TriggerDuoPushAuth(dispatch, redirectionUrl);
|
const body = await TriggerDuoPushAuth(dispatch, redirectionUrl);
|
||||||
if (!res) return;
|
await handleSuccess(dispatch, body, 1000);
|
||||||
await handleSuccess(dispatch, res, 2000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: Dispatch, ownProps: OwnProps): DispatchProps => {
|
const mapDispatchToProps = (dispatch: Dispatch, ownProps: OwnProps): DispatchProps => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import RemoteState from "../views/AuthenticationView/RemoteState";
|
import RemoteState from "../views/AuthenticationView/RemoteState";
|
||||||
import u2fApi, { SignRequest } from "u2f-api";
|
import u2fApi, { SignRequest } from "u2f-api";
|
||||||
import Method2FA from "../types/Method2FA";
|
import Method2FA from "../types/Method2FA";
|
||||||
|
import RedirectResponse from "./RedirectResponse";
|
||||||
|
|
||||||
class AutheliaService {
|
class AutheliaService {
|
||||||
static async fetchSafe(url: string, options?: RequestInit): Promise<Response> {
|
static async fetchSafe(url: string, options?: RequestInit): Promise<Response> {
|
||||||
|
@ -113,19 +114,28 @@ class AutheliaService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
static async triggerDuoPush(redirectionUrl: string | null): Promise<any> {
|
static async triggerDuoPush(redirectionUrl: string | null): Promise<RedirectResponse | undefined> {
|
||||||
|
const headers: Record<string, string> = {
|
||||||
const headers: Record<string, string> = {
|
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
}
|
}
|
||||||
if (redirectionUrl) {
|
if (redirectionUrl) {
|
||||||
headers['X-Target-Url'] = redirectionUrl;
|
headers['X-Target-Url'] = redirectionUrl;
|
||||||
}
|
}
|
||||||
return this.fetchSafe('/api/duo-push', {
|
const res = await this.fetchSafe('/api/duo-push', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: headers,
|
headers: headers,
|
||||||
})
|
});
|
||||||
|
|
||||||
|
if (res.status === 204) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await res.json();
|
||||||
|
if ('error' in body) {
|
||||||
|
throw new Error(body['error']);
|
||||||
|
}
|
||||||
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async initiatePasswordResetIdentityValidation(username: string) {
|
static async initiatePasswordResetIdentityValidation(username: string) {
|
||||||
|
|
6
client/src/services/RedirectResponse.ts
Normal file
6
client/src/services/RedirectResponse.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
export default interface RedirectResponse {
|
||||||
|
redirect?: string;
|
||||||
|
error?: string;
|
||||||
|
}
|
|
@ -15,7 +15,8 @@ class AutheliaServerFromDist implements AutheliaServerInterface {
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
this.serverProcess = ChildProcess.spawn('./scripts/authelia-scripts serve ' + this.configPath, {
|
this.serverProcess = ChildProcess.spawn('./scripts/authelia-scripts serve ' + this.configPath, {
|
||||||
shell: true
|
shell: true,
|
||||||
|
env: process.env,
|
||||||
} as any);
|
} as any);
|
||||||
if (this.logInFile) {
|
if (this.logInFile) {
|
||||||
var logStream = fs.createWriteStream('/tmp/authelia-server.log', {flags: 'a'});
|
var logStream = fs.createWriteStream('/tmp/authelia-server.log', {flags: 'a'});
|
||||||
|
|
|
@ -20,11 +20,19 @@ storage:
|
||||||
local:
|
local:
|
||||||
path: /tmp/authelia/db
|
path: /tmp/authelia/db
|
||||||
|
|
||||||
|
# The Duo Push Notification API configuration
|
||||||
|
duo_api:
|
||||||
|
hostname: duo.example.com
|
||||||
|
integration_key: ABCDEFGHIJKL
|
||||||
|
secret_key: abcdefghijklmnopqrstuvwxyz123456789
|
||||||
|
|
||||||
access_control:
|
access_control:
|
||||||
default_policy: bypass
|
default_policy: bypass
|
||||||
rules:
|
rules:
|
||||||
- domain: 'public.example.com'
|
- domain: 'public.example.com'
|
||||||
policy: bypass
|
policy: bypass
|
||||||
|
- domain: 'secure.example.com'
|
||||||
|
policy: two_factor
|
||||||
|
|
||||||
notifier:
|
notifier:
|
||||||
smtp:
|
smtp:
|
|
@ -3,12 +3,16 @@ import { exec } from "../../helpers/utils/exec";
|
||||||
import AutheliaServer from "../../helpers/context/AutheliaServer";
|
import AutheliaServer from "../../helpers/context/AutheliaServer";
|
||||||
import DockerEnvironment from "../../helpers/context/DockerEnvironment";
|
import DockerEnvironment from "../../helpers/context/DockerEnvironment";
|
||||||
|
|
||||||
|
// required to query duo-api over https
|
||||||
|
process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0 as any;
|
||||||
|
|
||||||
const autheliaServer = new AutheliaServer(__dirname + '/config.yml');
|
const autheliaServer = new AutheliaServer(__dirname + '/config.yml');
|
||||||
const dockerEnv = new DockerEnvironment([
|
const dockerEnv = new DockerEnvironment([
|
||||||
'docker-compose.yml',
|
'docker-compose.yml',
|
||||||
'example/compose/nginx/backend/docker-compose.yml',
|
'example/compose/nginx/backend/docker-compose.yml',
|
||||||
'example/compose/nginx/portal/docker-compose.yml',
|
'example/compose/nginx/portal/docker-compose.yml',
|
||||||
'example/compose/smtp/docker-compose.yml',
|
'example/compose/smtp/docker-compose.yml',
|
||||||
|
'example/compose/duo-api/docker-compose.yml',
|
||||||
])
|
])
|
||||||
|
|
||||||
async function setup() {
|
async function setup() {
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { StartDriver, StopDriver } from "../../../helpers/context/WithDriver";
|
||||||
|
import LoginAs from "../../../helpers/LoginAs";
|
||||||
|
import VerifyIsSecondFactorStage from "../../../helpers/assertions/VerifyIsSecondFactorStage";
|
||||||
|
import ClickOnLink from "../../../helpers/ClickOnLink";
|
||||||
|
import VerifyIsUseAnotherMethodView from "../../../helpers/assertions/VerifyIsUseAnotherMethodView";
|
||||||
|
import ClickOnButton from "../../../helpers/behaviors/ClickOnButton";
|
||||||
|
import Request from 'request-promise';
|
||||||
|
import VerifyIsAlreadyAuthenticatedStage from "../../../helpers/assertions/VerifyIsAlreadyAuthenticatedStage";
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
before(async function() {
|
||||||
|
this.driver = await StartDriver();
|
||||||
|
|
||||||
|
// Configure the fake API to return allowing response.
|
||||||
|
await Request('https://duo.example.com/allow', {method: 'POST'});
|
||||||
|
});
|
||||||
|
|
||||||
|
after(async function () {
|
||||||
|
await StopDriver(this.driver);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send user to already authenticated page', async function() {
|
||||||
|
await LoginAs(this.driver, "john", "password");
|
||||||
|
await VerifyIsSecondFactorStage(this.driver);
|
||||||
|
|
||||||
|
await ClickOnLink(this.driver, 'Use another method');
|
||||||
|
await VerifyIsUseAnotherMethodView(this.driver);
|
||||||
|
await ClickOnButton(this.driver, 'Duo Push Notification');
|
||||||
|
await VerifyIsAlreadyAuthenticatedStage(this.driver, 10000);
|
||||||
|
|
||||||
|
await ClickOnButton(this.driver, "Logout");
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import AutheliaSuite from "../../helpers/context/AutheliaSuite";
|
import AutheliaSuite from "../../helpers/context/AutheliaSuite";
|
||||||
import { exec } from '../../helpers/utils/exec';
|
import { exec } from '../../helpers/utils/exec';
|
||||||
import BypassPolicy from "./scenarii/BypassPolicy";
|
import BypassPolicy from "./scenarii/BypassPolicy";
|
||||||
|
import NoDefaultRedirectionUrl from "./scenarii/NoDefaultRedirectionUrl";
|
||||||
|
|
||||||
AutheliaSuite(__dirname, function() {
|
AutheliaSuite(__dirname, function() {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
|
@ -10,4 +11,5 @@ AutheliaSuite(__dirname, function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Bypass policy', BypassPolicy);
|
describe('Bypass policy', BypassPolicy);
|
||||||
|
describe("No default redirection", NoDefaultRedirectionUrl);
|
||||||
});
|
});
|
|
@ -87,18 +87,6 @@ regulation:
|
||||||
ban_time: 900
|
ban_time: 900
|
||||||
|
|
||||||
notifier:
|
notifier:
|
||||||
# For testing purpose, notifications can be sent in a file
|
|
||||||
# filesystem:
|
|
||||||
# filename: /tmp/authelia/notification.txt
|
|
||||||
|
|
||||||
# Use your email account to send the notifications. You can use an app password.
|
|
||||||
# List of valid services can be found here: https://nodemailer.com/smtp/well-known/
|
|
||||||
## email:
|
|
||||||
## username: user@example.com
|
|
||||||
## password: yourpassword
|
|
||||||
## sender: admin@example.com
|
|
||||||
## service: gmail
|
|
||||||
|
|
||||||
# Use a SMTP server for sending notifications
|
# Use a SMTP server for sending notifications
|
||||||
smtp:
|
smtp:
|
||||||
username: test
|
username: test
|
||||||
|
|
|
@ -6,8 +6,6 @@ port: 9091
|
||||||
|
|
||||||
logs_level: debug
|
logs_level: debug
|
||||||
|
|
||||||
default_redirection_url: https://home.example.com:8080/
|
|
||||||
|
|
||||||
authentication_backend:
|
authentication_backend:
|
||||||
file:
|
file:
|
||||||
path: ./test/suites/basic/users_database.test.yml
|
path: ./test/suites/basic/users_database.test.yml
|
||||||
|
@ -93,18 +91,6 @@ regulation:
|
||||||
ban_time: 900
|
ban_time: 900
|
||||||
|
|
||||||
notifier:
|
notifier:
|
||||||
# For testing purpose, notifications can be sent in a file
|
|
||||||
# filesystem:
|
|
||||||
# filename: /tmp/authelia/notification.txt
|
|
||||||
|
|
||||||
# Use your email account to send the notifications. You can use an app password.
|
|
||||||
# List of valid services can be found here: https://nodemailer.com/smtp/well-known/
|
|
||||||
## email:
|
|
||||||
## username: user@example.com
|
|
||||||
## password: yourpassword
|
|
||||||
## sender: admin@example.com
|
|
||||||
## service: gmail
|
|
||||||
|
|
||||||
# Use a SMTP server for sending notifications
|
# Use a SMTP server for sending notifications
|
||||||
smtp:
|
smtp:
|
||||||
username: test
|
username: test
|
||||||
|
|
Loading…
Reference in New Issue
Block a user