Add custom claim for token generation - Duende identity server - identityserver4

I am trying to configure my Duende (former known as identity server4) identity server for authentication and authorisation. For the authentication part, I am using an external authentication service and one of the things that I get as a result is a UserID. Then, I want to add this UserID as a custom claim inside my access token. However, I can't figure out how this is done.
Specifically, I want to implement something like this:
// Client/program.cs
var client = new HttpClient();
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = https://localhost:5001/connect/token,
ClientId = "1",
ClientSecret = "secret",
Scope = "api1",
UserID = UserID // here is the problem. It creates the correct access token without this line
});
The problem is that UserID is not defined as part of the RequestClientCredentialsTokenAsync.
Is there a way I can add it?
Thank you in advance.

ClientCredentials Flow doesn't involve any user interaction since there won't be any signed in user related data.
You can use legacy ResourceOwnerPassword Flow that uses user name and password for authentication. Your current approach is related with server-to-server interaction.

Related

How to obtain an Azure B2C bearer token for a non-interactive/daemon application and get it validated in an Azure HTTP-triggered function

There is a C# application under development that is supposed to be a part of a bigger backend application to process some data. This application is supposed to obtain a token from Azure AD B2C and send it to an HTTP-triggered function where it is supposed to be validated by the following code:
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
$"{_authenticationSettings.Authority}/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever());
var config = await configManager.GetConfigurationAsync();
_validationParameters = new TokenValidationParameters
{
IssuerSigningKeys = config.SigningKeys,
ValidateAudience = true,
// Audience MUST be the app ID aka clientId
ValidAudience = _authenticationSettings.ClientId,
ValidateIssuer = true,
ValidIssuer = config.Issuer,
ValidateLifetime = true
};
var tokenHandler = new JwtSecurityTokenHandler();
var result = tokenHandler.ValidateToken(authHeader.Parameter, _validationParameters, out var jwtToken);
First, we thought that obtaining an access token from Microsoft Graph API using MSAL would help us but the C# code above threw an invalid signature exception which we discovered makes sense due to this GitHub post. Apparently, we need to obtain an id_token instead in the application and send it to the HTTP-triggered function for validation by the code snippet above.
The application cannot obtain the id_token because it's not supposed to launch Azure AD B2C's login UI to have a user sign-in and redirect it through a URL. What is the solution to this problem so that the application would obtain a token without a UI and send that to the http-triggered function for validation?
Obtaining a token for the AAD B2C tenant without UI is possible in two ways and you should probably pick one depending on what exactly you want to achieve:
user token - by using Resource Owner Password Credentials flow - https://learn.microsoft.com/en-us/azure/active-directory-b2c/add-ropc-policy. This flow is deprecated though and mentioned usually in legacy application context
server-side application token - by using Client Cretendial flow - this on the other hand requires using requests specific for AAD but with AAD B2C tenant - https://learn.microsoft.com/en-us/azure/active-directory-b2c/application-types#daemonsserver-side-applications
I'm also not quite sure why should you use id_token for that. If the application needs to authorize the request to the function with the token then it should be an access token regardless of how the token is retrieved (interactive UI or not).

Co-hosting Identity Server 4 with API services using Roles

I've come across an example of co-hosting Identity Server 4 on the same App Host as the API Services that need it for authentication and authorization.
Now I was able to replicate this successfully by just pure authentication but when it comes to authorization using Roles I couldn't get it to work, i.e. adding the [Authorize(Roles = "My Role")] attribute on my Controller Action.
The access token contains the "role" scope and claim but it doesn't seem to be respected at all.
I initially tried the code below but it doesn't execute the JWT Bearer bit at all which leads me to believe that Identity Server uses its own handler for that purpose and I have no idea how to configure it if at all.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
// JWT tokens (default scheme)
.AddJwtBearer(options =>
{
options.Authority = "https://localhost:44367/";
options.Audience = "API";
options.TokenValidationParameters.RoleClaimType = "role";
});
Then I came across this line from the example code (I mentioned initially in this post) which seems like its supposed to grant me the ability to run API services along side with Identity Server:
services.AddLocalApiAuthentication();
But it also doesn't seem to do what I want.
So does the Identity Server authentication middleware allow me to accomplish role-based authentication or is there some other mechanism (i.e. Policies) that I need to look into?
Something worth noting, I was able to accomplish all of this successfully with Identity Server 4 but by hosting it all separately. I want to see what it takes to host it all together.
Just rechecked the example and it worked perfectly fine.
When you use
services.AddLocalApiAuthentication();
it sets up the IdentityServerAccessToken authenticationScheme.
To use it in your API controller you type
[Authorize(IdentityServerConstants.LocalApi.PolicyName)]
as described in the doc, or just
[Authorize(AuthenticationSchemes = "IdentityServerAccessToken")]
All you need to check the roles is one more argument for the attribute:
[Authorize(AuthenticationSchemes = "IdentityServerAccessToken", Roles = "test role1")]

