Compare commits
	
		
			12 Commits
		
	
	
		
			version/20
			...
			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
	