ValidateIssuer option in OpenID connect authentication - c#

We are using OIDC library and for now we allow only MSA account login. So we have configured parameters ValidateIssuer = true and Validissuers = https://login.microsoftonline.com/..” However, we now need to onboard other AAD tenants (for example : abc#dell.com) to our application so we decided to set ValidateIssuer = false.
Since my application is already backed up by custom authorization, I am finding it difficult to understand the purpose of this flag altogether. Basically my question is under what circumstances one would not like to set this flag to false ? And what risk one might ran into if set to false ?
I looked at AAD docs published here and still not able to find convincing response from the comments over sample code snippet :
// ValidateIssuer set to false to allow work accounts from any organization to sign in to your application
// To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name or Id (example: contoso.onmicrosoft.com)
// To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false
},

As the other answer already mentioned, if you leave ValidateIssuer = false, then OIDC middleware will not try to validate the issuer tenant and it would effectively mean that your application is open for anyone with a user in Azure AD.
Some suggestions on tackling multi-tenant case
If you know the list of valid issuers ahead of time, make use of a list of issuers in TokenValidationParameters.ValidIssuers.
Example:
ValidIssuers = new List<string>()
{
"https://sts.windows.net/6d9c0c36-c30e-442b-b60a-ca22d8994d14/",
"https://sts.windows.net/f69b5f46-9a0d-4a5c-9e25-54e42bbbd4c3/",
"https://sts.windows.net/fb674642-8965-493d-beee-2703caa74f9a/"
}
If valid issuers for your application are dynamic or if you want to write some logic to gather that list, you can write an implementation for TokenValidationParameters.IssuerValidator which has your custom logic. You just need to set a delegate that will be used to validate the issuer.
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
// Set this to a delegate and write your own custom implementation there. See code sample URL ahead for more details.
IssuerValidator = AadIssuerValidator.ValidateAadIssuer
};
If neither case makes sense, and your validation logic is unrelated to the tenant to which caller belongs, set TokenValidationParameters.ValidateIssuer to false, but make sure you add your custom logic at the end for example in SecurityTokenValidated notifications.
Sample Code
Build a multi-tenant SaaS web application using Azure AD & OpenID Connect
Look closely at these files in this sample:
App_Start/Startup.Auth.cs
Utils/AadIssuerValidator.cs

This flag allows the OpenID Connect authentication handler to make sure the person who logs in to your application comes from a known AAD tenant. If you set it to false, this validation doesn't happen. What it effectively means is that anyone accessing your application and logging in successfully when being redirected to AAD will be allowed in your application.
In summary, I'd say the only two circumstances you want to set this flag to false are if:
you don't know in advance which tenants can connect to your application; or
you want to allow people from every single AAD tenant to connect to your application.
Also, I'd be curious to know what you mean by "the application is already backed up by custom authorization".
Cheers

Related

Fetch permissions from identity server during authorization

