Compare commits
12 Commits
better-ver
...
web/use-oa
Author | SHA1 | Date | |
---|---|---|---|
a68f5deed9 | |||
441e4c48e9 | |||
d4a6874a45 | |||
40c5cb12fd | |||
89b7b735b4 | |||
6d82e568ae | |||
696175f6f7 | |||
ad3dbaa9c4 | |||
033617c5d2 | |||
0ce250dcd1 | |||
f9eed9f065 | |||
c0bb1f7347 |
@ -16,6 +16,7 @@ from django.views.decorators.clickjacking import xframe_options_sameorigin
|
||||
from django.views.generic import View
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, PolymorphicProxySerializer, extend_schema
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.views import APIView
|
||||
from sentry_sdk import capture_exception
|
||||
@ -23,6 +24,7 @@ from sentry_sdk.api import set_tag
|
||||
from sentry_sdk.hub import Hub
|
||||
from structlog.stdlib import BoundLogger, get_logger
|
||||
|
||||
from authentik.api.authentication import TokenAuthentication
|
||||
from authentik.brands.models import Brand
|
||||
from authentik.core.models import Application
|
||||
from authentik.events.models import Event, EventAction, cleanse_dict
|
||||
@ -104,6 +106,10 @@ class FlowExecutorView(APIView):
|
||||
"""Flow executor, passing requests to Stage Views"""
|
||||
|
||||
permission_classes = [AllowAny]
|
||||
authentication_classes = [
|
||||
TokenAuthentication,
|
||||
SessionAuthentication,
|
||||
]
|
||||
|
||||
flow: Flow
|
||||
|
||||
|
@ -180,10 +180,7 @@ REST_FRAMEWORK = {
|
||||
"rest_framework.filters.SearchFilter",
|
||||
],
|
||||
"DEFAULT_PERMISSION_CLASSES": ("authentik.rbac.permissions.ObjectPermissions",),
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||
"authentik.api.authentication.TokenAuthentication",
|
||||
"rest_framework.authentication.SessionAuthentication",
|
||||
),
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": ("authentik.api.authentication.TokenAuthentication",),
|
||||
"DEFAULT_RENDERER_CLASSES": [
|
||||
"drf_orjson_renderer.renderers.ORJSONRenderer",
|
||||
],
|
||||
|
45
blueprints/default/app-authentik-admin.yaml
Normal file
45
blueprints/default/app-authentik-admin.yaml
Normal file
@ -0,0 +1,45 @@
|
||||
version: 1
|
||||
metadata:
|
||||
name: Default - authentik Admin Interface
|
||||
entries:
|
||||
- model: authentik_providers_oauth2.oauth2provider
|
||||
id: provider
|
||||
identifiers:
|
||||
name: authentik-admin-interface
|
||||
attrs:
|
||||
authorization_flow:
|
||||
!Find [
|
||||
authentik_flows.flow,
|
||||
[slug, default-provider-authorization-implicit-consent],
|
||||
]
|
||||
client_type: public
|
||||
client_id: authentik-admin-interface
|
||||
property_mappings:
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[managed, goauthentik.io/providers/oauth2/scope-openid],
|
||||
]
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[managed, goauthentik.io/providers/oauth2/scope-email],
|
||||
]
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[managed, goauthentik.io/providers/oauth2/scope-profile],
|
||||
]
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[managed, goauthentik.io/providers/oauth2/scope-authentik_api],
|
||||
]
|
||||
signing_key:
|
||||
!Find [
|
||||
authentik_crypto.certificatekeypair,
|
||||
[name, authentik Self-signed Certificate],
|
||||
]
|
||||
- model: authentik_core.application
|
||||
identifiers:
|
||||
slug: authentik-admin-interface
|
||||
attrs:
|
||||
name: authentik Admin interface
|
||||
icon: https://goauthentik.io/img/icon.png
|
||||
provider: !KeyOf provider
|
45
blueprints/default/app-authentik-user.yaml
Normal file
45
blueprints/default/app-authentik-user.yaml
Normal file
@ -0,0 +1,45 @@
|
||||
version: 1
|
||||
metadata:
|
||||
name: Default - authentik User Interface
|
||||
entries:
|
||||
- model: authentik_providers_oauth2.oauth2provider
|
||||
id: provider
|
||||
identifiers:
|
||||
name: authentik-user-interface
|
||||
attrs:
|
||||
authorization_flow:
|
||||
!Find [
|
||||
authentik_flows.flow,
|
||||
[slug, default-provider-authorization-implicit-consent],
|
||||
]
|
||||
client_type: public
|
||||
client_id: authentik-user-interface
|
||||
property_mappings:
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[managed, goauthentik.io/providers/oauth2/scope-openid],
|
||||
]
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[managed, goauthentik.io/providers/oauth2/scope-email],
|
||||
]
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[managed, goauthentik.io/providers/oauth2/scope-profile],
|
||||
]
|
||||
- !Find [
|
||||
authentik_providers_oauth2.scopemapping,
|
||||
[managed, goauthentik.io/providers/oauth2/scope-authentik_api],
|
||||
]
|
||||
signing_key:
|
||||
!Find [
|
||||
authentik_crypto.certificatekeypair,
|
||||
[name, authentik Self-signed Certificate],
|
||||
]
|
||||
- model: authentik_core.application
|
||||
identifiers:
|
||||
slug: authentik-user-interface
|
||||
attrs:
|
||||
name: authentik User interface
|
||||
icon: https://goauthentik.io/img/icon.png
|
||||
provider: !KeyOf provider
|
1256
web/package-lock.json
generated
1256
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -65,6 +65,7 @@
|
||||
"lit": "^3.1.4",
|
||||
"md-front-matter": "^1.0.4",
|
||||
"mermaid": "^10.9.1",
|
||||
"oidc-client-ts": "^2.4.0",
|
||||
"rapidoc": "^9.3.4",
|
||||
"showdown": "^2.1.0",
|
||||
"style-mod": "^4.1.2",
|
||||
|
@ -4,9 +4,12 @@ import {
|
||||
EVENT_API_DRAWER_TOGGLE,
|
||||
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
||||
} from "@goauthentik/common/constants";
|
||||
import { WithOAuth } from "@goauthentik/common/oauth/interface";
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { OAuthLoginController } from "@goauthentik/components/oauth/controller";
|
||||
import { adminSettings } from "@goauthentik/components/oauth/settings";
|
||||
import { EnterpriseAwareInterface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/elements/enterprise/EnterpriseStatusBanner";
|
||||
@ -33,7 +36,7 @@ import { AdminApi, SessionUser, UiThemeEnum, Version } from "@goauthentik/api";
|
||||
import "./AdminSidebar";
|
||||
|
||||
@customElement("ak-interface-admin")
|
||||
export class AdminInterface extends EnterpriseAwareInterface {
|
||||
export class AdminInterface extends WithOAuth(EnterpriseAwareInterface, adminSettings) {
|
||||
@property({ type: Boolean })
|
||||
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
|
||||
|
||||
@ -48,6 +51,8 @@ export class AdminInterface extends EnterpriseAwareInterface {
|
||||
@state()
|
||||
user?: SessionUser;
|
||||
|
||||
oauthController: OAuthLoginController;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
@ -78,6 +83,7 @@ export class AdminInterface extends EnterpriseAwareInterface {
|
||||
constructor() {
|
||||
super();
|
||||
this.ws = new WebsocketClient();
|
||||
this.oauthController = new OAuthLoginController(this, adminSettings);
|
||||
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
|
||||
this.notificationDrawerOpen = !this.notificationDrawerOpen;
|
||||
updateURLParams({
|
||||
@ -92,7 +98,8 @@ export class AdminInterface extends EnterpriseAwareInterface {
|
||||
});
|
||||
}
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
async firstUpdated(_changedProperties: Map<PropertyKey, unknown>): Promise<void> {
|
||||
super.firstUpdated(_changedProperties);
|
||||
configureSentry(true);
|
||||
this.version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||
this.user = await me();
|
||||
|
@ -1,4 +1,7 @@
|
||||
import "@goauthentik/admin/admin-overview/AdminOverviewPage";
|
||||
import "@goauthentik/components/oauth/callback";
|
||||
import { adminSettings } from "@goauthentik/components/oauth/settings";
|
||||
import "@goauthentik/components/oauth/signout";
|
||||
import { ID_REGEX, Route, SLUG_REGEX, UUID_REGEX } from "@goauthentik/elements/router/Route";
|
||||
|
||||
import { html } from "lit";
|
||||
@ -8,6 +11,15 @@ export const ROUTES: Route[] = [
|
||||
new Route(new RegExp("^/$")).redirect("/administration/overview"),
|
||||
new Route(new RegExp("^#.*")).redirect("/administration/overview"),
|
||||
new Route(new RegExp("^/library$")).redirect("/if/user/", true),
|
||||
new Route(new RegExp("^/oauth-callback/(?<rest>.*)$"), async (args) => {
|
||||
return html`<ak-oauth-callback
|
||||
.settings=${adminSettings}
|
||||
params=${args.rest}
|
||||
></ak-oauth-callback>`;
|
||||
}),
|
||||
new Route(new RegExp("^/oauth-signout$"), async () => {
|
||||
return html`<ak-oauth-signout .settings=${adminSettings}></ak-oauth-signout>`;
|
||||
}),
|
||||
// statically imported since this is the default route
|
||||
new Route(new RegExp("^/administration/overview$"), async () => {
|
||||
return html`<ak-admin-overview></ak-admin-overview>`;
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
} from "@goauthentik/common/api/middleware";
|
||||
import { EVENT_LOCALE_REQUEST, VERSION } from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { TokenMiddleware } from "@goauthentik/common/oauth-middleware.js";
|
||||
|
||||
import { Config, Configuration, CoreApi, CurrentBrand, RootApi } from "@goauthentik/api";
|
||||
|
||||
@ -73,6 +74,7 @@ export const DEFAULT_CONFIG = new Configuration({
|
||||
"sentry-trace": getMetaContent("sentry-trace"),
|
||||
},
|
||||
middleware: [
|
||||
new TokenMiddleware(),
|
||||
new CSRFMiddleware(),
|
||||
new EventMiddleware(),
|
||||
new LoggingMiddleware(globalAK().brand),
|
||||
|
15
web/src/common/oauth-middleware.ts
Normal file
15
web/src/common/oauth-middleware.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { adminSettings } from "@goauthentik/components/oauth/settings";
|
||||
import { UserManager } from "oidc-client-ts";
|
||||
|
||||
import { FetchParams, Middleware, RequestContext } from "@goauthentik/api";
|
||||
|
||||
export class TokenMiddleware implements Middleware {
|
||||
async pre?(context: RequestContext): Promise<FetchParams | void> {
|
||||
const user = await new UserManager(adminSettings).getUser();
|
||||
if (user !== null) {
|
||||
// @ts-ignore
|
||||
context.init.headers["Authorization"] = `Bearer ${user.access_token}`;
|
||||
}
|
||||
return Promise.resolve(context);
|
||||
}
|
||||
}
|
24
web/src/common/oauth/callback.ts
Normal file
24
web/src/common/oauth/callback.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { state } from "@goauthentik/common/oauth/constants";
|
||||
import { refreshMe } from "@goauthentik/common/users";
|
||||
import { User, UserManager, UserManagerSettings } from "oidc-client-ts";
|
||||
|
||||
import { LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-oauth-callback")
|
||||
export class OAuthCallback extends LitElement {
|
||||
@property()
|
||||
params?: string;
|
||||
@property({ attribute: false })
|
||||
settings?: UserManagerSettings;
|
||||
async firstUpdated(): Promise<void> {
|
||||
if (!this.settings) {
|
||||
return;
|
||||
}
|
||||
const client = new UserManager(this.settings);
|
||||
const user = (await client.signinCallback(`#${this.params}`)) as User;
|
||||
const st = user.state as state;
|
||||
window.location.assign(st.url);
|
||||
refreshMe();
|
||||
}
|
||||
}
|
7
web/src/common/oauth/constants.ts
Normal file
7
web/src/common/oauth/constants.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export class state {
|
||||
url: string;
|
||||
|
||||
constructor() {
|
||||
this.url = window.location.href;
|
||||
}
|
||||
}
|
34
web/src/common/oauth/interface.ts
Normal file
34
web/src/common/oauth/interface.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { state } from "@goauthentik/common/oauth/constants";
|
||||
import { AbstractConstructor } from "@goauthentik/elements/types";
|
||||
import { UserManager, UserManagerSettings } from "oidc-client-ts";
|
||||
|
||||
import type { LitElement } from "lit";
|
||||
|
||||
export function WithOAuth<T extends AbstractConstructor<LitElement>>(
|
||||
superclass: T,
|
||||
settings: UserManagerSettings,
|
||||
) {
|
||||
abstract class OAuthInterface extends superclass {
|
||||
private async ensureLoggedIn() {
|
||||
const client = new UserManager(settings);
|
||||
const user = await client.getUser();
|
||||
if (user !== null) {
|
||||
return;
|
||||
}
|
||||
if (window.location.href.startsWith(settings.redirect_uri)) {
|
||||
return;
|
||||
}
|
||||
const s = new state();
|
||||
await client.signinRedirect({
|
||||
state: s,
|
||||
});
|
||||
}
|
||||
|
||||
async firstUpdated(_changedProperties: Map<PropertyKey, unknown>): Promise<void> {
|
||||
await this.ensureLoggedIn();
|
||||
await super.firstUpdated(_changedProperties);
|
||||
}
|
||||
}
|
||||
|
||||
return OAuthInterface;
|
||||
}
|
15
web/src/common/oauth/middleware.ts
Normal file
15
web/src/common/oauth/middleware.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { adminSettings } from "@goauthentik/common/oauth/settings";
|
||||
import { UserManager } from "oidc-client-ts";
|
||||
|
||||
import { FetchParams, Middleware, RequestContext } from "@goauthentik/api";
|
||||
|
||||
export class TokenMiddleware implements Middleware {
|
||||
async pre?(context: RequestContext): Promise<FetchParams | void> {
|
||||
const user = await new UserManager(adminSettings).getUser();
|
||||
if (user !== null) {
|
||||
// @ts-ignore
|
||||
context.init.headers["Authorization"] = `Bearer ${user.access_token}`;
|
||||
}
|
||||
return Promise.resolve(context);
|
||||
}
|
||||
}
|
25
web/src/common/oauth/settings.ts
Normal file
25
web/src/common/oauth/settings.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { MemoryStore } from "@goauthentik/common/oauth/storage";
|
||||
import { Log, OidcClientSettings, UserManagerSettings } from "oidc-client-ts";
|
||||
|
||||
Log.setLogger(console);
|
||||
Log.setLevel(Log.DEBUG);
|
||||
|
||||
export const userSettings: OidcClientSettings & UserManagerSettings = {
|
||||
authority: `${window.location.origin}/application/o/authentik-user-interface/`,
|
||||
redirect_uri: `${window.location.origin}/if/user/#/oauth-callback/`,
|
||||
client_id: "authentik-user-interface",
|
||||
scope: "openid profile email goauthentik.io/api",
|
||||
response_mode: "fragment",
|
||||
automaticSilentRenew: true,
|
||||
userStore: new MemoryStore(),
|
||||
};
|
||||
|
||||
export const adminSettings: OidcClientSettings & UserManagerSettings = {
|
||||
authority: `${window.location.origin}/application/o/authentik-admin-interface/`,
|
||||
redirect_uri: `${window.location.origin}/if/admin/#/oauth-callback/`,
|
||||
client_id: "authentik-admin-interface",
|
||||
scope: "openid profile email goauthentik.io/api",
|
||||
response_mode: "fragment",
|
||||
automaticSilentRenew: true,
|
||||
userStore: new MemoryStore(),
|
||||
};
|
17
web/src/common/oauth/signout.ts
Normal file
17
web/src/common/oauth/signout.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { UserManager, UserManagerSettings } from "oidc-client-ts";
|
||||
|
||||
import { LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-oauth-signout")
|
||||
export class OAuthSignout extends LitElement {
|
||||
@property({ attribute: false })
|
||||
settings?: UserManagerSettings;
|
||||
async firstUpdated(): Promise<void> {
|
||||
if (!this.settings) {
|
||||
return;
|
||||
}
|
||||
const client = new UserManager(this.settings);
|
||||
await client.signoutRedirect();
|
||||
}
|
||||
}
|
20
web/src/common/oauth/storage.ts
Normal file
20
web/src/common/oauth/storage.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { WebStorageStateStore } from "oidc-client-ts";
|
||||
|
||||
export class MemoryStore extends WebStorageStateStore {
|
||||
private map: Map<string, string> = new Map();
|
||||
async set(key: string, value: string): Promise<void> {
|
||||
this.map.set(key, value);
|
||||
}
|
||||
async get(key: string): Promise<string | null> {
|
||||
const value = this.map.get(key);
|
||||
return value ? value : null;
|
||||
}
|
||||
async remove(key: string): Promise<string | null> {
|
||||
const value = await this.get(key);
|
||||
this.map.delete(key);
|
||||
return value;
|
||||
}
|
||||
async getAllKeys(): Promise<string[]> {
|
||||
return Array.from(this.map.keys());
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_LOCALE_REQUEST } from "@goauthentik/common/constants";
|
||||
|
||||
import { CoreApi, ResponseError, SessionUser } from "@goauthentik/api";
|
||||
import { CoreApi, SessionUser } from "@goauthentik/api";
|
||||
|
||||
let globalMePromise: Promise<SessionUser> | undefined;
|
||||
|
||||
@ -33,7 +33,7 @@ export function me(): Promise<SessionUser> {
|
||||
}
|
||||
return user;
|
||||
})
|
||||
.catch((ex: ResponseError) => {
|
||||
.catch(() => {
|
||||
const defaultUser: SessionUser = {
|
||||
user: {
|
||||
pk: -1,
|
||||
@ -48,14 +48,6 @@ export function me(): Promise<SessionUser> {
|
||||
systemPermissions: [],
|
||||
},
|
||||
};
|
||||
if (ex.response?.status === 401 || ex.response?.status === 403) {
|
||||
const relativeUrl = window.location
|
||||
.toString()
|
||||
.substring(window.location.origin.length);
|
||||
window.location.assign(
|
||||
`/flows/-/default/authentication/?next=${encodeURIComponent(relativeUrl)}`,
|
||||
);
|
||||
}
|
||||
return defaultUser;
|
||||
});
|
||||
}
|
||||
|
24
web/src/components/oauth/callback.ts
Normal file
24
web/src/components/oauth/callback.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { refreshMe } from "@goauthentik/common/users";
|
||||
import { state } from "@goauthentik/components/oauth/constants";
|
||||
import { User, UserManager, UserManagerSettings } from "oidc-client-ts";
|
||||
|
||||
import { LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-oauth-callback")
|
||||
export class OAuthCallback extends LitElement {
|
||||
@property()
|
||||
params?: string;
|
||||
@property({ attribute: false })
|
||||
settings?: UserManagerSettings;
|
||||
async firstUpdated(): Promise<void> {
|
||||
if (!this.settings) {
|
||||
return;
|
||||
}
|
||||
const client = new UserManager(this.settings);
|
||||
const user = (await client.signinCallback(`#${this.params}`)) as User;
|
||||
const st = user.state as state;
|
||||
window.location.assign(st.url);
|
||||
refreshMe();
|
||||
}
|
||||
}
|
7
web/src/components/oauth/constants.ts
Normal file
7
web/src/components/oauth/constants.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export class state {
|
||||
url: string;
|
||||
|
||||
constructor() {
|
||||
this.url = window.location.href;
|
||||
}
|
||||
}
|
41
web/src/components/oauth/controller.ts
Normal file
41
web/src/components/oauth/controller.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { state } from "@goauthentik/components/oauth/constants";
|
||||
import { Interface } from "@goauthentik/elements/Interface/index.js";
|
||||
import { UserManager, UserManagerSettings } from "oidc-client-ts";
|
||||
|
||||
import { ReactiveController, ReactiveControllerHost } from "lit";
|
||||
|
||||
type ReactiveInterfaceHost = Partial<ReactiveControllerHost> & Interface;
|
||||
|
||||
export class OAuthLoginController implements ReactiveController {
|
||||
checked = false;
|
||||
|
||||
constructor(
|
||||
private host: ReactiveInterfaceHost,
|
||||
private settings: UserManagerSettings,
|
||||
) {
|
||||
this.host.addController(this);
|
||||
}
|
||||
|
||||
hostUpdated() {
|
||||
if (this.checked) {
|
||||
return;
|
||||
}
|
||||
this.checked = true;
|
||||
this.ensureLoggedIn();
|
||||
}
|
||||
|
||||
private async ensureLoggedIn() {
|
||||
const client = new UserManager(this.settings);
|
||||
const user = await client.getUser();
|
||||
if (user !== null) {
|
||||
return;
|
||||
}
|
||||
if (window.location.href.startsWith(this.settings.redirect_uri)) {
|
||||
return;
|
||||
}
|
||||
const s = new state();
|
||||
await client.signinRedirect({
|
||||
state: s,
|
||||
});
|
||||
}
|
||||
}
|
25
web/src/components/oauth/settings.ts
Normal file
25
web/src/components/oauth/settings.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { MemoryStore } from "@goauthentik/components/oauth/storage";
|
||||
import { Log, OidcClientSettings, UserManagerSettings } from "oidc-client-ts";
|
||||
|
||||
Log.setLogger(console);
|
||||
Log.setLevel(Log.DEBUG);
|
||||
|
||||
export const userSettings: OidcClientSettings & UserManagerSettings = {
|
||||
authority: `${window.location.origin}/application/o/authentik-user-interface/`,
|
||||
redirect_uri: `${window.location.origin}/if/user/#/oauth-callback/`,
|
||||
client_id: "authentik-user-interface",
|
||||
scope: "openid profile email goauthentik.io/api",
|
||||
response_mode: "fragment",
|
||||
automaticSilentRenew: true,
|
||||
userStore: new MemoryStore(),
|
||||
};
|
||||
|
||||
export const adminSettings: OidcClientSettings & UserManagerSettings = {
|
||||
authority: `${window.location.origin}/application/o/authentik-admin-interface/`,
|
||||
redirect_uri: `${window.location.origin}/if/admin/#/oauth-callback/`,
|
||||
client_id: "authentik-admin-interface",
|
||||
scope: "openid profile email goauthentik.io/api",
|
||||
response_mode: "fragment",
|
||||
automaticSilentRenew: true,
|
||||
userStore: new MemoryStore(),
|
||||
};
|
17
web/src/components/oauth/signout.ts
Normal file
17
web/src/components/oauth/signout.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { UserManager, UserManagerSettings } from "oidc-client-ts";
|
||||
|
||||
import { LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-oauth-signout")
|
||||
export class OAuthSignout extends LitElement {
|
||||
@property({ attribute: false })
|
||||
settings?: UserManagerSettings;
|
||||
async firstUpdated(): Promise<void> {
|
||||
if (!this.settings) {
|
||||
return;
|
||||
}
|
||||
const client = new UserManager(this.settings);
|
||||
await client.signoutRedirect();
|
||||
}
|
||||
}
|
20
web/src/components/oauth/storage.ts
Normal file
20
web/src/components/oauth/storage.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { WebStorageStateStore } from "oidc-client-ts";
|
||||
|
||||
export class MemoryStore extends WebStorageStateStore {
|
||||
private map: Map<string, string> = new Map();
|
||||
async set(key: string, value: string): Promise<void> {
|
||||
this.map.set(key, value);
|
||||
}
|
||||
async get(key: string): Promise<string | null> {
|
||||
const value = this.map.get(key);
|
||||
return value ? value : null;
|
||||
}
|
||||
async remove(key: string): Promise<string | null> {
|
||||
const value = await this.get(key);
|
||||
this.map.delete(key);
|
||||
return value;
|
||||
}
|
||||
async getAllKeys(): Promise<string[]> {
|
||||
return Array.from(this.map.keys());
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ export class SidebarUser extends AKElement {
|
||||
html``,
|
||||
)}
|
||||
</a>
|
||||
<a href="/flows/-/default/invalidation/" class="pf-c-nav__link user-logout" id="logout">
|
||||
<a href="#/oauth-signout" class="pf-c-nav__link user-logout" id="logout">
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||
</a>
|
||||
`;
|
||||
|
@ -1,3 +1,6 @@
|
||||
import "@goauthentik/components/oauth/callback";
|
||||
import { userSettings } from "@goauthentik/components/oauth/settings";
|
||||
import "@goauthentik/components/oauth/signout";
|
||||
import { Route } from "@goauthentik/elements/router/Route";
|
||||
import "@goauthentik/user/LibraryPage/ak-library.js";
|
||||
|
||||
@ -7,6 +10,15 @@ export const ROUTES: Route[] = [
|
||||
// Prevent infinite Shell loops
|
||||
new Route(new RegExp("^/$")).redirect("/library"),
|
||||
new Route(new RegExp("^#.*")).redirect("/library"),
|
||||
new Route(new RegExp("^/oauth-callback/(?<rest>.*)$"), async (args) => {
|
||||
return html`<ak-oauth-callback
|
||||
.settings=${userSettings}
|
||||
params=${args.rest}
|
||||
></ak-oauth-callback>`;
|
||||
}),
|
||||
new Route(new RegExp("^/oauth-signout$"), async () => {
|
||||
return html`<ak-oauth-signout .settings=${userSettings}></ak-oauth-signout>`;
|
||||
}),
|
||||
new Route(new RegExp("^/library$"), async () => html`<ak-library></ak-library>`),
|
||||
new Route(new RegExp("^/settings$"), async () => {
|
||||
await import("@goauthentik/user/user-settings/UserSettingsPage");
|
||||
|
@ -4,14 +4,18 @@ import {
|
||||
EVENT_NOTIFICATION_DRAWER_TOGGLE,
|
||||
EVENT_WS_MESSAGE,
|
||||
} from "@goauthentik/common/constants";
|
||||
import { WithOAuth } from "@goauthentik/common/oauth/interface";
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { UIConfig, UserDisplay } from "@goauthentik/common/ui/config";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { OAuthLoginController } from "@goauthentik/components/oauth/controller.js";
|
||||
import { userSettings } from "@goauthentik/components/oauth/settings";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import { EnterpriseAwareInterface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import "@goauthentik/elements/buttons/ActionButton";
|
||||
import { bound } from "@goauthentik/elements/decorators/bound.js";
|
||||
import "@goauthentik/elements/enterprise/EnterpriseStatusBanner";
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
import "@goauthentik/elements/notifications/APIDrawer";
|
||||
@ -206,10 +210,7 @@ class UserInterfacePresentation extends AKElement {
|
||||
<!-- -->
|
||||
${this.renderSettings()}
|
||||
<div class="pf-c-page__header-tools-item">
|
||||
<a
|
||||
href="/flows/-/default/invalidation/"
|
||||
class="pf-c-button pf-m-plain"
|
||||
>
|
||||
<a href="#/oauth-signout" class="pf-c-button pf-m-plain">
|
||||
<pf-tooltip position="top" content=${msg("Sign out")}>
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||
</pf-tooltip>
|
||||
@ -384,7 +385,7 @@ class UserInterfacePresentation extends AKElement {
|
||||
//
|
||||
//
|
||||
@customElement("ak-interface-user")
|
||||
export class UserInterface extends EnterpriseAwareInterface {
|
||||
export class UserInterface extends WithOAuth(EnterpriseAwareInterface, userSettings) {
|
||||
@property({ type: Boolean })
|
||||
notificationDrawerOpen = getURLParam("notificationDrawerOpen", false);
|
||||
|
||||
@ -399,14 +400,14 @@ export class UserInterface extends EnterpriseAwareInterface {
|
||||
@state()
|
||||
me?: SessionUser;
|
||||
|
||||
oauthController: OAuthLoginController;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ws = new WebsocketClient();
|
||||
this.fetchConfigurationDetails();
|
||||
configureSentry(true);
|
||||
this.toggleNotificationDrawer = this.toggleNotificationDrawer.bind(this);
|
||||
this.toggleApiDrawer = this.toggleApiDrawer.bind(this);
|
||||
this.fetchConfigurationDetails = this.fetchConfigurationDetails.bind(this);
|
||||
this.oauthController = new OAuthLoginController(this, userSettings);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@ -423,6 +424,7 @@ export class UserInterface extends EnterpriseAwareInterface {
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
@bound
|
||||
toggleNotificationDrawer() {
|
||||
this.notificationDrawerOpen = !this.notificationDrawerOpen;
|
||||
updateURLParams({
|
||||
@ -430,6 +432,7 @@ export class UserInterface extends EnterpriseAwareInterface {
|
||||
});
|
||||
}
|
||||
|
||||
@bound
|
||||
toggleApiDrawer() {
|
||||
this.apiDrawerOpen = !this.apiDrawerOpen;
|
||||
updateURLParams({
|
||||
@ -437,6 +440,7 @@ export class UserInterface extends EnterpriseAwareInterface {
|
||||
});
|
||||
}
|
||||
|
||||
@bound
|
||||
fetchConfigurationDetails() {
|
||||
me().then((me: SessionUser) => {
|
||||
this.me = me;
|
||||
|
Reference in New Issue
Block a user