-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fake-auth): add callback event to fake auth handler (#800)
- Loading branch information
Showing
6 changed files
with
403 additions
and
261 deletions.
There are no files selected for viewing
91 changes: 91 additions & 0 deletions
91
src/Zitadel/Authentication/Events/Context/LocalFakeZitadelAuthContext.cs
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
using System.Security.Claims; | ||
|
||
namespace Zitadel.Authentication.Events.Context | ||
{ | ||
public class LocalFakeZitadelAuthContext | ||
{ | ||
/// <summary> | ||
/// Constructor. | ||
/// </summary> | ||
/// <param name="identity">The created ClaimsIdentity.</param> | ||
public LocalFakeZitadelAuthContext(ClaimsIdentity identity) | ||
{ | ||
Identity = identity; | ||
} | ||
|
||
/// <summary> | ||
/// The created ClaimsIdentity. | ||
/// </summary> | ||
public ClaimsIdentity Identity { get; init; } | ||
|
||
/// <summary> | ||
/// The claims of the created ClaimsIdentity. | ||
/// </summary> | ||
public IEnumerable<Claim> Claims => Identity.Claims; | ||
|
||
/// <summary> | ||
/// The "user-id" of the fake user. | ||
/// Either set by the options or via HTTP header. | ||
/// </summary> | ||
public string FakeZitadelId => new ClaimsPrincipal(Identity).FindFirstValue("sub")!; | ||
|
||
/// <summary> | ||
/// Add a claim to the <see cref="Claims"/> list. | ||
/// This is a convenience method for modifying <see cref="Claims"/>. | ||
/// </summary> | ||
/// <param name="type">Type of the claim (examples: <see cref="ClaimTypes"/>).</param> | ||
/// <param name="value">The value.</param> | ||
/// <param name="valueType">Type of the value (examples: <see cref="ClaimValueTypes"/>).</param> | ||
/// <param name="issuer">The issuer for this claim.</param> | ||
/// <param name="originalIssuer">The original issuer of this claim.</param> | ||
/// <returns>The <see cref="LocalFakeZitadelAuthContext"/> for chaining.</returns> | ||
public LocalFakeZitadelAuthContext AddClaim( | ||
string type, | ||
string value, | ||
string? valueType = null, | ||
string? issuer = null, | ||
string? originalIssuer = null) => AddClaim(new(type, value, valueType, issuer, originalIssuer)); | ||
|
||
/// <summary> | ||
/// Add a claim to the <see cref="Claims"/> list. | ||
/// This is a convenience method for modifying <see cref="Claims"/>. | ||
/// </summary> | ||
/// <param name="claim">The claim to add.</param> | ||
/// <returns>The <see cref="LocalFakeZitadelAuthContext"/> for chaining.</returns> | ||
public LocalFakeZitadelAuthContext AddClaim(Claim claim) | ||
{ | ||
Identity.AddClaim(claim); | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Add a single role to the identity's claims. | ||
/// Note: the roles are actually "claims" but this method exists | ||
/// for convenience. | ||
/// </summary> | ||
/// <param name="role">The role to add.</param> | ||
/// <returns>The <see cref="LocalFakeZitadelAuthContext"/> for chaining.</returns> | ||
public LocalFakeZitadelAuthContext AddRole(string role) | ||
{ | ||
Identity.AddClaim(new(ClaimTypes.Role, role)); | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Add multiple roles to the identity's claims. | ||
/// Note: the roles are actually "claims" but this method exists | ||
/// for convenience. | ||
/// </summary> | ||
/// <param name="roles">The roles to add.</param> | ||
/// <returns>The <see cref="LocalFakeZitadelAuthContext"/> for chaining.</returns> | ||
public LocalFakeZitadelAuthContext AddRoles(string[] roles) | ||
{ | ||
foreach (var role in roles) | ||
{ | ||
AddRole(role); | ||
} | ||
|
||
return this; | ||
} | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
src/Zitadel/Authentication/Events/LocalFakeZitadelEvents.cs
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using Zitadel.Authentication.Events.Context; | ||
using Zitadel.Authentication.Handler; | ||
|
||
namespace Zitadel.Authentication.Events; | ||
|
||
public class LocalFakeZitadelEvents | ||
{ | ||
/// <summary> | ||
/// Invoked after a ClaimsIdentity has been generated in the <see cref="LocalFakeZitadelHandler"/>. | ||
/// </summary> | ||
public Func<LocalFakeZitadelAuthContext, Task> OnZitadelFakeAuth { get; set; } = context => Task.CompletedTask; | ||
} |
107 changes: 55 additions & 52 deletions
107
src/Zitadel/Authentication/Handler/LocalFakeZitadelHandler.cs
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,55 @@ | ||
using System.Security.Claims; | ||
using System.Text.Encodings.Web; | ||
|
||
using Microsoft.AspNetCore.Authentication; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
|
||
using Zitadel.Authentication.Options; | ||
|
||
namespace Zitadel.Authentication.Handler; | ||
|
||
#if NET8_0_OR_GREATER | ||
internal class LocalFakeZitadelHandler( | ||
IOptionsMonitor<LocalFakeZitadelSchemeOptions> options, | ||
ILoggerFactory logger, | ||
UrlEncoder encoder) | ||
: AuthenticationHandler<LocalFakeZitadelSchemeOptions>(options, logger, encoder) | ||
#else | ||
internal class LocalFakeZitadelHandler( | ||
IOptionsMonitor<LocalFakeZitadelSchemeOptions> options, | ||
ILoggerFactory logger, | ||
UrlEncoder encoder, | ||
ISystemClock clock) | ||
: AuthenticationHandler<LocalFakeZitadelSchemeOptions>(options, logger, encoder, clock) | ||
#endif | ||
{ | ||
private const string FakeAuthHeader = "x-zitadel-fake-auth"; | ||
private const string FakeUserIdHeader = "x-zitadel-fake-user-id"; | ||
|
||
protected override Task<AuthenticateResult> HandleAuthenticateAsync() | ||
{ | ||
if (Context.Request.Headers.TryGetValue(FakeAuthHeader, out var value) && value == "false") | ||
{ | ||
return Task.FromResult(AuthenticateResult.Fail($"""The {FakeAuthHeader} was set with value "false".""")); | ||
} | ||
|
||
var hasId = Context.Request.Headers.TryGetValue(FakeUserIdHeader, out var forceUserId); | ||
|
||
var claims = new List<Claim> | ||
{ | ||
new(ClaimTypes.NameIdentifier, hasId ? forceUserId.ToString() : Options.FakeZitadelOptions.FakeZitadelId), | ||
new("sub", hasId ? forceUserId.ToString() : Options.FakeZitadelOptions.FakeZitadelId), | ||
}.Concat(Options.FakeZitadelOptions.AdditionalClaims) | ||
.Concat(Options.FakeZitadelOptions.Roles.Select(r => new Claim(ClaimTypes.Role, r))); | ||
|
||
var identity = new ClaimsIdentity(claims, ZitadelDefaults.FakeAuthenticationScheme); | ||
|
||
return Task.FromResult( | ||
AuthenticateResult.Success( | ||
new(new(identity), ZitadelDefaults.FakeAuthenticationScheme))); | ||
} | ||
} | ||
using System.Security.Claims; | ||
using System.Text.Encodings.Web; | ||
|
||
using Microsoft.AspNetCore.Authentication; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
|
||
using Zitadel.Authentication.Options; | ||
|
||
namespace Zitadel.Authentication.Handler; | ||
|
||
#if NET8_0_OR_GREATER | ||
internal class LocalFakeZitadelHandler( | ||
IOptionsMonitor<LocalFakeZitadelSchemeOptions> options, | ||
ILoggerFactory logger, | ||
UrlEncoder encoder) | ||
: AuthenticationHandler<LocalFakeZitadelSchemeOptions>(options, logger, encoder) | ||
#else | ||
internal class LocalFakeZitadelHandler( | ||
IOptionsMonitor<LocalFakeZitadelSchemeOptions> options, | ||
ILoggerFactory logger, | ||
UrlEncoder encoder, | ||
ISystemClock clock) | ||
: AuthenticationHandler<LocalFakeZitadelSchemeOptions>(options, logger, encoder, clock) | ||
#endif | ||
{ | ||
private const string FakeAuthHeader = "x-zitadel-fake-auth"; | ||
private const string FakeUserIdHeader = "x-zitadel-fake-user-id"; | ||
|
||
protected override Task<AuthenticateResult> HandleAuthenticateAsync() | ||
{ | ||
if (Context.Request.Headers.TryGetValue(FakeAuthHeader, out var value) && value == "false") | ||
{ | ||
return Task.FromResult(AuthenticateResult.Fail($"""The {FakeAuthHeader} was set with value "false".""")); | ||
} | ||
|
||
var hasId = Context.Request.Headers.TryGetValue(FakeUserIdHeader, out var forceUserId); | ||
|
||
var claims = new List<Claim> | ||
{ | ||
new(ClaimTypes.NameIdentifier, hasId ? forceUserId.ToString() : Options.FakeZitadelOptions.FakeZitadelId), | ||
new("sub", hasId ? forceUserId.ToString() : Options.FakeZitadelOptions.FakeZitadelId), | ||
}.Concat(Options.FakeZitadelOptions.AdditionalClaims) | ||
.Concat(Options.FakeZitadelOptions.Roles.Select(r => new Claim(ClaimTypes.Role, r))); | ||
|
||
var identity = new ClaimsIdentity(claims, ZitadelDefaults.FakeAuthenticationScheme); | ||
|
||
// Callback to enable users to manipulate the ClaimsIdentity before it is used. | ||
Options.FakeZitadelOptions.Events.OnZitadelFakeAuth.Invoke(new(identity)); | ||
|
||
return Task.FromResult( | ||
AuthenticateResult.Success( | ||
new(new(identity), ZitadelDefaults.FakeAuthenticationScheme))); | ||
} | ||
} |
115 changes: 61 additions & 54 deletions
115
src/Zitadel/Authentication/Options/LocalFakeZitadelOptions.cs
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,61 @@ | ||
using System.Security.Claims; | ||
|
||
namespace Zitadel.Authentication.Options | ||
{ | ||
public class LocalFakeZitadelOptions | ||
{ | ||
/// <summary> | ||
/// The "user-id" of the fake user. | ||
/// This populates the "sub" and "nameidentifier" claims. | ||
/// </summary> | ||
public string FakeZitadelId { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// A list of additional claims to add to the identity. | ||
/// </summary> | ||
public IList<Claim> AdditionalClaims { get; set; } = new List<Claim>(); | ||
|
||
/// <summary> | ||
/// List of roles that are attached to the identity. | ||
/// Note: the roles are actually "claims" but this list exists | ||
/// for convenience. | ||
/// </summary> | ||
public IEnumerable<string> Roles { get; set; } = new List<string>(); | ||
|
||
/// <summary> | ||
/// Add a claim to the <see cref="AdditionalClaims"/> list. | ||
/// This is a convenience method for modifying <see cref="AdditionalClaims"/>. | ||
/// </summary> | ||
/// <param name="type">Type of the claim (examples: <see cref="ClaimTypes"/>).</param> | ||
/// <param name="value">The value.</param> | ||
/// <param name="valueType">Type of the value (examples: <see cref="ClaimValueTypes"/>).</param> | ||
/// <param name="issuer">The issuer for this claim.</param> | ||
/// <param name="originalIssuer">The original issuer of this claim.</param> | ||
/// <returns>The <see cref="LocalFakeZitadelOptions"/> for chaining.</returns> | ||
public LocalFakeZitadelOptions AddClaim( | ||
string type, | ||
string value, | ||
string? valueType = null, | ||
string? issuer = null, | ||
string? originalIssuer = null) => AddClaim(new(type, value, valueType, issuer, originalIssuer)); | ||
|
||
/// <summary> | ||
/// Add a claim to the <see cref="AdditionalClaims"/> list. | ||
/// This is a convenience method for modifying <see cref="AdditionalClaims"/>. | ||
/// </summary> | ||
/// <param name="claim">The claim to add.</param> | ||
/// <returns>The <see cref="LocalFakeZitadelOptions"/> for chaining.</returns> | ||
public LocalFakeZitadelOptions AddClaim(Claim claim) | ||
{ | ||
AdditionalClaims.Add(claim); | ||
return this; | ||
} | ||
} | ||
} | ||
using System.Security.Claims; | ||
|
||
using Zitadel.Authentication.Events; | ||
|
||
namespace Zitadel.Authentication.Options | ||
{ | ||
public class LocalFakeZitadelOptions | ||
{ | ||
/// <summary> | ||
/// The "user-id" of the fake user. | ||
/// This populates the "sub" and "nameidentifier" claims. | ||
/// </summary> | ||
public string FakeZitadelId { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// A list of additional claims to add to the identity. | ||
/// </summary> | ||
public IList<Claim> AdditionalClaims { get; set; } = new List<Claim>(); | ||
|
||
/// <summary> | ||
/// List of roles that are attached to the identity. | ||
/// Note: the roles are actually "claims" but this list exists | ||
/// for convenience. | ||
/// </summary> | ||
public IEnumerable<string> Roles { get; set; } = new List<string>(); | ||
|
||
/// <summary> | ||
/// Gets or sets the <see cref="LocalFakeZitadelEvents"/> used to enable mocking authentication data dynamically. | ||
/// </summary> | ||
public LocalFakeZitadelEvents Events { get; set; } = new LocalFakeZitadelEvents(); | ||
|
||
/// <summary> | ||
/// Add a claim to the <see cref="AdditionalClaims"/> list. | ||
/// This is a convenience method for modifying <see cref="AdditionalClaims"/>. | ||
/// </summary> | ||
/// <param name="type">Type of the claim (examples: <see cref="ClaimTypes"/>).</param> | ||
/// <param name="value">The value.</param> | ||
/// <param name="valueType">Type of the value (examples: <see cref="ClaimValueTypes"/>).</param> | ||
/// <param name="issuer">The issuer for this claim.</param> | ||
/// <param name="originalIssuer">The original issuer of this claim.</param> | ||
/// <returns>The <see cref="LocalFakeZitadelOptions"/> for chaining.</returns> | ||
public LocalFakeZitadelOptions AddClaim( | ||
string type, | ||
string value, | ||
string? valueType = null, | ||
string? issuer = null, | ||
string? originalIssuer = null) => AddClaim(new(type, value, valueType, issuer, originalIssuer)); | ||
|
||
/// <summary> | ||
/// Add a claim to the <see cref="AdditionalClaims"/> list. | ||
/// This is a convenience method for modifying <see cref="AdditionalClaims"/>. | ||
/// </summary> | ||
/// <param name="claim">The claim to add.</param> | ||
/// <returns>The <see cref="LocalFakeZitadelOptions"/> for chaining.</returns> | ||
public LocalFakeZitadelOptions AddClaim(Claim claim) | ||
{ | ||
AdditionalClaims.Add(claim); | ||
return this; | ||
} | ||
} | ||
} |
Oops, something went wrong.