Hand identity to an OIDC provider - #127
Merged
Merged
Conversation
Auth:Provider = oidc was the one value that did nothing. It now adds the OpenID Connect handler beside the existing cookie, so pages, [Authorize] and the cascading auth state are untouched — both schemes land on the same principal. Access is checked twice on purpose: the provider decides who may get a token, and Auth:Oidc:RequiredGroups re-checks the groups claim here. Neither layer relies on the other being right. Empty admits anyone the provider authenticated, so IdPs with no group model still work. /login challenges instead of showing a password box that cannot work, /logout is federated so the next visit does not sign straight back in, and first-run setup stops demanding an administrator that will never exist. Also fixes a regression from #122, released in v0.6.0: that diff moved the *arr env bootstrap under the `local` provider guard meant for the setup link, so the bootstrap silently did nothing unless local auth was configured. Closes #48 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #48 — the last open issue, and the last item on the README's "not there yet" list.
No
IAuthenticationProviderport, deliberatelyThe issue proposed one. The shipped local-auth work decided against it and that reasoning still holds: local credentials are a verification concern that fits a port (
ILocalCredentialStore+IPasswordHasher), while OIDC is a redirect/token protocol owned end to end by framework middleware. A port aroundAddOpenIdConnectwould have exactly one implementation and abstract nothing. So the pluggable thing stays the scheme, chosen in the composition root, and both land on the same cookie andClaimsPrincipal— nothing downstream of sign-in changed.Two independent checks on who gets in
Your identity blueprint already states the principle — authenticated must never imply authorised, because the Plex source auto-enrols friends. This adopts it rather than restating it:
groupsclaim againstAuth:Oidc:RequiredGroupsinOnTokenValidated, and fails the sign-in rather than admitting a principal that then bounces off every page.RequiredGroupsdefaults to empty — admitting anyone the provider authenticated — because this has to work against IdPs with no group model, and one homelab's access tiers must not become a precondition for the feature.The three details that would otherwise read as bugs
/loginchallenges instead of rendering a password form. A form that cannot work is worse than a redirect./logoutis federated. Clearing only our cookie leaves the provider session up, so the next visit signs straight back in — which looks like logout is broken rather than like SSO working.SetupStateHandlermeant "no local admin exists", which under OIDC is true forever: the wizard would never complete,/loginwould claim the instance was unconfigured, and startup would log a setup link leading to a step nobody can finish. It now takes aLocalAdminRequirement, absent meaning required, so nothing else changes.RequiredGroupsalso accepts"A,B", because configuration binds lists only from indexed keys and a compose.envhas nowhere to put an array. Silently ignoring the string form would leave an operator believing they had restricted access when they had not.A regression this also fixes
#122 broke the
*arrenv bootstrap, and it shipped in v0.6.0. My diff there inserted the bootstrap call under theif (authProvider is "local")guard that belonged to the setup-link log:So on
oidcornonethe bootstrap silently did nothing, and the setup link was logged where it leads nowhere. Both corrected, with the reason recorded in a comment so it does not come back.Verified
Twelve tests over the two rules that are ours — group admission (member, non-member, no requirement, casing, empty claim) and the setup-state change — plus the comma-separated parsing. The protocol itself is framework middleware and not ours to test.
PageAuthorizationSpecsstill green:Login/Logout/Setupremain the only anonymous pages.Auth__Providerand the fourAuth__Oidc__*settings are AppHost parameters, so they appear in the generated compose and.env; confirmed by regenerating the bundle. README and self-hosting guide updated with the redirect URI, scopes, and why the second check is worth setting../build.sh Testgreen: 512 passing. Plan indocs/plans/2026-08-23 - oidc authentication.md.Not tested against a live IdP yet — the Authentik registration is a separate PR on the Homelab repo, and I will report the end-to-end result rather than assume it.