I have an existing api that generates and uses jwt's with a header as follows
{
"typ": "JWT",
"alg": "HS256"
}
The api uses JWT bearer authentication
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { "Any" },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
}
});
I now want to use identity server for a client I want to integrate with.
How can I get Identity server to sign the token in the way that the existing api expects it (HS256).
IdentityServer does not support symmetric keys.
Related
I'm trying to add optional claims using Microsoft Identity Web - NuGet for user authentication in NET Core 3.1 WebApp. Reading the MS Docs, it seems that the only steps needed are to declare the optional claims within the App Registration Manifest file in Azure. But when testing the login process using two different apps (my own code and an MS project example) it looks like the optional claims are not being added to the ID Token when returned from Azure following a successful login i.e they're not present at all when viweing the token details in Debug.
I'm not sure how to diagnose this and where to trace the issue i.e am I missing any required steps in Azure setup?
Side Note: Just to confirm it is the jwt ID Token I want to receive the additional claims, NOT the jwt access token used for calling the graph or another Web API endpoint.
MS Docs reference: v2.0-specific optional claims set
Below is the extract from the Manifest file: (note I've even declared the "accessTokenAcceptedVersion": 2, given that optional claims I'm using are not available in ver.1, which if the above was left at default 'null' value then Azure will assume we're using legacy ver.1 - a possible gotcha)
"accessTokenAcceptedVersion": 2,
"optionalClaims": {
"idToken": [
{
"name": "given_name",
"source": "user",
"essential": false,
"additionalProperties": []
},
{
"name": "family_name",
"source": "user",
"essential": false,
"additionalProperties": []
}
],
"accessToken": [],
"saml2Token": []
},
Extract from startup class:
public void ConfigureServices(IServiceCollection services)
{
// Added to original .net core template.
// ASP.NET Core apps access the HttpContext through the IHttpContextAccessor interface and
// its default implementation HttpContextAccessor. It's only necessary to use IHttpContextAccessor
// when you need access to the HttpContext inside a service.
// Example usage - we're using this to retrieve the details of the currrently logged in user in page model actions.
services.AddHttpContextAccessor();
// DO NOT DELETE (for now...)
// This 'Microsoft.AspNetCore.Authentication.AzureAD.UI' library was originally used for Azure Ad authentication
// before we implemented the newer Microsoft.Identity.Web and Microsoft.Identity.Web.UI NuGet packages.
// Note after implememting the newer library for authetication, we had to modify the _LoginPartial.cshtml file.
//services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
// .AddAzureAD(options => Configuration.Bind("AzureAd", options));
///////////////////////////////////
// Add services required for using options.
// e.g used for calling Graph Api from WebOptions class, from config file.
services.AddOptions();
// Add service for MS Graph API Service Client.
services.AddTransient<OidcConnectEvents>();
// Sign-in users with the Microsoft identity platform
services.AddSignIn(Configuration);
// Token acquisition service based on MSAL.NET
// and chosen token cache implementation
services.AddWebAppCallsProtectedWebApi(Configuration, new string[] { Constants.ScopeUserRead })
.AddInMemoryTokenCaches();
// Add the MS Graph SDK Client as a service for Dependancy Injection.
services.AddGraphService(Configuration);
///////////////////////////////////
// The following lines code instruct the asp.net core middleware to use the data in the "roles" claim in the Authorize attribute and User.IsInrole()
// See https://learn.microsoft.com/aspnet/core/security/authorization/roles?view=aspnetcore-2.2 for more info.
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
// The claim in the Jwt token where App roles are available.
options.TokenValidationParameters.RoleClaimType = "roles";
});
// Adding authorization policies that enforce authorization using Azure AD roles. Polices defined in seperate classes.
services.AddAuthorization(options =>
{
options.AddPolicy(AuthorizationPolicies.AssignmentToViewLogsRoleRequired, policy => policy.RequireRole(AppRole.ViewLogs));
});
///////////////////////////////////
services.AddRazorPages().AddMvcOptions(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();
// Adds the service for creating the Jwt Token used for calling microservices.
// Note we are using our independant bearer token issuer service here, NOT Azure AD
services.AddScoped<JwtService>();
}
Sample Razor PageModel method:
public void OnGet()
{
var username = HttpContext.User.Identity.Name;
var forename = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "given_name")?.Value;
var surname = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "family_name")?.Value;
_logger.LogInformation("" + username + " requested the Index page");
}
UPDATE
Getting closer to a solution but not quite there yet. Couple of issues resolved:
I originally created the Tenant in Azure to use B2C AD, even though I was no longer using B2C and had switched to Azure AD. It wasn't until I deleted the tenant and created a new one before I started to see the optional claims come through to the webapp correctly. After creating the new tenant and assigning the tenant type to use Azure AD, I then found that the 'Token Configuration' menu was now available for configuring the optional claims through the UI, it seems that modifying the App manifest is still required as well, as shown above.
I had to add the 'profile' scope as type 'delegated' to the webapp API Permissions in Azure.
The final issue still unresolved is that although I can see the claims present during Debug, I cant figure out how to retrieve the claim values.
In the method below, I can see the required claims when using Debug, but can't figure out how to retrieve the values:
public void OnGet()
{
var username = HttpContext.User.Identity.Name;
var forename = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "given_name")?.Value;
var surname = HttpContext.User.Claims.FirstOrDefault(c => c.Type == "family_name")?.Value;
_logger.LogInformation("" + username + " requested the Index page");
}
Debug Screenshots shows the given_name & family_name are present:
I've tried different code examples using the claims principal to try and get the values out, but nothing is working for me. Hoping this final riddle is fairly simple to someone who knows the required syntax, as said we now have the required optional claims present, its just not knowing how to actually get the values out.
Big thanks to 'Dhivya G - MSFT Identity' for their assistance (see comments below my original question) method below now allows me to access the required claim values from the Token ID returned from Azure following successful login.
public void OnGet()
{
var username = HttpContext.User.Identity.Name;
var forename = HttpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value;
var surname = HttpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value;
_logger.LogInformation("" + username + " requested the Index page");
}
I have an application registered on my Azure AD tenant. When I receive the access token after logging in I send the token to my node.js API. I'm trying to validate the signature but I keep getting "invalid signature" error. The JWT header lists the specific key that it supposedly used to sign the token. So why can't I verify it? Is there something I need to tweak in the AAD configuration? Are there options I need to specify that I pass to azure-ad-jwt? I've tried passing in { audience: 'https://graph.windows.net'}. I've also tried passing in the audience that is in the decoded token as well as the specific kid that is listed in the header so that it only tries the specific key that the token says it is signed with.
This is my code that is giving me the error:
const aad = require('azure-ad-jwt');
module.exports = (req, res, next) => {
const jwtToken = req.headers.authorization.replace('Bearer ', '');
aad.verify(jwtToken, null, function (err, result) {
if (result) {
console.log("JWT is valid");
next();
} else {
console.log("JWT is invalid: " + err);
res.status(401).json({
message: "Auth failed"
});
}
});
};
If your front-end is acquiring a token for audience https://graph.windows.net, then that token is not meant for your API.
It is a token for Azure AD Graph API.
You should not be validating that.
Instead, your front-end needs to specify your API app's client id or app ID URI as the resource (if using the v1 endpoint/ADAL) or valid scopes defined by your API (if using the v2 endpoint/MSAL).
Then you will get a token you can validate.
I am using JWT for API authentication using RS256 private/public key.
At my client side I am using Vuejs/Angular/React, I am tempted to use JsonWebToken to do client JWT token verification for expiry date and issuer:
var cert = fs.readFileSync('public.pem'); // get public key
jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer' }, function(err, decoded) {
// if issuer mismatch, err == invalid issuer
});
Do you think is a good idea to expose public key, although public key is meant for distribute?
The public key can be published 'publicly' with no harms. The public key is used to verify that the signature isn't manipulated while the private key is the one which shall be kept secret as it is the one that signs the payload. So your client needs to know only if the payload hasn't been manipulated. More details here
How to implement a two factor authentication using Identity Server 4? The token end point returns a token with a username and password / client credentials.
Can we customize those end points?
Both the methods as per the sample does not allow to customize the end point:
> var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.client", "secret");
> var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync("brockallen#gmail.com",
> "Pass123$", "api1");
Is it possible to achieve 2 factor authentication using either asp.net identity Or EF Core implementation?
This shouldn't be a problem at all. When a user is redirected to the Identity Server for login in, if 2FA is enabled then he/she would have to enter the authenticator's code before the Identity Server returns the response back. I have created a repository and blog post series that explain in detail the related concepts. In the AccountController of the IdentityServer you have to check if 2FA is enabled and ask the user to proceed by providing an authenticator code before returning the response.
var signInResult = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, true,
lockoutOnFailure: false);
if (signInResult.RequiresTwoFactor)
{
result.Status = Status.Success;
result.Message = "Enter the code generated by your authenticator app";
result.Data = new {requires2FA = true};
return result;
}
You will also need a TwoFactorAuthenticationController that supports all the 2FA tasks (enable/disable 2FA, sign in with authenticator code/recovery tokens, reset authenticator, etc...)
I'm currently writing an angular application that first authenticates against think texture identityserver3.
This works fine, and I receive the bearer token without any issues.
When I use my token on an call to my API, I'm authenticated. I can see my userid, but have lost my claims (username, roles,...).
What do I have to do for transferring my claims with my token, or getting the roles from the identityserver?
You can tell Identity Server to include specific claims in an access token by adding that claim to your API's Scope.
Example:
var apiScope = new Scope {
Name = "myApi",
DisplayName = "My API",
Type = ScopeType.Resource,
Claims = new List<ScopeClaim> {
new ScopeClaim("myClaimType")
}
};
You can also use the AlwaysIncludeInIdToken property of ScopeClaim to include the claims in identity tokens as well as access tokens.
See https://identityserver.github.io/Documentation/docsv2/configuration/scopesAndClaims.html for more info.
We are doing something very similar using MS Web API 2 and a Thinktecture Identity Server v3.
To verify the user's claims we created an Authentication Filter, and then called the Identity server directly to get the user's claims. The bearer token only grants authentication and it is up to the API to get the claims separately.
protected override bool IsAuthorized(HttpActionContext actionContext)
{
string identityServerUrl = WebConfigurationManager.AppSettings.Get("IdentityServerUrl") + "/connect/userinfo";
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = actionContext.Request.Headers.Authorization;
var response = httpClient.GetAsync(identityServerUrl).Result;
if (response.IsSuccessStatusCode)
{
string responseString = response.Content.ReadAsStringAsync().Result;
Dictionary<string, string> claims = JsonConvert.DeserializeObject<Dictionary<string, string>>(responseString.ToLower());
... Do stuff with your claims here ...
}
}
}