web: detect deep links in flow interface and redirect locally

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-03-23 17:55:32 +01:00
parent cfe7bc8155
commit de6fa63d21
5 changed files with 36 additions and 8 deletions

View File

@ -311,13 +311,15 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
"""Convert normal HttpResponse into JSON Response""" """Convert normal HttpResponse into JSON Response"""
if isinstance(source, HttpResponseRedirect) or source.status_code == 302: if isinstance(source, HttpResponseRedirect) or source.status_code == 302:
redirect_url = source["Location"] redirect_url = source["Location"]
if request.path != redirect_url: # Redirects to the same URL usually indicate an Error within a form
if request.path == redirect_url:
return source
LOGGER.debug("converting to redirect challenge", to=str(redirect_url))
return HttpChallengeResponse( return HttpChallengeResponse(
RedirectChallenge( RedirectChallenge(
{"type": ChallengeTypes.redirect, "to": str(redirect_url)} {"type": ChallengeTypes.redirect, "to": str(redirect_url)}
) )
) )
return source
if isinstance(source, TemplateResponse): if isinstance(source, TemplateResponse):
return HttpChallengeResponse( return HttpChallengeResponse(
ShellChallenge( ShellChallenge(

View File

@ -13,7 +13,7 @@ from authentik.core.models import Application, Provider, User
from authentik.flows.views import SESSION_KEY_APPLICATION_PRE from authentik.flows.views import SESSION_KEY_APPLICATION_PRE
from authentik.lib.sentry import SentryIgnoredException from authentik.lib.sentry import SentryIgnoredException
from authentik.policies.engine import PolicyEngine from authentik.policies.engine import PolicyEngine
from authentik.policies.http import AccessDeniedResponse from authentik.policies.denied import AccessDeniedResponse
from authentik.policies.types import PolicyResult from authentik.policies.types import PolicyResult
LOGGER = get_logger() LOGGER = get_logger()

View File

@ -40,6 +40,9 @@ import { ifDefined } from "lit-html/directives/if-defined";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { TITLE_SUFFIX } from "../elements/router/RouterOutlet"; import { TITLE_SUFFIX } from "../elements/router/RouterOutlet";
import { AccessDeniedChallenge } from "./access_denied/FlowAccessDenied"; import { AccessDeniedChallenge } from "./access_denied/FlowAccessDenied";
import { getQueryVariables } from "./utils";
export const NEXT_ARG = "next";
@customElement("ak-flow-executor") @customElement("ak-flow-executor")
export class FlowExecutor extends LitElement implements StageHost { export class FlowExecutor extends LitElement implements StageHost {
@ -123,6 +126,14 @@ export class FlowExecutor extends LitElement implements StageHost {
} }
firstUpdated(): void { firstUpdated(): void {
// Check if there is a ?next arg and save it
// this is used for deep linking, if a user tries to access an application,
// but needs to authenticate first
const queryVars = getQueryVariables();
if (NEXT_ARG in queryVars) {
const next = queryVars[NEXT_ARG];
localStorage.setItem(NEXT_ARG, next);
}
new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => { new RootApi(DEFAULT_CONFIG).rootConfigList().then((config) => {
this.config = config; this.config = config;
}); });
@ -171,7 +182,12 @@ export class FlowExecutor extends LitElement implements StageHost {
switch (this.challenge.type) { switch (this.challenge.type) {
case ChallengeTypeEnum.Redirect: case ChallengeTypeEnum.Redirect:
console.debug(`authentik/flows: redirecting to ${(this.challenge as RedirectChallenge).to}`); console.debug(`authentik/flows: redirecting to ${(this.challenge as RedirectChallenge).to}`);
if (localStorage.getItem(NEXT_ARG) === null) {
window.location.assign((this.challenge as RedirectChallenge).to); window.location.assign((this.challenge as RedirectChallenge).to);
} else {
localStorage.clear();
window.location.assign(localStorage.getItem(NEXT_ARG) || "");
}
return this.renderLoading(); return this.renderLoading();
case ChallengeTypeEnum.Shell: case ChallengeTypeEnum.Shell:
return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`; return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`;

10
web/src/flows/utils.ts Normal file
View File

@ -0,0 +1,10 @@
export function getQueryVariables(): Record<string, string> {
const query = window.location.search.substring(1);
const vars = query.split("&");
const entries: Record<string, string> = {};
for (let i = 0; i < vars.length; i++) {
const pair = vars[i].split("=");
entries[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
return entries;
}