Skip to content

providers: use managed secrets for RADIUS and SCIM - #26100

Open
dominic-r wants to merge 38 commits into
dominic/rotation-providersfrom
dominic/rotation-radius-scim
Open

dominic-r wants to merge 38 commits into
dominic/rotation-providersfrom
dominic/rotation-radius-scim

Conversation

@dominic-r

@dominic-r dominic-r commented Sep 14, 2026 •

Copy link
Copy Markdown
Member

Details

Part of the managed secrets stack, see #25415.

What does this PR change?

RADIUS providers store their shared secret, and SCIM providers their token and Basic authentication password, as secrets. Like OAuth2 providers, RADIUS providers generate their shared secret (40 characters) when none is selected and always reference one.

Why is this change needed?

Each credential becomes a secret with its own permissions, rotation and audit trail. See #25415.

How was this tested?

Added tests for generated shared secrets, rejecting a cleared reference, and SCIM token and Basic authentication. Checked that outposts receive a rotated shared secret.

Linked issues


Checklist

  • The project has been linted, built, and tested (make all)
  • The documentation has been updated and formatted (make docs)
  • I have read the AI usage policy.

@netlify

netlify Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for authentik-docs ready!

Name Link
🔨 Latest commit edc3962
🔍 Latest deploy log https://app.netlify.com/projects/authentik-docs/deploys/6acaaaec55f2640008394d24
😎 Deploy Preview https://deploy-preview-26100--authentik-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@dominic-r
dominic-r force-pushed the dominic/rotation-radius-scim branch from 9a22fc8 to ee0cb37 Compare September 14, 2026 01:23
@codecov

codecov Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.53%. Comparing base (7eabd92) to head (6f05bc5).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@                      Coverage Diff                       @@
##           dominic/rotation-providers   #26100      +/-   ##
==============================================================
+ Coverage                       92.41%   92.53%   +0.12%     
==============================================================
  Files                            1207     1208       +1     
  Lines                           81843    81954     +111     
  Branches                         4489     4489              
==============================================================
+ Hits                            75634    75838     +204     
+ Misses                           6143     6050      -93     
  Partials                           66       66              
Flag Coverage Δ
conformance 32.07% <10.92%> (-0.04%) ⬇️
e2e 36.60% <11.76%> (+0.59%) ⬆️
integration 28.91% <10.92%> (-0.04%) ⬇️
rust 46.06% <ø> (ø)
unit 94.15% <100.00%> (+<0.01%) ⬆️
unit-migrate 94.15% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

authentik PR Installation instructions

Instructions for docker-compose

Add the following block to your .env file:

AUTHENTIK_IMAGE=ghcr.io/goauthentik/dev-server
AUTHENTIK_TAG=gh-eed79ff0cb6d09898235bd83460aa24c6acf078e
AUTHENTIK_OUTPOSTS__CONTAINER_IMAGE_BASE=ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s

Afterwards, run the upgrade commands from the latest release notes.

Instructions for Kubernetes

Add the following block to your values.yml file:

authentik:
    outposts:
        container_image_base: ghcr.io/goauthentik/dev-%(type)s:gh-%(build_hash)s
global:
    image:
        repository: ghcr.io/goauthentik/dev-server
        tag: gh-eed79ff0cb6d09898235bd83460aa24c6acf078e

Afterwards, run the upgrade commands from the latest release notes.

@dominic-r
dominic-r force-pushed the dominic/rotation-radius-scim branch from ee0cb37 to c7613e4 Compare September 14, 2026 01:55
@dominic-r
dominic-r force-pushed the dominic/rotation-radius-scim branch from c7613e4 to 835a51e Compare September 14, 2026 02:28
@dominic-r
dominic-r force-pushed the dominic/rotation-radius-scim branch from 835a51e to 8ab59ee Compare September 14, 2026 02:48
@dominic-r
dominic-r force-pushed the dominic/rotation-radius-scim branch from 8ab59ee to 03f1249 Compare September 14, 2026 03:32
@dominic-r
dominic-r force-pushed the dominic/rotation-radius-scim branch from 03f1249 to cf7295a Compare September 14, 2026 03:59
…s-scim