I am using identity server 4 for authentication and authorization, and user permissions are saved in JWT and then used on API-s to check if users has required permission.
But the problem is that JWT got too big and I would like to remove permissions from it, and make custom authorization on API-s so that its fetches permissions from identity server instead of getting it from JWT.
API would get only userId from JWT and then based on that fetch additional information from identity server. Is it possible to do something like that?
We basically have a similar problem in our application.
The way to solve this problem is using an event which is raised at the level of the API resource (the API which you are protecting by using JWT bearer tokens authentication) once the JWT token has been read from the incoming request and validated.
This event is called OnTokenValidated, see here for more details.
This is the top level plan:
keep your JWT bearer token minimal. At the very minimum it contains the subject id, which is the unique identifier of the user at the identity provider level. You can put other claims there, but the idea is that the JWT bearer token must be small
implement a way to get the user permissions given the user unique identifier (you can use the subject id as an identifier or any other id which makes sense in your system)
make the user permissions fetch mechanism of the previous point accessible via api call. Caching this API is a good idea, because usually permissions are stable. Defining a smart way to evict this cache is beyond the scope of this answer, but it's something you should definitely think about.
once you have fetched the user permissions (via an API call) you need to make them available to the ASP.NET core authorization framework. The simplest way to do so is create a custom claim type (for instance: "app_permission") and create one user claim per each user permission. Each of these permission claims has the custom claim type ("app_permission") and the permission name as the claim value. For instance a user having the two permissions "read-content" and "write-content" will have two claims both having "app_permission" as the claim type, the first one having "read-content" as the claim value and the second one having "write-content" as the claim value.
the permissions claims defined at the previous point can be injected in the user identity (at the API resource level) by defining an additional ClaimsIdentity for the user and by adding it to the current user identity. The process depicted here is quite similar to a claims transformation done by an MVC application using cookie authentication.
In the Startup class of your API resource, in the point where you register the authentication services, you can do something like this:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://localhost:8080";
options.Audience = "sample-api";
options.RequireHttpsMetadata = false;
// register callbacks for events
options.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
if (!context.Principal.Identity.IsAuthenticated)
{
return;
}
var subjectId = context.Principal.FindFirst(JwtClaimTypes.Subject)?.Value;
if (string.IsNullOrWhiteSpace(subjectId))
{
return;
}
// do whatever you want with the user subjectId in order to get user permissions.
//You can resolve services by using context.HttpContext.RequestServices which is an instance of IServiceProvider
//Usually you will perform an API call to fetch user permissions by using the subject id as the user unique identifier
// User permissions are usually transformed in additional user claims, so that they are accessible from ASP.NET core authorization handlers
var identity = new ClaimsIdentity(userPermissionsClaims);
context.Principal.AddIdentity(identity);
}
};
});
+1 for the accepted answer but I would just like to offer an alternative solution to this problem. If your permissions are pretty simple like readResource or writeResource then you could define all your permissions as enum and use integers instead of strings in JWT, that would reduce JWT size.
If permission list is still huge then you could also group permissions together so that the permission list is smaller for some customers e.g. merge readResource, writeResource, updateResource, deleteResource into one permission called crudResource.

IdentityServer4: Multiple Instances of a WinForm client with client credentials - Token question

I have a WinForm App and an API which I secure with IdentityServer4. The client setup in IS4 is as follows, since there are no individual credentials, and only this application itself should have general access to the API.
new Client
{
ClientId = "ClientApp",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "APIOne" }
}
This works well so far, but I have a question about multiple instances of the WinForm app. Do all clients receive the same token? Because there are no individual credentials, only a client secret.
If I take a look into the tokens with https://jwt.io/ I see that they are different, but only the "exp" field, rest seems to be the same.
This works well so far, but I have a question about multiple instances of the WinForm app. Do all clients receive the same token?
No, in general they have not. But do note: if they would receive the same token it shouldn't matter.
The exp field is most likely "always" different, because it depends on the time it has been created, and therefor your token will not likely to be the same.
But, again; the token is most likely signed (or even encrypted). This is basically your safe-guard that the token is valid. Even if the tokens are equal, it should not matter. This means, the server for example, shouldn't expect the token to be unique.
Having said that; there is a catch.
It seems, that you now have a couple application which log's in with the same api key and secret.
I would suggest to add something, like a client id just to be able to identify the different clients. It would also make your token unique.

How to authenticate azure functions with firebase auth

Before I explain my problem I want to share some insights about what I am trying to do. I am developing a serverless application in Azure. My azure app service plan contains multiple azure function application and each function app contains multiple azure functions(HTTP triggers/API) in it. Now I want to authenticate all the serverless API. I am using firebase to authenticate my users. How I can achieve it in a minimal and reusable manner.
I already use firebase in asp.net core for authentication in a monolithic manner but for a situation like this, I am not sure How to do.
In a Monolithic manner. In our ConfigureServices method, we need to register the services necessary to handle authentication and to specify the parameters of our Firebase project.
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://securetoken.google.com/my-firebase-project";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://securetoken.google.com/my-firebase-project",
ValidateAudience = true,
ValidAudience = "my-firebase-project",
ValidateLifetime = true
};
});
Then in our Configure method, we have to do one simple call to register the actual middleware that will handle the authentication.
app.UseAuthentication();

Web API 2.0 Owin authorization with multiple .net applications

