providers/oauth2: remove jwt_alg field and set algorithm based on selected keypair, select HS256 when no keypair is selected
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
89696edbee
commit
2f3026084e
|
@ -146,7 +146,7 @@ class TestCrypto(APITestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://localhost",
|
redirect_uris="http://localhost",
|
||||||
rsa_key=keypair,
|
signing_key=keypair,
|
||||||
)
|
)
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse(
|
reverse(
|
||||||
|
|
|
@ -7,25 +7,18 @@ from rest_framework.fields import CharField
|
||||||
from rest_framework.generics import get_object_or_404
|
from rest_framework.generics import get_object_or_404
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ValidationError
|
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from authentik.core.api.providers import ProviderSerializer
|
from authentik.core.api.providers import ProviderSerializer
|
||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
from authentik.core.models import Provider
|
from authentik.core.models import Provider
|
||||||
from authentik.providers.oauth2.models import JWTAlgorithms, OAuth2Provider
|
from authentik.providers.oauth2.models import OAuth2Provider
|
||||||
|
|
||||||
|
|
||||||
class OAuth2ProviderSerializer(ProviderSerializer):
|
class OAuth2ProviderSerializer(ProviderSerializer):
|
||||||
"""OAuth2Provider Serializer"""
|
"""OAuth2Provider Serializer"""
|
||||||
|
|
||||||
def validate_jwt_alg(self, value):
|
|
||||||
"""Ensure that when RS256 is selected, a certificate-key-pair is selected"""
|
|
||||||
if self.initial_data.get("rsa_key", None) is None and value == JWTAlgorithms.RS256:
|
|
||||||
raise ValidationError(_("RS256 requires a Certificate-Key-Pair to be selected."))
|
|
||||||
return value
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
model = OAuth2Provider
|
model = OAuth2Provider
|
||||||
|
@ -37,8 +30,7 @@ class OAuth2ProviderSerializer(ProviderSerializer):
|
||||||
"access_code_validity",
|
"access_code_validity",
|
||||||
"token_validity",
|
"token_validity",
|
||||||
"include_claims_in_id_token",
|
"include_claims_in_id_token",
|
||||||
"jwt_alg",
|
"signing_key",
|
||||||
"rsa_key",
|
|
||||||
"redirect_uris",
|
"redirect_uris",
|
||||||
"sub_mode",
|
"sub_mode",
|
||||||
"property_mappings",
|
"property_mappings",
|
||||||
|
@ -73,8 +65,7 @@ class OAuth2ProviderViewSet(UsedByMixin, ModelViewSet):
|
||||||
"access_code_validity",
|
"access_code_validity",
|
||||||
"token_validity",
|
"token_validity",
|
||||||
"include_claims_in_id_token",
|
"include_claims_in_id_token",
|
||||||
"jwt_alg",
|
"signing_key",
|
||||||
"rsa_key",
|
|
||||||
"redirect_uris",
|
"redirect_uris",
|
||||||
"sub_mode",
|
"sub_mode",
|
||||||
"property_mappings",
|
"property_mappings",
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 4.0 on 2021-12-22 21:04
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('authentik_providers_oauth2', '0007_auto_20201016_1107_squashed_0017_alter_oauth2provider_token_validity'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='oauth2provider',
|
||||||
|
old_name='rsa_key',
|
||||||
|
new_name='signing_key',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='oauth2provider',
|
||||||
|
name='jwt_alg',
|
||||||
|
),
|
||||||
|
]
|
|
@ -8,6 +8,8 @@ from datetime import datetime
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from typing import Any, Optional, Type
|
from typing import Any, Optional, Type
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
|
||||||
|
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
||||||
|
|
||||||
from dacite import from_dict
|
from dacite import from_dict
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -88,6 +90,7 @@ class JWTAlgorithms(models.TextChoices):
|
||||||
|
|
||||||
HS256 = "HS256", _("HS256 (Symmetric Encryption)")
|
HS256 = "HS256", _("HS256 (Symmetric Encryption)")
|
||||||
RS256 = "RS256", _("RS256 (Asymmetric Encryption)")
|
RS256 = "RS256", _("RS256 (Asymmetric Encryption)")
|
||||||
|
EC256 = "EC256", _("EC256 (Asymmetric Encryption)")
|
||||||
|
|
||||||
|
|
||||||
class ScopeMapping(PropertyMapping):
|
class ScopeMapping(PropertyMapping):
|
||||||
|
@ -145,13 +148,6 @@ class OAuth2Provider(Provider):
|
||||||
verbose_name=_("Client Secret"),
|
verbose_name=_("Client Secret"),
|
||||||
default=generate_key,
|
default=generate_key,
|
||||||
)
|
)
|
||||||
jwt_alg = models.CharField(
|
|
||||||
max_length=10,
|
|
||||||
choices=JWTAlgorithms.choices,
|
|
||||||
default=JWTAlgorithms.RS256,
|
|
||||||
verbose_name=_("JWT Algorithm"),
|
|
||||||
help_text=_(JWTAlgorithms.__doc__),
|
|
||||||
)
|
|
||||||
redirect_uris = models.TextField(
|
redirect_uris = models.TextField(
|
||||||
default="",
|
default="",
|
||||||
blank=True,
|
blank=True,
|
||||||
|
@ -207,7 +203,7 @@ class OAuth2Provider(Provider):
|
||||||
help_text=_(("Configure how the issuer field of the ID Token should be filled.")),
|
help_text=_(("Configure how the issuer field of the ID Token should be filled.")),
|
||||||
)
|
)
|
||||||
|
|
||||||
rsa_key = models.ForeignKey(
|
signing_key = models.ForeignKey(
|
||||||
CertificateKeyPair,
|
CertificateKeyPair,
|
||||||
verbose_name=_("RSA Key"),
|
verbose_name=_("RSA Key"),
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
|
@ -231,29 +227,18 @@ class OAuth2Provider(Provider):
|
||||||
token.access_token = token.create_access_token(user, request)
|
token.access_token = token.create_access_token(user, request)
|
||||||
return token
|
return token
|
||||||
|
|
||||||
def get_jwt_key(self) -> str:
|
def get_jwt_key(self) -> tuple[str, str]:
|
||||||
"""
|
"""Get either the configured certificate or the client secret"""
|
||||||
Takes a provider and returns the set of keys associated with it.
|
if not self.signing_key:
|
||||||
Returns a list of keys.
|
# No Certificate at all, assume HS256
|
||||||
"""
|
return self.client_secret, JWTAlgorithms.HS256
|
||||||
if self.jwt_alg == JWTAlgorithms.RS256:
|
key: CertificateKeyPair = self.signing_key
|
||||||
# if the user selected RS256 but didn't select a
|
private_key = key.private_key
|
||||||
# CertificateKeyPair, we fall back to HS256
|
if isinstance(private_key, RSAPrivateKey):
|
||||||
if not self.rsa_key:
|
return key.key_data, JWTAlgorithms.RS256
|
||||||
Event.new(
|
if isinstance(private_key, EllipticCurvePrivateKey):
|
||||||
EventAction.CONFIGURATION_ERROR,
|
return key.key_data, JWTAlgorithms.EC256
|
||||||
provider=self,
|
raise Exception(f"Invalid private key type: {type(private_key)}")
|
||||||
message="Provider was configured for RS256, but no key was selected.",
|
|
||||||
).save()
|
|
||||||
self.jwt_alg = JWTAlgorithms.HS256
|
|
||||||
self.save()
|
|
||||||
else:
|
|
||||||
return self.rsa_key.key_data
|
|
||||||
|
|
||||||
if self.jwt_alg == JWTAlgorithms.HS256:
|
|
||||||
return self.client_secret
|
|
||||||
|
|
||||||
raise Exception("Unsupported key algorithm.")
|
|
||||||
|
|
||||||
def get_issuer(self, request: HttpRequest) -> Optional[str]:
|
def get_issuer(self, request: HttpRequest) -> Optional[str]:
|
||||||
"""Get issuer, based on request"""
|
"""Get issuer, based on request"""
|
||||||
|
@ -293,13 +278,13 @@ class OAuth2Provider(Provider):
|
||||||
def encode(self, payload: dict[str, Any]) -> str:
|
def encode(self, payload: dict[str, Any]) -> str:
|
||||||
"""Represent the ID Token as a JSON Web Token (JWT)."""
|
"""Represent the ID Token as a JSON Web Token (JWT)."""
|
||||||
headers = {}
|
headers = {}
|
||||||
if self.rsa_key:
|
if self.signing_key:
|
||||||
headers["kid"] = self.rsa_key.kid
|
headers["kid"] = self.signing_key.kid
|
||||||
key = self.get_jwt_key()
|
key, alg = self.get_jwt_key()
|
||||||
# If the provider does not have an RSA Key assigned, it was switched to Symmetric
|
# If the provider does not have an RSA Key assigned, it was switched to Symmetric
|
||||||
self.refresh_from_db()
|
self.refresh_from_db()
|
||||||
# pyright: reportGeneralTypeIssues=false
|
# pyright: reportGeneralTypeIssues=false
|
||||||
return encode(payload, key, algorithm=self.jwt_alg, headers=headers)
|
return encode(payload, key, algorithm=alg, headers=headers)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
"""Test oauth2 provider API"""
|
|
||||||
from django.urls import reverse
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
|
||||||
from authentik.providers.oauth2.models import JWTAlgorithms
|
|
||||||
|
|
||||||
|
|
||||||
class TestOAuth2ProviderAPI(APITestCase):
|
|
||||||
"""Test oauth2 provider API"""
|
|
||||||
|
|
||||||
def setUp(self) -> None:
|
|
||||||
super().setUp()
|
|
||||||
self.user = create_test_admin_user()
|
|
||||||
self.client.force_login(self.user)
|
|
||||||
|
|
||||||
def test_validate(self):
|
|
||||||
"""Test OAuth2 Provider validation"""
|
|
||||||
response = self.client.post(
|
|
||||||
reverse(
|
|
||||||
"authentik_api:oauth2provider-list",
|
|
||||||
),
|
|
||||||
data={
|
|
||||||
"name": "test",
|
|
||||||
"jwt_alg": str(JWTAlgorithms.RS256),
|
|
||||||
"authorization_flow": create_test_flow().pk,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.assertJSONEqual(
|
|
||||||
response.content.decode(),
|
|
||||||
{"jwt_alg": ["RS256 requires a Certificate-Key-Pair to be selected."]},
|
|
||||||
)
|
|
|
@ -218,7 +218,7 @@ class TestAuthorize(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=flow,
|
authorization_flow=flow,
|
||||||
redirect_uris="http://localhost",
|
redirect_uris="http://localhost",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
Application.objects.create(name="app", slug="app", provider=provider)
|
Application.objects.create(name="app", slug="app", provider=provider)
|
||||||
state = generate_id()
|
state = generate_id()
|
||||||
|
|
|
@ -25,7 +25,7 @@ class TestJWKS(OAuthTestCase):
|
||||||
client_id="test",
|
client_id="test",
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://local.invalid",
|
redirect_uris="http://local.invalid",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
app = Application.objects.create(name="test", slug="test", provider=provider)
|
app = Application.objects.create(name="test", slug="test", provider=provider)
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
|
|
|
@ -35,7 +35,7 @@ class TestToken(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://testserver",
|
redirect_uris="http://testserver",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
||||||
user = create_test_admin_user()
|
user = create_test_admin_user()
|
||||||
|
@ -62,7 +62,7 @@ class TestToken(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://testserver",
|
redirect_uris="http://testserver",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
||||||
request = self.factory.post(
|
request = self.factory.post(
|
||||||
|
@ -85,7 +85,7 @@ class TestToken(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://local.invalid",
|
redirect_uris="http://local.invalid",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
||||||
user = create_test_admin_user()
|
user = create_test_admin_user()
|
||||||
|
@ -114,7 +114,7 @@ class TestToken(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://local.invalid",
|
redirect_uris="http://local.invalid",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
# Needs to be assigned to an application for iss to be set
|
# Needs to be assigned to an application for iss to be set
|
||||||
self.app.provider = provider
|
self.app.provider = provider
|
||||||
|
@ -156,7 +156,7 @@ class TestToken(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://local.invalid",
|
redirect_uris="http://local.invalid",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
# Needs to be assigned to an application for iss to be set
|
# Needs to be assigned to an application for iss to be set
|
||||||
self.app.provider = provider
|
self.app.provider = provider
|
||||||
|
@ -205,7 +205,7 @@ class TestToken(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://local.invalid",
|
redirect_uris="http://local.invalid",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
header = b64encode(f"{provider.client_id}:{provider.client_secret}".encode()).decode()
|
||||||
user = create_test_admin_user()
|
user = create_test_admin_user()
|
||||||
|
@ -250,7 +250,7 @@ class TestToken(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://testserver",
|
redirect_uris="http://testserver",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
# Needs to be assigned to an application for iss to be set
|
# Needs to be assigned to an application for iss to be set
|
||||||
self.app.provider = provider
|
self.app.provider = provider
|
||||||
|
|
|
@ -27,7 +27,7 @@ class TestUserinfo(OAuthTestCase):
|
||||||
client_secret=generate_key(),
|
client_secret=generate_key(),
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="",
|
redirect_uris="",
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
)
|
)
|
||||||
self.provider.property_mappings.set(ScopeMapping.objects.all())
|
self.provider.property_mappings.set(ScopeMapping.objects.all())
|
||||||
# Needs to be assigned to an application for iss to be set
|
# Needs to be assigned to an application for iss to be set
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from jwt import decode
|
from jwt import decode
|
||||||
|
|
||||||
from authentik.providers.oauth2.models import JWTAlgorithms, OAuth2Provider, RefreshToken
|
from authentik.providers.oauth2.models import OAuth2Provider, RefreshToken
|
||||||
|
|
||||||
|
|
||||||
class OAuthTestCase(TestCase):
|
class OAuthTestCase(TestCase):
|
||||||
|
@ -19,13 +19,11 @@ class OAuthTestCase(TestCase):
|
||||||
|
|
||||||
def validate_jwt(self, token: RefreshToken, provider: OAuth2Provider):
|
def validate_jwt(self, token: RefreshToken, provider: OAuth2Provider):
|
||||||
"""Validate that all required fields are set"""
|
"""Validate that all required fields are set"""
|
||||||
key = provider.client_secret
|
key, alg = provider.get_jwt_key()
|
||||||
if provider.jwt_alg == JWTAlgorithms.RS256:
|
|
||||||
key = provider.rsa_key.public_key
|
|
||||||
jwt = decode(
|
jwt = decode(
|
||||||
token.access_token,
|
token.access_token,
|
||||||
key,
|
key,
|
||||||
algorithms=[provider.jwt_alg],
|
algorithms=[alg],
|
||||||
audience=provider.client_id,
|
audience=provider.client_id,
|
||||||
)
|
)
|
||||||
id_token = token.id_token.to_dict()
|
id_token = token.id_token.to_dict()
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
"""authentik OAuth2 JWKS Views"""
|
"""authentik OAuth2 JWKS Views"""
|
||||||
from base64 import urlsafe_b64encode
|
from base64 import urlsafe_b64encode
|
||||||
|
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey, EllipticCurvePublicKey
|
||||||
|
|
||||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
|
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey
|
||||||
from django.http import HttpRequest, HttpResponse, JsonResponse
|
from django.http import HttpRequest, HttpResponse, JsonResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
@ -25,22 +26,38 @@ class JWKSView(View):
|
||||||
"""Show RSA Key data for Provider"""
|
"""Show RSA Key data for Provider"""
|
||||||
application = get_object_or_404(Application, slug=application_slug)
|
application = get_object_or_404(Application, slug=application_slug)
|
||||||
provider: OAuth2Provider = get_object_or_404(OAuth2Provider, pk=application.provider_id)
|
provider: OAuth2Provider = get_object_or_404(OAuth2Provider, pk=application.provider_id)
|
||||||
|
private_key = provider.signing_key
|
||||||
|
|
||||||
response_data = {}
|
response_data = {}
|
||||||
|
|
||||||
if provider.jwt_alg == JWTAlgorithms.RS256 and provider.rsa_key:
|
if private_key:
|
||||||
public_key: RSAPublicKey = provider.rsa_key.private_key.public_key()
|
if isinstance(private_key, RSAPrivateKey):
|
||||||
public_numbers = public_key.public_numbers()
|
public_key: RSAPublicKey = private_key.public_key()
|
||||||
response_data["keys"] = [
|
public_numbers = public_key.public_numbers()
|
||||||
{
|
response_data["keys"] = [
|
||||||
"kty": "RSA",
|
{
|
||||||
"alg": "RS256",
|
"kty": "RSA",
|
||||||
"use": "sig",
|
"alg": JWTAlgorithms.RS256,
|
||||||
"kid": provider.rsa_key.kid,
|
"use": "sig",
|
||||||
"n": b64_enc(public_numbers.n),
|
"kid": private_key.kid,
|
||||||
"e": b64_enc(public_numbers.e),
|
"n": b64_enc(public_numbers.n),
|
||||||
}
|
"e": b64_enc(public_numbers.e),
|
||||||
]
|
}
|
||||||
|
]
|
||||||
|
elif isinstance(private_key, EllipticCurvePrivateKey):
|
||||||
|
public_key: EllipticCurvePublicKey = private_key.public_key()
|
||||||
|
public_numbers = public_key.public_numbers()
|
||||||
|
response_data["keys"] = [
|
||||||
|
{
|
||||||
|
"kty": "EC",
|
||||||
|
"alg": JWTAlgorithms.EC256,
|
||||||
|
"use": "sig",
|
||||||
|
"kid": private_key.kid,
|
||||||
|
"n": b64_enc(public_numbers.n),
|
||||||
|
"e": b64_enc(public_numbers.e),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
response = JsonResponse(response_data)
|
response = JsonResponse(response_data)
|
||||||
response["Access-Control-Allow-Origin"] = "*"
|
response["Access-Control-Allow-Origin"] = "*"
|
||||||
|
|
|
@ -39,6 +39,7 @@ class ProviderInfoView(View):
|
||||||
)
|
)
|
||||||
if SCOPE_OPENID not in scopes:
|
if SCOPE_OPENID not in scopes:
|
||||||
scopes.append(SCOPE_OPENID)
|
scopes.append(SCOPE_OPENID)
|
||||||
|
_, supported_alg = provider.get_jwt_key()
|
||||||
return {
|
return {
|
||||||
"issuer": provider.get_issuer(self.request),
|
"issuer": provider.get_issuer(self.request),
|
||||||
"authorization_endpoint": self.request.build_absolute_uri(
|
"authorization_endpoint": self.request.build_absolute_uri(
|
||||||
|
@ -78,7 +79,7 @@ class ProviderInfoView(View):
|
||||||
GRANT_TYPE_REFRESH_TOKEN,
|
GRANT_TYPE_REFRESH_TOKEN,
|
||||||
GrantTypes.IMPLICIT,
|
GrantTypes.IMPLICIT,
|
||||||
],
|
],
|
||||||
"id_token_signing_alg_values_supported": [provider.jwt_alg],
|
"id_token_signing_alg_values_supported": [supported_alg],
|
||||||
# See: http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
# See: http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes
|
||||||
"subject_types_supported": ["public"],
|
"subject_types_supported": ["public"],
|
||||||
"token_endpoint_auth_methods_supported": [
|
"token_endpoint_auth_methods_supported": [
|
||||||
|
|
|
@ -18,7 +18,6 @@ from authentik.providers.oauth2.constants import (
|
||||||
)
|
)
|
||||||
from authentik.providers.oauth2.models import (
|
from authentik.providers.oauth2.models import (
|
||||||
ClientTypes,
|
ClientTypes,
|
||||||
JWTAlgorithms,
|
|
||||||
OAuth2Provider,
|
OAuth2Provider,
|
||||||
ScopeMapping,
|
ScopeMapping,
|
||||||
)
|
)
|
||||||
|
@ -128,8 +127,7 @@ class ProxyProvider(OutpostModel, OAuth2Provider):
|
||||||
def set_oauth_defaults(self):
|
def set_oauth_defaults(self):
|
||||||
"""Ensure all OAuth2-related settings are correct"""
|
"""Ensure all OAuth2-related settings are correct"""
|
||||||
self.client_type = ClientTypes.CONFIDENTIAL
|
self.client_type = ClientTypes.CONFIDENTIAL
|
||||||
self.jwt_alg = JWTAlgorithms.HS256
|
self.signing_key = None
|
||||||
self.rsa_key = None
|
|
||||||
scopes = ScopeMapping.objects.filter(
|
scopes = ScopeMapping.objects.filter(
|
||||||
scope_name__in=[
|
scope_name__in=[
|
||||||
SCOPE_OPENID,
|
SCOPE_OPENID,
|
||||||
|
|
48
schema.yml
48
schema.yml
|
@ -10858,15 +10858,6 @@ paths:
|
||||||
- global
|
- global
|
||||||
- per_provider
|
- per_provider
|
||||||
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.
|
||||||
- in: query
|
|
||||||
name: jwt_alg
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
title: JWT Algorithm
|
|
||||||
enum:
|
|
||||||
- HS256
|
|
||||||
- RS256
|
|
||||||
description: Algorithm used to sign the JWT Token
|
|
||||||
- in: query
|
- in: query
|
||||||
name: name
|
name: name
|
||||||
schema:
|
schema:
|
||||||
|
@ -10902,17 +10893,17 @@ paths:
|
||||||
name: redirect_uris
|
name: redirect_uris
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
- in: query
|
|
||||||
name: rsa_key
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
format: uuid
|
|
||||||
- name: search
|
- name: search
|
||||||
required: false
|
required: false
|
||||||
in: query
|
in: query
|
||||||
description: A search term.
|
description: A search term.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: signing_key
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
- in: query
|
- in: query
|
||||||
name: sub_mode
|
name: sub_mode
|
||||||
schema:
|
schema:
|
||||||
|
@ -22221,11 +22212,6 @@ components:
|
||||||
- global
|
- global
|
||||||
- per_provider
|
- per_provider
|
||||||
type: string
|
type: string
|
||||||
JwtAlgEnum:
|
|
||||||
enum:
|
|
||||||
- HS256
|
|
||||||
- RS256
|
|
||||||
type: string
|
|
||||||
KubernetesServiceConnection:
|
KubernetesServiceConnection:
|
||||||
type: object
|
type: object
|
||||||
description: KubernetesServiceConnection Serializer
|
description: KubernetesServiceConnection Serializer
|
||||||
|
@ -23099,15 +23085,11 @@ components:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Include User claims from scopes in the id_token, for applications
|
description: Include User claims from scopes in the id_token, for applications
|
||||||
that don't access the userinfo endpoint.
|
that don't access the userinfo endpoint.
|
||||||
jwt_alg:
|
signing_key:
|
||||||
allOf:
|
|
||||||
- $ref: '#/components/schemas/JwtAlgEnum'
|
|
||||||
title: JWT Algorithm
|
|
||||||
description: Algorithm used to sign the JWT Token
|
|
||||||
rsa_key:
|
|
||||||
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:
|
||||||
|
@ -23175,15 +23157,11 @@ components:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Include User claims from scopes in the id_token, for applications
|
description: Include User claims from scopes in the id_token, for applications
|
||||||
that don't access the userinfo endpoint.
|
that don't access the userinfo endpoint.
|
||||||
jwt_alg:
|
signing_key:
|
||||||
allOf:
|
|
||||||
- $ref: '#/components/schemas/JwtAlgEnum'
|
|
||||||
title: JWT Algorithm
|
|
||||||
description: Algorithm used to sign the JWT Token
|
|
||||||
rsa_key:
|
|
||||||
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:
|
||||||
|
@ -27509,15 +27487,11 @@ components:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Include User claims from scopes in the id_token, for applications
|
description: Include User claims from scopes in the id_token, for applications
|
||||||
that don't access the userinfo endpoint.
|
that don't access the userinfo endpoint.
|
||||||
jwt_alg:
|
signing_key:
|
||||||
allOf:
|
|
||||||
- $ref: '#/components/schemas/JwtAlgEnum'
|
|
||||||
title: JWT Algorithm
|
|
||||||
description: Algorithm used to sign the JWT Token
|
|
||||||
rsa_key:
|
|
||||||
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:
|
||||||
|
|
|
@ -81,7 +81,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:3000/",
|
redirect_uris="http://localhost:3000/",
|
||||||
authorization_flow=authorization_flow,
|
authorization_flow=authorization_flow,
|
||||||
)
|
)
|
||||||
|
@ -123,7 +123,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:3000/login/generic_oauth",
|
redirect_uris="http://localhost:3000/login/generic_oauth",
|
||||||
authorization_flow=authorization_flow,
|
authorization_flow=authorization_flow,
|
||||||
)
|
)
|
||||||
|
@ -178,7 +178,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:3000/login/generic_oauth",
|
redirect_uris="http://localhost:3000/login/generic_oauth",
|
||||||
authorization_flow=authorization_flow,
|
authorization_flow=authorization_flow,
|
||||||
)
|
)
|
||||||
|
@ -243,7 +243,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:3000/login/generic_oauth",
|
redirect_uris="http://localhost:3000/login/generic_oauth",
|
||||||
)
|
)
|
||||||
provider.property_mappings.set(
|
provider.property_mappings.set(
|
||||||
|
@ -315,7 +315,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:3000/login/generic_oauth",
|
redirect_uris="http://localhost:3000/login/generic_oauth",
|
||||||
)
|
)
|
||||||
provider.property_mappings.set(
|
provider.property_mappings.set(
|
||||||
|
|
|
@ -80,7 +80,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:9009/",
|
redirect_uris="http://localhost:9009/",
|
||||||
authorization_flow=authorization_flow,
|
authorization_flow=authorization_flow,
|
||||||
)
|
)
|
||||||
|
@ -122,7 +122,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:9009/auth/callback",
|
redirect_uris="http://localhost:9009/auth/callback",
|
||||||
authorization_flow=authorization_flow,
|
authorization_flow=authorization_flow,
|
||||||
)
|
)
|
||||||
|
@ -172,7 +172,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:9009/auth/callback",
|
redirect_uris="http://localhost:9009/auth/callback",
|
||||||
)
|
)
|
||||||
provider.property_mappings.set(
|
provider.property_mappings.set(
|
||||||
|
@ -235,7 +235,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:9009/auth/callback",
|
redirect_uris="http://localhost:9009/auth/callback",
|
||||||
)
|
)
|
||||||
provider.property_mappings.set(
|
provider.property_mappings.set(
|
||||||
|
|
|
@ -80,7 +80,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:9009/",
|
redirect_uris="http://localhost:9009/",
|
||||||
authorization_flow=authorization_flow,
|
authorization_flow=authorization_flow,
|
||||||
)
|
)
|
||||||
|
@ -122,7 +122,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:9009/implicit/",
|
redirect_uris="http://localhost:9009/implicit/",
|
||||||
authorization_flow=authorization_flow,
|
authorization_flow=authorization_flow,
|
||||||
)
|
)
|
||||||
|
@ -168,7 +168,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:9009/implicit/",
|
redirect_uris="http://localhost:9009/implicit/",
|
||||||
)
|
)
|
||||||
provider.property_mappings.set(
|
provider.property_mappings.set(
|
||||||
|
@ -228,7 +228,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase):
|
||||||
client_type=ClientTypes.CONFIDENTIAL,
|
client_type=ClientTypes.CONFIDENTIAL,
|
||||||
client_id=self.client_id,
|
client_id=self.client_id,
|
||||||
client_secret=self.client_secret,
|
client_secret=self.client_secret,
|
||||||
rsa_key=create_test_cert(),
|
signing_key=create_test_cert(),
|
||||||
redirect_uris="http://localhost:9009/implicit/",
|
redirect_uris="http://localhost:9009/implicit/",
|
||||||
)
|
)
|
||||||
provider.property_mappings.set(
|
provider.property_mappings.set(
|
||||||
|
|
|
@ -175,9 +175,9 @@ ${this.instance?.redirectUris}</textarea
|
||||||
${t`If no explicit redirect URIs are specified, any redirect URI is allowed.`}
|
${t`If no explicit redirect URIs are specified, any redirect URI is allowed.`}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal label=${t`RSA Key`} name="rsaKey">
|
<ak-form-element-horizontal label=${t`Signing Key`} name="signingKey">
|
||||||
<select class="pf-c-form-control">
|
<select class="pf-c-form-control">
|
||||||
<option value="" ?selected=${this.instance?.rsaKey === undefined}>
|
<option value="" ?selected=${this.instance?.signingKey === undefined}>
|
||||||
---------
|
---------
|
||||||
</option>
|
</option>
|
||||||
${until(
|
${until(
|
||||||
|
@ -188,7 +188,7 @@ ${this.instance?.redirectUris}</textarea
|
||||||
})
|
})
|
||||||
.then((keys) => {
|
.then((keys) => {
|
||||||
return keys.results.map((key) => {
|
return keys.results.map((key) => {
|
||||||
let selected = this.instance?.rsaKey === key.pk;
|
let selected = this.instance?.signingKey === key.pk;
|
||||||
if (keys.results.length === 1) {
|
if (keys.results.length === 1) {
|
||||||
selected = true;
|
selected = true;
|
||||||
}
|
}
|
||||||
|
@ -203,9 +203,7 @@ ${this.instance?.redirectUris}</textarea
|
||||||
html`<option>${t`Loading...`}</option>`,
|
html`<option>${t`Loading...`}</option>`,
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">${t`Key used to sign the tokens.`}</p>
|
||||||
${t`Key used to sign the tokens. Only required when JWT Algorithm is set to RS256.`}
|
|
||||||
</p>
|
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
</div>
|
</div>
|
||||||
</ak-form-group>
|
</ak-form-group>
|
||||||
|
@ -252,29 +250,6 @@ ${this.instance?.redirectUris}</textarea
|
||||||
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
${t`(Format: hours=-1;minutes=-2;seconds=-3).`}
|
||||||
</p>
|
</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal
|
|
||||||
label=${t`JWT Algorithm`}
|
|
||||||
?required=${true}
|
|
||||||
name="jwtAlg"
|
|
||||||
>
|
|
||||||
<select class="pf-c-form-control">
|
|
||||||
<option
|
|
||||||
value=${JwtAlgEnum.Rs256}
|
|
||||||
?selected=${this.instance?.jwtAlg === JwtAlgEnum.Rs256}
|
|
||||||
>
|
|
||||||
${t`RS256 (Asymmetric Encryption)`}
|
|
||||||
</option>
|
|
||||||
<option
|
|
||||||
value=${JwtAlgEnum.Hs256}
|
|
||||||
?selected=${this.instance?.jwtAlg === JwtAlgEnum.Hs256}
|
|
||||||
>
|
|
||||||
${t`HS256 (Symmetric Encryption)`}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
<p class="pf-c-form__helper-text">
|
|
||||||
${t`Algorithm used to sign the JWT Tokens.`}
|
|
||||||
</p>
|
|
||||||
</ak-form-element-horizontal>
|
|
||||||
<ak-form-element-horizontal label=${t`Scopes`} name="propertyMappings">
|
<ak-form-element-horizontal label=${t`Scopes`} name="propertyMappings">
|
||||||
<select class="pf-c-form-control" multiple>
|
<select class="pf-c-form-control" multiple>
|
||||||
${until(
|
${until(
|
||||||
|
|
|
@ -20,7 +20,6 @@ The following placeholders will be used:
|
||||||
Create an OAuth2/OpenID provider with the following parameters:
|
Create an OAuth2/OpenID provider with the following parameters:
|
||||||
|
|
||||||
- Client Type: `Confidential`
|
- Client Type: `Confidential`
|
||||||
- JWT Algorithm: `RS256`
|
|
||||||
- Redirect URIs: `https://guacamole.company/` (depending on your Tomcat setup, you might have to add `/guacamole/` if the application runs in a subfolder)
|
- Redirect URIs: `https://guacamole.company/` (depending on your Tomcat setup, you might have to add `/guacamole/` if the application runs in a subfolder)
|
||||||
- Scopes: OpenID, Email and Profile
|
- Scopes: OpenID, Email and Profile
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ title: Budibase
|
||||||
From https://github.com/Budibase/budibase
|
From https://github.com/Budibase/budibase
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Budibase is an open source low-code platform, and the easiest way to build internal tools that improve productivity.
|
Budibase is an open source low-code platform, and the easiest way to build internal tools that improve productivity.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Preparation
|
## Preparation
|
||||||
|
@ -20,7 +20,6 @@ The following placeholders will be used:
|
||||||
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
||||||
|
|
||||||
- Client Type: `Confidential`
|
- Client Type: `Confidential`
|
||||||
- JWT Algorithm: `RS256`
|
|
||||||
- Scopes: OpenID, Email and Profile
|
- Scopes: OpenID, Email and Profile
|
||||||
- RSA Key: Select any available key
|
- RSA Key: Select any available key
|
||||||
- Redirect URIs: `https://budibase.company/api/global/auth/oidc/callback`
|
- Redirect URIs: `https://budibase.company/api/global/auth/oidc/callback`
|
||||||
|
@ -33,4 +32,4 @@ In Budibase under `Auth` set the following values
|
||||||
|
|
||||||
- Config URL: `https://authentik.company/application/o/<Slug of the application from above>/.well-known/openid-configuration`
|
- Config URL: `https://authentik.company/application/o/<Slug of the application from above>/.well-known/openid-configuration`
|
||||||
- Client ID: `Client ID from above`
|
- Client ID: `Client ID from above`
|
||||||
- Client Secret: `Client Secret from above`
|
- Client Secret: `Client Secret from above`
|
||||||
|
|
|
@ -20,7 +20,6 @@ The following placeholders will be used:
|
||||||
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
||||||
|
|
||||||
- Client Type: `Confidential`
|
- Client Type: `Confidential`
|
||||||
- JWT Algorithm: `RS256`
|
|
||||||
- Scopes: OpenID, Email and Profile
|
- Scopes: OpenID, Email and Profile
|
||||||
- RSA Key: Select any available key
|
- RSA Key: Select any available key
|
||||||
- Redirect URIs: `https://grafana.company/login/generic_oauth`
|
- Redirect URIs: `https://grafana.company/login/generic_oauth`
|
||||||
|
@ -86,12 +85,12 @@ role_attribute_path = contains(groups[*], 'Grafana Admins') && 'Admin' || contai
|
||||||
|
|
||||||
In the configuration above you can see an example of a role mapping. Upon login, this configuration looks at the groups of which the current user is a member. If any of the specified group names are found, the user will be granted the resulting role in Grafana.
|
In the configuration above you can see an example of a role mapping. Upon login, this configuration looks at the groups of which the current user is a member. If any of the specified group names are found, the user will be granted the resulting role in Grafana.
|
||||||
|
|
||||||
In the example shown above, one of the specified group names is "Grafana Admins". If the user is a member of this group, they will be granted the "Admin" role in Grafana.
|
In the example shown above, one of the specified group names is "Grafana Admins". If the user is a member of this group, they will be granted the "Admin" role in Grafana.
|
||||||
If the user is not a member of the "Grafana Admins" group, it moves on to see if the user is a member of the "Grafana Editors" group. If they are, they are granted the "Editor" role. Finally, if the user is not found to be a member of either of these groups, it fails back to granting the "Viewer" role.
|
If the user is not a member of the "Grafana Admins" group, it moves on to see if the user is a member of the "Grafana Editors" group. If they are, they are granted the "Editor" role. Finally, if the user is not found to be a member of either of these groups, it fails back to granting the "Viewer" role.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
contains(groups[*], 'Grafana Admins') && 'Admin' || contains(groups[*], 'Grafana Editors') && 'Editor' || 'Viewer'
|
contains(groups[*], 'Grafana Admins') && 'Admin' || contains(groups[*], 'Grafana Editors') && 'Editor' || 'Viewer'
|
||||||
^ attribute to search ^ group to search for ^ role to grant ^ or grant "Viewer" role.
|
^ attribute to search ^ group to search for ^ role to grant ^ or grant "Viewer" role.
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information on group/role mappings, see [Grafana's docs](https://grafana.com/docs/grafana/latest/auth/generic-oauth/#role-mapping).
|
For more information on group/role mappings, see [Grafana's docs](https://grafana.com/docs/grafana/latest/auth/generic-oauth/#role-mapping).
|
||||||
|
@ -105,4 +104,4 @@ If you get `user does not belong to org` error when trying to log into grafana f
|
||||||
[users]
|
[users]
|
||||||
auto_assign_org = true
|
auto_assign_org = true
|
||||||
auto_assign_org_id = <id-of-your-default-organization>
|
auto_assign_org_id = <id-of-your-default-organization>
|
||||||
```
|
```
|
||||||
|
|
|
@ -20,7 +20,6 @@ The following placeholders will be used:
|
||||||
Create an OAuth2/OpenID provider with the following parameters:
|
Create an OAuth2/OpenID provider with the following parameters:
|
||||||
|
|
||||||
- Client Type: `Confidential`
|
- Client Type: `Confidential`
|
||||||
- JWT Algorithm: `RS256`
|
|
||||||
- Redirect URIs: `https://harbor.company/c/oidc/callback`
|
- Redirect URIs: `https://harbor.company/c/oidc/callback`
|
||||||
- Scopes: OpenID, Email and Profile
|
- Scopes: OpenID, Email and Profile
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ The following placeholders will be used:
|
||||||
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
||||||
|
|
||||||
- Client Type: `Confidential`
|
- Client Type: `Confidential`
|
||||||
- JWT Algorithm: `RS256`
|
|
||||||
- Scopes: OpenID, Email and Profile
|
- Scopes: OpenID, Email and Profile
|
||||||
- RSA Key: Select any available key
|
- RSA Key: Select any available key
|
||||||
- Redirect URIs: `https://hedgedoc.company/auth/oauth2/callback`
|
- Redirect URIs: `https://hedgedoc.company/auth/oauth2/callback`
|
||||||
|
|
|
@ -21,7 +21,6 @@ The following placeholders will be used:
|
||||||
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
||||||
|
|
||||||
- Client Type: `Confidential`
|
- Client Type: `Confidential`
|
||||||
- JWT Algorithm: `RS256`
|
|
||||||
- Scopes: OpenID, Email and Profile
|
- Scopes: OpenID, Email and Profile
|
||||||
- RSA Key: Select any available key
|
- RSA Key: Select any available key
|
||||||
- Redirect URIs: `https://matrix.company/_synapse/client/oidc/callback`
|
- Redirect URIs: `https://matrix.company/_synapse/client/oidc/callback`
|
||||||
|
|
|
@ -28,7 +28,6 @@ return {
|
||||||
Create an application in authentik. Create an _OAuth2/OpenID Provider_ with the following parameters:
|
Create an application in authentik. Create an _OAuth2/OpenID Provider_ with the following parameters:
|
||||||
|
|
||||||
- Client Type: `Public`
|
- Client Type: `Public`
|
||||||
- JWT Algorithm: `RS256`
|
|
||||||
- Scopes: OpenID, Email, Profile and the scope you created above
|
- Scopes: OpenID, Email, Profile and the scope you created above
|
||||||
- RSA Key: Select any available key
|
- RSA Key: Select any available key
|
||||||
- Redirect URIs: `https://minio.company/oauth_callback`
|
- Redirect URIs: `https://minio.company/oauth_callback`
|
||||||
|
|
|
@ -20,7 +20,6 @@ The following placeholders will be used:
|
||||||
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
||||||
|
|
||||||
- Client Type: `Confidential`
|
- Client Type: `Confidential`
|
||||||
- JWT Algorithm: `RS256`
|
|
||||||
- Scopes: OpenID, Email and Profile
|
- Scopes: OpenID, Email and Profile
|
||||||
- RSA Key: Select any available key
|
- RSA Key: Select any available key
|
||||||
- Redirect URIs: `https://wekan.company/_oauth/oidc`
|
- Redirect URIs: `https://wekan.company/_oauth/oidc`
|
||||||
|
|
Reference in a new issue