# Conflicts:
#	packages/client-ts/src/models/PatchedSCIMProviderRequest.ts
#	packages/client-ts/src/models/RadiusProvider.ts
#	packages/client-ts/src/models/SCIMProvider.ts
#	packages/client-ts/src/models/SCIMProviderRequest.ts
@dominic-r
dominic-r force-pushed the dominic/rotation-radius-scim branch from 7e3015c to 9d951e8 Compare October 5, 2026 17:57
…s-scim

# Conflicts:
#	web/src/admin/providers/radius/RadiusProviderFormForm.ts
#	web/src/admin/providers/scim/SCIMProviderFormForm.ts
…quired

RADIUS providers generate their shared secret with their own generator
again and always reference a secret, like OAuth2 providers, so clearing
the secret of an existing provider fails instead of replacing it.

SCIM keeps its legacy columns where they were, migrates both credentials
as text, and the form shows field errors and requires a token or
password when creating a provider again.
@dominic-r
dominic-r removed this pull request from stack #26110 October 10, 2026 21:17
@dominic-r
dominic-r added this pull request to stack #26820 October 10, 2026 21:17

@melizeche melizeche left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, same pattern as OAuth2. Left three comments: RADIUS providers sharing a secret, SCIM sending its token to a changed URL, and the rotated RADIUS secret length

# Shared secret is not a write-only field, as
# an admin might have to view it
"shared_secret",
"shared_secret_ref",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RADIUS outpost finds the provider by its shared secret:

// Lookup provider by shared secret
var pi *ProviderInstance
for _, p := range rs.providers {
if string(p.SharedSecret) == string(r.Secret) {
pi = p
selectedApp = pi.appSlug
break
}
}

and the first match wins. If two providers on the same outpost share a secret, requests can use the wrong provider’s flow and application policies. The picker makes this easy to configure now. Could we prevent that combination, or adjust the lookup so sharing a secret doesn’t make provider selection ambiguous?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 836aa1b. The API and blueprints reject a shared secret that another RADIUS provider already uses. The docs say RADIUS providers can't share a secret.


def __call__(self, request: Request) -> Request:
request.headers["Authorization"] = f"Bearer {self.provider.token}"
token = self.provider.token_ref.secret_value if self.provider.token_ref else ""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A user with change_scimprovider but without view_secret_value can still change url to a server they control and receive the existing token on the next sync. This predates the PR, but should changing the destination require permission to view the attached secret? Email, webhook and LDAP configurations probably have similar considerations wherever an editable destination receives stored credentials. If that’s out of scope, documenting the limitation would help

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope here, since it predates the stack and applies to every object that sends a stored credential to a destination you can edit. I documented it in df45d66 as a warning on the permissions page: changing where an object sends its credential sends the credential there, with SCIM URLs and LDAP and email servers as examples. Only assign change permission on those objects to roles that may know their credentials.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extended the docs warning in 361075c to the other objects that send a stored credential to a destination you can edit: LDAP sources, email and email authenticator setup stages, OAuth source token URLs, generic SMS provider URLs, and Fleet connector URLs.

class RadiusProvider(GeneratedSecretsMixin, OutpostModel, Provider):
"""Allow applications to authenticate against authentik's users using Radius."""

generated_secrets = {"shared_secret_ref": ("shared secret", generate_id)}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rotation increases this to at least default_token_length (60 by default). Some RADIUS clients have lower limits; e.g: Extreme ERS 3600 documents a maximum of 16 characters. So rotating a manually configured short secret can produce a value the client can’t accept, even when the admin updates both sides. Could rotation preserve the current length, or let the admin choose a compatible length?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 19a1ec2. Rotation now keeps the current length, so a 16-character secret stays 16 characters. The default token length only applies to new generated secrets. To change the length, replace the value.

@github-project-automation github-project-automation Bot moved this from Todo to In Progress in authentik Core Oct 27, 2026
@dominic-r
dominic-r requested a review from melizeche October 27, 2026 17:02

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants