Compare commits
	
		
			9 Commits
		
	
	
		
			version/20
			...
			web/sdk/em
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a407d903ab | |||
| 7e6f36b0b4 | |||
| 8dd7b0569c | |||
| 616be78e10 | |||
| 7125db1fbd | |||
| 8c7c7c3fee | |||
| bbbd00db22 | |||
| 6d60c5f7c7 | |||
| accf25a626 | 
| @ -25,3 +25,31 @@ class BrandMiddleware: | ||||
|             if locale != "": | ||||
|                 activate(locale) | ||||
|         return self.get_response(request) | ||||
|  | ||||
|  | ||||
| class BrandCORSAPIMiddleware: | ||||
|     """CORS for API requests depending on Brand""" | ||||
|  | ||||
|     get_response: Callable[[HttpRequest], HttpResponse] | ||||
|  | ||||
|     def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]): | ||||
|         self.get_response = get_response | ||||
|  | ||||
|     def set_headers(self, request: HttpRequest, response: HttpResponse): | ||||
|         response["Access-Control-Allow-Origin"] = "http://localhost:8080" | ||||
|         response["Access-Control-Allow-Credentials"] = "true" | ||||
|  | ||||
|     def __call__(self, request: HttpRequest) -> HttpResponse: | ||||
|         if request.method == "OPTIONS": | ||||
|             response = HttpResponse( | ||||
|                 status=200, | ||||
|             ) | ||||
|             self.set_headers(request, response) | ||||
|             response["Access-Control-Allow-Headers"] = ( | ||||
|                 "authorization,sentry-trace,x-authentik-csrf,content-type" | ||||
|             ) | ||||
|             response["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS" | ||||
|             return response | ||||
|         response = self.get_response(request) | ||||
|         self.set_headers(request, response) | ||||
|         return response | ||||
|  | ||||
| @ -13,6 +13,7 @@ | ||||
|         <link rel="shortcut icon" href="{{ brand.branding_favicon }}"> | ||||
|         {% block head_before %} | ||||
|         {% endblock %} | ||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-base.css' %}"> | ||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}"> | ||||
|         <link rel="stylesheet" type="text/css" href="{% static 'dist/custom.css' %}" data-inject> | ||||
|         {% versioned_script "dist/poly-%v.js" %} | ||||
|  | ||||
| @ -16,12 +16,14 @@ 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.exceptions import AuthenticationFailed | ||||
| from rest_framework.permissions import AllowAny | ||||
| from rest_framework.views import APIView | ||||
| from sentry_sdk import capture_exception, start_span | ||||
| from sentry_sdk.api import set_tag | ||||
| from structlog.stdlib import BoundLogger, get_logger | ||||
|  | ||||
| from authentik.api.authentication import bearer_auth, get_authorization_header | ||||
| from authentik.brands.models import Brand | ||||
| from authentik.core.models import Application | ||||
| from authentik.events.models import Event, EventAction, cleanse_dict | ||||
| @ -116,6 +118,14 @@ class FlowExecutorView(APIView): | ||||
|         super().setup(request, flow_slug=flow_slug) | ||||
|         self.flow = get_object_or_404(Flow.objects.select_related(), slug=flow_slug) | ||||
|         self._logger = get_logger().bind(flow_slug=flow_slug) | ||||
|         # Usually flows are authenticated by session, we don't really use rest_framework's | ||||
|         # authentication method. | ||||
|         try: | ||||
|             user = bearer_auth(get_authorization_header(request)) | ||||
|             if user: | ||||
|                 request.user = user | ||||
|         except AuthenticationFailed: | ||||
|             pass | ||||
|         set_tag("authentik.flow", self.flow.slug) | ||||
|  | ||||
|     def handle_invalid_flow(self, exc: FlowNonApplicableException) -> HttpResponse: | ||||
|  | ||||
| @ -248,6 +248,7 @@ MIDDLEWARE = [ | ||||
|     "django.contrib.auth.middleware.AuthenticationMiddleware", | ||||
|     "authentik.core.middleware.RequestIDMiddleware", | ||||
|     "authentik.brands.middleware.BrandMiddleware", | ||||
|     "authentik.brands.middleware.BrandCORSAPIMiddleware", | ||||
|     "authentik.events.middleware.AuditMiddleware", | ||||
|     "django.middleware.security.SecurityMiddleware", | ||||
|     "django.middleware.common.CommonMiddleware", | ||||
|  | ||||
| @ -41,6 +41,7 @@ const definitions = { | ||||
|  | ||||
| const otherFiles = [ | ||||
|     ["node_modules/@patternfly/patternfly/patternfly.min.css", "."], | ||||
|     ["node_modules/@patternfly/patternfly/patternfly-base.css", "."], | ||||
|     ["node_modules/@patternfly/patternfly/assets/**", ".", "node_modules/@patternfly/patternfly/"], | ||||
|     ["src/custom.css", "."], | ||||
|     ["src/common/styles/**", "."], | ||||
| @ -79,6 +80,12 @@ const interfaces = [ | ||||
|     ["polyfill/poly.ts", "."], | ||||
| ]; | ||||
|  | ||||
| const extraTargets = [ | ||||
|     ["sdk/index.ts", "sdk", { entryNames: "[dir]/[name]" }], | ||||
|     ["sdk/user-settings.ts", "sdk/user-settings", { entryNames: "[dir]/[name]" }], | ||||
|     ["sdk/flow.ts", "sdk/flow", { entryNames: "[dir]/[name]" }], | ||||
| ]; | ||||
|  | ||||
| const baseArgs = { | ||||
|     bundle: true, | ||||
|     write: true, | ||||
| @ -101,7 +108,11 @@ function getVersion() { | ||||
|     return version; | ||||
| } | ||||
|  | ||||
| async function buildOneSource(source, dest) { | ||||
| function getAllTargets() { | ||||
|     return [...interfaces, ...extraTargets]; | ||||
| } | ||||
|  | ||||
| async function buildSingleTarget(source, dest, options) { | ||||
|     const DIST = path.join(__dirname, "./dist", dest); | ||||
|     console.log(`[${new Date(Date.now()).toISOString()}] Starting build for target ${source}`); | ||||
|  | ||||
| @ -112,6 +123,7 @@ async function buildOneSource(source, dest) { | ||||
|             entryPoints: [`./src/${source}`], | ||||
|             entryNames: `[dir]/[name]-${getVersion()}`, | ||||
|             outdir: DIST, | ||||
|             ...options, | ||||
|         }); | ||||
|         const end = Date.now(); | ||||
|         console.log( | ||||
| @ -124,8 +136,10 @@ async function buildOneSource(source, dest) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function buildAuthentik(interfaces) { | ||||
|     await Promise.allSettled(interfaces.map(([source, dest]) => buildOneSource(source, dest))); | ||||
| async function buildTargets(targets) { | ||||
|     await Promise.allSettled( | ||||
|         targets.map(([source, dest, options]) => buildSingleTarget(source, dest, options)), | ||||
|     ); | ||||
| } | ||||
|  | ||||
| let timeoutId = null; | ||||
| @ -135,7 +149,7 @@ function debouncedBuild() { | ||||
|     } | ||||
|     timeoutId = setTimeout(() => { | ||||
|         console.clear(); | ||||
|         buildAuthentik(interfaces); | ||||
|         buildTargets(getAllTargets()); | ||||
|     }, 250); | ||||
| } | ||||
|  | ||||
| @ -143,7 +157,7 @@ if (process.argv.length > 2 && (process.argv[2] === "-h" || process.argv[2] === | ||||
|     console.log(`Build the authentikUI | ||||
|  | ||||
| options: | ||||
|   -w, --watch: Build all ${interfaces.length} interfaces | ||||
|   -w, --watch: Build all ${getAllTargets().length} interfaces | ||||
|   -p, --proxy: Build only the polyfills and the loading application | ||||
|   -h, --help: This help message | ||||
| `); | ||||
| @ -163,11 +177,11 @@ if (process.argv.length > 2 && (process.argv[2] === "-w" || process.argv[2] === | ||||
|     }); | ||||
| } else if (process.argv.length > 2 && (process.argv[2] === "-p" || process.argv[2] === "--proxy")) { | ||||
|     // There's no watch-for-proxy, sorry. | ||||
|     await buildAuthentik( | ||||
|     await buildTargets( | ||||
|         interfaces.filter(([_, dest]) => ["standalone/loading", "."].includes(dest)), | ||||
|     ); | ||||
|     process.exit(0); | ||||
| } else { | ||||
|     // And the fallback: just build it. | ||||
|     await buildAuthentik(interfaces); | ||||
|     await buildTargets(interfaces); | ||||
| } | ||||
|  | ||||
							
								
								
									
										3057
									
								
								web/sfe/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3057
									
								
								web/sfe/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,28 +0,0 @@ | ||||
| { | ||||
|     "name": "@goauthentik/web-sfe", | ||||
|     "version": "0.0.0", | ||||
|     "private": true, | ||||
|     "license": "MIT", | ||||
|     "dependencies": { | ||||
|         "@goauthentik/api": "^2024.6.3-1724414734", | ||||
|         "base64-js": "^1.5.1", | ||||
|         "bootstrap": "^4.6.1", | ||||
|         "formdata-polyfill": "^4.0.10", | ||||
|         "jquery": "^3.7.1", | ||||
|         "weakmap-polyfill": "^2.0.4" | ||||
|     }, | ||||
|     "scripts": { | ||||
|         "build": "rollup -c rollup.config.js --bundleConfigAsCjs", | ||||
|         "watch": "rollup -w -c rollup.config.js --bundleConfigAsCjs" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@rollup/plugin-commonjs": "^26.0.1", | ||||
|         "@rollup/plugin-node-resolve": "^15.2.3", | ||||
|         "@rollup/plugin-swc": "^0.3.1", | ||||
|         "@swc/cli": "^0.4.0", | ||||
|         "@swc/core": "^1.7.23", | ||||
|         "@types/jquery": "^3.5.30", | ||||
|         "rollup": "^4.21.2", | ||||
|         "rollup-plugin-copy": "^3.5.0" | ||||
|     } | ||||
| } | ||||
| @ -7,6 +7,9 @@ export function renderSourceIcon(name: string, iconUrl: string | undefined | nul | ||||
|             const url = iconUrl.replaceAll("fa://", ""); | ||||
|             return html`<i class="fas ${url}" title="${name}"></i>`; | ||||
|         } | ||||
|         if (window.authentik_sdk?.base) { | ||||
|             return html`<img src="${window.authentik_sdk?.base}${iconUrl}" alt="${name}" />`; | ||||
|         } | ||||
|         return html`<img src="${iconUrl}" alt="${name}" />`; | ||||
|     } | ||||
|     return icon; | ||||
|  | ||||
| @ -2,6 +2,7 @@ import { | ||||
|     CSRFMiddleware, | ||||
|     EventMiddleware, | ||||
|     LoggingMiddleware, | ||||
|     SDKMiddleware, | ||||
| } from "@goauthentik/common/api/middleware"; | ||||
| import { EVENT_LOCALE_REQUEST, VERSION } from "@goauthentik/common/constants"; | ||||
| import { globalAK } from "@goauthentik/common/global"; | ||||
| @ -67,8 +68,18 @@ export function getMetaContent(key: string): string { | ||||
|     return metaEl.content; | ||||
| } | ||||
|  | ||||
| export function apiBase(): string { | ||||
|     if (process.env.AK_API_BASE_PATH) { | ||||
|         return process.env.AK_API_BASE_PATH; | ||||
|     } | ||||
|     if (window.authentik_sdk?.base) { | ||||
|         return window.authentik_sdk?.base; | ||||
|     } | ||||
|     return window.location.origin; | ||||
| } | ||||
|  | ||||
| export const DEFAULT_CONFIG = new Configuration({ | ||||
|     basePath: (process.env.AK_API_BASE_PATH || window.location.origin) + "/api/v3", | ||||
|     basePath: `${apiBase()}/api/v3`, | ||||
|     headers: { | ||||
|         "sentry-trace": getMetaContent("sentry-trace"), | ||||
|     }, | ||||
| @ -76,6 +87,7 @@ export const DEFAULT_CONFIG = new Configuration({ | ||||
|         new CSRFMiddleware(), | ||||
|         new EventMiddleware(), | ||||
|         new LoggingMiddleware(globalAK().brand), | ||||
|         new SDKMiddleware(), | ||||
|     ], | ||||
| }); | ||||
|  | ||||
|  | ||||
| @ -44,6 +44,21 @@ export class CSRFMiddleware implements Middleware { | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class SDKMiddleware implements Middleware { | ||||
|     token?: string; | ||||
|     constructor() { | ||||
|         this.token = window.authentik_sdk?.token; | ||||
|     } | ||||
|     pre?(context: RequestContext): Promise<FetchParams | void> { | ||||
|         if (this.token) { | ||||
|             context.init.credentials = "include"; | ||||
|             // @ts-ignore | ||||
|             context.init.headers["Authorization"] = `Bearer ${this.token}`; | ||||
|         } | ||||
|         return Promise.resolve(context); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export class EventMiddleware implements Middleware { | ||||
|     post?(context: ResponseContext): Promise<Response | void> { | ||||
|         const request: RequestInfo = { | ||||
|  | ||||
| @ -1,4 +1,11 @@ | ||||
| import { Config, ConfigFromJSON, CurrentBrand, CurrentBrandFromJSON } from "@goauthentik/api"; | ||||
| import { | ||||
|     Config, | ||||
|     ConfigFromJSON, | ||||
|     CurrentBrand, | ||||
|     CurrentBrandFromJSON, | ||||
|     ErrorReportingConfigFromJSON, | ||||
|     UiThemeEnum, | ||||
| } from "@goauthentik/api"; | ||||
|  | ||||
| export interface GlobalAuthentik { | ||||
|     _converted?: boolean; | ||||
| @ -28,9 +35,12 @@ export function globalAK(): GlobalAuthentik { | ||||
|         return { | ||||
|             config: ConfigFromJSON({ | ||||
|                 capabilities: [], | ||||
|                 error_reporting: ErrorReportingConfigFromJSON({}), | ||||
|             }), | ||||
|             brand: CurrentBrandFromJSON({ | ||||
|                 matched_domain: window.location.host, | ||||
|                 ui_footer_links: [], | ||||
|                 ui_theme: window.authentik_sdk?.forceTheme ?? UiThemeEnum.Automatic, | ||||
|             }), | ||||
|             versionFamily: "", | ||||
|             versionSubdomain: "", | ||||
| @ -40,6 +50,10 @@ export function globalAK(): GlobalAuthentik { | ||||
|     return ak; | ||||
| } | ||||
|  | ||||
| export function isEmbedded() { | ||||
|     return !!window.authentik_sdk; | ||||
| } | ||||
|  | ||||
| export function docLink(path: string): string { | ||||
|     const ak = globalAK(); | ||||
|     // Default case or beta build which should always point to latest | ||||
|  | ||||
| @ -21,9 +21,12 @@ export class WebsocketClient { | ||||
|  | ||||
|     connect(): void { | ||||
|         if (navigator.webdriver) return; | ||||
|         const wsUrl = `${window.location.protocol.replace("http", "ws")}//${ | ||||
|         let wsUrl = `${window.location.protocol.replace("http", "ws")}//${ | ||||
|             window.location.host | ||||
|         }/ws/client/`; | ||||
|         if (window.authentik_sdk?.base) { | ||||
|             wsUrl = `${window.authentik_sdk?.base.replace("http", "ws")}/ws/client/`; | ||||
|         } | ||||
|         this.messageSocket = new WebSocket(wsUrl); | ||||
|         this.messageSocket.addEventListener("open", () => { | ||||
|             console.debug(`authentik/ws: connected to ${wsUrl}`); | ||||
|  | ||||
| @ -1,10 +1,11 @@ | ||||
| import { isEmbedded } from "@goauthentik/common/global"; | ||||
| import { UIConfig, uiConfig } from "@goauthentik/common/ui/config"; | ||||
| import { ModalOrchestrationController } from "@goauthentik/elements/controllers/ModalOrchestrationController.js"; | ||||
| import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet"; | ||||
|  | ||||
| import { state } from "lit/decorators.js"; | ||||
|  | ||||
| import PFBase from "@patternfly/patternfly/patternfly-base.css"; | ||||
| import PFVariables from "@patternfly/patternfly/base/patternfly-variables.css"; | ||||
|  | ||||
| import type { Config, CurrentBrand, LicenseSummary } from "@goauthentik/api"; | ||||
| import { UiThemeEnum } from "@goauthentik/api"; | ||||
| @ -43,7 +44,10 @@ export class Interface extends AKElement implements AkInterface { | ||||
|  | ||||
|     constructor() { | ||||
|         super(); | ||||
|         document.adoptedStyleSheets = [...document.adoptedStyleSheets, ensureCSSStyleSheet(PFBase)]; | ||||
|         document.adoptedStyleSheets = [ | ||||
|             ...document.adoptedStyleSheets, | ||||
|             ensureCSSStyleSheet(PFVariables), | ||||
|         ]; | ||||
|         this[brandContext] = new BrandContextController(this); | ||||
|         this[configContext] = new ConfigContextController(this); | ||||
|         this[modalController] = new ModalOrchestrationController(this); | ||||
| @ -61,7 +65,9 @@ export class Interface extends AKElement implements AkInterface { | ||||
|         // Instead of calling ._activateTheme() twice, we insert the root document in the call | ||||
|         // since multiple calls to ._activateTheme() would not do anything after the first call | ||||
|         // as the theme is already enabled. | ||||
|         roots.unshift(document as unknown as DocumentOrShadowRoot); | ||||
|         if (!isEmbedded()) { | ||||
|             roots.unshift(document as unknown as DocumentOrShadowRoot); | ||||
|         } | ||||
|         super._activateTheme(theme, ...roots); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -4,7 +4,7 @@ import { | ||||
|     EVENT_FLOW_INSPECTOR_TOGGLE, | ||||
|     TITLE_DEFAULT, | ||||
| } from "@goauthentik/common/constants"; | ||||
| import { globalAK } from "@goauthentik/common/global"; | ||||
| import { globalAK, isEmbedded } from "@goauthentik/common/global"; | ||||
| import { configureSentry } from "@goauthentik/common/sentry"; | ||||
| import { first } from "@goauthentik/common/utils"; | ||||
| import { WebsocketClient } from "@goauthentik/common/ws"; | ||||
| @ -84,6 +84,7 @@ export class FlowExecutor extends Interface implements StageHost { | ||||
|         return [PFBase, PFLogin, PFDrawer, PFButton, PFTitle, PFList, PFBackgroundImage].concat(css` | ||||
|             :host { | ||||
|                 --pf-c-login__main-body--PaddingBottom: var(--pf-global--spacer--2xl); | ||||
|                 position: relative; | ||||
|             } | ||||
|             .pf-c-background-image::before { | ||||
|                 --pf-c-background-image--BackgroundImage: var(--ak-flow-background); | ||||
| @ -95,9 +96,6 @@ export class FlowExecutor extends Interface implements StageHost { | ||||
|             .ak-hidden { | ||||
|                 display: none; | ||||
|             } | ||||
|             :host { | ||||
|                 position: relative; | ||||
|             } | ||||
|             .pf-c-drawer__content { | ||||
|                 background-color: transparent; | ||||
|             } | ||||
| @ -262,10 +260,13 @@ export class FlowExecutor extends Interface implements StageHost { | ||||
|         this.challenge = challenge as ChallengeTypes; | ||||
|     } | ||||
|  | ||||
|     setShadowStyles(value: ContextualFlowInfo) { | ||||
|     setBackgroundImage(value: ContextualFlowInfo) { | ||||
|         if (!value) { | ||||
|             return; | ||||
|         } | ||||
|         if (isEmbedded()) { | ||||
|             return; | ||||
|         } | ||||
|         this.shadowRoot | ||||
|             ?.querySelectorAll<HTMLDivElement>(".pf-c-background-image") | ||||
|             .forEach((bg) => { | ||||
| @ -276,7 +277,7 @@ export class FlowExecutor extends Interface implements StageHost { | ||||
|     // DOM post-processing has to happen after the render. | ||||
|     updated(changedProperties: PropertyValues<this>) { | ||||
|         if (changedProperties.has("flowInfo") && this.flowInfo !== undefined) { | ||||
|             this.setShadowStyles(this.flowInfo); | ||||
|             this.setBackgroundImage(this.flowInfo); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -459,56 +460,63 @@ export class FlowExecutor extends Interface implements StageHost { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     renderCard() { | ||||
|         return html`<div class="pf-c-login ${this.getLayout()}"> | ||||
|             <div class="${this.getLayoutClass()}"> | ||||
|                 <div class="pf-c-login__main"> | ||||
|                     ${this.loading && this.challenge | ||||
|                         ? html`<ak-loading-overlay></ak-loading-overlay>` | ||||
|                         : nothing} | ||||
|                     ${isEmbedded() | ||||
|                         ? nothing | ||||
|                         : html` <div class="pf-c-login__main-header pf-c-brand ak-brand"> | ||||
|                               <img | ||||
|                                   src="${themeImage( | ||||
|                                       first( | ||||
|                                           this.brand?.brandingLogo, | ||||
|                                           globalAK()?.brand.brandingLogo, | ||||
|                                           DefaultBrand.brandingLogo, | ||||
|                                       ), | ||||
|                                   )}" | ||||
|                                   alt="authentik Logo" | ||||
|                               /> | ||||
|                           </div>`} | ||||
|                     ${until(this.renderChallenge())} | ||||
|                 </div> | ||||
|                 ${isEmbedded() | ||||
|                     ? nothing | ||||
|                     : html` <footer class="pf-c-login__footer"> | ||||
|                           <ul class="pf-c-list pf-m-inline"> | ||||
|                               ${this.brand?.uiFooterLinks?.map((link) => { | ||||
|                                   if (link.href) { | ||||
|                                       return html`<li> | ||||
|                                           <a href="${link.href}">${link.name}</a> | ||||
|                                       </li>`; | ||||
|                                   } | ||||
|                                   return html`<li> | ||||
|                                       <span>${link.name}</span> | ||||
|                                   </li>`; | ||||
|                               })} | ||||
|                               <li> | ||||
|                                   <span>${msg("Powered by authentik")}</span> | ||||
|                               </li> | ||||
|                           </ul> | ||||
|                       </footer>`} | ||||
|             </div> | ||||
|         </div>`; | ||||
|     } | ||||
|  | ||||
|     render(): TemplateResult { | ||||
|         if (isEmbedded()) { | ||||
|             return this.renderCard(); | ||||
|         } | ||||
|         return html` <ak-locale-context> | ||||
|             <div class="pf-c-background-image"></div> | ||||
|             <div class="pf-c-page__drawer"> | ||||
|                 <div class="pf-c-drawer ${this.inspectorOpen ? "pf-m-expanded" : "pf-m-collapsed"}"> | ||||
|                     <div class="pf-c-drawer__main"> | ||||
|                         <div class="pf-c-drawer__content"> | ||||
|                             <div class="pf-c-drawer__body"> | ||||
|                                 <div class="pf-c-login ${this.getLayout()}"> | ||||
|                                     <div class="${this.getLayoutClass()}"> | ||||
|                                         <div class="pf-c-login__main"> | ||||
|                                             ${this.loading && this.challenge | ||||
|                                                 ? html`<ak-loading-overlay></ak-loading-overlay>` | ||||
|                                                 : nothing} | ||||
|                                             <div | ||||
|                                                 class="pf-c-login__main-header pf-c-brand ak-brand" | ||||
|                                             > | ||||
|                                                 <img | ||||
|                                                     src="${themeImage( | ||||
|                                                         first( | ||||
|                                                             this.brand?.brandingLogo, | ||||
|                                                             globalAK()?.brand.brandingLogo, | ||||
|                                                             DefaultBrand.brandingLogo, | ||||
|                                                         ), | ||||
|                                                     )}" | ||||
|                                                     alt="authentik Logo" | ||||
|                                                 /> | ||||
|                                             </div> | ||||
|                                             ${until(this.renderChallenge())} | ||||
|                                         </div> | ||||
|                                         <footer class="pf-c-login__footer"> | ||||
|                                             <ul class="pf-c-list pf-m-inline"> | ||||
|                                                 ${this.brand?.uiFooterLinks?.map((link) => { | ||||
|                                                     if (link.href) { | ||||
|                                                         return html`<li> | ||||
|                                                             <a href="${link.href}">${link.name}</a> | ||||
|                                                         </li>`; | ||||
|                                                     } | ||||
|                                                     return html`<li> | ||||
|                                                         <span>${link.name}</span> | ||||
|                                                     </li>`; | ||||
|                                                 })} | ||||
|                                                 <li> | ||||
|                                                     <span>${msg("Powered by authentik")}</span> | ||||
|                                                 </li> | ||||
|                                             </ul> | ||||
|                                         </footer> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                             <div class="pf-c-drawer__body">${this.renderCard()}</div> | ||||
|                         </div> | ||||
|                         ${until(this.renderInspector())} | ||||
|                     </div> | ||||
|  | ||||
| @ -99,6 +99,7 @@ export class IdentificationStage extends BaseStage< | ||||
|     createHelperForm(): void { | ||||
|         const compatMode = "ShadyDOM" in window; | ||||
|         this.form = document.createElement("form"); | ||||
|         this.form.style.display = "none"; | ||||
|         document.documentElement.appendChild(this.form); | ||||
|         // Only add the additional username input if we're in a shadow dom | ||||
|         // otherwise it just confuses browsers | ||||
|  | ||||
							
								
								
									
										8
									
								
								web/src/global.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								web/src/global.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -12,3 +12,11 @@ declare namespace Intl { | ||||
|         public format: (items: string[]) => string; | ||||
|     } | ||||
| } | ||||
|  | ||||
| declare interface Window { | ||||
|     authentik_sdk?: { | ||||
|         base: string; | ||||
|         token?: string; | ||||
|         forceTheme?: string; | ||||
|     }; | ||||
| } | ||||
|  | ||||
							
								
								
									
										19
									
								
								web/src/sdk/common.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								web/src/sdk/common.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| import { Interface } from "@goauthentik/elements/Interface/Interface"; | ||||
|  | ||||
| import { html } from "lit"; | ||||
| import { customElement } from "lit/decorators.js"; | ||||
|  | ||||
| import { UiThemeEnum, UiThemeEnumFromJSON } from "@goauthentik/api"; | ||||
|  | ||||
| @customElement("ak-sdk-interface") | ||||
| export class SDKInterface extends Interface { | ||||
|     constructor() { | ||||
|         super(); | ||||
|     } | ||||
|     render() { | ||||
|         return html`<slot></slot>`; | ||||
|     } | ||||
|     async getTheme(): Promise<UiThemeEnum> { | ||||
|         return UiThemeEnumFromJSON(window.authentik_sdk?.forceTheme) || UiThemeEnum.Automatic; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								web/src/sdk/flow.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								web/src/sdk/flow.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| import "@goauthentik/elements/messages/MessageContainer"; | ||||
| import { FlowExecutor } from "@goauthentik/flow/FlowExecutor"; | ||||
| // Statically import some stages to speed up load speed | ||||
| import "@goauthentik/flow/stages/access_denied/AccessDeniedStage"; | ||||
| // Import webauthn-related stages to prevent issues on safari | ||||
| // Which is overly sensitive to allowing things only in the context of a | ||||
| // user interaction | ||||
| import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage"; | ||||
| import "@goauthentik/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage"; | ||||
| import "@goauthentik/flow/stages/autosubmit/AutosubmitStage"; | ||||
| import "@goauthentik/flow/stages/captcha/CaptchaStage"; | ||||
| import "@goauthentik/flow/stages/identification/IdentificationStage"; | ||||
| import "@goauthentik/flow/stages/password/PasswordStage"; | ||||
| import "@goauthentik/sdk/common"; | ||||
| // end of stage import | ||||
|  | ||||
| import { html, nothing } from "lit"; | ||||
| import { customElement } from "lit/decorators.js"; | ||||
| import { until } from "lit/directives/until.js"; | ||||
|  | ||||
|  | ||||
| @customElement("ak-embedded-flow-executor") | ||||
| export class EmbeddedFlowExecutor extends FlowExecutor { | ||||
|     renderCard() { | ||||
|         return html`<div class="pf-c-login"> | ||||
|             <div class="pf-c-login__main"> | ||||
|                 ${this.loading && this.challenge | ||||
|                     ? html`<ak-loading-overlay></ak-loading-overlay>` | ||||
|                     : nothing} | ||||
|                 ${until(this.renderChallenge())} | ||||
|             </div> | ||||
|         </div>`; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										2
									
								
								web/src/sdk/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								web/src/sdk/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| import "@goauthentik/sdk/flow"; | ||||
| import "@goauthentik/sdk/user-settings"; | ||||
							
								
								
									
										3
									
								
								web/src/sdk/user-settings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								web/src/sdk/user-settings.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| import "@goauthentik/elements/messages/MessageContainer"; | ||||
| import "@goauthentik/sdk/common"; | ||||
| import "@goauthentik/user/user-settings/UserSettingsPage"; | ||||
| @ -21,6 +21,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; | ||||
|  | ||||
| import { | ||||
|     ChallengeTypes, | ||||
|     CoreApi, | ||||
|     FlowChallengeResponseRequest, | ||||
|     FlowErrorChallenge, | ||||
|     FlowsApi, | ||||
| @ -82,8 +83,11 @@ export class UserSettingsFlowExecutor | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     firstUpdated(): void { | ||||
|         this.flowSlug = this.brand?.flowUserSettings; | ||||
|     async firstUpdated(): Promise<void> { | ||||
|         if (!this.brand) { | ||||
|             this.brand = await new CoreApi(DEFAULT_CONFIG).coreBrandsCurrentRetrieve(); | ||||
|         } | ||||
|         this.flowSlug = this.brand.flowUserSettings; | ||||
|         if (!this.flowSlug) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @ -10,6 +10,7 @@ | ||||
|             "@goauthentik/flow/*": ["./src/flow/*"], | ||||
|             "@goauthentik/locales/*": ["./src/locales/*"], | ||||
|             "@goauthentik/polyfill/*": ["./src/polyfill/*"], | ||||
|             "@goauthentik/sdk/*": ["./src/sdk/*"], | ||||
|             "@goauthentik/standalone/*": ["./src/standalone/*"], | ||||
|             "@goauthentik/user/*": ["./src/user/*"] | ||||
|         } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	