providers/oauth2: add client_assertion_type jwt bearer support (#2618)

This commit is contained in:
Jens L 2022-03-31 00:30:55 +02:00 committed by GitHub
parent 996bd05ba6
commit bb8af2f19b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 681 additions and 99 deletions

View File

@ -36,6 +36,9 @@ from authentik.policies.models import PolicyBindingModel
LOGGER = get_logger() LOGGER = get_logger()
USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug" USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug"
USER_ATTRIBUTE_SA = "goauthentik.io/user/service-account" USER_ATTRIBUTE_SA = "goauthentik.io/user/service-account"
USER_ATTRIBUTE_GENERATED = "goauthentik.io/user/generated"
USER_ATTRIBUTE_EXPIRES = "goauthentik.io/user/expires"
USER_ATTRIBUTE_DELETE_ON_LOGOUT = "goauthentik.io/user/delete-on-logout"
USER_ATTRIBUTE_SOURCES = "goauthentik.io/user/sources" USER_ATTRIBUTE_SOURCES = "goauthentik.io/user/sources"
USER_ATTRIBUTE_TOKEN_EXPIRING = "goauthentik.io/user/token-expires" # nosec USER_ATTRIBUTE_TOKEN_EXPIRING = "goauthentik.io/user/token-expires" # nosec
USER_ATTRIBUTE_CHANGE_USERNAME = "goauthentik.io/user/can-change-username" USER_ATTRIBUTE_CHANGE_USERNAME = "goauthentik.io/user/can-change-username"

View File

@ -1,10 +1,18 @@
"""authentik core tasks""" """authentik core tasks"""
from datetime import datetime, timedelta
from django.contrib.sessions.backends.cache import KEY_PREFIX from django.contrib.sessions.backends.cache import KEY_PREFIX
from django.core.cache import cache from django.core.cache import cache
from django.utils.timezone import now from django.utils.timezone import now
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.models import AuthenticatedSession, ExpiringModel from authentik.core.models import (
USER_ATTRIBUTE_EXPIRES,
USER_ATTRIBUTE_GENERATED,
AuthenticatedSession,
ExpiringModel,
User,
)
from authentik.events.monitored_tasks import ( from authentik.events.monitored_tasks import (
MonitoredTask, MonitoredTask,
TaskResult, TaskResult,
@ -42,3 +50,22 @@ def clean_expired_models(self: MonitoredTask):
LOGGER.debug("Expired sessions", model=AuthenticatedSession, amount=amount) LOGGER.debug("Expired sessions", model=AuthenticatedSession, amount=amount)
messages.append(f"Expired {amount} {AuthenticatedSession._meta.verbose_name_plural}") messages.append(f"Expired {amount} {AuthenticatedSession._meta.verbose_name_plural}")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages)) self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))
@CELERY_APP.task(bind=True, base=MonitoredTask)
@prefill_task
def clean_temporary_users(self: MonitoredTask):
"""Remove temporary users created by SAML Sources"""
_now = datetime.now()
messages = []
deleted_users = 0
for user in User.objects.filter(**{f"attributes__{USER_ATTRIBUTE_GENERATED}": True}):
delta: timedelta = _now - datetime.fromtimestamp(
user.attributes.get(USER_ATTRIBUTE_EXPIRES)
)
if delta.total_seconds() > 0:
LOGGER.debug("User is expired and will be deleted.", user=user, delta=delta)
user.delete()
deleted_users += 1
messages.append(f"Successfully deleted {deleted_users} users.")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))

View File

@ -0,0 +1,50 @@
"""Test tasks"""
from time import mktime
from django.utils.timezone import now
from guardian.shortcuts import get_anonymous_user
from rest_framework.test import APITestCase
from authentik.core.models import (
USER_ATTRIBUTE_EXPIRES,
USER_ATTRIBUTE_GENERATED,
Token,
TokenIntents,
User,
)
from authentik.core.tasks import clean_expired_models, clean_temporary_users
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id
class TestTasks(APITestCase):
"""Test token API"""
def setUp(self) -> None:
super().setUp()
self.user = User.objects.create(username="testuser")
self.admin = create_test_admin_user()
self.client.force_login(self.user)
def test_token_expire(self):
"""Test Token expire task"""
token: Token = Token.objects.create(
expires=now(), user=get_anonymous_user(), intent=TokenIntents.INTENT_API
)
key = token.key
clean_expired_models.delay().get()
token.refresh_from_db()
self.assertNotEqual(key, token.key)
def test_clean_temporary_users(self):
"""Test clean_temporary_users task"""
username = generate_id
User.objects.create(
username=username,
attributes={
USER_ATTRIBUTE_GENERATED: True,
USER_ATTRIBUTE_EXPIRES: mktime(now().timetuple()),
},
)
clean_temporary_users.delay().get()
self.assertFalse(User.objects.filter(username=username))

View File