Get an identity token for a client

Using IdentitServer4 I've create a client for a windows application. To call into another authentication service (ie, AWS STS) I need to setup federation to my ID server and using an identity token.
Is it possible to get an identity token for a client?
The following code give me the access token but the identity token is null.
var disco = await DiscoveryClient.GetAsync(Properties.Settings.Default.IdentityUrl);
if (disco.IsError)
{
return false;
}
var tokenClient = new TokenClient(disco.TokenEndpoint, _executionContext.ClientID, _executionContext.Secret);
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api.v1");
_executionContext.AgentToken = tokenResponse.AccessToken; // OK
_executionContext.IdentityToken = tokenResponse.IdentityToken; // NULL
No, by definition a client cannot request an identity token for itself. Only on behalf of a user. From the docs:
User
A user is a human that is using a registered client to access
resources.
Client
A client is a piece of software that requests tokens from
IdentityServer - either for authenticating a user (requesting an
identity token) or for accessing a resource (requesting an access
token).
The reason that a client can't request an identity token for itself is because it doesn't have (and can't have) a sub claim:
The presence (or absence) of the sub claim lets the API distinguish
between calls on behalf of clients and calls on behalf of users.
Here's an example on how to request an identity token on behalf of a user using the password grant.

How to add a claim in the bearer send by httpinterceptor of adal-angular

In a angularjs application, i use adal and adal-angular libraries to authentify user on Azure AD. On backend I use OWIN middleware in my webAPI to add custom claims with :
app.UseWindowsAzureActiveDirectoryBearerAuthentication(new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
Provider = new OAuthBearerAuthenticationProvider()
{
OnValidateIdentity = async context =>
{
if(!context.IsValidated)
return;
var userManager = context.OwinContext.Get<UserManager>();
string email = context.Ticket.Identity.GetClaimValue(System.Security.Claims.ClaimTypes.Email);
User user = userManager.GetByEmail(email);
context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.UserId, user.Id.ToString(CultureInfo.InvariantCulture));
}
}
});
It's work for the current request, but how to add the claim in the bearer send by httpinterceptor of adal-angular for the next requests?
To answer your direct question: you cannot modify the access token, period.
It has a digital signature which will no longer be valid if you change anything in the token.
It's a security measure that prevents tampering.
Your OWIN middleware downloads the public keys of the signing key pairs from Azure AD on startup, and uses those to validate tokens.
Now if you stored your internal ids as extension attributes,
it would actually be possible to tell Azure AD to include that in the tokens.
This feature is in preview though, and not recommended for production use: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims#configuring-custom-claims-via-directory-extensions.
Here you would set the internal id as an extension on the User entity, and have it included for access tokens to your API.
A pragmatic solution to your issue might be an in-memory cache.
How often do emails change?

identity server 4 using multiple external identity providers

I would like to use identity server 4 with a bunch of different external identity providers. Not just one.
For example: one business might use ADFS for their EIP another will use AZURE identity and so on. In this scenario there would only be one instance of identity server accessing different External id providers.
Is this possible or has anyone ever tried this. If not, do you know of a service that does do this.
Of course this is possible. You can register as many external IdP's in your Startup class as you like. Be sure to also check out the sample quickstart repositories from Identityserver.
This code will register AzureAd and Google as external IdP's.
In advance to set this up you will have to register your application in developers.google and AzureAd to authorize your application.
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = Configuration["AzureAd:clientid"],
Authority = Configuration["AzureAd:authority"],
ClientSecret = Configuration["AzureAd:secret"],
PostLogoutRedirectUri = "/signed-out",
AuthenticationScheme = "AzureAd",
ResponseType = OpenIdConnectResponseType.CodeIdToken,
SaveToken = true,
});
app.UseGoogleAuthentication(new GoogleOptions
{
ClientId = Configuration["Google:clientid"],
ClientSecret = Configuration["Google:secret"],
AuthenticationScheme = "Google",
SaveTokens = true
});

Resources