diff --git a/README.md b/README.md
index 1fb6bf6..9361126 100644
--- a/README.md
+++ b/README.md
@@ -221,6 +221,21 @@ You also need to copy it from the server given claims to the local claims. E.g.
yourContext.StoreRemoteAuthInSchemeAsync(..., (identity, remote)=>OidcClaimsCultureProviderHelper.CopyClaims(identity, remote))))
```
+## Claims helpers
+
+https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter specifies how to request claims from the request.
+
+Notice, you still have to manually check they are present in the response.
+
+```csharp
+//in the login controller
+var claims = new RequestClaimsParameterValue()
+ .IdTokenClaim(Claims.AuthenticationTime, true);
+this.InitiateAuthorizationCodeLogin(returnUrl, ..., claims.AsOpenIddictParameter());
+...
+//in the callback controller
+this.StoreRemoteAuthInSchemeAsync(..., (principal)=>principal.GetClaim(Claims.AuthenticationTime)...)
+```
## Http helpers
diff --git a/src/Catglobe.Openiddict.Contrib.Client/Catglobe.Openiddict.Contrib.Client.csproj b/src/Catglobe.Openiddict.Contrib.Client/Catglobe.Openiddict.Contrib.Client.csproj
index 43b791d..f7d4d52 100644
--- a/src/Catglobe.Openiddict.Contrib.Client/Catglobe.Openiddict.Contrib.Client.csproj
+++ b/src/Catglobe.Openiddict.Contrib.Client/Catglobe.Openiddict.Contrib.Client.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/src/Catglobe.Openiddict.Contrib.Client/ControllerHelpers/AuthorizationCodeHelpers.cs b/src/Catglobe.Openiddict.Contrib.Client/ControllerHelpers/AuthorizationCodeHelpers.cs
index 814ed43..4e6d9be 100644
--- a/src/Catglobe.Openiddict.Contrib.Client/ControllerHelpers/AuthorizationCodeHelpers.cs
+++ b/src/Catglobe.Openiddict.Contrib.Client/ControllerHelpers/AuthorizationCodeHelpers.cs
@@ -2,6 +2,7 @@
using OpenIddict.Client.AspNetCore;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
+using static OpenIddict.Abstractions.OpenIddictConstants;
namespace Openiddict.Contrib.Client.ControllerHelpers;
@@ -22,8 +23,9 @@ public static class AuthorizationCodeHelpers
/// The controller
/// Parameter from UI where the user will be redirected after the auth is done
/// The client you want to authenticate with
+ /// Any additional claims you want to request. See .
/// The result you need to return from the controller
- public static ChallengeResult InitiateAuthorizationCodeLogin(this ControllerBase controller, string returnUrl, string? provider = null)
+ public static ChallengeResult InitiateAuthorizationCodeLogin(this ControllerBase controller, string returnUrl, string? provider = null, OpenIddictParameter? claims = default)
{
var properties = new AuthenticationProperties {
// Only allow local return URLs to prevent open redirect attacks.
@@ -31,6 +33,8 @@ public static ChallengeResult InitiateAuthorizationCodeLogin(this ControllerBase
};
if (!string.IsNullOrEmpty(provider))
properties.Items[OpenIddictClientAspNetCoreConstants.Properties.ProviderName] = provider;
+ if (claims is {} claim)
+ properties.Parameters[Parameters.Claims] = claim;
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
return controller.Challenge(properties, OpenIddictClientAspNetCoreDefaults.AuthenticationScheme);
}
diff --git a/src/Catglobe.Openiddict.Contrib.Client/MinimalApiHelpers/AuthorizationCodeHelpers.cs b/src/Catglobe.Openiddict.Contrib.Client/MinimalApiHelpers/AuthorizationCodeHelpers.cs
index bfaf14c..28ef4eb 100644
--- a/src/Catglobe.Openiddict.Contrib.Client/MinimalApiHelpers/AuthorizationCodeHelpers.cs
+++ b/src/Catglobe.Openiddict.Contrib.Client/MinimalApiHelpers/AuthorizationCodeHelpers.cs
@@ -21,8 +21,9 @@ public static class AuthorizationCodeHelpers
/// The controller
/// Parameter from UI where the user will be redirected after the auth is done
/// The client you want to authenticate with
+ /// Any additional claims you want to request. See .
/// The result you need to return from the controller
- public static IResult InitiateAuthorizationCodeLogin(this HttpContext httpContext, string returnUrl, string? provider = null)
+ public static IResult InitiateAuthorizationCodeLogin(this HttpContext httpContext, string returnUrl, string? provider = null, OpenIddictParameter? claims = default)
{
var properties = new AuthenticationProperties {
// Only allow local return URLs to prevent open redirect attacks.
@@ -30,6 +31,8 @@ public static IResult InitiateAuthorizationCodeLogin(this HttpContext httpContex
};
if (!string.IsNullOrEmpty(provider))
properties.Items[OpenIddictClientAspNetCoreConstants.Properties.ProviderName] = provider;
+ if (claims is {} claim)
+ properties.Parameters[Parameters.Claims] = claim;
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
return Results.Challenge(properties, new List { OpenIddictClientAspNetCoreDefaults.AuthenticationScheme });
}
diff --git a/src/Catglobe.Openiddict.Contrib.Client/RazorPageHelpers/AuthorizationCodeHelpers.cs b/src/Catglobe.Openiddict.Contrib.Client/RazorPageHelpers/AuthorizationCodeHelpers.cs
index 66a5eb2..46b6d20 100644
--- a/src/Catglobe.Openiddict.Contrib.Client/RazorPageHelpers/AuthorizationCodeHelpers.cs
+++ b/src/Catglobe.Openiddict.Contrib.Client/RazorPageHelpers/AuthorizationCodeHelpers.cs
@@ -23,8 +23,9 @@ public static class AuthorizationCodeHelpers
/// The PageModel
/// Parameter from UI where the user will be redirected after the auth is done
/// The client you want to authenticate with
+ /// Any additional claims you want to request. See .
/// The result you need to return from the PageModel
- public static ChallengeResult InitiateAuthorizationCodeLogin(this PageModel model, string returnUrl, string? provider = null)
+ public static ChallengeResult InitiateAuthorizationCodeLogin(this PageModel model, string returnUrl, string? provider = null, OpenIddictParameter? claims = default)
{
var properties = new AuthenticationProperties {
// Only allow local return URLs to prevent open redirect attacks.
@@ -32,6 +33,8 @@ public static ChallengeResult InitiateAuthorizationCodeLogin(this PageModel mode
};
if (!string.IsNullOrEmpty(provider))
properties.Items[OpenIddictClientAspNetCoreConstants.Properties.ProviderName] = provider;
+ if (claims is {} claim)
+ properties.Parameters[Parameters.Claims] = claim;
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
return model.Challenge(properties, OpenIddictClientAspNetCoreDefaults.AuthenticationScheme);
}
diff --git a/src/Catglobe.Openiddict.Contrib.Client/RequestClaimsParameterValue.cs b/src/Catglobe.Openiddict.Contrib.Client/RequestClaimsParameterValue.cs
new file mode 100644
index 0000000..9a78af1
--- /dev/null
+++ b/src/Catglobe.Openiddict.Contrib.Client/RequestClaimsParameterValue.cs
@@ -0,0 +1,129 @@
+#if NET
+using System.Text.Json.Nodes;
+
+namespace Openiddict.Contrib.Client;
+
+///
+/// Helper class for setting parameters matching https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter.
+///
+public class RequestClaimsParameterValue
+{
+ private readonly Dictionary _userinfo = [];
+ private readonly Dictionary _claims = [];
+
+ ///
+ /// Specify a user info claim that should be returned from the authentication request.
+ ///
+ /// See .
+ /// Indicates whether the Claim being requested is an Essential Claim. If the value is true, this indicates that the Claim is an Essential Claim.
+ /// This class for chaining.
+ public RequestClaimsParameterValue UserInfoClaim(string claim, bool essential = false)
+ {
+ _userinfo.Add(claim, AsNode(essential, null, null));
+ return this;
+ }
+
+ ///
+ /// Specify a user info claim that should be returned from the authentication request.
+ ///
+ /// See .
+ /// Requests that the Claim be returned with a particular value.
+ /// Indicates whether the Claim being requested is an Essential Claim. If the value is true, this indicates that the Claim is an Essential Claim.
+ /// This class for chaining.
+ public RequestClaimsParameterValue UserInfoClaim(string claim, string value, bool essential = false)
+ {
+ _userinfo.Add(claim, AsNode(essential, value, null));
+ return this;
+ }
+
+ ///
+ /// Specify a user info claim that should be returned from the authentication request.
+ ///
+ /// See .
+ /// Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.
+ /// Indicates whether the Claim being requested is an Essential Claim. If the value is true, this indicates that the Claim is an Essential Claim.
+ /// This class for chaining.
+ public RequestClaimsParameterValue UserInfoClaim(string claim, IReadOnlyCollection values, bool essential)
+ {
+ _userinfo.Add(claim, AsNode(essential, null, values));
+ return this;
+ }
+
+ private static JsonObject? AsNode(bool essential, string? value, IReadOnlyCollection? values)
+ {
+ var json = new JsonObject(GetJsonNode(essential, value, values));
+ return json.Count == 0 ? default : json;
+ }
+
+ ///
+ /// Specify a claim that should be returned from the authentication request.
+ ///
+ /// See .
+ /// Indicates whether the Claim being requested is an Essential Claim. If the value is true, this indicates that the Claim is an Essential Claim.
+ /// This class for chaining.
+ public RequestClaimsParameterValue IdTokenClaim(string claim, bool essential = false)
+ {
+ _claims.Add(claim, AsNode(essential, null, null));
+ return this;
+ }
+
+ ///
+ /// Specify a claim that should be returned from the authentication request.
+ ///
+ /// See .
+ /// Requests that the Claim be returned with a particular value.
+ /// Indicates whether the Claim being requested is an Essential Claim. If the value is true, this indicates that the Claim is an Essential Claim.
+ /// This class for chaining.
+ public RequestClaimsParameterValue IdTokenClaim(string claim, string value, bool essential = false)
+ {
+ _claims.Add(claim, AsNode(essential, value, null));
+ return this;
+ }
+
+ ///
+ /// Specify a claim that should be returned from the authentication request.
+ ///
+ /// See .
+ /// Requests that the Claim be returned with one of a set of values, with the values appearing in order of preference.
+ /// Indicates whether the Claim being requested is an Essential Claim. If the value is true, this indicates that the Claim is an Essential Claim.
+ /// This class for chaining.
+ public RequestClaimsParameterValue IdTokenClaim(string claim, IReadOnlyCollection values, bool essential)
+ {
+ _claims.Add(claim, AsNode(essential, null, values));
+ return this;
+ }
+
+ ///
+ /// Convert the current settings to a that can be used in a request.
+ ///
+ ///
+ /// new AuthenticationProperties().SetParameter(Parameters.Claims, new RequestClaimsParameterValue().IdTokenClaim(Claims.AuthenticationTime, true).AsOpenIddictParameter());
+ ///
+ ///
+ ///
+ public OpenIddictParameter? AsOpenIddictParameter()
+ {
+ if (_userinfo.Count == 0 && _claims.Count == 0)
+ return null;
+ var lst = new List>();
+
+ if (_userinfo.Count != 0)
+ lst.Add(new("userinfo", new JsonObject(_userinfo)));
+ if (_claims.Count != 0)
+ lst.Add(new(Parameters.IdToken, new JsonObject(_claims)));
+ return new OpenIddictParameter(new JsonObject(lst));
+ }
+
+ private static IEnumerable> GetJsonNode(bool essential, string? value, IReadOnlyCollection? values)
+ {
+ if (essential)
+ yield return new("essential", true);
+ if (value is not null)
+ yield return new("value", value!);
+ if (values is not null)
+ yield return new("values", new JsonArray(values.Select(x => (JsonNode?)JsonValue.Create(x)).ToArray()));
+ }
+
+
+}
+#endif