web: detect deep links in flow interface and redirect locally
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
cfe7bc8155
commit
de6fa63d21
|
@ -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
|
||||||
return HttpChallengeResponse(
|
if request.path == redirect_url:
|
||||||
RedirectChallenge(
|
return source
|
||||||
{"type": ChallengeTypes.redirect, "to": str(redirect_url)}
|
LOGGER.debug("converting to redirect challenge", to=str(redirect_url))
|
||||||
)
|
return HttpChallengeResponse(
|
||||||
|
RedirectChallenge(
|
||||||
|
{"type": ChallengeTypes.redirect, "to": str(redirect_url)}
|
||||||
)
|
)
|
||||||
return source
|
)
|
||||||
if isinstance(source, TemplateResponse):
|
if isinstance(source, TemplateResponse):
|
||||||
return HttpChallengeResponse(
|
return HttpChallengeResponse(
|
||||||
ShellChallenge(
|
ShellChallenge(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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}`);
|
||||||
window.location.assign((this.challenge as RedirectChallenge).to);
|
if (localStorage.getItem(NEXT_ARG) === null) {
|
||||||
|
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)}`;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Reference in New Issue