@ -2,12 +2,10 @@
from json import loads from json import loads
from django.urls.base import reverse from django.urls.base import reverse
from django.utils.timezone import now
from guardian.shortcuts import get_anonymous_user from guardian.shortcuts import get_anonymous_user
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
from authentik.core.models import USER_ATTRIBUTE_TOKEN_EXPIRING, Token, TokenIntents, User from authentik.core.models import USER_ATTRIBUTE_TOKEN_EXPIRING, Token, TokenIntents, User
from authentik.core.tasks import clean_expired_models
from authentik.core.tests.utils import create_test_admin_user from authentik.core.tests.utils import create_test_admin_user
@ -53,16 +51,6 @@ class TestTokenAPI(APITestCase):
self.assertEqual(token.intent, TokenIntents.INTENT_API) self.assertEqual(token.intent, TokenIntents.INTENT_API)
self.assertEqual(token.expiring, False) self.assertEqual(token.expiring, False)
def test_token_expire(self):
"""Test Token expire task"""
token: Token = Token.objects.create(
expires=now(), user=get_anonymous_user(), intent=TokenIntents.INTENT_API
)
key = token.key
clean_expired_models.delay().get()
token.refresh_from_db()
self.assertNotEqual(key, token.key)
def test_list(self): def test_list(self):
"""Test Token List (Test normal authentication)""" """Test Token List (Test normal authentication)"""
token_should: Token = Token.objects.create( token_should: Token = Token.objects.create(

View File

@ -34,6 +34,7 @@ class OAuth2ProviderSerializer(ProviderSerializer):
"sub_mode", "sub_mode",
"property_mappings", "property_mappings",
"issuer_mode", "issuer_mode",
"verification_keys",
] ]

View File

@ -4,6 +4,10 @@ GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code"
GRANT_TYPE_REFRESH_TOKEN = "refresh_token" # nosec GRANT_TYPE_REFRESH_TOKEN = "refresh_token" # nosec
GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials" GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"
CLIENT_ASSERTION_TYPE = "client_assertion_type"
CLIENT_ASSERTION = "client_assertion"
CLIENT_ASSERTION_TYPE_JWT = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
PROMPT_NONE = "none" PROMPT_NONE = "none"
PROMPT_CONSNET = "consent" PROMPT_CONSNET = "consent"
PROMPT_LOGIN = "login" PROMPT_LOGIN = "login"

View File

@ -0,0 +1,36 @@
# Generated by Django 4.0.3 on 2022-03-29 19:37
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_crypto", "0003_certificatekeypair_managed"),
("authentik_providers_oauth2", "0008_rename_rsa_key_oauth2provider_signing_key_and_more"),
]
operations = [
migrations.AddField(
model_name="oauth2provider",
name="verification_keys",
field=models.ManyToManyField(
help_text="JWTs created with the configured certificates can authenticate with this provider.",
related_name="+",
to="authentik_crypto.certificatekeypair",
verbose_name="Allowed certificates for JWT-based client_credentials",
),
),
migrations.AlterField(
model_name="oauth2provider",
name="signing_key",
field=models.ForeignKey(
help_text="Key used to sign the tokens. Only required when JWT Algorithm is set to RS256.",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="authentik_crypto.certificatekeypair",
verbose_name="Signing Key",
),
),
]

View File

@ -212,7 +212,7 @@ class OAuth2Provider(Provider):
signing_key = models.ForeignKey( signing_key = models.ForeignKey(
CertificateKeyPair, CertificateKeyPair,
verbose_name=_("RSA Key"), verbose_name=_("Signing Key"),
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
null=True, null=True,
help_text=_( help_text=_(
@ -220,6 +220,15 @@ class OAuth2Provider(Provider):
), ),
) )
verification_keys = models.ManyToManyField(
CertificateKeyPair,
verbose_name=_("Allowed certificates for JWT-based client_credentials"),
help_text=_(
"JWTs created with the configured certificates can authenticate with this provider."
),
related_name="+",
)
def create_refresh_token( def create_refresh_token(
self, user: User, scope: list[str], request: HttpRequest self, user: User, scope: list[str], request: HttpRequest
) -> "RefreshToken": ) -> "RefreshToken":

View File

@ -0,0 +1,206 @@
"""Test token view"""
from datetime import datetime, timedelta
from json import loads
from django.test import RequestFactory
from django.urls import reverse
from jwt import decode
from authentik.core.models import USER_ATTRIBUTE_SA, Application, Group
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.lib.generators import generate_id, generate_key
from authentik.managed.manager import ObjectManager
from authentik.policies.models import PolicyBinding
from authentik.providers.oauth2.constants import (
GRANT_TYPE_CLIENT_CREDENTIALS,
SCOPE_OPENID,
SCOPE_OPENID_EMAIL,
SCOPE_OPENID_PROFILE,
)
from authentik.providers.oauth2.models import OAuth2Provider, ScopeMapping
from authentik.providers.oauth2.tests.utils import OAuthTestCase
class TestTokenClientCredentialsJWT(OAuthTestCase):
"""Test token (client_credentials, with JWT) view"""
def setUp(self) -> None:
super().setUp()
ObjectManager().run()
self.factory = RequestFactory()
self.cert = create_test_cert()
self.provider: OAuth2Provider = OAuth2Provider.objects.create(
name="test",
client_id=generate_id(),
client_secret=generate_key(),
authorization_flow=create_test_flow(),
redirect_uris="http://testserver",
signing_key=self.cert,
)
self.provider.verification_keys.set([self.cert])
self.provider.property_mappings.set(ScopeMapping.objects.all())
self.app = Application.objects.create(name="test", slug="test", provider=self.provider)
self.user = create_test_admin_user("sa")
self.user.attributes[USER_ATTRIBUTE_SA] = True
self.user.save()
def test_invalid_type(self):
"""test invalid type"""
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "foo",
"client_assertion": "foo.bar",
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_jwt(self):
"""test invalid JWT"""
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": "foo.bar",
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_signautre(self):
"""test invalid JWT"""
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() + timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token + "foo",
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_expired(self):
"""test invalid JWT"""
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() - timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token,
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_no_app(self):
"""test invalid JWT"""
self.app.provider = None
self.app.save()
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() + timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token,
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_invalid_access_denied(self):
"""test invalid JWT"""
group = Group.objects.create(name="foo")
PolicyBinding.objects.create(
group=group,
target=self.app,
order=0,
)
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() + timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token,
},
)
self.assertEqual(response.status_code, 400)
body = loads(response.content.decode())
self.assertEqual(body["error"], "invalid_grant")
def test_successful(self):
"""test successful"""
token = self.provider.encode(
{
"sub": "foo",
"exp": datetime.now() + timedelta(hours=2),
}
)
response = self.client.post(
reverse("authentik_providers_oauth2:token"),
{
"grant_type": GRANT_TYPE_CLIENT_CREDENTIALS,
"scope": f"{SCOPE_OPENID} {SCOPE_OPENID_EMAIL} {SCOPE_OPENID_PROFILE}",
"client_id": self.provider.client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": token,
},
)
self.assertEqual(response.status_code, 200)
body = loads(response.content.decode())
self.assertEqual(body["token_type"], "bearer")
_, alg = self.provider.get_jwt_key()
jwt = decode(
body["access_token"],
key=self.provider.signing_key.public_key,
algorithms=[alg],
audience=self.provider.client_id,
)
self.assertEqual(
jwt["given_name"], "Autogenerated user from application test (client credentials JWT)"
)
self.assertEqual(jwt["preferred_username"], "test-foo")

