Compare commits
24 Commits
version/20
...
version/20
| Author | SHA1 | Date | |
|---|---|---|---|
| 31fe0e5923 | |||
| 8b619635ea | |||
| 1f1db523c0 | |||
| bbc23e1d77 | |||
| c30b7ee3e9 | |||
| 2ba79627bc | |||
| 198cbe1d9d | |||
| db6da159d5 | |||
| 9862e32078 | |||
| a7714e2892 | |||
| 073e1d241b | |||
| 5c5cc1c7da | |||
| 3dccce1095 | |||
| 78f997fbee | |||
| ed83c2b0b1 | |||
| af780deb27 | |||
| a4be38567f | |||
| 39aafbb34a | |||
| 07eb5fe533 | |||
| 301a89dd92 | |||
| cd6d0a47f3 | |||
| 8a23eaef1e | |||
| 8f285fbcc5 | |||
| 5d391424f7 |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2025.2.0-rc2
|
||||
current_version = 2025.2.1
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
from os import environ
|
||||
|
||||
__version__ = "2025.2.0"
|
||||
__version__ = "2025.2.1"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
build: "{{ build }}",
|
||||
api: {
|
||||
base: "{{ base_url }}",
|
||||
relBase: "{{ base_url_rel }}",
|
||||
},
|
||||
};
|
||||
window.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
{# Darkreader breaks the site regardless of theme as its not compatible with webcomponents, and we default to a dark theme based on preferred colour-scheme #}
|
||||
<meta name="darkreader-lock">
|
||||
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
|
||||
<link rel="icon" href="{{ brand.branding_favicon_url }}">
|
||||
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}">
|
||||
|
||||
@ -53,6 +53,7 @@ class InterfaceView(TemplateView):
|
||||
kwargs["build"] = get_build_hash()
|
||||
kwargs["url_kwargs"] = self.kwargs
|
||||
kwargs["base_url"] = self.request.build_absolute_uri(CONFIG.get("web.path", "/"))
|
||||
kwargs["base_url_rel"] = CONFIG.get("web.path", "/")
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
|
||||
@ -300,9 +300,11 @@ class TestAuthenticatorEmailStage(FlowTestCase):
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(device.confirmed)
|
||||
# Session key should be removed after device is saved
|
||||
device.save()
|
||||
self.assertNotIn(SESSION_KEY_EMAIL_DEVICE, self.client.session)
|
||||
# Get a fresh session to check if the key was removed
|
||||
session = self.client.session
|
||||
session.save()
|
||||
session.load()
|
||||
self.assertNotIn(SESSION_KEY_EMAIL_DEVICE, session)
|
||||
|
||||
def test_model_properties_and_methods(self):
|
||||
"""Test model properties"""
|
||||
|
||||
@ -12,6 +12,7 @@ from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.events.models import Event, EventAction, TaskStatus
|
||||
from authentik.events.system_tasks import SystemTask
|
||||
from authentik.lib.utils.reflection import class_to_path, path_to_class
|
||||
from authentik.root.celery import CELERY_APP
|
||||
from authentik.stages.authenticator_email.models import AuthenticatorEmailStage
|
||||
from authentik.stages.email.models import EmailStage
|
||||
@ -32,9 +33,10 @@ def send_mails(
|
||||
Celery group promise for the email sending tasks
|
||||
"""
|
||||
tasks = []
|
||||
stage_class = stage.__class__
|
||||
# Use the class path instead of the class itself for serialization
|
||||
stage_class_path = class_to_path(stage.__class__)
|
||||
for message in messages:
|
||||
tasks.append(send_mail.s(message.__dict__, stage_class, str(stage.pk)))
|
||||
tasks.append(send_mail.s(message.__dict__, stage_class_path, str(stage.pk)))
|
||||
lazy_group = group(*tasks)
|
||||
promise = lazy_group()
|
||||
return promise
|
||||
@ -61,7 +63,7 @@ def get_email_body(email: EmailMultiAlternatives) -> str:
|
||||
def send_mail(
|
||||
self: SystemTask,
|
||||
message: dict[Any, Any],
|
||||
stage_class: EmailStage | AuthenticatorEmailStage = EmailStage,
|
||||
stage_class_path: str | None = None,
|
||||
email_stage_pk: str | None = None,
|
||||
):
|
||||
"""Send Email for Email Stage. Retries are scheduled automatically."""
|
||||
@ -69,9 +71,10 @@ def send_mail(
|
||||
message_id = make_msgid(domain=DNS_NAME)
|
||||
self.set_uid(slugify(message_id.replace(".", "_").replace("@", "_")))
|
||||
try:
|
||||
if not email_stage_pk:
|
||||
stage: EmailStage | AuthenticatorEmailStage = stage_class(use_global_settings=True)
|
||||
if not stage_class_path or not email_stage_pk:
|
||||
stage = EmailStage(use_global_settings=True)
|
||||
else:
|
||||
stage_class = path_to_class(stage_class_path)
|
||||
stages = stage_class.objects.filter(pk=email_stage_pk)
|
||||
if not stages.exists():
|
||||
self.set_status(
|
||||
|
||||
58
authentik/stages/email/tests/test_tasks.py
Normal file
58
authentik/stages/email/tests/test_tasks.py
Normal file
@ -0,0 +1,58 @@
|
||||
"""Test email stage tasks"""
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.test import TestCase
|
||||
|
||||
from authentik.core.tests.utils import create_test_admin_user
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.stages.authenticator_email.models import AuthenticatorEmailStage
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.tasks import get_email_body, send_mails
|
||||
|
||||
|
||||
class TestEmailTasks(TestCase):
|
||||
"""Test email stage tasks"""
|
||||
|
||||
def setUp(self):
|
||||
self.user = create_test_admin_user()
|
||||
self.stage = EmailStage.objects.create(
|
||||
name="test-email",
|
||||
use_global_settings=True,
|
||||
)
|
||||
self.auth_stage = AuthenticatorEmailStage.objects.create(
|
||||
name="test-auth-email",
|
||||
use_global_settings=True,
|
||||
)
|
||||
|
||||
def test_get_email_body_html(self):
|
||||
"""Test get_email_body with HTML alternative"""
|
||||
message = EmailMultiAlternatives()
|
||||
message.body = "plain text"
|
||||
message.attach_alternative("<p>html content</p>", "text/html")
|
||||
self.assertEqual(get_email_body(message), "<p>html content</p>")
|
||||
|
||||
def test_get_email_body_plain(self):
|
||||
"""Test get_email_body with plain text only"""
|
||||
message = EmailMultiAlternatives()
|
||||
message.body = "plain text"
|
||||
self.assertEqual(get_email_body(message), "plain text")
|
||||
|
||||
def test_send_mails_email_stage(self):
|
||||
"""Test send_mails with EmailStage"""
|
||||
message = EmailMultiAlternatives()
|
||||
with patch("authentik.stages.email.tasks.send_mail") as mock_send:
|
||||
send_mails(self.stage, message)
|
||||
mock_send.s.assert_called_once_with(
|
||||
message.__dict__, class_to_path(EmailStage), str(self.stage.pk)
|
||||
)
|
||||
|
||||
def test_send_mails_authenticator_stage(self):
|
||||
"""Test send_mails with AuthenticatorEmailStage"""
|
||||
message = EmailMultiAlternatives()
|
||||
with patch("authentik.stages.email.tasks.send_mail") as mock_send:
|
||||
send_mails(self.auth_stage, message)
|
||||
mock_send.s.assert_called_once_with(
|
||||
message.__dict__, class_to_path(AuthenticatorEmailStage), str(self.auth_stage.pk)
|
||||
)
|
||||
@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "https://goauthentik.io/blueprints/schema.json",
|
||||
"type": "object",
|
||||
"title": "authentik 2025.2.0 Blueprint schema",
|
||||
"title": "authentik 2025.2.1 Blueprint schema",
|
||||
"required": [
|
||||
"version",
|
||||
"entries"
|
||||
|
||||
@ -31,7 +31,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.0}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.1}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -54,7 +54,7 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.0}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.2.1}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
||||
@ -29,4 +29,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2025.2.0"
|
||||
const VERSION = "2025.2.1"
|
||||
|
||||
@ -26,7 +26,7 @@ Parameters:
|
||||
Description: authentik Docker image
|
||||
AuthentikVersion:
|
||||
Type: String
|
||||
Default: 2025.2.0
|
||||
Default: 2025.2.1
|
||||
Description: authentik Docker image tag
|
||||
AuthentikServerCPU:
|
||||
Type: Number
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@goauthentik/authentik",
|
||||
"version": "2025.2.0",
|
||||
"version": "2025.2.1",
|
||||
"private": true
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "authentik"
|
||||
version = "2025.2.0"
|
||||
version = "2025.2.1"
|
||||
description = ""
|
||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2025.2.0
|
||||
version: 2025.2.1
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
|
||||
@ -22,7 +22,7 @@ import "@goauthentik/elements/forms/SearchSelect";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { TemplateResult, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@ -126,7 +126,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
);
|
||||
|
||||
return html`<form class="pf-c-form pf-m-horizontal">
|
||||
<ak-alert level="pf-m-info">${alertMsg}</ak-alert>
|
||||
${this.instance ? nothing : html`<ak-alert level="pf-m-info">${alertMsg}</ak-alert>`}
|
||||
<ak-text-input
|
||||
name="name"
|
||||
value=${ifDefined(this.instance?.name)}
|
||||
|
||||
@ -31,9 +31,9 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
|
||||
@property({ type: Array })
|
||||
allowedTypes: PolicyBindingCheckTarget[] = [
|
||||
PolicyBindingCheckTarget.policy,
|
||||
PolicyBindingCheckTarget.group,
|
||||
PolicyBindingCheckTarget.user,
|
||||
PolicyBindingCheckTarget.policy,
|
||||
];
|
||||
|
||||
@property({ type: Array })
|
||||
|
||||
@ -58,9 +58,9 @@ export class PolicyBindingForm extends ModelForm<PolicyBinding, string> {
|
||||
|
||||
@property({ type: Array })
|
||||
allowedTypes: PolicyBindingCheckTarget[] = [
|
||||
PolicyBindingCheckTarget.policy,
|
||||
PolicyBindingCheckTarget.group,
|
||||
PolicyBindingCheckTarget.user,
|
||||
PolicyBindingCheckTarget.policy,
|
||||
];
|
||||
|
||||
@property({ type: Array })
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 772 KiB After Width: | Height: | Size: 628 KiB |
@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||
export const ERROR_CLASS = "pf-m-danger";
|
||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||
export const CURRENT_CLASS = "pf-m-current";
|
||||
export const VERSION = "2025.2.0";
|
||||
export const VERSION = "2025.2.1";
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
export const ROUTE_SEPARATOR = ";";
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ export interface GlobalAuthentik {
|
||||
build: string;
|
||||
api: {
|
||||
base: string;
|
||||
relBase: string;
|
||||
};
|
||||
}
|
||||
|
||||
@ -27,6 +28,7 @@ export function globalAK(): GlobalAuthentik {
|
||||
ak.brand = CurrentBrandFromJSON(ak.brand);
|
||||
ak.config = ConfigFromJSON(ak.config);
|
||||
}
|
||||
const apiBase = new URL(process.env.AK_API_BASE_PATH || window.location.origin);
|
||||
if (!ak) {
|
||||
return {
|
||||
config: ConfigFromJSON({
|
||||
@ -39,7 +41,8 @@ export function globalAK(): GlobalAuthentik {
|
||||
versionSubdomain: "",
|
||||
build: "",
|
||||
api: {
|
||||
base: process.env.AK_API_BASE_PATH || window.location.origin,
|
||||
base: apiBase.toString(),
|
||||
relBase: apiBase.pathname,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -45,6 +45,8 @@ html > form > input {
|
||||
left: -2000px;
|
||||
}
|
||||
|
||||
/*#region Icons*/
|
||||
|
||||
.pf-icon {
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
@ -54,6 +56,18 @@ html > form > input {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.pf-c-form-control {
|
||||
--pf-c-form-control--m-caps-lock--BackgroundUrl: url("data:image/svg+xml;charset=utf8,%3Csvg fill='%23aaabac' viewBox='0 0 56 56' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 20.7812 37.6211 L 35.2421 37.6211 C 38.5233 37.6211 40.2577 35.6992 40.2577 32.6055 L 40.2577 28.4570 L 49.1404 28.4570 C 51.0859 28.4570 52.6329 27.3086 52.6329 25.5039 C 52.6329 24.4024 52.0703 23.5351 51.0158 22.6211 L 30.9062 4.8789 C 29.9452 4.0351 29.0546 3.4727 27.9999 3.4727 C 26.9687 3.4727 26.0780 4.0351 25.1171 4.8789 L 4.9843 22.6445 C 3.8828 23.6055 3.3671 24.4024 3.3671 25.5039 C 3.3671 27.3086 4.9140 28.4570 6.8828 28.4570 L 15.7421 28.4570 L 15.7421 32.6055 C 15.7421 35.6992 17.4999 37.6211 20.7812 37.6211 Z M 21.1562 34.0820 C 20.2655 34.0820 19.6562 33.4961 19.6562 32.6055 L 19.6562 25.7149 C 19.6562 25.1524 19.4452 24.9180 18.8828 24.9180 L 8.6640 24.9180 C 8.4999 24.9180 8.4296 24.8476 8.4296 24.7305 C 8.4296 24.6367 8.4530 24.5430 8.5702 24.4492 L 27.5077 7.9961 C 27.7187 7.8086 27.8359 7.7383 27.9999 7.7383 C 28.1640 7.7383 28.3046 7.8086 28.4921 7.9961 L 47.4532 24.4492 C 47.5703 24.5430 47.5939 24.6367 47.5939 24.7305 C 47.5939 24.8476 47.4998 24.9180 47.3356 24.9180 L 37.1406 24.9180 C 36.5780 24.9180 36.3671 25.1524 36.3671 25.7149 L 36.3671 32.6055 C 36.3671 33.4727 35.7109 34.0820 34.8671 34.0820 Z M 19.7733 52.5273 L 36.0624 52.5273 C 38.7577 52.5273 40.3046 51.0273 40.3046 48.3086 L 40.3046 44.9336 C 40.3046 42.2148 38.7577 40.6680 36.0624 40.6680 L 19.7733 40.6680 C 17.0546 40.6680 15.5077 42.2383 15.5077 44.9336 L 15.5077 48.3086 C 15.5077 51.0039 17.0546 52.5273 19.7733 52.5273 Z M 20.3124 49.2227 C 19.4921 49.2227 19.0468 48.8008 19.0468 47.9805 L 19.0468 45.2617 C 19.0468 44.4414 19.4921 43.9727 20.3124 43.9727 L 35.5233 43.9727 C 36.3202 43.9727 36.7655 44.4414 36.7655 45.2617 L 36.7655 47.9805 C 36.7655 48.8008 36.3202 49.2227 35.5233 49.2227 Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.pf-c-form-control.pf-m-icon.pf-m-caps-lock {
|
||||
--pf-c-form-control--m-icon--BackgroundUrl: var(
|
||||
--pf-c-form-control--m-caps-lock--BackgroundUrl
|
||||
);
|
||||
}
|
||||
|
||||
/*#endregion*/
|
||||
|
||||
.pf-c-page__header {
|
||||
z-index: 0;
|
||||
background-color: var(--ak-dark-background-light);
|
||||
|
||||
@ -3,6 +3,7 @@ import type { AbstractConstructor } from "@goauthentik/elements/types.js";
|
||||
|
||||
import { consume } from "@lit/context";
|
||||
import type { LitElement } from "lit";
|
||||
import { state } from "lit/decorators.js";
|
||||
|
||||
import type { CurrentBrand } from "@goauthentik/api";
|
||||
|
||||
@ -12,6 +13,7 @@ export function WithBrandConfig<T extends AbstractConstructor<LitElement>>(
|
||||
) {
|
||||
abstract class WithBrandProvider extends superclass {
|
||||
@consume({ context: authentikBrandContext, subscribe })
|
||||
@state()
|
||||
public brand!: CurrentBrand;
|
||||
}
|
||||
return WithBrandProvider;
|
||||
|
||||
27
web/src/elements/utils/focus.ts
Normal file
27
web/src/elements/utils/focus.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @fileoverview Utilities for DOM element interaction, focus management, and event handling.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Recursively check if the target element or any of its children are active (i.e. "focused").
|
||||
*
|
||||
* @param targetElement The element to check if it is active.
|
||||
* @param containerElement The container element to check if the target element is active within.
|
||||
*/
|
||||
export function isActiveElement(
|
||||
targetElement: Element | null,
|
||||
containerElement: Element | null,
|
||||
): boolean {
|
||||
// Does the container element even exist?
|
||||
if (!containerElement) return false;
|
||||
|
||||
// Does the container element have a shadow root?
|
||||
if (!("shadowRoot" in containerElement)) return false;
|
||||
if (containerElement.shadowRoot === null) return false;
|
||||
|
||||
// Is the target element the active element?
|
||||
if (containerElement.shadowRoot.activeElement === targetElement) return true;
|
||||
|
||||
// Let's check the children of the container element...
|
||||
return isActiveElement(containerElement.shadowRoot.activeElement, containerElement);
|
||||
}
|
||||
@ -1,36 +1,93 @@
|
||||
import { AKElement } from "@goauthentik/elements/Base.js";
|
||||
import { bound } from "@goauthentik/elements/decorators/bound";
|
||||
import "@goauthentik/elements/forms/FormElement";
|
||||
import { isActiveElement } from "@goauthentik/elements/utils/focus";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html, nothing, render } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { Ref, createRef, ref } from "lit/directives/ref.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFInputGroup from "@patternfly/patternfly/components/InputGroup/input-group.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
/**
|
||||
* A configuration object for the visibility states of the password input.
|
||||
*/
|
||||
interface VisibilityProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum-like object for the visibility states of the password input.
|
||||
*/
|
||||
const Visibility = {
|
||||
Reveal: {
|
||||
icon: "fa-eye",
|
||||
label: msg("Show password"),
|
||||
},
|
||||
Mask: {
|
||||
icon: "fa-eye-slash",
|
||||
label: msg("Hide password"),
|
||||
},
|
||||
} as const satisfies Record<string, VisibilityProps>;
|
||||
|
||||
@customElement("ak-flow-input-password")
|
||||
export class InputPassword extends AKElement {
|
||||
static get styles() {
|
||||
return [PFBase, PFInputGroup, PFFormControl, PFButton];
|
||||
}
|
||||
|
||||
//#region Properties
|
||||
|
||||
/**
|
||||
* The ID of the input field.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String, attribute: "input-id" })
|
||||
inputId = "ak-stage-password-input";
|
||||
|
||||
/**
|
||||
* The name of the input field.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
name = "password";
|
||||
|
||||
/**
|
||||
* The label for the input field.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
label = msg("Password");
|
||||
|
||||
/**
|
||||
* The placeholder text for the input field.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
placeholder = msg("Please enter your password");
|
||||
|
||||
/**
|
||||
* The initial value of the input field.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String, attribute: "prefill" })
|
||||
passwordPrefill = "";
|
||||
initialValue = "";
|
||||
|
||||
/**
|
||||
* The errors for the input field.
|
||||
*/
|
||||
@property({ type: Object })
|
||||
errors: Record<string, string> = {};
|
||||
|
||||
@ -41,113 +98,220 @@ export class InputPassword extends AKElement {
|
||||
@property({ type: String })
|
||||
invalid?: string;
|
||||
|
||||
/**
|
||||
* Whether to allow the user to toggle the visibility of the password.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "allow-show-password" })
|
||||
allowShowPassword = false;
|
||||
|
||||
/**
|
||||
* Whether the password is currently visible.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "password-visible" })
|
||||
passwordVisible = false;
|
||||
|
||||
/**
|
||||
* Automatically grab focus after rendering.
|
||||
*
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "grab-focus" })
|
||||
grabFocus = false;
|
||||
|
||||
timer?: number;
|
||||
//#endregion
|
||||
|
||||
input?: HTMLInputElement;
|
||||
//#region Refs
|
||||
|
||||
cleanup(): void {
|
||||
if (this.timer) {
|
||||
console.debug("authentik/stages/password: cleared focus timer");
|
||||
window.clearInterval(this.timer);
|
||||
this.timer = undefined;
|
||||
inputRef: Ref<HTMLInputElement> = createRef();
|
||||
|
||||
toggleVisibilityRef: Ref<HTMLButtonElement> = createRef();
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region State
|
||||
|
||||
/**
|
||||
* Whether the caps lock key is enabled.
|
||||
*/
|
||||
@state()
|
||||
capsLock = false;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Listeners
|
||||
|
||||
/**
|
||||
* Toggle the visibility of the password field.
|
||||
*
|
||||
* Directly affects the DOM, so no `.requestUpdate()` required. Effect is immediately visible.
|
||||
*
|
||||
* @param event The event that triggered the visibility toggle.
|
||||
*/
|
||||
@bound
|
||||
togglePasswordVisibility(event?: PointerEvent) {
|
||||
event?.stopPropagation();
|
||||
event?.preventDefault();
|
||||
|
||||
const input = this.inputRef.value;
|
||||
|
||||
if (!input) {
|
||||
console.warn("ak-flow-password-input: unable to identify input field");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
input.type = input.type === "password" ? "text" : "password";
|
||||
|
||||
this.syncVisibilityToggle(input);
|
||||
}
|
||||
|
||||
// Must support both older browsers and shadyDom; we'll keep using this in-line, but it'll still
|
||||
// be in the scope of the parent element, not an independent shadowDOM.
|
||||
/**
|
||||
* Listen for key events, synchronizing the caps lock indicators.
|
||||
*/
|
||||
@bound
|
||||
capsLockListener(event: KeyboardEvent) {
|
||||
this.capsLock = event.getModifierState("CapsLock");
|
||||
}
|
||||
|
||||
//#region Lifecycle
|
||||
|
||||
/**
|
||||
* Interval ID for the focus observer.
|
||||
*
|
||||
* @see {@linkcode observeInputFocus}
|
||||
*/
|
||||
inputFocusIntervalID?: ReturnType<typeof setInterval>;
|
||||
|
||||
/**
|
||||
* Periodically attempt to focus the input field until it is focused.
|
||||
*
|
||||
* This is some-what of a crude way to get autofocus, but in most cases
|
||||
* the `autofocus` attribute isn't enough, due to timing within shadow doms and such.
|
||||
*/
|
||||
observeInputFocus(): void {
|
||||
if (!this.grabFocus) {
|
||||
return;
|
||||
}
|
||||
this.inputFocusIntervalID = setInterval(() => {
|
||||
const input = this.inputRef.value;
|
||||
|
||||
if (!input) return;
|
||||
|
||||
if (isActiveElement(input, document.activeElement)) {
|
||||
console.debug("authentik/stages/password: cleared focus observer");
|
||||
clearInterval(this.inputFocusIntervalID);
|
||||
}
|
||||
|
||||
input.focus();
|
||||
}, 10);
|
||||
|
||||
console.debug("authentik/stages/password: started focus observer");
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
this.observeInputFocus();
|
||||
|
||||
addEventListener("keydown", this.capsLockListener);
|
||||
addEventListener("keyup", this.capsLockListener);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
if (this.inputFocusIntervalID) {
|
||||
clearInterval(this.inputFocusIntervalID);
|
||||
}
|
||||
|
||||
super.disconnectedCallback();
|
||||
|
||||
removeEventListener("keydown", this.capsLockListener);
|
||||
removeEventListener("keyup", this.capsLockListener);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Render
|
||||
|
||||
/**
|
||||
* Create the render root for the password input.
|
||||
*
|
||||
* Must support both older browsers and shadyDom; we'll keep using this in-line,
|
||||
* but it'll still be in the scope of the parent element, not an independent shadowDOM.
|
||||
*/
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
// State is saved in the DOM, and read from the DOM. Directly affects the DOM,
|
||||
// so no `.requestUpdate()` required. Effect is immediately visible.
|
||||
togglePasswordVisibility(ev: PointerEvent) {
|
||||
const passwordField = this.renderRoot.querySelector(`#${this.inputId}`) as HTMLInputElement;
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
/**
|
||||
* Render the password visibility toggle button.
|
||||
*
|
||||
* In the unlikely event that we want to make "show password" the _default_ behavior,
|
||||
* this effect handler is broken out into its own method.
|
||||
*
|
||||
* The current behavior in the main {@linkcode render} method assumes the field is of type "password."
|
||||
*
|
||||
* To have this effect, er, take effect, call it in an {@linkcode updated} method.
|
||||
*
|
||||
* @param input The password field to render the visibility features for.
|
||||
*/
|
||||
syncVisibilityToggle(input: HTMLInputElement | undefined = this.inputRef.value): void {
|
||||
if (!input) return;
|
||||
|
||||
if (!passwordField) {
|
||||
throw new Error("ak-flow-password-input: unable to identify input field");
|
||||
}
|
||||
const toggleElement = this.toggleVisibilityRef.value;
|
||||
|
||||
passwordField.type = passwordField.type === "password" ? "text" : "password";
|
||||
this.renderPasswordVisibilityFeatures(passwordField);
|
||||
}
|
||||
if (!toggleElement) return;
|
||||
|
||||
// In the unlikely event that we want to make "show password" the _default_ behavior, this
|
||||
// effect handler is broken out into its own method. The current behavior in the main
|
||||
// `.render()` method assumes the field is of type "password." To have this effect, er, take
|
||||
// effect, call it in an `.updated()` method.
|
||||
renderPasswordVisibilityFeatures(passwordField: HTMLInputElement) {
|
||||
const toggleId = `#${this.inputId}-visibility-toggle`;
|
||||
const visibilityToggle = this.renderRoot.querySelector(toggleId) as HTMLButtonElement;
|
||||
if (!visibilityToggle) {
|
||||
return;
|
||||
}
|
||||
const show = passwordField.type === "password";
|
||||
visibilityToggle?.setAttribute(
|
||||
const masked = input.type === "password";
|
||||
|
||||
toggleElement.setAttribute(
|
||||
"aria-label",
|
||||
show ? msg("Show password") : msg("Hide password"),
|
||||
);
|
||||
visibilityToggle?.querySelector("i")?.remove();
|
||||
render(
|
||||
show
|
||||
? html`<i class="fas fa-eye" aria-hidden="true"></i>`
|
||||
: html`<i class="fas fa-eye-slash" aria-hidden="true"></i>`,
|
||||
visibilityToggle,
|
||||
masked ? Visibility.Reveal.label : Visibility.Mask.label,
|
||||
);
|
||||
|
||||
const iconElement = toggleElement.querySelector("i")!;
|
||||
|
||||
iconElement.classList.remove(Visibility.Mask.icon, Visibility.Reveal.icon);
|
||||
iconElement.classList.add(masked ? Visibility.Reveal.icon : Visibility.Mask.icon);
|
||||
}
|
||||
|
||||
renderInput(): HTMLInputElement {
|
||||
this.input = document.createElement("input");
|
||||
this.input.id = `${this.inputId}`;
|
||||
this.input.type = "password";
|
||||
this.input.name = this.name;
|
||||
this.input.placeholder = this.placeholder;
|
||||
this.input.autofocus = this.grabFocus;
|
||||
this.input.autocomplete = "current-password";
|
||||
this.input.classList.add("pf-c-form-control");
|
||||
this.input.required = true;
|
||||
this.input.value = this.passwordPrefill ?? "";
|
||||
if (this.invalid) {
|
||||
this.input.setAttribute("aria-invalid", this.invalid);
|
||||
}
|
||||
// This is somewhat of a crude way to get autofocus, but in most cases the `autofocus` attribute
|
||||
// isn't enough, due to timing within shadow doms and such.
|
||||
renderVisibilityToggle() {
|
||||
if (!this.allowShowPassword) return nothing;
|
||||
|
||||
if (this.grabFocus) {
|
||||
this.timer = window.setInterval(() => {
|
||||
if (!this.input) {
|
||||
return;
|
||||
}
|
||||
// Because activeElement behaves differently with shadow dom
|
||||
// we need to recursively check
|
||||
const rootEl = document.activeElement;
|
||||
const isActive = (el: Element | null): boolean => {
|
||||
if (!rootEl) return false;
|
||||
if (!("shadowRoot" in rootEl)) return false;
|
||||
if (rootEl.shadowRoot === null) return false;
|
||||
if (rootEl.shadowRoot.activeElement === el) return true;
|
||||
return isActive(rootEl.shadowRoot.activeElement);
|
||||
};
|
||||
if (isActive(this.input)) {
|
||||
this.cleanup();
|
||||
}
|
||||
this.input.focus();
|
||||
}, 10);
|
||||
console.debug("authentik/stages/password: started focus timer");
|
||||
}
|
||||
return this.input;
|
||||
const { label, icon } = this.passwordVisible ? Visibility.Mask : Visibility.Reveal;
|
||||
|
||||
return html`<button
|
||||
${ref(this.toggleVisibilityRef)}
|
||||
aria-label=${label}
|
||||
@click=${this.togglePasswordVisibility}
|
||||
class="pf-c-button pf-m-control"
|
||||
type="button"
|
||||
>
|
||||
<i class="fas ${icon}" aria-hidden="true"></i>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
renderHelperText() {
|
||||
if (!this.capsLock) return nothing;
|
||||
|
||||
return html`<div
|
||||
class="pf-c-form__helper-text"
|
||||
id="helper-text-form-caps-lock-helper"
|
||||
aria-live="polite"
|
||||
>
|
||||
<div class="pf-c-helper-text">
|
||||
<div class="pf-c-helper-text__item pf-m-warning">
|
||||
<span class="pf-c-helper-text__item-icon">
|
||||
<i class="fas fa-fw fa-exclamation-triangle" aria-hidden="true"></i>
|
||||
</span>
|
||||
|
||||
<span class="pf-c-helper-text__item-text">${msg("Caps Lock is enabled.")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -157,22 +321,34 @@ export class InputPassword extends AKElement {
|
||||
class="pf-c-form__group"
|
||||
.errors=${this.errors}
|
||||
>
|
||||
<div class="pf-c-input-group">
|
||||
${this.renderInput()}
|
||||
${this.allowShowPassword
|
||||
? html` <button
|
||||
id="${this.inputId}-visibility-toggle"
|
||||
class="pf-c-button pf-m-control ak-stage-password-toggle-visibility"
|
||||
type="button"
|
||||
aria-label=${msg("Show password")}
|
||||
@click=${(ev: PointerEvent) => this.togglePasswordVisibility(ev)}
|
||||
>
|
||||
<i class="fas fa-eye" aria-hidden="true"></i>
|
||||
</button>`
|
||||
: nothing}
|
||||
<div class="pf-c-form__group-control">
|
||||
<div class="pf-c-input-group">
|
||||
<input
|
||||
type=${this.passwordVisible ? "text" : "password"}
|
||||
id=${this.inputId}
|
||||
name=${this.name}
|
||||
placeholder=${this.placeholder}
|
||||
autocomplete="current-password"
|
||||
class="${classMap({
|
||||
"pf-c-form-control": true,
|
||||
"pf-m-icon": true,
|
||||
"pf-m-caps-lock": this.capsLock,
|
||||
})}"
|
||||
required
|
||||
aria-invalid=${ifDefined(this.invalid)}
|
||||
value=${this.initialValue}
|
||||
${ref(this.inputRef)}
|
||||
/>
|
||||
|
||||
${this.renderVisibilityToggle()}
|
||||
</div>
|
||||
|
||||
${this.renderHelperText()}
|
||||
</div>
|
||||
</ak-form-element>`;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@ -3,7 +3,7 @@ import "@goauthentik/elements/forms/FormElement";
|
||||
import { BaseDeviceStage } from "@goauthentik/flow/stages/authenticator_validate/base";
|
||||
import { PasswordManagerPrefill } from "@goauthentik/flow/stages/identification/IdentificationStage";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
@ -35,7 +35,7 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
|
||||
switch (this.deviceChallenge?.deviceClass) {
|
||||
case DeviceClassesEnum.Email: {
|
||||
const email = this.deviceChallenge.challenge?.email;
|
||||
return msg(`A code has been sent to you via email${email ? ` ${email}` : ""}`);
|
||||
return msg(str`A code has been sent to you via email${email ? ` ${email}` : ""}`);
|
||||
}
|
||||
case DeviceClassesEnum.Sms:
|
||||
return msg("A code has been sent to you via SMS.");
|
||||
|
||||
@ -97,8 +97,19 @@ export class LibraryApplication extends AKElement {
|
||||
return html``;
|
||||
}
|
||||
if (this.application?.launchUrl === "goauthentik.io://providers/rac/launch") {
|
||||
return html`<ak-library-rac-endpoint-launch .app=${this.application}>
|
||||
</ak-library-rac-endpoint-launch>
|
||||
return html`<div class="pf-c-card__header">
|
||||
<a
|
||||
@click=${() => {
|
||||
this.racEndpointLaunch?.onClick();
|
||||
}}
|
||||
>
|
||||
<ak-app-icon
|
||||
size=${PFSize.Large}
|
||||
name=${this.application.name}
|
||||
icon=${ifDefined(this.application.metaIcon || undefined)}
|
||||
></ak-app-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<a
|
||||
@click=${() => {
|
||||
@ -107,15 +118,29 @@ export class LibraryApplication extends AKElement {
|
||||
>
|
||||
${this.application.name}
|
||||
</a>
|
||||
</div>`;
|
||||
</div>
|
||||
<ak-library-rac-endpoint-launch .app=${this.application}>
|
||||
</ak-library-rac-endpoint-launch>`;
|
||||
}
|
||||
return html`<div class="pf-c-card__title">
|
||||
<a
|
||||
href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
|
||||
>${this.application.name}</a
|
||||
>
|
||||
</div>`;
|
||||
return html`<div class="pf-c-card__header">
|
||||
<a
|
||||
href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
|
||||
>
|
||||
<ak-app-icon
|
||||
size=${PFSize.Large}
|
||||
name=${this.application.name}
|
||||
icon=${ifDefined(this.application.metaIcon || undefined)}
|
||||
></ak-app-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="pf-c-card__title">
|
||||
<a
|
||||
href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
|
||||
>${this.application.name}</a
|
||||
>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
@ -135,18 +160,6 @@ export class LibraryApplication extends AKElement {
|
||||
class="pf-c-card pf-m-hoverable pf-m-compact ${classMap(classes)}"
|
||||
style=${styleMap(styles)}
|
||||
>
|
||||
<div class="pf-c-card__header">
|
||||
<a
|
||||
href="${ifDefined(this.application.launchUrl ?? "")}"
|
||||
target="${ifDefined(this.application.openInNewTab ? "_blank" : undefined)}"
|
||||
>
|
||||
<ak-app-icon
|
||||
size=${PFSize.Large}
|
||||
name=${this.application.name}
|
||||
icon=${ifDefined(this.application.metaIcon || undefined)}
|
||||
></ak-app-icon>
|
||||
</a>
|
||||
</div>
|
||||
${this.renderLaunch()}
|
||||
<div class="expander"></div>
|
||||
${expandable ? this.renderExpansion(this.application) : nothing}
|
||||
|
||||
@ -32,7 +32,7 @@ export class UserSettingsPassword extends AKElement {
|
||||
<div class="pf-c-card__body">
|
||||
<a
|
||||
href="${ifDefined(this.configureUrl)}${AndNext(
|
||||
`${globalAK().api.base}if/user/#/settings;${JSON.stringify({ page: "page-details" })}`,
|
||||
`${globalAK().api.relBase}if/user/#/settings;${JSON.stringify({ page: "page-details" })}`,
|
||||
)}"
|
||||
class="pf-c-button pf-m-primary"
|
||||
>
|
||||
|
||||
@ -10,7 +10,7 @@ import { StageHost } from "@goauthentik/flow/stages/base";
|
||||
import "@goauthentik/user/user-settings/details/stages/prompt/PromptStage";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, html } from "lit";
|
||||
import { CSSResult, PropertyValues, TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
|
||||
@ -83,12 +83,14 @@ export class UserSettingsFlowExecutor
|
||||
});
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
this.flowSlug = this.brand?.flowUserSettings;
|
||||
if (!this.flowSlug) {
|
||||
return;
|
||||
updated(changedProperties: PropertyValues<this>): void {
|
||||
if (changedProperties.has("brand") && this.brand) {
|
||||
this.flowSlug = this.brand?.flowUserSettings;
|
||||
if (!this.flowSlug) {
|
||||
return;
|
||||
}
|
||||
this.nextChallenge();
|
||||
}
|
||||
this.nextChallenge();
|
||||
}
|
||||
|
||||
async nextChallenge(): Promise<void> {
|
||||
@ -161,7 +163,7 @@ export class UserSettingsFlowExecutor
|
||||
// Flow has finished, so let's load while in the background we can restart the flow
|
||||
this.loading = true;
|
||||
console.debug("authentik/user/flows: redirect to '/', restarting flow.");
|
||||
this.firstUpdated();
|
||||
this.nextChallenge();
|
||||
this.globalRefresh();
|
||||
showMessage({
|
||||
level: MessageLevel.success,
|
||||
|
||||
@ -74,7 +74,7 @@ export class MFADevicesPage extends Table<Device> {
|
||||
return html`<li>
|
||||
<a
|
||||
href="${ifDefined(stage.configureUrl)}${AndNext(
|
||||
`${globalAK().api.base}if/user/#/settings;${JSON.stringify({
|
||||
`${globalAK().api.relBase}if/user/#/settings;${JSON.stringify({
|
||||
page: "page-mfa",
|
||||
})}`,
|
||||
)}"
|
||||
|
||||
@ -4,29 +4,21 @@ title: Manage applications
|
||||
|
||||
Managing the applications that your team uses involves several tasks, from initially adding the application and provider, to controlling access and visibility of the application, to providing access URLs.
|
||||
|
||||
## Add new applications
|
||||
|
||||
Learn how to add new applications from our video or follow the instructions below.
|
||||
|
||||
### Video
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/broUAWrIWDI;start=22" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||
|
||||
### Instructions
|
||||
|
||||
To add an application to authentik and have it display on users' **My applications** page, you can use the Application Wizard, which creates both the new application and the required provider at the same time.
|
||||
To add an application to authentik and have it display on users' **My applications** page, follow these steps:
|
||||
|
||||
1. Log into authentik as an admin, and navigate to **Applications --> Applications**.
|
||||
1. Log in to authentik as an admin, and open the authentik Admin interface.
|
||||
|
||||
2. Click **Create with Wizard**. (Alternatively, use our legacy process and click **Create**. The legacy process requires that the application and its authentication provider be configured separately.)
|
||||
2. Navigate to **Applications -> Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can create only an application, without a provider, by clicking **Create.)**
|
||||
|
||||
3. In the **New application** wizard, define the application details, the provider type, bindings for the application.
|
||||
3. In the **New application** box, define the application details, the provider type and configuration settings, and bindings for the application.
|
||||
|
||||
- **Application**: provide a name, an optional group for the type of application, the policy engine mode, and optional UI settings.
|
||||
|
||||
- **Choose a Provider**: select the provider types for this application.
|
||||
|
||||
- **Configure a Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and any additional required configurations.
|
||||
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and any additional required configurations.
|
||||
|
||||
- **Configure Bindings**: to manage the listing and access to applications on a user's **My applications** page, you can optionally create a [binding](../flows-stages/bindings/index.md) between the application and a specific policy, group, or user. Note that if you do not define any bindings, then all users have access to the application. For more information about user access, refer to our documentation about [authorization](#policy-driven-authorization) and [hiding an application](#hide-applications).
|
||||
|
||||
@ -83,8 +75,8 @@ return {
|
||||
3. Click the **Application entitlements** tab at the top of the page, and then click **Create entitlement**. Provide a name for the entitlement, enter any optional **Attributes**, and then click **Create**.
|
||||
4. In the list locate the entitlement to which you want to bind a user or group, and then **click the caret (>) to expand the entitlement details.**
|
||||
5. In the expanded area, click **Bind existing Group/User**.
|
||||
6. In the **Create Binding** modal box, select either the tab for **Group** or **User**, and then in the drop-down list, select the group or user.
|
||||
7. Optionally, configure additional settings for the binding, and then click **Create** to create the binding and close the modal box.
|
||||
6. In the **Create Binding** box, select either the tab for **Group** or **User**, and then in the drop-down list, select the group or user.
|
||||
7. Optionally, configure additional settings for the binding, and then click **Create** to create the binding and close the box.
|
||||
|
||||
## Hide applications
|
||||
|
||||
|
||||
@ -9,5 +9,5 @@ For instructions to create a binding, refer to the documentation for the specifi
|
||||
- [Bind a stage to a flow](../stages/index.md#bind-a-stage-to-a-flow)
|
||||
- [Bind a policy to a flow or stage](../../../customize/policies/working_with_policies#bind-a-policy-to-a-flow-or-stage)
|
||||
- [Bind users or groups to a specific application with an Application Entitlement](../../applications/manage_apps.md#application-entitlements)
|
||||
- [Bind a policy to a specific application when you create a new app using the Wizard](../../applications/manage_apps.md#instructions)
|
||||
- [Bind a policy to a specific application when you create a new application and provider](../../applications/manage_apps.md#instructions)
|
||||
- [Bind users and groups to a stage binding, to define whether or not that stage is shown](../stages/index.md#bind-users-and-groups-to-a-flows-stage-binding)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Duo authenticator setup stage
|
||||
title: Duo Authenticator Setup stage
|
||||
---
|
||||
|
||||
This stage configures a Duo authenticator. To get the API Credentials for this stage, open your Duo Admin dashboard.
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
---
|
||||
title: Email Authenticator Setup stage
|
||||
---
|
||||
|
||||
<span class="badge badge--version">authentik 2025.2+</span>
|
||||
|
||||
This stage configures an email-based authenticator that sends a one-time code to a user's email address for authentication.
|
||||
|
||||
When a user goes through a flow that includes this stage, they are prompted for their email address (if not already set). The user then receives an email with a one-time code, which they enter into the authentik Login panel.
|
||||
|
||||
The email address will be saved and can be used with the [Authenticator validation](../authenticator_validate/index.md) stage for future authentications.
|
||||
|
||||
## Flow integration
|
||||
|
||||
To use the Email Authenticator Setup stage in a flow, follow these steps:
|
||||
|
||||
1. [Create](../../flow/index.md#create-a-custom-flow) a new flow or edit an existing one.
|
||||
2. On the flow's **Stage Bindings** tab, click **Create and bind stage** to create and add the Email Authenticator Setup stage. (If the stage already exists, click **Bind existing stage**.)
|
||||
3. Configure the stage settings as described below.
|
||||
|
||||
- **Name**: provide a descriptive name, such as Email Authenticator Setup.
|
||||
- **Authenticator type name**: define the display name for this stage.
|
||||
- **Use global connection settings**: the stage can be configured in two ways: global settings or stage-specific settings.
|
||||
|
||||
- Enable (toggle on) the **Use global connection settings** option to use authentik's global email configuration. Note that you must already have configured your environment variables to use the global settings. See instructions for [Docker Compose](../../../../install-config/install/docker-compose#email-configuration-optional-but-recommended) and for [Kubernetes](../../../../install-config/install/kubernetes#optional-step-configure-global-email-credentials).
|
||||
|
||||
- If you need different email settings for this stage, disable (toggle off) **Use global connection settings** and configure the following options:
|
||||
|
||||
- **Connection settings**:
|
||||
|
||||
- **SMTP Host**: SMTP server hostname (default: localhost)
|
||||
- **SMTP Port**: SMTP server port number(default: 25)
|
||||
- **SMTP Username**: SMTP authentication username (optional)
|
||||
- **SMTP Password**: SMTP authentication password (optional)
|
||||
- **Use TLS**: Enable TLS encryption
|
||||
- **Use SSL**: Enable SSL encryption
|
||||
- **Timeout**: Connection timeout in seconds (default: 10)
|
||||
- **From Address**: Email address that messages are sent from (default: system@authentik.local)
|
||||
|
||||
- **Stage-specific settings**:
|
||||
|
||||
- **Subject**: Email subject line (default: "authentik Sign-in code")
|
||||
- **Token Expiration**: Time in minutes that the sent token is valid (default: 30)
|
||||
- **Configuration flow**: select the flow to which you are binding this stage.
|
||||
|
||||
4. Click **Update** to complete the creation and binding of the stage to the flow.
|
||||
|
||||
The new Email Authenticator Setup stage now appears on the **Stage Bindings** tab for the flow.
|
||||
@ -28,7 +28,7 @@ For detailed instructions, refer to Google documentation.
|
||||
### Create a Google cloud project
|
||||
|
||||
1. Open the Google Cloud Console (https://cloud.google.com/cloud-console).
|
||||
2. In upper left, click the drop-down box to open the **Select a project** modal box, and then select **New Project**.
|
||||
2. In upper left, click the drop-down box to open the **Select a project** box, and then select **New Project**.
|
||||
3. Create a new project and give it a name like "authentik GWS".
|
||||
4. Use the search bar at the top of your new project page to search for "API Library".
|
||||
5. On the **API Library** page, use the search bar again to find "Chrome Verified Access API".
|
||||
@ -49,7 +49,7 @@ For detailed instructions, refer to Google documentation.
|
||||
|
||||
1. On the **Service accounts** page, click the account that you just created.
|
||||
2. Click the **Keys** tab at top of the page, the click **Add Key -> Create new key**.
|
||||
3. In the Create modal box, select JSON as the key type, and then click **Create**.
|
||||
3. In the Create box, select JSON as the key type, and then click **Create**.
|
||||
A pop-up displays with the private key, and the key is saved to your computer as a JSON file.
|
||||
Later, when you create the stage in authentik, you will add this key in the **Credentials** field.
|
||||
4. On the service account page, click the **Details** tab, and expand the **Advanced settings** area.
|
||||
@ -66,7 +66,7 @@ For detailed instructions, refer to Google documentation.
|
||||
|
||||
2. In the Admin interface, navigate to **Flows -> Stages**.
|
||||
|
||||
3. Click **Create**, and select **Endpoint Authenticator Google Device Trust Connector Stage**, and in the **New stage** modal box, define the following fields:
|
||||
3. Click **Create**, and select **Endpoint Authenticator Google Device Trust Connector Stage**, and in the **New stage** box, define the following fields:
|
||||
|
||||
- **Name**: define a descriptive name, such as "chrome-device-trust".
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: SMS authenticator setup stage
|
||||
title: SMS Authenticator Setup stage
|
||||
---
|
||||
|
||||
This stage configures an SMS-based authenticator using either Twilio, or a generic HTTP endpoint.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Static authenticator setup stage
|
||||
title: Static Authenticator Setup stage
|
||||
---
|
||||
|
||||
This stage configures static Tokens, which can be used as a backup method to time-based OTP tokens.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: TOTP authenticator setup stage
|
||||
title: TOTP Authenticator Setup stage
|
||||
---
|
||||
|
||||
This stage configures a time-based OTP Device, such as Google Authenticator or Authy.
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
---
|
||||
title: Authenticator validation stage
|
||||
title: Authenticator Validation stage
|
||||
---
|
||||
|
||||
This stage validates an already configured Authenticator Device. This device has to be configured using any of the other authenticator stages:
|
||||
|
||||
- [Duo authenticator stage](../authenticator_duo/index.md)
|
||||
- [Email authenticator stage](../authenticator_email/index.md)
|
||||
- [SMS authenticator stage](../authenticator_sms/index.md)
|
||||
- [Static authenticator stage](../authenticator_static/index.md)
|
||||
- [TOTP authenticator stage](../authenticator_totp/index.md)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: WebAuthn authenticator setup stage
|
||||
title: WebAuthn Authenticator Setup stage
|
||||
---
|
||||
|
||||
This stage configures a WebAuthn-based Authenticator. This can either be a browser, biometrics or a Security stick like a YubiKey.
|
||||
|
||||
@ -70,8 +70,8 @@ To bind a user or a group to a stage binding for a specific flow, follow these s
|
||||

|
||||
|
||||
6. In the expanded area, click **Bind existing policy/group/user**.
|
||||
7. In the **Create Binding** modal box, select either the tab for **Group** or **User**.
|
||||
7. In the **Create Binding** box, select either the tab for **Group** or **User**.
|
||||
8. In the drop-down list, select the group or user.
|
||||
9. Optionally, configure additional settings for the binding, and then click **Create** to create the binding and close the modal box.
|
||||
9. Optionally, configure additional settings for the binding, and then click **Create** to create the binding and close the box.
|
||||
|
||||
Learn more about [bindings](../bindings/index.md) and [working with them](../bindings/work_with_bindings.md).
|
||||
|
||||
@ -35,7 +35,7 @@ Any change made to the outpost's associated app or provider immediately triggers
|
||||
- **Applications**: select the applications that you want the outpost to serve
|
||||
- **Advanced settings** (*optional*): For further optional configuration settings, refer to [Configuration](#configuration) below.
|
||||
|
||||
4. Click **Create** to save your new outpost settings and close the modal.
|
||||
4. Click **Create** to save your new outpost settings and close the box.
|
||||
|
||||
Upon creation, a service account and a token is generated. The service account only has permissions to read the outpost and provider configuration. This token is used by the outpost to connect to authentik.
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ As detailed in the steps below, when you add an Entra ID provider in authentik y
|
||||
|
||||
1. Log in as an admin to authentik, and go to the Admin interface.
|
||||
2. In the Admin interface, navigate to **Applications -> Providers**.
|
||||
3. Click **Create**, and in the **New provider** modal box select **Microsoft Entra Provider** as the type and click **Next**.
|
||||
3. Click **Create**, and in the **New provider** box select **Microsoft Entra Provider** as the type and click **Next**.
|
||||
4. Define the following fields:
|
||||
|
||||
- **Name**: define a descriptive name, such as "Entra provider".
|
||||
@ -49,7 +49,7 @@ As detailed in the steps below, when you add an Entra ID provider in authentik y
|
||||
|
||||
1. Log in as an admin to authentik, and go to the Admin interface.
|
||||
2. In the Admin interface, navigate to **Applications -> Applications**.
|
||||
3. Click **Create**, and in the **Create Application** modal box define the following fields:
|
||||
3. Click **Create**, and define the following fields:
|
||||
|
||||
- **Name**: provide a descriptive name.
|
||||
- **Slug**: enter the name of the app as you want it to appear in the URL.
|
||||
|
||||
@ -22,7 +22,7 @@ When adding the Google Workspace provider in authentik, you must define the **Ba
|
||||
|
||||
2. In the Admin interface, navigate to **Applications -> Providers**.
|
||||
|
||||
3. Click **Create**, and select **Google Workspace Provider**, and in the **New provider** modal box, define the following fields:
|
||||
3. Click **Create**, and select **Google Workspace Provider**, and in the **New provider** box, define the following fields:
|
||||
|
||||
- **Name**: define a descriptive name, such as "GWS provider".
|
||||
|
||||
@ -53,7 +53,7 @@ When adding the Google Workspace provider in authentik, you must define the **Ba
|
||||
:::info
|
||||
If you have also configured Google Workspace to log in using authentik following [these](https://docs.goauthentik.io/integrations/services/google/index), then this configuration can be done on the same app by adding this new provider as a backchannel provider on the existing app instead of creating a new app.
|
||||
:::
|
||||
3. Click **Create**, and in the **New provider** modal box, and define the following fields:
|
||||
3. Click **Create**, and in the **New provider** box, and define the following fields:
|
||||
|
||||
- **Slug**: enter the name of the app as you want it to appear in the URL.
|
||||
- **Provider**: when _not_ used in conjunction with the Google SAML configuration should be left empty.
|
||||
|
||||
@ -23,7 +23,7 @@ For detailed instructions, refer to Google documentation.
|
||||
### Create a Google cloud project
|
||||
|
||||
1. Open the Google Cloud Console (https://cloud.google.com/cloud-console).
|
||||
2. In upper left, click the drop-down box to open the **Select a project** modal box, and then select **New Project**.
|
||||
2. In upper left, click the drop-down box to open the **Select a project** box, and then select **New Project**.
|
||||
3. Create a new project and give it a name like "authentik GWS"
|
||||
4. Use the search bar at the top of your new project page to search for "API Library".
|
||||
5. On the **API Library** page, use the search bar again to find "Admin SDK API".
|
||||
@ -44,7 +44,7 @@ For detailed instructions, refer to Google documentation.
|
||||
|
||||
1. On the **Service accounts** page, click the account that you just created.
|
||||
2. Click the **Keys** tab at top of the page, the click **Add Key -> Create new key**.
|
||||
3. In the Create modal box, select JSON as the key type, and then click **Create**.
|
||||
3. In the Create box, select JSON as the key type, and then click **Create**.
|
||||
A pop-up displays with the private key, and the key is saved to your computer as a JSON file.
|
||||
Later, when you create your authentik provider for Google Workspace, you will add this key in the **Credentials** field.
|
||||
4. On the service account page, click the **Details** tab, and expand the **Advanced settings** area.
|
||||
@ -52,7 +52,7 @@ For detailed instructions, refer to Google documentation.
|
||||
6. Log in to the Admin Console, and then navigate to **Security -> Access and data control -> API controls**.
|
||||
7. On the **API controls** page, click **Manage Domain Wide Delegation**.
|
||||
8. On the **Domain Wide Delegation** page, click **Add new**.
|
||||
9. In the **Add a new client ID** modal box, paste in the Client ID that you copied from the Admin console earlier (the value from the downloaded JSON file) and paste in the following scope documents:
|
||||
9. In the **Add a new client ID** box, paste in the Client ID that you copied from the Admin console earlier (the value from the downloaded JSON file) and paste in the following scope documents:
|
||||
- `https://www.googleapis.com/auth/admin.directory.user`
|
||||
- `https://www.googleapis.com/auth/admin.directory.group`
|
||||
- `https://www.googleapis.com/auth/admin.directory.group.member`
|
||||
|
||||
@ -11,7 +11,7 @@ Providers are the "other half" of [applications](../applications/index.md). They
|
||||
|
||||
Applications can use additional providers to augment the functionality of the main provider. For more information, see [Backchannel providers](../applications/manage_apps.md#backchannel-providers).
|
||||
|
||||
You can create a new provider in the Admin interface, or you can use the [Application wizard](../applications/manage_apps.md#instructions) to create a new application and its provider at the same time.
|
||||
You can create a new provider in the Admin interface, or you can use the [**Create with provider** option](../applications/manage_apps.md#instructions) to create a new application and its provider at the same time.
|
||||
|
||||
When you create certain types of providers, you need to select specific [flows](../flows-stages/flow/index.md) to apply to users who access authentik via the provider. To learn more, refer to our [default flow documentation](../flows-stages/flow/examples/default_flows.md).
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ Note: The `default-authentication-flow` validates MFA by default, and currently
|
||||
|
||||
### Create LDAP Application & Provider
|
||||
|
||||
1. Create the LDAP Application under _Applications_ -> _Applications_ -> _Create With Wizard_ and name it `LDAP`.
|
||||
1. Create the LDAP Application under _Applications_ -> _Applications_ -> _Create With provider_ and name it `LDAP`.
|
||||

|
||||

|
||||
|
||||
@ -55,7 +55,7 @@ Note: The `default-authentication-flow` validates MFA by default, and currently
|
||||
1. Navigate to the LDAP Provider under _Applications_ -> _Providers_ -> `Provider for LDAP`.
|
||||
2. Switch to the _Permissions_ tab.
|
||||
3. Click the _Assign to new user_ button to select a user to assign the full directory search permission to.
|
||||
4. Select the `ldapservice` user in the modal by typing in its username. Select the _Search full LDAP directory_ permission and click _Assign_
|
||||
4. Select the `ldapservice` user typing in its username. Select the _Search full LDAP directory_ permission and click _Assign_
|
||||
|
||||
### Create LDAP Outpost
|
||||
|
||||
|
||||
@ -2,13 +2,13 @@
|
||||
title: Create an OAuth2 provider
|
||||
---
|
||||
|
||||
To add a provider (and the application that uses the provider for authentication) use the Application Wizard, which creates both the new application and the required provider at the same time. For typical scenarios, authentik recommends that you use the Wizard to create both the application and the provider together. (Alternatively, use our legacy process: navigate to **Applications --> Providers**, and then click **Create**.)
|
||||
To add a provider (and the application that uses the provider for authentication) use the ** Create with provider** option, which creates both the new application and the required provider at the same time. For typical scenarios, authentik recommends that you create both the application and the provider together. (Alternatively, use our legacy process: navigate to **Applications --> Providers**, and then click **Create**.)
|
||||
|
||||
1. Log into authentik as an admin, and navigate to **Applications --> Applications**.
|
||||
1. Log in to authentik as an admin, and open the authentik Admin interface.
|
||||
|
||||
2. Click **Create with Wizard**.
|
||||
2. Navigate to **Applications -> Applications** and click **Create with provider** to create an application and provider pair. (Alternatively you can create only an application, without a provider, by clicking **Create**.)
|
||||
|
||||
3. In the **New application** wizard, define the application details, and then click **Next**.
|
||||
3. In the **New application** box, define the application details, and then click **Next**.
|
||||
|
||||
4. Select the **Provider Type** of **OAuth2/OIDC**, and then click **Next**.
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ The first step is to create the RAC app and provider.
|
||||
|
||||
2. In the Admin interface, navigate to **Applications -> Applications**.
|
||||
|
||||
3. Click **Create with Wizard**. Follow the [instructions](../../applications/manage_apps.md#instructions) to create your RAC application and provider.
|
||||
3. Click **Create with provider**. Follow the [instructions](../../applications/manage_apps.md#instructions) to create your RAC application and provider.
|
||||
|
||||
### Step 2. Create RAC property mapping
|
||||
|
||||
@ -36,7 +36,7 @@ Next, you need to add a property mapping for each of the remote machines you wan
|
||||
|
||||
2. On the **Property Mappings** page, click **Create**.
|
||||
|
||||
3. On the **New property mapping** modal, set the following:
|
||||
3. On the **New property mapping** box, set the following:
|
||||
|
||||
- **Select Type**: RAC Property Mappings
|
||||
- **Create RAC Property Mapping**:
|
||||
@ -52,7 +52,7 @@ Next, you need to add a property mapping for each of the remote machines you wan
|
||||
- Advanced settings:
|
||||
- **Expressions**: optional, using Python you can define custom [expressions](../property-mappings/expression.mdx).
|
||||
|
||||
4. Click **Finish** to save your settings and close the modal.
|
||||
4. Click **Finish** to save your settings and close the box.
|
||||
|
||||
### Step 3. Create Endpoints for the Provider
|
||||
|
||||
@ -64,7 +64,7 @@ Finally, you need to create an endpoint for each remote machine. Endpoints are d
|
||||
|
||||
3. On the Provider page, under **Endpoints**, click **Create**.
|
||||
|
||||
4. On the **Create Endpoint** modal, provide the following settings:
|
||||
4. On the **Create Endpoint** box, provide the following settings:
|
||||
|
||||
- **Name**: define a name for the endpoint, perhaps include the type of connection (RDP, SSH, VNC)
|
||||
- **Protocol**: select the appropriate protocol
|
||||
@ -73,7 +73,7 @@ Finally, you need to create an endpoint for each remote machine. Endpoints are d
|
||||
- **Property mapping**: select either the property mapping that you created in Step 2, or use one of the default settings.
|
||||
- **Advance settings**: optional
|
||||
|
||||
5. Click **Create** to save your settings and close the modal.
|
||||
5. Click **Create** to save your settings and close the box.
|
||||
|
||||
### Access the remote machine
|
||||
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: Configure an SSF provider
|
||||
---
|
||||
|
||||
The workflow to implement an SSF provider as a [backchannel provider](../../applications/manage_apps#backchannel-providers) for an application/provider pair is as follows:
|
||||
|
||||
1. Create the SSF provider (which serves as the backchannel provider).
|
||||
2. Create an OIDC provider (which serves as the protocol provider for the application).
|
||||
3. Create the application, and assign both the OIDC provider and the SSF provider.
|
||||
|
||||
## Create the SSF provider
|
||||
|
||||
1. Log in to authentik as an admin, and in the Admin interface navigate to **Applications -> Providers**.
|
||||
|
||||
2. Click **Create**.
|
||||
|
||||
3. In the modal, select the **Provider Type** of **SSF**, and then click **Next**.
|
||||
|
||||
4. On the **New provider** page, provide the configuration settings. Be sure to select a **Signing Key**.
|
||||
|
||||
5. Click **Finish** to create and save the provider.
|
||||
|
||||
## Create the OIDC provider
|
||||
|
||||
1. Log in to authentik as an admin, and in the Admin interface navigate to **Applications -> Providers**.
|
||||
|
||||
2. Click **Create**.
|
||||
|
||||
3. In the modal, select the **Provider Type** of **OIDC**, and then click **Next**.
|
||||
|
||||
4. Define the settings for the provider, and then click **Finish** to save the new provider.
|
||||
|
||||
## Create the application
|
||||
|
||||
1. Log in to authentik as an admin, and in the Admin interface navigate to **Applications -> Applications**.
|
||||
|
||||
2. Click **Create**.
|
||||
|
||||
3. Define the settings for the application:
|
||||
|
||||
- **Name**: define a descriptive name ofr the application.
|
||||
- **Slug**: optionally define the internal application name used in URLs.
|
||||
- **Group**: optionally select a group that you want to have access to this application.
|
||||
- **Provider**: select the OIDC provider that you created.
|
||||
- **Backchannel Providers**: select the SSF provider you created.
|
||||
- **Policy engine mode**: define policy-based access.
|
||||
- **UI Settings**: optionally define a launch URL, an icon, and other UI elements.
|
||||
|
||||
4. Click **Create** to save the new application.
|
||||
|
||||
The new application, with its OIDC provider and the backchannel SFF provider, should now appear in your list of Applications.
|
||||
48
website/docs/add-secure-apps/providers/ssf/index.md
Normal file
48
website/docs/add-secure-apps/providers/ssf/index.md
Normal file
@ -0,0 +1,48 @@
|
||||
---
|
||||
title: Shared Signals Framework (SSF) Provider
|
||||
sidebar_label: SSF Provider
|
||||
---
|
||||
|
||||
<span class="badge badge--preview">Preview</span>
|
||||
<span class="badge badge--version">authentik 2025.2+</span>
|
||||
|
||||
|
||||
Shared Signals Framework (SSF) is a common standard for sharing asynchronous real-time security signals and events across multiple applications and an identity provider. The framework is a collection of standards and communication processes, documented in a [specification](https://openid.net/specs/openid-sharedsignals-framework-1_0-ID3.html). SSF leverages the APIs of the application and the IdP, using privacy-protected, secure webhooks.
|
||||
|
||||
## About Shared Signals Framework
|
||||
|
||||
In authentik, an SSF provider allows applications to subscribe to certain types of security signals (which are then translated into SETs, or Security Event Tokens) that are captured by authentik (the IdP), and then the application can respond to each event. In this scenario, authentik acts as the _transmitter_ and the application acts as the _receiver_ of the events.
|
||||
|
||||
Events in authentik that are tracked via SSF include when an MFA device is added or removed, logouts, sessions being revoked by Admin or user clicking logout, or credentials changed.
|
||||
|
||||
## Example use cases
|
||||
|
||||
A common use case for SSF is when an Admin wants to know if a user logs out of authentik, so that the user is then also automaticlaly logged out of all other work-focused applications.
|
||||
|
||||
Another example use case is when an application uses SSF to subscribe to authorization events because the application needs to know if a user changed their password in authentik. If a user did change their password, then the application receives a POST request to write the fact that the password was changed.
|
||||
|
||||
## About using SSF in authentik
|
||||
|
||||
Let's look at a few details about using SSF in authentik.
|
||||
|
||||
The SSF provider in authentik serves as a [backchannel provider](../../applications/manage_apps#backchannel-providers). Backchannel providers are used to augment the functionality of the main provider for an application. Thus you will still need to [create a typical application/provider pair](../../applications/manage_apps#instructions) (using an OIDC provider), and when creating the application, assign the SSF provider as a backchannel provider.
|
||||
|
||||
When an authentik Admin [creates an SSF provider](./create-ssf-provider), they need to configure both the application (the receiver) and authentik (the IdP and the transmitter).
|
||||
|
||||
### The application (the receiver)
|
||||
|
||||
Within the application, the admin creates an SSF stream (which comprises all the signals that the app wants to subscribe to) and defines the audience, called `aud` in the specification (the URL that identifies the stream). A stream is basically an API request to authentik, which asks for a POST of all events. How that request is sent varies from application to application. An application can change or delete the stream.
|
||||
|
||||
Note that authentik doesn't specify which events to subscribe to; instead the application defines which they want to listen for.
|
||||
|
||||
### authentik (the transmitter)
|
||||
|
||||
To configure authentik as a shared signals transmitter, the authentik Admin [creates a new provider](./create-ssf-provider), selecting the type "SSF", to serve as the backchannelprovider for the application.
|
||||
|
||||
When creating the SSF provider you will need to select a signing key. This is the key that the Security Event Tokens (SET) is signed with.
|
||||
|
||||
Optionally, you can specify a event retention time period: this value determines how long events are stored for. If an event could not be sent correctly, and retries occur, the event's expiration is also increased by this duration.
|
||||
|
||||
:::info
|
||||
Be aware that the SET events are different events than those displayed in the authentik Admin interface under **Events**.
|
||||
:::
|
||||
@ -26,6 +26,22 @@ See [Expression Policy](./expression.mdx).
|
||||
|
||||
Use this policy for simple GeoIP lookups, such as country or ASN matching. (For a more advanced GeoIP lookup, use an [Expression policy](./expression.mdx).)
|
||||
|
||||
With the GeoIP policy, you can use the **Distance Settings** options to set travel "expectations" and control login attempts based on GeoIP location. The GeoIP policy calculates the values defined for travel distances (in kilometers), and then either passes or fails based on the results. If the GeoIP policy failed, the current login attempt is not allowed.
|
||||
|
||||
- **Maximum distance**: define the allowed maximum distance between a login's initial GeoIP location and the GeoIP location of a subsequent login attempt.
|
||||
|
||||
- **Distance tolerance**: optionally, add an additional "tolerance" distance. This value is added to the **Maximum distance** value, then the total is used in the calculations that determine if the policy fails or passes.
|
||||
|
||||
- **Historical Login Count**: define the number of login events that you want to use for the distance calculations. For example, with the default value of 5, the policy will check the distance between each of the past 5 login attempts, and if any of those distances exceed the **Maximum distance** PLUS the **Distance tolerance**, then the policy will fail and the current login attempt will not be allowed.
|
||||
|
||||
- **Check impossible travel**: this option, when enabled, provides an additional layer of calculations to the policy. With Impossible travel, a built-in value of 1,000 kilometers is used as the base distance. This distance, PLUS the value defined for **Impossible travel tolerance**, is the maximum allowed distance for the policy to pass. Note that the value defined in **Historical Login Count** (the number of login events to check) is also used for Impossible travel calculations.
|
||||
|
||||
- **Impossible travel tolerance**: optionally, you can add an additional "tolerance" distance. This value is added to the built-in allowance of 1000 kilometers per hour, then the total is used in the calculations that run against each of the login events (to determine if the travel would have been possible in the amount of time since the previous login event) to determine if the policy fails or passes.
|
||||
|
||||
:::info
|
||||
GeoIP is included in every release of authentik and does not require any additional setup for creating GeoIP policies. For information about advanced uses (configuring your own database, etc.) and system management of GeoIP data, refer to our [GeoIP documentation](../../sys-mgmt/ops/geoip.mdx).
|
||||
:::
|
||||
|
||||
### Password-Expiry Policy
|
||||
|
||||
This policy can enforce regular password rotation by expiring set passwords after a finite amount of time. This forces users to set a new password.
|
||||
|
||||
@ -8,7 +8,7 @@ authentik provides several [standard policy types](./index.md#standard-policies)
|
||||
|
||||
We also document how to use a policy to [whitelist email domains](./expression/whitelist_email.md) and to [ensure unique email addresses](./expression/unique_email.md).
|
||||
|
||||
To learn more see also [bindings](../../add-secure-apps/flows-stages/bindings/index.md) and how to use the [authentik Wizard to bind policy bindings to the new application](../../add-secure-apps/applications/manage_apps.md#add-new-applications) (for example, to configure application-specific access).
|
||||
To learn more see also [bindings](../../add-secure-apps/flows-stages/bindings/index.md) and how to [bind policy bindings to a new application when yo create the application](../../add-secure-apps/applications/manage_apps.md#instructions) (for example, to configure application-specific access).
|
||||
|
||||
## Create a policy
|
||||
|
||||
|
||||
@ -20,6 +20,12 @@ To try out the release candidate, replace your Docker image tag with the latest
|
||||
|
||||
## Breaking changes
|
||||
|
||||
- **Fixed behaviour in Source stage <span class="badge badge--primary">Enterprise</span>**
|
||||
|
||||
In previous versions, the Source stage would incorrectly continue with the initial flow after returning from the source, which didn't match the documented behaviour.
|
||||
|
||||
With this release this behaviour has been corrected and the source stage will now correctly run the selected enrollment/authentication flow before returning to the flow from which the source stage was executed.
|
||||
|
||||
- **Deprecated and frozen `:latest` container image tag after 2025.2**
|
||||
|
||||
Using the `:latest` tag with container images is not recommended as it can lead to unintentional updates and potentially broken setups.
|
||||
|
||||
@ -51,12 +51,12 @@ To assign or remove _object_ permissions for a specific user:
|
||||
1. Click the **User Object Permissions** tab, and then click **Assign to new user**.
|
||||
2. In the **User** drop-down, select the user object.
|
||||
3. Use the toggles to set which permissions on that selected user object you want to grant to (or remove from) the specific user.
|
||||
4. Click **Assign** to save your settings and close the modal.
|
||||
4. Click **Assign** to save your settings and close the box.
|
||||
5. To assign or remove permissions that another _role_ has on this specific user:
|
||||
1. Click the **Role Object Permissions** tab, and then click **Assign to new role**.
|
||||
2. In the **User** drop-down, select the user object.
|
||||
3. Use the toggles to set which permissions you want to grant to (or remove from) the selected role.
|
||||
4. Click **Assign** to save your settings and close the modal.
|
||||
4. Click **Assign** to save your settings and close the box.
|
||||
|
||||
To assign or remove _global_ permissions for a user:
|
||||
|
||||
@ -65,8 +65,8 @@ To assign or remove _global_ permissions for a user:
|
||||
3. Click the **Permissions** tab at the top of the page.
|
||||
4. Click **Assigned Global Permissions** to the left.
|
||||
5. In the **Assign permissions** area, click **Assign Permission**.
|
||||
6. In the **Assign permission to user** modal box, click the plus sign (**+**) and then click the checkbox beside each permission that you want to assign to the user. To remove permissions, deselect the checkbox.
|
||||
7. Click **Add**, and then click **Assign** to save your changes and close the modal.
|
||||
6. In the **Assign permission to user** box, click the plus sign (**+**) and then click the checkbox beside each permission that you want to assign to the user. To remove permissions, deselect the checkbox.
|
||||
7. Click **Add**, and then click **Assign** to save your changes and close the box.
|
||||
|
||||
### Assign or remove permissions on a specific group
|
||||
|
||||
@ -84,12 +84,12 @@ To assign or remove _object_ permissions on a specific group by users and roles:
|
||||
1. Click **User Object Permissions** to the left, and then click **Assign to new user**.
|
||||
2. In the **User** drop-down, select the user object.
|
||||
3. Use the toggles to set which permissions on that selected group you want to grant to (or remove from) the specific user.
|
||||
4. Click **Assign** to save your settings and close the modal.
|
||||
4. Click **Assign** to save your settings and close the box.
|
||||
4. To assign or remove permissions that another _role_ has on this specific group:
|
||||
1. Click **Role Object Permissions** to the left, and then click **Assign to new role**.
|
||||
2. In the **Role** drop-down, select the role.
|
||||
3. Use the toggles to set which permissions you want to grant to (or remove from ) the selected role.
|
||||
4. Click **Assign** to save your settings and close the modal.
|
||||
4. Click **Assign** to save your settings and close the box.
|
||||
|
||||
### Assign or remove permissions for a specific role
|
||||
|
||||
@ -102,12 +102,12 @@ To assign or remove _object_ permissions for a specific role:
|
||||
1. Click **User Object Permissions** to the left, and then click **Assign to new user**.
|
||||
2. In the **User** drop-down, select the user object.
|
||||
3. Use the toggles to set which permissions on that role you want to grant to (or remove from) the selected user.
|
||||
4. Click **Assign** to save your settings and close the modal.
|
||||
4. Click **Assign** to save your settings and close the box.
|
||||
4. To assign or remove permissions that another _role_ has on this specific group:
|
||||
1. Click **Role Object Permissions** to the left, and then click **Assign to new role**.
|
||||
2. In the **Role** drop-down, select the role.
|
||||
3. Use the toggles to set which permissions you want to grant to (or remove from) the selected role.
|
||||
4. Click **Assign** to save your settings and close the modal.
|
||||
4. Click **Assign** to save your settings and close the box.
|
||||
|
||||
To assign or remove _global_ permissions for a role:
|
||||
|
||||
@ -115,8 +115,8 @@ To assign or remove _global_ permissions for a role:
|
||||
2. Select a specific role by clicking on the role's name.
|
||||
3. Click the **Permissions** tab at the top of the page.
|
||||
4. Click **Assigned Global Permissions** to the left, and then click **Assign Permission**.
|
||||
5. In the **Assign permissions to role** modal, click the plus sign (**+**) and then click the checkbox beside each permission that you want to assign to the role. To remove permissions, deselect the checkbox.
|
||||
6. Click **Assign** to save your changes and close the modal.
|
||||
5. In the **Assign permissions to role** box, click the plus sign (**+**) and then click the checkbox beside each permission that you want to assign to the role. To remove permissions, deselect the checkbox.
|
||||
6. Click **Assign** to save your changes and close the box.
|
||||
|
||||
### Assign or remove flow permissions
|
||||
|
||||
@ -129,4 +129,4 @@ To assign or remove _global_ permissions for a role:
|
||||
|
||||
1. Go to the Admin interface and navigate to **Flows and Stages -> Stagess**.
|
||||
2. On the row for the specific stage that you want to manage permissions, click the **lock icon**.
|
||||
3. On the **Update Permissions** modal window, you can add or remove the assigned permissions using the **User Object Permissions** and the **Role Object Permissions** tabs.
|
||||
3. On the **Update Permissions** box, you can add or remove the assigned permissions using the **User Object Permissions** and the **Role Object Permissions** tabs.
|
||||
|
||||
@ -11,7 +11,7 @@ To create a new group, follow these steps:
|
||||
|
||||
1. In the Admin interface, navigate to **Directory > Groups**.
|
||||
2. Click **Create** at the top of the Groups page.
|
||||
3. In the Create modal, define the following:
|
||||
3. In the Create box, define the following:
|
||||
- **Name** of the group
|
||||
- Whether or not users in that group will all be **super-users** (means anyone in that group has all permissions on everything)
|
||||
- The **Parent** group
|
||||
@ -25,9 +25,11 @@ To create a super-user, you need to add the user to a group that has super-user
|
||||
|
||||
## Modify a group
|
||||
|
||||
To edit the group's name, parent group, whether or not the group is for superusers, associated roles, and any custom attributes, click the Edit icon beside the role's name. Make the changes, and then click **Update**.
|
||||
To edit the group's name, parent group, whether the group grants superuser permissions, associated roles, and any custom attributes, click the Edit icon beside the role's name. Make the changes and then click **Update**.
|
||||
|
||||
To [add or remove users](../user/user_basic_operations.md#add-a-user-to-a-group) from the group, or to manage permissions assigned to the group, click on the name of the group to go to the group's detail page.
|
||||
Starting with authentik version 2025.2, the permission to change super-user status has been separated from the permission required to change the group. Now, the `Enable superuser status` and `Disable superuser status` permissions are explicitly required to enable and disable the super-user status.
|
||||
|
||||
To [add or remove users](../user/user_basic_operations.md#add-a-user-to-a-group) from the group, or to manage permissions assigned to the group, click on the name of the group to go to the group's detail page and then click on the **Permissions** tab.
|
||||
|
||||
For more information about permissions, refer to [Assign or remove permissions for a specific group](../access-control/manage_permissions.md#assign-or-remove-permissions-on-a-specific-group).
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ In authentik, we assign roles to groups, not to individual users.
|
||||
To create a new role, follow these steps:
|
||||
|
||||
1. In the Admin interface, navigate to **Directory > Roles**.
|
||||
2. Click **Create**, enter the name of the role, and then click **Create** in the modal.
|
||||
2. Click **Create**, enter the name of the role, and then click **Create** in the box.
|
||||
3. Next, [assign permissions to the role](../access-control/manage_permissions.md#assign-or-remove-permissions-for-a-specific-role).
|
||||
|
||||
## Modify a role
|
||||
@ -44,5 +44,5 @@ In authentik, each role can only be applied to a single group at a time.
|
||||
1. To assign the role to a group, navigate to **Directory -> Groups**.
|
||||
2. Click the name of the group to which you want to add a role.
|
||||
3. On the group's detail page, on the Overview tab, click **Edit** in the **Group Info** area.
|
||||
4. On the **Update Group** modal, in the **Roles** field, select the roles you want to assign to the group from the list of **Available Roles** in the left box (you can select multiple roles at once by holding the Shift key while selecting the roles), and then click the appropriate arrow icon to move them into the **Selected Roles** box.
|
||||
5. Click **Update** to add the role(s) and close the modal.
|
||||
4. On the **Update Group** box, in the **Roles** field, select the roles you want to assign to the group from the list of **Available Roles** in the left box (you can select multiple roles at once by holding the Shift key while selecting the roles), and then click the appropriate arrow icon to move them into the **Selected Roles** box.
|
||||
5. Click **Update** to add the role(s) and close the box.
|
||||
|
||||
@ -8,7 +8,7 @@ The base SCIM URL is in the format of `https://authentik.company/source/scim/<so
|
||||
|
||||
## First steps
|
||||
|
||||
To set up an SCIM source, log in as an administrator into authentik. Navigate to **Directory->Federation & Social login**, and click on **Create**. Select the **SCIM Source** type in the wizard, and give the source a name.
|
||||
To set up an SCIM source, log in as an administrator into authentik. Navigate to **Directory->Federation & Social login**, and click on **Create**. Select the **SCIM Source** type, and give the source a name.
|
||||
|
||||
After the source is created, click on the name of the source in the list, and you will see the **SCIM Base URL** which is used by the SCIM client. Use the **Click to copy token** button to copy the token which is used by the client to authenticate SCIM requests.
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ Finally, you need to publish the Facebook app.
|
||||
|
||||
1. Log into authentik as admin, and then navigate to **Directory -> Federation & Social login**
|
||||
2. Click **Create**.
|
||||
3. In the **New Source** modal box, for **Select type** select **Facebook OAuth Source** and then click **Next**.
|
||||
3. In the **New Source** box, for **Select type** select **Facebook OAuth Source** and then click **Next**.
|
||||
4. Define the following fields:
|
||||
- **Name**: provide a descriptive name
|
||||
- **Slug**: leave default value (If you choose a different slug then the default, the URL will need to be updated to reflect the change)
|
||||
@ -65,7 +65,7 @@ Finally, you need to publish the Facebook app.
|
||||
- **Flow settings**
|
||||
- **Authentication flow**: leave the default `default-source-authentication` option.
|
||||
- **Enrollment flow**: leave the default `default-source-enrollment` option.
|
||||
5. Click **Finish** to save your settings and close the modal box.
|
||||
5. Click **Finish** to save your settings and close the box.
|
||||
|
||||
You now have Facebook as a source. Verify by checking that appears on the **Directory -> Federation & Social login** page in authentik.
|
||||
|
||||
|
||||
@ -138,7 +138,7 @@ Start by logging into your authentik instance as an administrator and navigating
|
||||
|
||||
In the Admin interface, navigate to **Directory -> Federation & Social login** and press **Create**.
|
||||
|
||||
In the **New source** modal, choose **SAML Source** and continue by filling in the following fields:
|
||||
In the **New source** box, choose **SAML Source** and continue by filling in the following fields:
|
||||
|
||||
| Field | Value |
|
||||
| ----- | ---------------- |
|
||||
|
||||
@ -31,7 +31,7 @@ At the top of the Flows page, click **Import**, and then select the `flows-enrol
|
||||
|
||||
**Step 3. Create the invitation object**
|
||||
|
||||
In the Admin UI, navigate to **Directory --> Invitations**, and then click **Create** to open the **Create Invitation** modal. Define the following fields:
|
||||
In the Admin UI, navigate to **Directory --> Invitations**, and then click **Create** to open the **Create Invitation** box. Define the following fields:
|
||||
|
||||
- **Name**: provide a name for your invitation object.
|
||||
- **Expires**: select a date for when you want the invitation to expire.
|
||||
@ -42,7 +42,7 @@ In the Admin UI, navigate to **Directory --> Invitations**, and then click **Cre
|
||||
|
||||
- **Single use**: specify whether or not you want the invitation to expire after a single use.
|
||||
|
||||
Click **Save** to save the new invitation and close the modal and return to the **Invitations** page.
|
||||
Click **Save** to save the new invitation and close the box and return to the **Invitations** page.
|
||||
|
||||
**Step 3. Email the invitation**
|
||||
|
||||
|
||||
@ -27,11 +27,12 @@ This documentation lists only the settings that you need to change from their de
|
||||
## authentik configuration
|
||||
|
||||
1. From the authentik Admin interface navigate to **Applications** -> **Applications** on the left sidebar.
|
||||
2. Create an application and an OAuth2/OpenID provider using the [wizard](https://docs.goauthentik.io/docs/add-secure-apps/applications/manage_apps#add-new-applications).
|
||||
|
||||
2. Create an application and an OAuth2/OpenID provider using the [Application modal](https://docs.goauthentik.io/docs/add-secure-apps/applications/manage_apps#instructions).
|
||||
- Note the application slug, client ID, and client secret, as they will be required later.
|
||||
- Set a strict redirect URI to `https://chronograf.company/oauth/authentik/callback`.
|
||||
- Choose a signing key (any available key is acceptable).
|
||||
3. Complete and submit the settings to close the wizard.
|
||||
3. Complete and submit the settings to close the modal.
|
||||
|
||||
## Chronograf configuration
|
||||
|
||||
|
||||
@ -200,8 +200,6 @@ export default {
|
||||
"add-secure-apps/providers/oauth2/github-compatibility",
|
||||
],
|
||||
},
|
||||
"add-secure-apps/providers/saml/index",
|
||||
"add-secure-apps/providers/radius/index",
|
||||
{
|
||||
type: "category",
|
||||
label: "Proxy Provider",
|
||||
@ -228,7 +226,6 @@ export default {
|
||||
},
|
||||
],
|
||||
},
|
||||
"add-secure-apps/providers/scim/index",
|
||||
{
|
||||
type: "category",
|
||||
label: "RAC (Remote Access Control) Provider",
|
||||
@ -238,6 +235,20 @@ export default {
|
||||
},
|
||||
items: ["add-secure-apps/providers/rac/how-to-rac"],
|
||||
},
|
||||
"add-secure-apps/providers/radius/index",
|
||||
"add-secure-apps/providers/saml/index",
|
||||
"add-secure-apps/providers/scim/index",
|
||||
{
|
||||
type: "category",
|
||||
label: "SSF Provider",
|
||||
link: {
|
||||
type: "doc",
|
||||
id: "add-secure-apps/providers/ssf/index",
|
||||
},
|
||||
items: [
|
||||
"add-secure-apps/providers/ssf/create-ssf-provider",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -286,11 +297,12 @@ export default {
|
||||
items: [
|
||||
"add-secure-apps/flows-stages/stages/authenticator_duo/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_endpoint_gdtc/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_email/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_sms/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_static/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_totp/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_validate/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_webauthn/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_validate/index",
|
||||
"add-secure-apps/flows-stages/stages/captcha/index",
|
||||
"add-secure-apps/flows-stages/stages/deny",
|
||||
"add-secure-apps/flows-stages/stages/email/index",
|
||||
|
||||
Reference in New Issue
Block a user