Skip to content

Commit 78ea468

Browse files
zstinnettXadmin
andauthored
Add OIDC/SSO authentication support (fixes #512) (#1678)
* Add OIDC/SSO authentication support Implements OpenID Connect (OIDC) Single Sign-On authentication to address issue #512. Features: - OIDC authentication via ASP.NET Core middleware - Support for multiple IdPs (Entra ID, Okta, Auth0, etc.) - Automatic user provisioning with configurable group mappings - HttpOnly cookie-based session management - Rate limiting for provisioning attempts - Comprehensive environment variable configuration - Docker secrets support for sensitive values - Security headers (CSP, HSTS, X-Frame-Options, etc.) - Backward compatible with existing local authentication Security: - JWT signature validation via OIDC discovery - Cryptographically secure cookie secrets (32-byte) - SameSite=Lax cookie protection - No secrets in frontend bundles - Proper error handling without information leakage Documentation: - Added SSO configuration to DockerEnvironmentVariables.md - Includes examples for major IdP providers - Environment variable reference with _FILE variants Closes #512 * Fix HttpOnly cookie logout and token handling * Add OIDC/SSO authentication with full backward compatibility Addresses feedback from PR review: - Restored MapGetAndPost for all endpoints to support existing GET integrations - Login API now returns tokens by default, preserving compatibility for generic clients - Added cookie_auth opt-in parameter for Web GUI to use secure HttpOnly cookies - Added #region SSO markers to isolate SSO-related code - Enhanced OIDC claim extraction with fallbacks for Azure AD/Entra ID - SSO users blocked from standard login form (must use OIDC flow) - Reduced HSTS lifetime from 1 year to 24 hours (homelab-friendly) - Added security warnings for client secret storage (code, docs, UI) - Added environment vars to configure rate limiting for SSO This commit ensures the SSO implementation adds new functionality without modifying existing API behavior. --------- Co-authored-by: Xadmin <xadmin@dontfail.us>
1 parent 7f7ce26 commit 78ea468

File tree

10 files changed

+1454
-48
lines changed

10 files changed

+1454
-48
lines changed

DnsServerCore/Auth/AuthManager.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,9 @@ public async Task<UserSession> CreateSessionAsync(UserSessionType type, string t
840840
{
841841
User user = await AuthenticateUserAsync(username, password, totp, remoteAddress);
842842

843+
if (user.IsSsoUser && (type == UserSessionType.Standard))
844+
throw new DnsWebServiceException("SSO users must login via SSO provider.");
845+
843846
UserSession session = new UserSession(type, tokenName, user, remoteAddress, userAgent);
844847

845848
if (!_sessions.TryAdd(session.Token, session))
@@ -869,6 +872,21 @@ public UserSession CreateApiToken(string tokenName, string username, IPAddress r
869872
return session;
870873
}
871874

875+
public UserSession CreateSsoSession(User user, IPAddress remoteAddress, string userAgent)
876+
{
877+
if (user.Disabled)
878+
throw new DnsWebServiceException("Account is suspended.");
879+
880+
UserSession session = new UserSession(UserSessionType.Standard, null, user, remoteAddress, userAgent);
881+
882+
if (!_sessions.TryAdd(session.Token, session))
883+
throw new DnsWebServiceException("Error while creating session. Please try again.");
884+
885+
user.LoggedInFrom(remoteAddress);
886+
887+
return session;
888+
}
889+
872890
public UserSession DeleteSession(string token)
873891
{
874892
if (_sessions.TryRemove(token, out UserSession session))

DnsServerCore/Auth/User.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class User : IComparable<User>
5252
AuthenticatorKeyUri _totpKeyUri;
5353
bool _totpEnabled;
5454
bool _disabled;
55+
bool _isSsoUser; // New field for SSO tracking
5556
int _sessionTimeoutSeconds = 30 * 60; //default 30 mins
5657

5758
DateTime _previousSessionLoggedOn;
@@ -85,6 +86,7 @@ public User(BinaryReader bR, IReadOnlyDictionary<string, Group> groups)
8586
{
8687
case 1:
8788
case 2:
89+
case 3: // Version 3 adds IsSsoUser
8890
_displayName = bR.ReadShortString();
8991
_username = bR.ReadShortString();
9092
_passwordHashType = (UserPasswordHashType)bR.ReadByte();
@@ -102,6 +104,12 @@ public User(BinaryReader bR, IReadOnlyDictionary<string, Group> groups)
102104
}
103105

104106
_disabled = bR.ReadBoolean();
107+
108+
if (version >= 3)
109+
{
110+
_isSsoUser = bR.ReadBoolean();
111+
}
112+
105113
_sessionTimeoutSeconds = bR.ReadInt32();
106114

107115
_previousSessionLoggedOn = bR.ReadDateTime();
@@ -259,7 +267,7 @@ public bool IsMemberOfGroup(Group group)
259267

260268
public void WriteTo(BinaryWriter bW)
261269
{
262-
bW.Write((byte)2);
270+
bW.Write((byte)3); // Bump version to 3
263271
bW.WriteShortString(_displayName);
264272
bW.WriteShortString(_username);
265273
bW.Write((byte)_passwordHashType);
@@ -274,6 +282,7 @@ public void WriteTo(BinaryWriter bW)
274282

275283
bW.Write(_totpEnabled);
276284
bW.Write(_disabled);
285+
bW.Write(_isSsoUser); // Write IsSsoUser
277286
bW.Write(_sessionTimeoutSeconds);
278287

279288
bW.Write(_previousSessionLoggedOn);
@@ -417,6 +426,12 @@ public IPAddress RecentSessionRemoteAddress
417426
public ICollection<Group> MemberOfGroups
418427
{ get { return _memberOfGroups.Values; } }
419428

429+
public bool IsSsoUser
430+
{
431+
get { return _isSsoUser; }
432+
set { _isSsoUser = value; }
433+
}
434+
420435
#endregion
421436
}
422437
}

DnsServerCore/DnsServerCore.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net9.0</TargetFramework>
@@ -46,6 +46,7 @@
4646
<ItemGroup>
4747
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
4848
<PackageReference Include="QRCoder" Version="1.7.0" />
49+
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.*" />
4950
</ItemGroup>
5051

5152
<ItemGroup>

0 commit comments

Comments
 (0)