View File

@ -36,7 +36,6 @@ class JWKSView(View):
if signing_key: if signing_key:
private_key = signing_key.private_key private_key = signing_key.private_key
print(type(private_key))
if isinstance(private_key, RSAPrivateKey): if isinstance(private_key, RSAPrivateKey):
public_key: RSAPublicKey = private_key.public_key() public_key: RSAPublicKey = private_key.public_key()
public_numbers = public_key.public_numbers() public_numbers = public_key.public_numbers()

View File

@ -5,14 +5,27 @@ from hashlib import sha256
from typing import Any, Optional from typing import Any, Optional
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.utils.timezone import datetime, now
from django.views import View from django.views import View
from jwt import InvalidTokenError, decode
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.models import USER_ATTRIBUTE_SA, Application, Token, TokenIntents, User from authentik.core.models import (
USER_ATTRIBUTE_EXPIRES,
USER_ATTRIBUTE_GENERATED,
USER_ATTRIBUTE_SA,
Application,
Token,
TokenIntents,
User,
)
from authentik.events.models import Event, EventAction from authentik.events.models import Event, EventAction
from authentik.lib.utils.time import timedelta_from_string from authentik.lib.utils.time import timedelta_from_string
from authentik.policies.engine import PolicyEngine from authentik.policies.engine import PolicyEngine
from authentik.providers.oauth2.constants import ( from authentik.providers.oauth2.constants import (
CLIENT_ASSERTION,
CLIENT_ASSERTION_TYPE,
CLIENT_ASSERTION_TYPE_JWT,
GRANT_TYPE_AUTHORIZATION_CODE, GRANT_TYPE_AUTHORIZATION_CODE,
GRANT_TYPE_CLIENT_CREDENTIALS, GRANT_TYPE_CLIENT_CREDENTIALS,
GRANT_TYPE_REFRESH_TOKEN, GRANT_TYPE_REFRESH_TOKEN,
@ -21,6 +34,7 @@ from authentik.providers.oauth2.errors import TokenError, UserAuthError
from authentik.providers.oauth2.models import ( from authentik.providers.oauth2.models import (
AuthorizationCode, AuthorizationCode,
ClientTypes, ClientTypes,
JWTAlgorithms,
OAuth2Provider, OAuth2Provider,
RefreshToken, RefreshToken,
) )
@ -187,6 +201,8 @@ class TokenParams:
raise TokenError("invalid_grant") raise TokenError("invalid_grant")
def __post_init_client_credentials(self, request: HttpRequest): def __post_init_client_credentials(self, request: HttpRequest):
if request.POST.get(CLIENT_ASSERTION_TYPE, "") != "":
return self.__post_init_client_credentials_jwt(request)
# Authenticate user based on credentials # Authenticate user based on credentials
username = request.POST.get("username") username = request.POST.get("username")
password = request.POST.get("password") password = request.POST.get("password")
@ -222,6 +238,72 @@ class TokenParams:
if not result.passing: if not result.passing:
LOGGER.info("User not authenticated for application", user=self.user, app=app) LOGGER.info("User not authenticated for application", user=self.user, app=app)
raise TokenError("invalid_grant") raise TokenError("invalid_grant")
return None
def __post_init_client_credentials_jwt(self, request: HttpRequest):
assertion_type = request.POST.get(CLIENT_ASSERTION_TYPE, "")
if assertion_type != CLIENT_ASSERTION_TYPE_JWT:
raise TokenError("invalid_grant")
client_secret = request.POST.get("client_secret", None)
assertion = request.POST.get(CLIENT_ASSERTION, client_secret)
if not assertion:
raise TokenError("invalid_grant")
token = None
for cert in self.provider.verification_keys.all():
LOGGER.debug("verifying jwt with key", key=cert.name)
try:
token = decode(
assertion,
cert.certificate.public_key(),
algorithms=[JWTAlgorithms.RS256, JWTAlgorithms.EC256],
options={
"verify_aud": False,
},
)
except (InvalidTokenError, ValueError) as last_exc:
LOGGER.warning("failed to validate jwt", last_exc=last_exc)
if not token:
raise TokenError("invalid_grant")
exp = datetime.fromtimestamp(token["exp"])
# Non-timezone aware check since we assume `exp` is in UTC
if datetime.now() >= exp:
LOGGER.info("JWT token expired")
raise TokenError("invalid_grant")
app = Application.objects.filter(provider=self.provider).first()
if not app or not app.provider:
LOGGER.info("client_credentials grant for provider without application")
raise TokenError("invalid_grant")
engine = PolicyEngine(app, self.user, request)
engine.request.context["JWT"] = token
engine.build()
result = engine.result
if not result.passing:
LOGGER.info("JWT not authenticated for application", app=app)
raise TokenError("invalid_grant")
self.user, _ = User.objects.update_or_create(
username=f"{self.provider.name}-{token.get('sub')}",
defaults={
"attributes": {
USER_ATTRIBUTE_GENERATED: True,
USER_ATTRIBUTE_EXPIRES: token["exp"],
},
"last_login": now(),
"name": f"Autogenerated user from application {app.name} (client credentials JWT)",
},
)
Event.new(
action=EventAction.LOGIN,
PLAN_CONTEXT_METHOD="jwt",
PLAN_CONTEXT_METHOD_ARGS={
"jwt": token,
},
).from_http(request, user=self.user)
class TokenView(View): class TokenView(View):

View File

@ -345,6 +345,11 @@ CELERY_BEAT_SCHEDULE = {
"schedule": crontab(hour="*/24", minute=0), "schedule": crontab(hour="*/24", minute=0),
"options": {"queue": "authentik_scheduled"}, "options": {"queue": "authentik_scheduled"},
}, },
"user_cleanup": {
"task": "authentik.core.tasks.clean_temporary_users",
"schedule": crontab(minute="*/5"),
"options": {"queue": "authentik_scheduled"},
},
} }
CELERY_TASK_CREATE_MISSING_QUEUES = True CELERY_TASK_CREATE_MISSING_QUEUES = True
CELERY_TASK_DEFAULT_QUEUE = "authentik" CELERY_TASK_DEFAULT_QUEUE = "authentik"