I have two .net applications.
Both applications have WebAPI 2.O APIs using C#.
Let's say one is parent application another one is a child.
Parent application has Owin authentication and all APIs working as expected with Authorization.
In child application, I want to use same Authorization provider used in the parent application. I don't want to use authentication for child application again.
Two things I have tried:
Use of same machine keys in both the applications
Tried to create a third independent .net application which will provide authentication and authorization for both the applications.
First one didn't work. I am not sure how I can achieve the second one.
Any help would be appreciated.
Thanks.
So, if I understood correctly, you want a way to authenticate a child service, based on the parent service authentication passing authentication between the services.
We just need the same thing here, to authenticate the microservices behind our front service (parent service).
We used JWT for that, using it we can solve that, because on the child services (in our case microservices) they trust the parent authentication.
The services work like this, the Parent Service or maybe another Authentication service creates the valid JWT to be used on the Parent Service.
When the Parent Service, receveives the JWT they will validate everything that's need to ensure the client is corret. When the Parent Service need to call the Child Service, it'll send the same JWT, but on the Child Service the JWT will be not the same, in our case we just validate the Lifetime and Issuer Sign Key.
We end up with a code like this on our Startup.cs file on our child services, our parent service/auth service was kept the same.
public static void ConfigureAuth(IServiceCollection services)
{
services
.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.SaveToken = true;
o.TokenValidationParameters = new TokenValidationParameters
{
// Must validate the signing key
ValidateIssuerSigningKey = true,
// Must validate the life time
ValidateLifetime = true,
// The issuer may vary in a multitenant scenario,
// that's why we not valid the issuer.
ValidateIssuer = false,
ValidIssuer = o.ClaimsIssuer,
// Allowing passing a token among multiple services (audiences).
ValidateAudience = false,
ValidAudience = "",
// Does not require expiration
RequireExpirationTime = false,
ClockSkew = TimeSpan.Zero
};
});
}
If you still have doubts I recommend you to look for Authentication Between Microservice, maybe that can help.
Store the generated authentication token (along with user identity info if needed) from the Parent application in a secure Redis cache.
You can then get the token from subsequent requests on the Parent API's authorized endpoints, and append it on any calls to your Child API:
public class ValuesController : ApiController
{
[Authorize]
public IHttpActionResult Get()
{
var authToken = Request.Headers.Authorization;
// send authToken with requests to child endpoints
}
}
Then on the Child API you can get the auth token in a similar manner, and lookup & validate it against the stored Redis tokens.
Extra points if you're getting the token in middleware.

JWT Authorization in .net Core - communicate with OAuth server

I start building new application with JWT authorization. Our team already have OAuth 2 server written in java, so my target is: check key with public key.
But I don't know how to do it. If I use .net identity I have to use entity framework but I use only Cassandra as a database.
How I can implement it without using EF? Do you know any tutorials?
You don't need any ASP.NET Core stuff. A simple approach would be:
Nu-get the Packages
System.IdentityModel.Tokens.Jwt,
Microsoft.IdentityModel.Tokens
Set up some validation parameters:
var validationParameters = new TokenValidationParameters
{
RequireExpirationTime = true,
ValidateLifetime = true,
IssuerSigningKeys = keys, // Your public keys.
ValidAudience = "my valid audience",
ValidIssuer = "my valid issuer"
}
Call ValidateToken to get a ClaimsPrincipal with claims and stuff.
token is your JWT string, e.g. parsed from Authorization HTTP header.
var handler = new JwtSecurityTokenHandler();
handler.ValidateToken(token, validationParameters, out SecurityToken validatedToken);
Using JsonWebKeySet from the above IdentityModel.Tokens package, you can automagically obtain keys from an OpenID Connect configuration:
https://github.com/IdentityModel/IdentityModel/blob/master/source/IdentityModel.Shared/Jwt/JsonWebKeySet.cs
There are quite a few Microsoft (and other) documents available (make sure you are looking at documents relevant to the version that you are working with!) - googling will find them pretty easily, but EF is certainly not required as seen below.
No identity or user information is managed by the app directly. Instead, it will get all the user information it needs directly from the JWT token that authenticates a caller.
https://blogs.msdn.microsoft.com/webdev/2017/04/06/jwt-validation-and-authorization-in-asp-net-core/
Here is a simple example for version 1.1
https://github.com/williamhallatt/aspnet-core-webapi-jwt-auth-example
and the same example for 2.0
https://github.com/williamhallatt/aspnet-core-webapi-jwt-auth-example/tree/dotnecore2.0

Categories