I'm currently using Ocelot as Api Gateway for a micro-services architecture. I have some authenticated reroutes and to be able to use it I declared a authentication Middleware like this :
var authenticationProviderKey = "Authentification";
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(authenticationProviderKey, x =>
{
x.RequireHttpsMetadata = false;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),
ValidIssuer = token.Issuer,
ValidAudience = token.Audience,
ValidateIssuer = true,
ValidateAudience = true
};
});
I wanted to run some custom validation to implement refresh token workflow, to do so I implemented the preAuthentication Middleware to make so tests :
PreAuthenticationMiddleware = async (ctx, next) =>
{
IEnumerable<string> header;
ctx.DownstreamRequest.Headers.TryGetValues("Authorization", out header);
if (header.FirstOrDefault() != null)
{
if (JwtUtils.ValidateExpirationToken(header.FirstOrDefault()))
{
//On validate refresh token
Console.WriteLine("PreAuthentification Middleware");
Tuple<int, string> credentials = JwtUtils.retrieveInfos(header.FirstOrDefault());
string token = JwtUtils.GenerateToken(credentials.Item1, credentials.Item2);
ctx.DownstreamRequest.Headers.Remove("Authorization");
ctx.DownstreamRequest.Headers.Add("Authorization", token);
await next.Invoke();
}
}
}
From what I understood, when I make an api Call, the preAuthenticate Middleware would be called, and with next.Invoke() my Authentication middleware would be called. The newly generated token in my PreAuthentication middleware is a valid one, but my authentication middleware throws an expiredToken exception even tho he's not. Therefore I think the authentication Middleware is run against the first JWT when the new one has not been yet set to the Authorization Header. Is it the attended behaviour? Or maybe I did not understood correctly the middleware in Ocelot?
Anyway, some help would be much appreciated !
Have a good day,
Lio
Related
guys!
Currently, I get a Bearer Token from an API.
What I want to do is:
Get token from external API
Use .NET [Authorize] in controller
Compare token returned from API with token informed in Authorization Header of request
If they are equal, authorize the request
If they are not equal, return 401
What's the best way of doing this?
After using [Authorize]attribute in your controller class; 3rd, 4th and 5th steps automatically performed by .NET built-in functions.
As far as I know, the asp.net core provide the build-in authentication schema to authenticate the JWT bear token.
You could put the right settings inside the registered service with the right secret and issuer settings, then your application will check the token and set the claims into your application to achieve the authentication.
More details about how it works, you could refer to this article.
If JTW is the only way you are authenticating endpoints, it should be as "simple" as;
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
options.SaveToken = true;
options.RequireHttpsMetadata = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "..TODO..",
ValidateAudience = true,
ValidAudience = "..TODO..",
IssuerSigningKey = new SymmetricSecurityKey(..TODO..);
};
});
From what I have researched, then it's best practice to seperate the API and IdentityServer into 2 projects (also 2 domains-i.e.: https://api.mywebsite.com and https://identity.mywebsite.com), so I did that.
In my IdentityServer/AuthenticationController.cs # login-method it is currently making a JWT-token with a list of Claims(role(s), name, Jti(idk what that is), email, etc, that it puts into the JwtSecurityToken.
How does i.e. [Authorize(Roles = "Admin")] in my WebAPI/ProductController.cs communicate with my IdentityServer and then authorizing it. Is it in "AddAuthentication(...);" in Program.cs services? Or in AddJwtBearer(...)? Or specifically in ValidIssuer?
Where is the connection between API(i.e. https://api.mywebsite.com) and IdentityServer(https://identityserver.mywebsite.com)?
My IdentityServer-project Program.cs:
...
builder.Services.AddIdentityServer().AddAspNetIdentity<IdentityUser>().AddClientStore<InMemoryClientStore>().AddResourceStore<InMemoryResourcesStore>();
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidAudiences = builder.Configuration.GetSection("JWT:ValidAudiences").Get<string[]>(),
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]))
};
});
...
My API-project Program.cs:
...
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidAudiences = builder.Configuration.GetSection("JWT:ValidAudiences").Get<string[]>(),
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]))
};
});
...
Also, are my both codeblocks well-written when it comes to IdentityServer and API authentication and authorization?
AddJwtBearer in the API is in charge of taking the incoming access token and converting it to a "ClaimsPrincipal" user. Then that user is passed with all its claim to the authorization handler that will determine if the user is allowed to pass or not.
For example the [Authorize(Roles = "Admin")] attribute will check the role claims in the user if it contains the Admin role claim or not.
Usually you don't need to specify the IssuerSigningKey in AddJwtBearer and instead let AddJwtBearer to automatically retrieve it from IdentityServer directly at startup. (just remove IssuerSigningKey). You typically add IssuerSigningKey if the API can't talk to IdentityServer.
I have an application behind an SAML Service Provider. That means, the SP does authentication for me and I get user data in HTTP request headers (like SP_USER_NAME or SP_USER_ROLE). For each endpoint, I would like to authorize users according to roles, and show some error if they have insufficient permissions.
I looked at policy-based authorization in the docs, but that seems a bit overkill for checking a header.
How do I authorize in a simple way, based on a specific header? Plese note that I develop in .NET Core 3.1
Hey I have solution for this please refer below point
first of all you need to add authentication. public void ConfigureServices(IServiceCollection services)
services.AddSession();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = >JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = >JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
// Adding Jwt Bearer
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidAudience = Configuration["JWTConfig:ValidAudience"],
ValidIssuer = Configuration["JWTConfig:ValidIssuer"],
IssuerSigningKey = new >SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWTConfig:Secret"]))
};
});
After that you have to Use Session for storing authentication token and in this token you have to encrypt token combination of role list whatever role want to pass for the authorization.
Here i have used JWT Bearer token
Using this session you have to configure in public void Configure(IApplicationBuilder app, IWebHostEnvironment env)startup.cs file for use header authentication.
app.UseSession();
app.Use(async (context, next) =>
{
var token = context.Session.GetString("Token");
if (!string.IsNullOrEmpty(token))
{
context.Request.Headers.Add("Authorization", "Bearer " + token);
}
await next();
});
then after you you have to add in your controller
[Authorize(Roles = "Employee,Student")]
public ActionResult Leave()
{
// your code here
}
I have an ASP.NET Core Web API targeting 2.2, and I'm having trouble handling an authentication request in the event that it has failed. The authentication request needs to pass a valid refresh token. My code is handling the failed event when the JWT has expired (I also provide a lifetime validator), and then issue a new JWT and refresh token when the provided refresh token is correctly validated, and thus consumed. However, the pipeline does not continue executing and instead returns a 403 regardless of my code calling Success() on the context.
Here is the relevant Startup.cs section:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = true;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Authentication:JwtSecret"])),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
LifetimeValidator = l10n.Api.Code.Auth.Bearer.JwtBearerExtensions.LifetimeValidator
};
options.Authority = Configuration["Authentication:JwtAuthority"];
options.ClaimsIssuer = Configuration["Authentication:JwtIssuer"];
options.Events = new JwtBearerEvents();
options.Events.OnAuthenticationFailed = async context => await context.AuthenticationFailed();
})
And the fail handler:
public static async Task AuthenticationFailed(this AuthenticationFailedContext context)
{
ILoggerFactory loggerFactory = context.HttpContext.RequestServices.GetService<ILoggerFactory>();
ILogger logger = loggerFactory.CreateLogger(nameof(JwtBearerExtensions));
string refreshToken = context.HttpContext.Request.Cookies[Defaults.RefreshTokenCookie];
if (string.IsNullOrEmpty(refreshToken))
{
logger.LogWarning("No refresh token supplied with invalid JWT, Cookies are {0}", string.Join(", ", context.HttpContext.Request.Cookies.Keys));
return;
}
logger.LogInformation("Processing refresh token '{0}'.", refreshToken);
IConfiguration configuration = context.HttpContext.RequestServices.GetService<IConfiguration>();
IUserService userService = context.HttpContext.RequestServices.GetService<IUserService>();
ITokenHandler handler = context.HttpContext.RequestServices.GetService<ITokenHandler>();
long? userId = await handler.ValidateRefreshToken(refreshToken);
if (userId.HasValue)
{
User user = await userService.GetUserAsync(userId.Value);
refreshToken = await handler.GenerateRefreshToken(userId.Value);
string jwtToken = BuildJwtToken(user, configuration);
context.HttpContext.Response.AddBearerAuthorization(jwtToken);
context.HttpContext.Response.AddRefreshTokenCookie(refreshToken);
context.Principal = new ClaimsPrincipal(BuildClaimsIdentity(user));
context.Success();
}
}
When inspecting the 403 result in Postman, I can see the new JWT in the Authorization header, and the new refresh token in the cookie. Those are correctly attributed to the Response object. It's just the pipeline that abandons the rest of the processing, and my controller action is never invoked.
How do I allow the request to continue and complete, returning the expected JSON and also issuing (or refreshing) a new auth session?
The solution was to use the correct constructor overload for the ClaimIdentity by passing in the authentication type. More information at this github issue.
For those interested, in this solution, the 403 result happened because of app.UseAuthorization(), in which a policy was failing to pass as a result of the authentication issue. In the github issue post, the purpose-built repro did not have this and resulted in the 401 instead.
Is it possible to support multiple JWT Token issuers in ASP.NET Core 2?
I want to provide an API for external service and I need to use two sources of JWT tokens - Firebase and custom JWT token issuers. In ASP.NET core I can set the JWT authentication for Bearer auth scheme, but only for one Authority:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://securetoken.google.com/my-firebase-project"
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "my-firebase-project"
ValidateAudience = true,
ValidAudience = "my-firebase-project"
ValidateLifetime = true
};
}
I can have multiple issuers and audiences, but I can't set several Authorities.
You can totally achieve what you want:
services
.AddAuthentication()
.AddJwtBearer("Firebase", options =>
{
options.Authority = "https://securetoken.google.com/my-firebase-project"
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "my-firebase-project"
ValidateAudience = true,
ValidAudience = "my-firebase-project"
ValidateLifetime = true
};
})
.AddJwtBearer("Custom", options =>
{
// Configuration for your custom
// JWT tokens here
});
services
.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("Firebase", "Custom")
.Build();
});
Let's go through the differences between your code and that one.
AddAuthentication has no parameter
If you set a default authentication scheme, then on every single request the authentication middleware will try to run the authentication handler associated with the default authentication scheme. Since we now have two possible authentication schemes, there's no point in running one of them.
Use another overload of AddJwtBearer
Every single AddXXX method to add an authentication has several overloads:
One where the default authentication scheme associated with the authentication method is used, as you can see here for cookies authentication
One where you pass, in addition to the configuration of the options, the name of the authentication scheme, as on this overload
Now, because you use the same authentication method twice but authentication schemes must be unique, you need to use the second overload.
Update the default policy
Since the requests won't be authenticated automatically anymore, putting [Authorize] attributes on some actions will result in the requests being rejected and an HTTP 401 will be issued.
Since that's not what we want because we want to give the authentication handlers a chance to authenticate the request, we change the default policy of the authorization system by indicating both the Firebase and Custom authentication schemes should be tried to authenticate the request.
That doesn't prevent you from being more restrictive on some actions; the [Authorize] attribute has an AuthenticationSchemes property that allows you to override which authentication schemes are valid.
If you have more complex scenarios, you can make use of policy-based authorization. I find the official documentation is great.
Let's imagine some actions are only available to JWT tokens issued by Firebase and must have a claim with a specific value; you could do it this way:
// Authentication code omitted for brevity
services
.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("Firebase", "Custom")
.Build();
options.AddPolicy("FirebaseAdministrators", new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes("Firebase")
.RequireClaim("role", "admin")
.Build());
});
You could then use [Authorize(Policy = "FirebaseAdministrators")] on some actions.
A final point to note: If you are catching AuthenticationFailed events and using anything but the first AddJwtBearer policy, you may see IDX10501: Signature validation failed. Unable to match key... This is caused by the system checking each AddJwtBearer in turn until it gets a match. The error can usually be ignored.
This is an extension of Mickaƫl Derriey's answer.
Our app has a custom authorization requirement that we resolve from an internal source. We were using Auth0 but are switching to Microsoft Account authentication using OpenID. Here is the slightly edited code from our ASP.Net Core 2.1 Startup. For future readers, this works as of this writing for the versions specified. The caller uses the id_token from OpenID on incoming requests passed as a Bearer token. Hope it helps someone else trying to do an identity authority conversion as much as this question and answer helped me.
const string Auth0 = nameof(Auth0);
const string MsaOpenId = nameof(MsaOpenId);
string domain = "https://myAuth0App.auth0.com/";
services.AddAuthentication()
.AddJwtBearer(Auth0, options =>
{
options.Authority = domain;
options.Audience = "https://myAuth0Audience.com";
})
.AddJwtBearer(MsaOpenId, options =>
{
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateAudience = true,
ValidAudience = "00000000-0000-0000-0000-000000000000",
ValidateIssuer = true,
ValidIssuer = "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0",
ValidateIssuerSigningKey = true,
RequireExpirationTime = true,
ValidateLifetime = true,
RequireSignedTokens = true,
ClockSkew = TimeSpan.FromMinutes(10),
};
options.MetadataAddress = "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0/.well-known/openid-configuration";
}
);
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes( Auth0, MsaOpenId )
.Build();
var approvedPolicyBuilder = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes(Auth0, MsaOpenId)
;
approvedPolicyBuilder.Requirements.Add(new HasApprovedRequirement(domain));
options.AddPolicy("approved", approvedPolicyBuilder.Build());
});
The solution to your question, is available in following blog post https://oliviervaillancourt.com/posts/Fixing-IDX10501-MultipleAuthScheme
Basically the solutions exists of overriding the regular JWTBearer handler with you own generic handler that can check through the JWTBearerConfig if the issuer in the cfg is the same to the isseur in your token.
The blog post suggests to use seperate handlers for each scheme, that doesn't seem to be needed, a generic class JWTAuthenticationHandler that overrides the HandleAuthenticateAsync method seems to suffice!
Code wise you could implement your startup like this:
//Using multiple schemes can cause issues when validating the issuesSigningKey therefore we need to implement seperate handlers for each scheme! => cfr: https://oliviervaillancourt.com/posts/Fixing-IDX10501-MultipleAuthScheme
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>, JwtBearerPostConfigureOptions>());
services.AddAuthentication()
//Set the authenticationScheme by using the identityServer helper methods (we are using a Bearer token)
.AddScheme<JwtBearerOptions, JWTAuthenticationHandler>(IdentityServerAuthenticationDefaults.AuthenticationScheme, options =>
{
//TO DO Get the origin url's from configuration file, instead of setting all url's here
options.Authority = _identityServerSettings.Authority;
options.Audience = _identityServerSettings.Audience;
options.Events = new JwtBearerEvents
{
OnChallenge = context =>
{
return Task.CompletedTask;
},
//When using multiple JwtBearer schemes we can run into "OnAuthenticationFailed" for instance when logging in via IdentityServer the AuthenticationHandler will still check in these events, this can be ignored...
//Cfr => https://stackoverflow.com/questions/49694383/use-multiple-jwt-bearer-authentication
//If you are catching AuthenticationFailed events and using anything but the first AddJwtBearer policy, you may see IDX10501: Signature validation failed.Unable to match key... This is caused by the system checking each AddJwtBearer in turn until it gets a match. The error can usually be ignored.
//We managed to fix this issue by adding seperate AuthenticationHandlers for each type of bearer token... cfr: https://oliviervaillancourt.com/posts/Fixing-IDX10501-MultipleAuthScheme
OnAuthenticationFailed = context =>
{
return Task.CompletedTask;
},
OnMessageReceived = context =>
{
return Task.CompletedTask;
},
OnForbidden = context =>
{
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
return Task.CompletedTask;
}
};
})
//Set the authentication scheme for the AzureAd integration (we are using a bearer token)
.AddScheme<JwtBearerOptions, JWTAuthenticationHandler>("AzureAD", "AzureAD", options =>
{
options.Audience = _azureAdSettings.Audience; //ClientId
options.Authority = _azureAdSettings.Authority; //"https://login.microsoftonline.com/{tenantId}/v2.0/"
options.TokenValidationParameters = new TokenValidationParameters
{
//Set built in claimTypes => Role
RoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
};
options.Events = new JwtBearerEvents
{
OnChallenge = context =>
{
return Task.CompletedTask;
},
//When using multiple JwtBearer schemes we can run into "OnAuthenticationFailed" for instance when logging in via IdentityServer the AuthenticationHandler will still check in these events, this can be ignored...
//Cfr => https://stackoverflow.com/questions/49694383/use-multiple-jwt-bearer-authentication
//A final point to note: If you are catching AuthenticationFailed events and using anything but the first AddJwtBearer policy, you may see IDX10501: Signature validation failed.Unable to match key... This is caused by the system checking each AddJwtBearer in turn until it gets a match. The error can usually be ignored.
//We managed to fix this issue by adding seperate AuthenticationHandlers for each type of bearer token... cfr: https://oliviervaillancourt.com/posts/Fixing-IDX10501-MultipleAuthScheme
OnAuthenticationFailed = context =>
{
return Task.CompletedTask;
},
OnMessageReceived = context =>
{
return Task.CompletedTask;
},
OnForbidden = context =>
{
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
return Task.CompletedTask;
}
};
});
}
The JWTAuthenticationHandlerClass can look like this
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
namespace WebAPI.Auth
{
public class JWTAuthenticationHandler: JwtBearerHandler
{
public JWTAuthenticationHandler(IOptionsMonitor<JwtBearerOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{ }
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
//Fetch OIDC configuration for the IDP we are handling
var authorityConfig = await this.Options.ConfigurationManager.GetConfigurationAsync(this.Context.RequestAborted);
//Determine the issuer from the configuration
var authorityIssuer = authorityConfig.Issuer;
var jwtToken = this.ReadTokenFromHeader();
var jwtHandler = new JwtSecurityTokenHandler();
//Check if we can read the token as a valid JWT, if not let the JwtBearerHandler do it's thing...
if (jwtHandler.CanReadToken(jwtToken))
{
//Read the token and determine if the issuer in config is the same as the one in the token, if this is true we know we want to let the JwtBearerHandler continue, if not we skip and return noResult
//This way the next IDP configuration will pass here until we find a matching issuer and then we know that is the IDP we are dealing with
var token = jwtHandler.ReadJwtToken(jwtToken);
if (string.Equals(token.Issuer, authorityIssuer, StringComparison.OrdinalIgnoreCase))
{
return await base.HandleAuthenticateAsync();
}
else
{
// return NoResult since the issuer in cfg did not match the one in the token, so no need to proceed to tokenValidation
this.Logger.LogDebug($"Skipping jwt token validation because token issuer was {token.Issuer} but the authority issuer is: {authorityIssuer}");
return AuthenticateResult.NoResult();
}
}
return await base.HandleAuthenticateAsync();
}
//Fetch the bearer token from the authorization header on the request!
private string ReadTokenFromHeader()
{
string token = null;
string authorization = Request.Headers["Authorization"];
//If we don't find the authorization header return null
if (string.IsNullOrEmpty(authorization))
{
return null;
}
//get the token from the auth header
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
token = authorization.Substring("Bearer ".Length).Trim();
}
return token;
}
}
}
One thing that was missing in Mickael's answer is that scheme needs to be specified in Authorize attribute (If you want to use authorization)
[Authorize(AuthenticationSchemes = "Firebase,Custom", Policy ="FirebaseAdministrators")]
Without AuthenticationSchemes provided, and AddAuthentication() has no parameter, NetCore fails to Authenticate and Request.HttpContext.User.Identity.IsAuthenticated is set to false