View File

@ -6,7 +6,7 @@ from django.apps import AppConfig
class AuthentikSourceSAMLConfig(AppConfig): class AuthentikSourceSAMLConfig(AppConfig):
"""authentik saml_idp app config""" """authentik saml source app config"""
name = "authentik.sources.saml" name = "authentik.sources.saml"
label = "authentik_sources_saml" label = "authentik_sources_saml"

View File

@ -1,5 +1,6 @@
"""authentik saml source processor""" """authentik saml source processor"""
from base64 import b64decode from base64 import b64decode
from time import mktime
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
import xmlsec import xmlsec
@ -7,9 +8,16 @@ from defusedxml.lxml import fromstring
from django.core.cache import cache from django.core.cache import cache
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.utils.timezone import now
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.models import User from authentik.core.models import (
USER_ATTRIBUTE_DELETE_ON_LOGOUT,
USER_ATTRIBUTE_EXPIRES,
USER_ATTRIBUTE_GENERATED,
USER_ATTRIBUTE_SOURCES,
User,
)
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.flows.planner import ( from authentik.flows.planner import (
PLAN_CONTEXT_PENDING_USER, PLAN_CONTEXT_PENDING_USER,
@ -19,6 +27,7 @@ from authentik.flows.planner import (
FlowPlanner, FlowPlanner,
) )
from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN from authentik.flows.views.executor import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
from authentik.lib.utils.time import timedelta_from_string
from authentik.lib.utils.urls import redirect_with_qs from authentik.lib.utils.urls import redirect_with_qs
from authentik.policies.utils import delete_none_keys from authentik.policies.utils import delete_none_keys
from authentik.sources.saml.exceptions import ( from authentik.sources.saml.exceptions import (
@ -124,9 +133,19 @@ class ResponseProcessor:
on logout and periodically.""" on logout and periodically."""
# Create a temporary User # Create a temporary User
name_id = self._get_name_id().text name_id = self._get_name_id().text
expiry = mktime(
(now() + timedelta_from_string(self._source.temporary_user_delete_after)).timetuple()
)
user: User = User.objects.create( user: User = User.objects.create(
username=name_id, username=name_id,
attributes={"saml": {"source": self._source.pk.hex, "delete_on_logout": True}}, attributes={
USER_ATTRIBUTE_GENERATED: True,
USER_ATTRIBUTE_SOURCES: [
self._source.name,
],
USER_ATTRIBUTE_DELETE_ON_LOGOUT: True,
USER_ATTRIBUTE_EXPIRES: expiry,
},
) )
LOGGER.debug("Created temporary user for NameID Transient", username=name_id) LOGGER.debug("Created temporary user for NameID Transient", username=name_id)
user.set_unusable_password() user.set_unusable_password()

View File

@ -1,10 +0,0 @@
"""saml source settings"""
from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
"saml_source_cleanup": {
"task": "authentik.sources.saml.tasks.clean_temporary_users",
"schedule": crontab(minute="*/5"),
"options": {"queue": "authentik_scheduled"},
}
}

View File

@ -4,7 +4,7 @@ from django.dispatch import receiver
from django.http import HttpRequest from django.http import HttpRequest
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.models import User from authentik.core.models import USER_ATTRIBUTE_DELETE_ON_LOGOUT, User
LOGGER = get_logger() LOGGER = get_logger()
@ -15,8 +15,6 @@ def on_user_logged_out(sender, request: HttpRequest, user: User, **_):
"""Delete temporary user if the `delete_on_logout` flag is enabled""" """Delete temporary user if the `delete_on_logout` flag is enabled"""
if not user: if not user:
return return
if "saml" in user.attributes: if user.attributes.get(USER_ATTRIBUTE_DELETE_ON_LOGOUT, False):
if "delete_on_logout" in user.attributes["saml"]: LOGGER.debug("Deleted temporary user", user=user)
if user.attributes["saml"]["delete_on_logout"]: user.delete()
LOGGER.debug("Deleted temporary user", user=user)
user.delete()

View File

@ -1,42 +0,0 @@
"""authentik saml source tasks"""
from django.utils.timezone import now
from structlog.stdlib import get_logger
from authentik.core.models import AuthenticatedSession, User
from authentik.events.monitored_tasks import (
MonitoredTask,
TaskResult,
TaskResultStatus,
prefill_task,
)
from authentik.lib.utils.time import timedelta_from_string
from authentik.root.celery import CELERY_APP
from authentik.sources.saml.models import SAMLSource
LOGGER = get_logger()
@CELERY_APP.task(bind=True, base=MonitoredTask)
@prefill_task
def clean_temporary_users(self: MonitoredTask):
"""Remove temporary users created by SAML Sources"""
_now = now()
messages = []
deleted_users = 0
for user in User.objects.filter(attributes__saml__isnull=False):
sources = SAMLSource.objects.filter(pk=user.attributes.get("saml", {}).get("source", ""))
if not sources.exists():
LOGGER.warning("User has an invalid SAML Source and won't be deleted!", user=user)
messages.append(f"User {user} has an invalid SAML Source and won't be deleted!")
continue
source = sources.first()
source_delta = timedelta_from_string(source.temporary_user_delete_after)
if (
_now - user.last_login >= source_delta
and not AuthenticatedSession.objects.filter(user=user).exists()
):
LOGGER.debug("User is expired and will be deleted.", user=user, delta=source_delta)
user.delete()
deleted_users += 1
messages.append(f"Successfully deleted {deleted_users} users.")
self.set_status(TaskResult(TaskResultStatus.SUCCESSFUL, messages))

View File

@ -23091,7 +23091,6 @@ components:
type: string type: string
format: uuid format: uuid
nullable: true nullable: true
title: RSA Key
description: Key used to sign the tokens. Only required when JWT Algorithm description: Key used to sign the tokens. Only required when JWT Algorithm
is set to RS256. is set to RS256.
redirect_uris: redirect_uris:
@ -23106,6 +23105,15 @@ components:
allOf: allOf:
- $ref: '#/components/schemas/IssuerModeEnum' - $ref: '#/components/schemas/IssuerModeEnum'
description: Configure how the issuer field of the ID Token should be filled. description: Configure how the issuer field of the ID Token should be filled.
verification_keys:
type: array
items:
type: string
format: uuid
title: Allowed certificates for JWT-based client_credentials
title: Allowed certificates for JWT-based client_credentials
description: JWTs created with the configured certificates can authenticate
with this provider.
required: required:
- assigned_application_name - assigned_application_name
- assigned_application_slug - assigned_application_slug
@ -23116,6 +23124,7 @@ components:
- pk - pk
- verbose_name - verbose_name
- verbose_name_plural - verbose_name_plural
- verification_keys
OAuth2ProviderRequest: OAuth2ProviderRequest:
type: object type: object
description: OAuth2Provider Serializer description: OAuth2Provider Serializer
@ -23163,7 +23172,6 @@ components:
type: string type: string
format: uuid format: uuid
nullable: true nullable: true
title: RSA Key
description: Key used to sign the tokens. Only required when JWT Algorithm description: Key used to sign the tokens. Only required when JWT Algorithm
is set to RS256. is set to RS256.
redirect_uris: redirect_uris:
@ -23178,9 +23186,19 @@ components:
allOf: allOf:
- $ref: '#/components/schemas/IssuerModeEnum' - $ref: '#/components/schemas/IssuerModeEnum'
description: Configure how the issuer field of the ID Token should be filled. description: Configure how the issuer field of the ID Token should be filled.
verification_keys:
type: array
items:
type: string
format: uuid
title: Allowed certificates for JWT-based client_credentials
title: Allowed certificates for JWT-based client_credentials
description: JWTs created with the configured certificates can authenticate
with this provider.
required: required:
- authorization_flow - authorization_flow
- name - name
- verification_keys
OAuth2ProviderSetupURLs: OAuth2ProviderSetupURLs:
type: object type: object
description: OAuth2 Provider Metadata serializer description: OAuth2 Provider Metadata serializer
@ -27488,7 +27506,6 @@ components:
type: string type: string
format: uuid format: uuid
nullable: true nullable: true
title: RSA Key
description: Key used to sign the tokens. Only required when JWT Algorithm description: Key used to sign the tokens. Only required when JWT Algorithm
is set to RS256. is set to RS256.
redirect_uris: redirect_uris:
@ -27503,6 +27520,15 @@ components:
allOf: allOf:
- $ref: '#/components/schemas/IssuerModeEnum' - $ref: '#/components/schemas/IssuerModeEnum'
description: Configure how the issuer field of the ID Token should be filled. description: Configure how the issuer field of the ID Token should be filled.
verification_keys:
type: array
items:
type: string
format: uuid
title: Allowed certificates for JWT-based client_credentials
title: Allowed certificates for JWT-based client_credentials
description: JWTs created with the configured certificates can authenticate
with this provider.
PatchedOAuthSourceRequest: PatchedOAuthSourceRequest:
type: object type: object
description: OAuth Source Serializer description: OAuth Source Serializer

View File

@ -2327,6 +2327,7 @@ msgstr "Interne Konten ausblenden"
#: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts
#: src/pages/outposts/OutpostForm.ts #: src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2582,6 +2583,10 @@ msgstr "Ausstellermodus"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "JWT Algorithmus" #~ msgstr "JWT Algorithmus"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "Schlüssel zum Signieren der Token." msgstr "Schlüssel zum Signieren der Token."
@ -2745,6 +2750,7 @@ msgstr "Wird geladen"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -5906,6 +5912,10 @@ msgstr "Überprüfung"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "Zertifikat zur Überprüfung" msgstr "Zertifikat zur Überprüfung"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity." msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity."
msgstr "Überprüfen Sie die E-Mail-Adresse des Benutzers, indem Sie ihm einen einmaligen Link senden. Kann auch für die Wiederherstellung verwendet werden, um die Authentizität des Benutzers zu überprüfen." msgstr "Überprüfen Sie die E-Mail-Adresse des Benutzers, indem Sie ihm einen einmaligen Link senden. Kann auch für die Wiederherstellung verwendet werden, um die Authentizität des Benutzers zu überprüfen."

View File

@ -2360,6 +2360,7 @@ msgstr "Hide service-accounts"
#: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts
#: src/pages/outposts/OutpostForm.ts #: src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2624,6 +2625,10 @@ msgstr "Issuer mode"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "JWT Algorithm" #~ msgstr "JWT Algorithm"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr "JWTs signed by certificates configured here can be used to authenticate to the provider."
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "Key used to sign the tokens." msgstr "Key used to sign the tokens."
@ -2789,6 +2794,7 @@ msgstr "Loading"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -6022,6 +6028,10 @@ msgstr "Verification"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "Verification Certificate" msgstr "Verification Certificate"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr "Verification certificates"
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity." msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity."
msgstr "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity." msgstr "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity."

View File

@ -2318,6 +2318,7 @@ msgstr "Ocultar cuentas de servicio"
#: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts
#: src/pages/outposts/OutpostForm.ts #: src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2575,6 +2576,10 @@ msgstr "Modo emisor"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "algoritmo JWT" #~ msgstr "algoritmo JWT"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "Clave utilizada para firmar los tokens." msgstr "Clave utilizada para firmar los tokens."
@ -2738,6 +2743,7 @@ msgstr "Cargando"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -5900,6 +5906,10 @@ msgstr "Verificación"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "Certificado de verificación" msgstr "Certificado de verificación"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity." msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity."
msgstr "Verifique la dirección de correo electrónico del usuario enviándole un enlace único. También se puede utilizar para la recuperación para verificar la autenticidad del usuario." msgstr "Verifique la dirección de correo electrónico del usuario enviándole un enlace único. También se puede utilizar para la recuperación para verificar la autenticidad del usuario."

View File

@ -2344,6 +2344,7 @@ msgstr "Cacher les comptes de service"
#: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts
#: src/pages/outposts/OutpostForm.ts #: src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2605,6 +2606,10 @@ msgstr "Mode de l'émetteur"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "Algorithme JWT" #~ msgstr "Algorithme JWT"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "" msgstr ""
@ -2769,6 +2774,7 @@ msgstr "Chargement en cours"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -5961,6 +5967,10 @@ msgstr "Vérification"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "Certificat de validation" msgstr "Certificat de validation"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity." msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity."
msgstr "Vérifier le courriel de l'utilisateur en lui envoyant un lien à usage unique. Peut également être utilisé lors de la récupération afin de vérifier l'authenticité de l'utilisateur." msgstr "Vérifier le courriel de l'utilisateur en lui envoyant un lien à usage unique. Peut également être utilisé lors de la récupération afin de vérifier l'authenticité de l'utilisateur."

View File

@ -2315,6 +2315,7 @@ msgstr "Ukryj konta serwisowe"
#: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts
#: src/pages/outposts/OutpostForm.ts #: src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2572,6 +2573,10 @@ msgstr "Tryb wystawcy"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "Algorytm JWT" #~ msgstr "Algorytm JWT"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "Klucz używany do podpisywania tokenów." msgstr "Klucz używany do podpisywania tokenów."
@ -2735,6 +2740,7 @@ msgstr "Ładowanie"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -5897,6 +5903,10 @@ msgstr "Weryfikacja"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "Certyfikat weryfikacji" msgstr "Certyfikat weryfikacji"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity." msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity."
msgstr "Zweryfikuj adres e-mail użytkownika, wysyłając mu jednorazowy link. Może być również używany do odzyskiwania w celu weryfikacji autentyczności użytkownika." msgstr "Zweryfikuj adres e-mail użytkownika, wysyłając mu jednorazowy link. Może być również używany do odzyskiwania w celu weryfikacji autentyczności użytkownika."

View File

@ -2352,6 +2352,7 @@ msgstr ""
#: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts
#: src/pages/outposts/OutpostForm.ts #: src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2614,6 +2615,10 @@ msgstr ""
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "" #~ msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "" msgstr ""
@ -2779,6 +2784,7 @@ msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -6002,6 +6008,10 @@ msgstr ""
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "" msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity." msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity."
msgstr "" msgstr ""

View File

@ -2318,6 +2318,7 @@ msgstr "Hizmet hesaplarını gizle"
#: src/pages/events/RuleForm.ts #: src/pages/events/RuleForm.ts
#: src/pages/outposts/OutpostForm.ts #: src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2576,6 +2577,10 @@ msgstr "Yayımcı kipi"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "JWT Algoritması" #~ msgstr "JWT Algoritması"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "Anahtar belirteçleri imzalamak için kullanılır." msgstr "Anahtar belirteçleri imzalamak için kullanılır."
@ -2739,6 +2744,7 @@ msgstr "Yükleniyor"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -5902,6 +5908,10 @@ msgstr "Doğrulama"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "Doğrulama Sertifikası" msgstr "Doğrulama Sertifikası"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity." msgid "Verify the user's email address by sending them a one-time-link. Can also be used for recovery to verify the user's authenticity."
msgstr "Kullanıcının e-posta adresini bir kerelik bağlantı göndererek doğrulayın. Kullanıcının orijinalliğini doğrulamak için kurtarma için de kullanılabilir." msgstr "Kullanıcının e-posta adresini bir kerelik bağlantı göndererek doğrulayın. Kullanıcının orijinalliğini doğrulamak için kurtarma için de kullanılabilir."

View File

@ -2333,6 +2333,7 @@ msgstr "隐藏服务账户"
#: src/pages/events/RuleForm.ts src/pages/outposts/OutpostForm.ts #: src/pages/events/RuleForm.ts src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2612,6 +2613,10 @@ msgstr "Issuer 模式"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "JWT 算法" #~ msgstr "JWT 算法"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "用于签名令牌的密钥。" msgstr "用于签名令牌的密钥。"
@ -2771,6 +2776,7 @@ msgstr "正在加载"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -6015,6 +6021,10 @@ msgstr "验证"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "验证证书" msgstr "验证证书"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "" msgid ""
"Verify the user's email address by sending them a one-time-link. Can also be" "Verify the user's email address by sending them a one-time-link. Can also be"

View File

@ -2332,6 +2332,7 @@ msgstr "隐藏服务账户"
#: src/pages/events/RuleForm.ts src/pages/outposts/OutpostForm.ts #: src/pages/events/RuleForm.ts src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2611,6 +2612,10 @@ msgstr "Issuer mode"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "JWT 算法" #~ msgstr "JWT 算法"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "用于对令牌进行签名的密钥。" msgstr "用于对令牌进行签名的密钥。"
@ -2770,6 +2775,7 @@ msgstr "正在加载"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -6015,6 +6021,10 @@ msgstr "验证"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "验证证书" msgstr "验证证书"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "" msgid ""
"Verify the user's email address by sending them a one-time-link. Can also be" "Verify the user's email address by sending them a one-time-link. Can also be"

View File

@ -2332,6 +2332,7 @@ msgstr "隐藏服务账户"
#: src/pages/events/RuleForm.ts src/pages/outposts/OutpostForm.ts #: src/pages/events/RuleForm.ts src/pages/outposts/OutpostForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/saml/SAMLProviderForm.ts #: src/pages/providers/saml/SAMLProviderForm.ts
#: src/pages/sources/ldap/LDAPSourceForm.ts #: src/pages/sources/ldap/LDAPSourceForm.ts
@ -2611,6 +2612,10 @@ msgstr "Issuer mode"
#~ msgid "JWT Algorithm" #~ msgid "JWT Algorithm"
#~ msgstr "JWT 算法" #~ msgstr "JWT 算法"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "JWTs signed by certificates configured here can be used to authenticate to the provider."
msgstr ""
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Key used to sign the tokens." msgid "Key used to sign the tokens."
msgstr "用于对令牌进行签名的密钥。" msgstr "用于对令牌进行签名的密钥。"
@ -2770,6 +2775,7 @@ msgstr "正在加载"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts #: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
#: src/pages/providers/proxy/ProxyProviderForm.ts #: src/pages/providers/proxy/ProxyProviderForm.ts
@ -6015,6 +6021,10 @@ msgstr "验证"
msgid "Verification Certificate" msgid "Verification Certificate"
msgstr "验证证书" msgstr "验证证书"
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
msgid "Verification certificates"
msgstr ""
#: src/pages/stages/email/EmailStageForm.ts #: src/pages/stages/email/EmailStageForm.ts
msgid "" msgid ""
"Verify the user's email address by sending them a one-time-link. Can also be" "Verify the user's email address by sending them a one-time-link. Can also be"

View File

@ -117,7 +117,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
} }
renderExpanded(item: CertificateKeyPair): TemplateResult { renderExpanded(item: CertificateKeyPair): TemplateResult {
return html`<td role="cell" colspan="3"> return html`<td role="cell" colspan="4">
<div class="pf-c-table__expandable-row-content"> <div class="pf-c-table__expandable-row-content">
<dl class="pf-c-description-list pf-m-horizontal"> <dl class="pf-c-description-list pf-m-horizontal">
<div class="pf-c-description-list__group"> <div class="pf-c-description-list__group">

View File

@ -292,6 +292,41 @@ ${this.instance?.redirectUris}</textarea
${t`Hold control/command to select multiple items.`} ${t`Hold control/command to select multiple items.`}
</p> </p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal
label=${t`Verification certificates`}
name="verificationKeys"
>
<select class="pf-c-form-control" multiple>
${until(
new CryptoApi(DEFAULT_CONFIG)
.cryptoCertificatekeypairsList({
ordering: "name",
})
.then((keys) => {
return keys.results.map((key) => {
const selected = (
this.instance?.verificationKeys || []
).some((su) => {
return su == key.pk;
});
return html`<option
value=${key.pk}
?selected=${selected}
>
${key.name} (${key.privateKeyType?.toUpperCase()})
</option>`;
});
}),
html`<option>${t`Loading...`}</option>`,
)}
</select>
<p class="pf-c-form__helper-text">
${t`JWTs signed by certificates configured here can be used to authenticate to the provider.`}
</p>
<p class="pf-c-form__helper-text">
${t`Hold control/command to select multiple items.`}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal <ak-form-element-horizontal
label=${t`Subject mode`} label=${t`Subject mode`}
?required=${true} ?required=${true}

View File

@ -0,0 +1,53 @@
# Machine-to-machine authentication
Client credentials can be used for machine-to-machine communication authentication. Clients can authenticate themselves using service-accounts; standard client_id + client_secret is not sufficient. This behavior is due to providers only being able to have a single secret at any given time.
### Static authentication
Hence identification is based on service-accounts, and authentication is based on App-password tokens. These objects can be created in a single step using the *Create Service account* function.
An example request can look like this:
```
POST /application/o/token/ HTTP/1.1
Host: authentik.company
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_id=application_client_id&
username=my-service-account&
password=my-token
```
This will return a JSON response with an `access_token`, which is a signed JWT token. This token can be sent along requests to other hosts, which can then validate the JWT based on the signing key configured in authentik.
### JWT-authentication
Starting with authentik 2022.4, you can authenticate and get a token using an existing JWT.
(For readability we will refer to the JWT issued by the external issuer/platform as input JWT, and the resulting JWT from authentik as the output JWT)
To configure this, the certificate used to sign the input JWT must be created in authentik. The certificate is enough, a private key is not required. Afterwards, configure the certificate in the OAuth2 provider settings under *Verification certificates*.
With this configure, any JWT issued by the configured certificates can be used to authenticate:
```
POST /application/o/token/ HTTP/1.1
Host: authentik.company
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&
client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&
client_assertion=$inputJWT&
client_id=application_client_id
```
Alternatively, you can set the `client_secret` parameter to the `$inputJWT`, for applications which can set the password from a file but not other parameters.
Input JWTs are checked to be signed by any of the selected *Verification certificates*, and their `exp` attribute must not be now or in the past.
To do additional checks, you can use *[Expression policies](../../policies/expression)*:
```python
return request.context["JWT"]["iss"] == "https://my.issuer"
```

View File

@ -4,7 +4,7 @@ title: OAuth2 Provider
This provider supports both generic OAuth2 as well as OpenID Connect This provider supports both generic OAuth2 as well as OpenID Connect
Scopes can be configured using Scope Mappings, a type of [Property Mappings](../property-mappings/#scope-mapping). Scopes can be configured using Scope Mappings, a type of [Property Mappings](../../property-mappings/#scope-mapping).
| Endpoint | URL | | Endpoint | URL |
| -------------------- | -------------------------------------------------------------------- | | -------------------- | -------------------------------------------------------------------- |
@ -42,18 +42,4 @@ Refresh tokens can be used as long-lived tokens to access user data, and further
### `client_credentials`: ### `client_credentials`:
Client credentials can be used for machine-to-machine communication authentication. Clients can authenticate themselves using service-accounts; standard client_id + client_secret is not sufficient. This behavior is due to providers only being able to have a single secret at any given time. See [Machine-to-machine authentication](./client_credentials)
Hence identification is based on service-accounts, and authentication is based on App-password tokens. These objects can be created in a single step using the *Create Service account* function.
An example request can look like this:
```
POST /application/o/token/ HTTP/1.1
Host: authentik.company
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&username=my-service-account&password=my-token&client_id=application_client_id
```
This will return a JSON response with an `access_token`, which is a signed JWT token. This token can be sent along requests to other hosts, which can then validate the JWT based on the signing key configured in authentik.

View File

@ -73,4 +73,4 @@ This upgrade only applies if you are upgrading from a running 0.9 instance. auth
Because this upgrade brings the new OAuth2 Provider, the old providers will be lost in the process. Make sure to take note of the providers you want to bring over. Because this upgrade brings the new OAuth2 Provider, the old providers will be lost in the process. Make sure to take note of the providers you want to bring over.
Another side-effect of this upgrade is the change of OAuth2 URLs, see [here](../providers/oauth2.md). Another side-effect of this upgrade is the change of OAuth2 URLs, see [here](../providers/oauth2).

View File

@ -33,7 +33,14 @@ module.exports = {
type: "category", type: "category",
label: "Providers", label: "Providers",
items: [ items: [
"providers/oauth2", {
type: "category",
label: "OAuth2 Provider",
items: [
"providers/oauth2/index",
"providers/oauth2/client_credentials",
],
},
"providers/saml", "providers/saml",
{ {
type: "category", type: "category",