From 6f3fc22c9b96e34b2ff0b5969709b91b9a4c42e4 Mon Sep 17 00:00:00 2001
From: Jens L
Date: Mon, 9 Oct 2023 00:08:16 +0200
Subject: [PATCH] providers/saml: add default RelayState value for
IDP-initiated requests (#7100)
Signed-off-by: Jens Langhammer
---
authentik/providers/saml/api/providers.py | 1 +
.../0013_samlprovider_default_relay_state.py | 21 +++
authentik/providers/saml/models.py | 4 +
.../saml/processors/authn_request_parser.py | 5 +-
.../saml/tests/test_auth_n_request.py | 8 +
blueprints/schema.json | 155 +++---------------
schema.yml | 13 ++
.../admin/providers/saml/SAMLProviderForm.ts | 18 ++
web/xliff/de.xlf | 13 +-
web/xliff/en.xlf | 13 +-
web/xliff/es.xlf | 13 +-
web/xliff/fr.xlf | 14 +-
web/xliff/pl.xlf | 13 +-
web/xliff/pseudo-LOCALE.xlf | 13 +-
web/xliff/tr.xlf | 13 +-
web/xliff/zh-Hans.xlf | 14 +-
web/xliff/zh-Hant.xlf | 13 +-
web/xliff/zh_TW.xlf | 13 +-
18 files changed, 182 insertions(+), 175 deletions(-)
create mode 100644 authentik/providers/saml/migrations/0013_samlprovider_default_relay_state.py
diff --git a/authentik/providers/saml/api/providers.py b/authentik/providers/saml/api/providers.py
index 891289a08..226ec7e58 100644
--- a/authentik/providers/saml/api/providers.py
+++ b/authentik/providers/saml/api/providers.py
@@ -146,6 +146,7 @@ class SAMLProviderSerializer(ProviderSerializer):
"signing_kp",
"verification_kp",
"sp_binding",
+ "default_relay_state",
"url_download_metadata",
"url_sso_post",
"url_sso_redirect",
diff --git a/authentik/providers/saml/migrations/0013_samlprovider_default_relay_state.py b/authentik/providers/saml/migrations/0013_samlprovider_default_relay_state.py
new file mode 100644
index 000000000..5ece7f52d
--- /dev/null
+++ b/authentik/providers/saml/migrations/0013_samlprovider_default_relay_state.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.6 on 2023-10-08 20:29
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("authentik_providers_saml", "0012_managed"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="samlprovider",
+ name="default_relay_state",
+ field=models.TextField(
+ blank=True,
+ default="",
+ help_text="Default relay_state value for IDP-initiated logins",
+ ),
+ ),
+ ]
diff --git a/authentik/providers/saml/models.py b/authentik/providers/saml/models.py
index e92f62943..1a706df8e 100644
--- a/authentik/providers/saml/models.py
+++ b/authentik/providers/saml/models.py
@@ -138,6 +138,10 @@ class SAMLProvider(Provider):
verbose_name=_("Signing Keypair"),
)
+ default_relay_state = models.TextField(
+ default="", blank=True, help_text=_("Default relay_state value for IDP-initiated logins")
+ )
+
@property
def launch_url(self) -> Optional[str]:
"""Use IDP-Initiated SAML flow as launch URL"""
diff --git a/authentik/providers/saml/processors/authn_request_parser.py b/authentik/providers/saml/processors/authn_request_parser.py
index 30eddf0d3..5587cb695 100644
--- a/authentik/providers/saml/processors/authn_request_parser.py
+++ b/authentik/providers/saml/processors/authn_request_parser.py
@@ -175,4 +175,7 @@ class AuthNRequestParser:
def idp_initiated(self) -> AuthNRequest:
"""Create IdP Initiated AuthNRequest"""
- return AuthNRequest()
+ relay_state = None
+ if self.provider.default_relay_state != "":
+ relay_state = self.provider.default_relay_state
+ return AuthNRequest(relay_state=relay_state)
diff --git a/authentik/providers/saml/tests/test_auth_n_request.py b/authentik/providers/saml/tests/test_auth_n_request.py
index 69326bd5f..df19eb736 100644
--- a/authentik/providers/saml/tests/test_auth_n_request.py
+++ b/authentik/providers/saml/tests/test_auth_n_request.py
@@ -8,6 +8,7 @@ from authentik.blueprints.tests import apply_blueprint
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.crypto.models import CertificateKeyPair
from authentik.events.models import Event, EventAction
+from authentik.lib.generators import generate_id
from authentik.lib.tests.utils import get_request
from authentik.providers.saml.models import SAMLPropertyMapping, SAMLProvider
from authentik.providers.saml.processors.assertion import AssertionProcessor
@@ -264,3 +265,10 @@ class TestAuthNRequest(TestCase):
events.first().context["message"],
"Failed to evaluate property-mapping: 'test'",
)
+
+ def test_idp_initiated(self):
+ """Test IDP-initiated login"""
+ self.provider.default_relay_state = generate_id()
+ request = AuthNRequestParser(self.provider).idp_initiated()
+ self.assertEqual(request.id, None)
+ self.assertEqual(request.relay_state, self.provider.default_relay_state)
diff --git a/blueprints/schema.json b/blueprints/schema.json
index 7f7757a28..9dc77b796 100644
--- a/blueprints/schema.json
+++ b/blueprints/schema.json
@@ -4826,6 +4826,11 @@
],
"title": "Service Provider Binding",
"description": "This determines how authentik sends the response back to the Service Provider."
+ },
+ "default_relay_state": {
+ "type": "string",
+ "title": "Default relay state",
+ "description": "Default relay_state value for IDP-initiated logins"
}
},
"required": []
@@ -7427,146 +7432,32 @@
"model_authentik_stages_invitation.invitation": {
"type": "object",
"properties": {
+ "name": {
+ "type": "string",
+ "maxLength": 50,
+ "minLength": 1,
+ "pattern": "^[-a-zA-Z0-9_]+$",
+ "title": "Name"
+ },
"expires": {
"type": "string",
"format": "date-time",
"title": "Expires"
},
- "user": {
+ "fixed_data": {
"type": "object",
- "properties": {
- "username": {
- "type": "string",
- "maxLength": 150,
- "minLength": 1,
- "title": "Username"
- },
- "name": {
- "type": "string",
- "title": "Name",
- "description": "User's display name."
- },
- "is_active": {
- "type": "boolean",
- "title": "Active",
- "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts."
- },
- "last_login": {
- "type": [
- "string",
- "null"
- ],
- "format": "date-time",
- "title": "Last login"
- },
- "groups": {
- "type": "array",
- "items": {
- "type": "integer"
- },
- "title": "Groups"
- },
- "email": {
- "type": "string",
- "format": "email",
- "maxLength": 254,
- "title": "Email address"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true,
- "title": "Attributes"
- },
- "path": {
- "type": "string",
- "minLength": 1,
- "title": "Path"
- },
- "type": {
- "type": "string",
- "enum": [
- "internal",
- "external",
- "service_account",
- "internal_service_account"
- ],
- "title": "Type"
- }
- },
- "required": [
- "username",
- "name"
- ],
- "title": "User"
+ "additionalProperties": true,
+ "title": "Fixed data"
},
- "application": {
- "type": "object",
- "properties": {
- "name": {
- "type": "string",
- "minLength": 1,
- "title": "Name",
- "description": "Application's display Name."
- },
- "slug": {
- "type": "string",
- "maxLength": 50,
- "minLength": 1,
- "pattern": "^[-a-zA-Z0-9_]+$",
- "title": "Slug",
- "description": "Internal application name, used in URLs."
- },
- "provider": {
- "type": "integer",
- "title": "Provider"
- },
- "backchannel_providers": {
- "type": "array",
- "items": {
- "type": "integer"
- },
- "title": "Backchannel providers"
- },
- "open_in_new_tab": {
- "type": "boolean",
- "title": "Open in new tab",
- "description": "Open launch URL in a new browser tab or window."
- },
- "meta_launch_url": {
- "type": "string",
- "title": "Meta launch url"
- },
- "meta_description": {
- "type": "string",
- "title": "Meta description"
- },
- "meta_publisher": {
- "type": "string",
- "title": "Meta publisher"
- },
- "policy_engine_mode": {
- "type": "string",
- "enum": [
- "all",
- "any"
- ],
- "title": "Policy engine mode"
- },
- "group": {
- "type": "string",
- "title": "Group"
- }
- },
- "required": [
- "name",
- "slug"
- ],
- "title": "Application"
+ "single_use": {
+ "type": "boolean",
+ "title": "Single use",
+ "description": "When enabled, the invitation will be deleted after usage."
},
- "permissions": {
- "type": "string",
- "minLength": 1,
- "title": "Permissions"
+ "flow": {
+ "type": "integer",
+ "title": "Flow",
+ "description": "When set, only the configured flow can use this invitation."
}
},
"required": []
diff --git a/schema.yml b/schema.yml
index 5b682a7ff..1d36b4530 100644
--- a/schema.yml
+++ b/schema.yml
@@ -16292,6 +16292,10 @@ paths:
schema:
type: string
format: uuid
+ - in: query
+ name: default_relay_state
+ schema:
+ type: string
- in: query
name: digest_algorithm
schema:
@@ -36303,6 +36307,9 @@ components:
* `redirect` - Redirect
* `post` - Post
+ default_relay_state:
+ type: string
+ description: Default relay_state value for IDP-initiated logins
PatchedSAMLSourceRequest:
type: object
description: SAMLSource Serializer
@@ -38480,6 +38487,9 @@ components:
* `redirect` - Redirect
* `post` - Post
+ default_relay_state:
+ type: string
+ description: Default relay_state value for IDP-initiated logins
url_download_metadata:
type: string
description: Get metadata download URL
@@ -38624,6 +38634,9 @@ components:
* `redirect` - Redirect
* `post` - Post
+ default_relay_state:
+ type: string
+ description: Default relay_state value for IDP-initiated logins
required:
- acs_url
- authorization_flow
diff --git a/web/src/admin/providers/saml/SAMLProviderForm.ts b/web/src/admin/providers/saml/SAMLProviderForm.ts
index e74fa76b3..012abdcae 100644
--- a/web/src/admin/providers/saml/SAMLProviderForm.ts
+++ b/web/src/admin/providers/saml/SAMLProviderForm.ts
@@ -318,6 +318,24 @@ export class SAMLProviderFormPage extends ModelForm {
+
+
+
+ ${msg(
+ "When using IDP-initiated logins, the relay state will be set to this value.",
+ )}
+
+
+
Validate SSL Certificates of upstream servers.
SSL-Zertifikate der Upstream-Server prüfen.
-
- Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a manged outpost, this is done for you).
- Verwenden Sie diesen Provider mit auth_request von Nginx oder forwardAuth von Traefik. Jede Anwendung/Domäne benötigt ihren eigenen Provider. Zusätzlich muss auf jeder Domain /outpost.goauthentik.io an den Außenposten weitergeleitet werden (wenn Sie einen gemanagten Außenposten verwenden, wird dies für Sie erledigt).
-
Use this provider with nginx's auth_request or traefik's forwardAuth. Only a single provider is required per root domain. You can't do per-application authorization, but you don't have to create a provider for each application.
Verwenden Sie diesen Anbieter mit auth_request von nginx oder forwardAuth von traefik. Pro Root-Domain wird nur ein einziger Anbieter benötigt. Sie können keine Autorisierung pro Anwendung vornehmen, aber Sie müssen nicht für jede Anwendung einen Anbieter erstellen.
@@ -5925,6 +5921,15 @@ Bindings to groups/users are checked against the user of the event.
WebAuthn not supported by browser.
+
+
+ Use this provider with nginx's auth_request or traefik's forwardAuth. Each application/domain needs its own provider. Additionally, on each domain, /outpost.goauthentik.io must be routed to the outpost (when using a managed outpost, this is done for you).
+
+
+ Default relay state
+
+
+ When using IDP-initiated logins, the relay state will be set to this value.