Allow claims to have an array value and not just a string value.
I have an application where I have multiple roles for my users. For endpoints that should only be reached by admins, I can use the following:
"RouteClaimsRequirement": {
"Role": "Admin"
}
For endpoints that should only be reached by users, I can use the following:
"RouteClaimsRequirement": {
"Role": "User"
}
For endpoints that should be reached by both users and admins I would like to use the following:
"RouteClaimsRequirement": {
"Role": ["User", "Admin"]
}
When I tried adding this, all requests to the endpoint respond with a 404.
This should let the request go through if the request has a claim for the role user or admin. This is equivalent to the built-in asp .net core attribute:
[Authorize(Roles = "User, Admin")]
I believe adding more sophisticated route claims requirements in general would help with this, will discuss with the team best approaches.
Is this fixed?
I'm interested too. Does another workaround exist?
as workaround override authorisation middleware for claims or policy based with claims in policies as suggested here
I've tried the override authorisation middleware method , but the claims are strictly converted in a Dictionary
This format break the Route because the value cannot be converted into a string;
"RouteClaimsRequirement": {
"Role": ["User", "Admin"]
}
This format also don't work because the Role key in dictionary will be ovverride with the second value;
"RouteClaimsRequirement": {
"Role": "Admin"
"Role": "User"
}
so.. My personal solution will be
Workaround example
"RouteClaimsRequirement": {
"Role": "Admin , User"
} }
else {
ctx.Errors.Add(new UnauthorisedError($"Fail to authorize"));
}
}
};
.
.
.
await app.UseOcelot(configuration);
}
The logic of Authorize Method
```c#
private bool Authorize(DownstreamContext ctx)
{
if (ctx.DownstreamReRoute.AuthenticationOptions.AuthenticationProviderKey == null) return true;
else {
//flag for authorization
bool auth = false;
//where are stored the claims of the jwt token
Claim[] claims = ctx.HttpContext.User.Claims.ToArray<Claim>();
//where are stored the required claims for the route
Dictionary<string, string> required = ctx.DownstreamReRoute.RouteClaimsRequirement;
.
.
((AUTHORIZATION LOGIC))
.
.
return auth;
}
remeber to add in the ConfigureService method
c#
services.AddAuthorization();
services.AddAuthentication()
.AddJwtBearer("TestKey", x =>
{
// x.RequireHttpsMetadata = false;
x.TokenValidationParameters = tokenValidationParameters;
});
(I Still working on my Authorization logic that will implement the multiple claims with And/Or logic with regex of strings , but the claims data structure implemented with Dictionary
((AUTHORIZATION LOGIC)) Example
```c#
Regex reor = new Regex(@"[^,\s+$ ][^\,]*[^,\s+$ ]");
MatchCollection matches;
Regex reand = new Regex(@"[^&\s+$ ][^\&]*[^&\s+$ ]");
MatchCollection matchesand;
int cont=0;
foreach (KeyValuePair<string,string> claim in required)
{
matches = reor.Matches(claim.Value);
foreach (Match match in matches)
{
matchesand = reand.Matches(match.Value);
cont = 0;
foreach (Match m in matchesand)
{
foreach (Claim cl in claims)
{
if (cl.Type == claim.Key)
{
if (cl.Value == m.Value)
{
cont++;
}
}
}
}
if (cont == matchesand.Count)
{
auth= true;
break;
}
}
}
```
Example of Configuration
"RouteClaimsRequirement": {
"Role": "User & IT , Admin"
}
Logic result :
IF((User and IT) or Admin)
May I please know any update on this?
by using @arro000 's trick I changed a few things to make it work for me on .Net Core 3.1 & Ocelot 16.0.1.
My answer on Stackoverflow.
https://stackoverflow.com/questions/60300349/how-to-check-claim-value-in-array-or-any-in-ocelot-gateway/62390542#62390542
tokenValidationParameters
What is tokenValidationParameters?
Is the instance of object with the bearer authentication rules, in my case for example:
```c#
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey
};
((AUTHORIZATION LOGIC)) Example
Regex reor = new Regex(@"[^,\s+$ ][^\,]*[^,\s+$ ]"); MatchCollection matches; Regex reand = new Regex(@"[^&\s+$ ][^\&]*[^&\s+$ ]"); MatchCollection matchesand; int cont=0; foreach (KeyValuePair<string,string> claim in required) { matches = reor.Matches(claim.Value); foreach (Match match in matches) { matchesand = reand.Matches(match.Value); cont = 0; foreach (Match m in matchesand) { foreach (Claim cl in claims) { if (cl.Type == claim.Key) { if (cl.Value == m.Value) { cont++; } } } } if (cont == matchesand.Count) { auth= true; break; } } }Example of Configuration
"RouteClaimsRequirement": {
"Role": "User & IT , Admin"
}Logic result :
IF((User and IT) or Admin)
With this change do you still need to add [Authorize(Roles = "User, Admin")] to your controllers?
Most helpful comment
I've tried the override authorisation middleware method , but the claims are strictly converted in a Dictionary format before the middleware was called ;
This format break the Route because the value cannot be converted into a string;
"RouteClaimsRequirement": { "Role": ["User", "Admin"] }This format also don't work because the Role key in dictionary will be ovverride with the second value;
"RouteClaimsRequirement": { "Role": "Admin" "Role": "User" }so.. My personal solution will be
Workaround example
"RouteClaimsRequirement": {"Role": "Admin , User"
}
In the authorisation middleware method , i will parse the string with a regex pattern to obtain the single role value:
```c#
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var configuration = new OcelotPipelineConfiguration
{
AuthorisationMiddleware = async (ctx, next) =>
{
if (this.Authorize(ctx))
{
await next.Invoke();
remeber to add in the ConfigureService method
c# services.AddAuthorization(); services.AddAuthentication() .AddJwtBearer("TestKey", x => { // x.RequireHttpsMetadata = false; x.TokenValidationParameters = tokenValidationParameters; });(I Still working on my Authorization logic that will implement the multiple claims with And/Or logic with regex of strings , but the claims data structure implemented with Dictionary is very ugly and not very flexible)