ASP NET CORE Identity and Checktoken URL - asp.net-core

Well, I'm trying to use ASP NET CORE 2.1 with OAuth2 to authenticate in a IdP (Identity Provider), so I have the following:
services.AddAuthentication()
.AddJwtBearer(options =>
{
// The API resource scope issued in authorization server
options.Audience = "resource.server.api";
// URL of my authorization server
options.Authority = "https://myidp.com.br";
});
// Making JWT authentication scheme the default
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
When I try to call my API thought POSTMAN, I got following:
System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://myidp.com.br/.well-known/openid-configuration'.
Well, I don't have well-known URL in my IdP and I can't add it in this moment of project. Is there other way to configure URLs manually without well-known ?
Another important thing: We have a URL https://myidp.com.br/oauth/tokeninfo that check if JWT TOKEN is valid or not.

I assume you are using the Asymmetric Keys . Usually, the public key information is automatically retrieved from the discovery document. If you need to specify it manually, you’ll need to get the key parameters and create a SecurityKey object . You can refer to belwo links for code samples :
https://github.com/IdentityServer/IdentityServer4/blob/master/samples/Clients/src/MvcManual/Controllers/HomeController.cs#L148
Verifying JWT signed with the RS256 algorithm using public key in C#
You can also write the custom JwtSecurityTokenHandler in the System.IdentityModel.Tokens.Jwt package , and override the ValidateToken event to implement the custom validation logic .
You can also not using the AddJwtBearer middleware , the code sample is same as above , create your keys and apply to the validation .
Normally , the noraml process of validating token is :
Decode token
Validate claims(issuer,audience,expire time...)
Validate signature
Creating user principal and sign in user
Updated :
You can also add your own signature validation to the TokenValidationParameters :
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
SignatureValidator =
delegate (string token, TokenValidationParameters parameters)
{
var jwt = new JwtSecurityToken(token);
var httpClient = new HttpClient();
var requestData = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("xxxxxx"),
};
//pass toekn to your endpoint and check result
if (false)
{
throw new Exception("Token signature validation failed.");
}
return jwt;
}
};
});

Related

ASP.NET Core Refresh Token Logic still calling /signin-oidc endpoint

Okay, so I am working on creating an OIDC client that will also handle refresh tokens. I have made some progress, but have some questions.
Here is my ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/Login/Index";
options.Events.OnValidatePrincipal = async context => await OnValidatePrincipalAsync(context);
})
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = Configuration["auth:oidc:authority"];
options.ClientId = Configuration["auth:oidc:clientid"];
options.ClientSecret = Configuration["auth:oidc:clientsecret"];
options.ResponseType = OpenIdConnectResponseType.Code;
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.UseTokenLifetime = true;
options.SignedOutRedirectUri = "https://contoso.com";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = Configuration["auth:oidc:authority"],
ValidAudience = Configuration["auth:oidc:clientid"],
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.FromSeconds(3)
};
});
services.AddAccessTokenManagement();
services.Configure<OidcOptions>(Configuration.GetSection("oidc"));
}
Here is my OnValidatePrincipalAsync(context)
private async Task OnValidatePrincipalAsync(CookieValidatePrincipalContext context)
{
const string AccessTokenName = "access_token";
const string RefreshTokenName = "refresh_token";
const string ExpirationTokenName = "expires_at";
if (context.Principal.Identity.IsAuthenticated)
{
var exp = context.Properties.GetTokenValue(ExpirationTokenName);
var expires = DateTime.Parse(exp, CultureInfo.InvariantCulture).ToUniversalTime();
if (expires < DateTime.UtcNow)
{
// If we don't have the refresh token, then check if this client has set the
// "AllowOfflineAccess" property set in Identity Server and if we have requested
// the "OpenIdConnectScope.OfflineAccess" scope when requesting an access token.
var refreshToken = context.Properties.GetTokenValue(RefreshTokenName);
if (refreshToken == null)
{
context.RejectPrincipal();
return;
}
var cancellationToken = context.HttpContext.RequestAborted;
// Obtain the OpenIdConnect options that have been registered with the
// "AddOpenIdConnect" call. Make sure we get the same scheme that has
// been passed to the "AddOpenIdConnect" call.
//
// TODO: Cache the token client options
// The OpenId Connect configuration will not change, unless there has
// been a change to the client's settings. In that case, it is a good
// idea not to refresh and make sure the user does re-authenticate.
var serviceProvider = context.HttpContext.RequestServices;
var openIdConnectOptions = serviceProvider.GetRequiredService<IOptionsSnapshot<OpenIdConnectOptions>>().Get("OpenIdConnect");
openIdConnectOptions.Scope.Clear();
openIdConnectOptions.Scope.Add("email");
openIdConnectOptions.Scope.Add("profile");
openIdConnectOptions.Scope.Add("offline_access");
var configuration = openIdConnectOptions.Configuration ?? await openIdConnectOptions.ConfigurationManager.GetConfigurationAsync(cancellationToken).ConfigureAwait(false);
// Set the proper token client options
var tokenClientOptions = new TokenClientOptions
{
Address = configuration.TokenEndpoint,
ClientId = openIdConnectOptions.ClientId,
ClientSecret = openIdConnectOptions.ClientSecret,
};
var httpClientFactory = serviceProvider.GetService<IHttpClientFactory>();
using var httpClient = httpClientFactory.CreateClient();
var tokenClient = new TokenClient(httpClient, tokenClientOptions);
var tokenResponse = await tokenClient.RequestRefreshTokenAsync(refreshToken, cancellationToken: cancellationToken).ConfigureAwait(false);
if (tokenResponse.IsError)
{
context.RejectPrincipal();
return;
}
// Update the tokens
var expirationValue = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn).ToString("o", CultureInfo.InvariantCulture);
context.Properties.StoreTokens(new[]
{
new AuthenticationToken { Name = RefreshTokenName, Value = tokenResponse.RefreshToken },
new AuthenticationToken { Name = AccessTokenName, Value = tokenResponse.AccessToken },
new AuthenticationToken { Name = ExpirationTokenName, Value = expirationValue }
});
// Update the cookie with the new tokens
context.ShouldRenew = true;
}
}
}
I've done some experimenting which includes not using the Configuration to get the OpenIdConnectOptions in my OnValidatePrincipal and just create a new OpenIdConnectOptions object , and I still have not been able to understand my issue.
Here are my Current Issues
First Issue
I seem to be able to successfully send a request to the token endpoint after my desired period of time (every 2 minutes and five seconds). I notice that my client application is making a request to the ?authorize endpoint of my authorization server, even though I don't believe I have it configured to do so in my OnValidatePrincipalContext fucntion. I created an all new OpenIdConnectOptions object because I thought the current configuration was triggering it.
First Question
When is this signin-oidc request triggered? I think that's what's triggering the request to my authN server's authorize endpoint. I should not have to query this endpoint if I'm doing silent refresh?
Second Issue
My authorization server is picking up the openid scope when my client makes this request:
POST https://<authorization-server>/oauth/oidc/token HTTP/1.1
Accept: application/json
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=<refresh-token>&client_id=<client-id>&client_secret=<client-secret>
But, in my OnValidatePrincipalContext function I explicitly remove the openid scope by calling
openIdConnectOptions.Scope.Clear();
openIdConnectOptions.Scope.Add("email");
openIdConnectOptions.Scope.Add("profile");
openIdConnectOptions.Scope.Add("offline_access");
Second Question
How do I properly handle the Oidc configuration middleware so that when I go to request a new refresh token the correct request is built and sent to my authN server? Am I doint the wrong kind of authentication scheme (i.e cookie vs bearer)? If I am, how can I tell?
Thank you.
When is this signin-oidc request triggered?
Its triggered by the authorization server when the user have successfully authenticated and given consent to the requested scopes. It will ask the browser to post the authorization code to this endpoint. Its typically performed done by using a self-submitting HTML form that will create a post request to this endpoint.
You should always ask for the openid scope, otherwise it won't work.
A picture showing the flow for the endpoint is:
For the second question one alternative is to take a look at the IdentityModel.AspNetCore library. This library can automatically handle the automatic renewal of the access token using the refresh token.
See also this blog post

aspnet core 2.2 External Authentication

Created an Authentication Api to handle the auth for several apps. This is a basic auth. username and pw. No OAuth with Google etc. The api gets called with the credentials and it responds with an AthenticationResult. It works correctly except on AuthenticationResult.Success. As I learned I cannot serialize the ClaimsPrincipal. As I am reading it seems the answer it to convert to a token. Is this correct? The AuthenticationResult.Failed serializes w/o issue. What is the best solution here. I will continue to look.
thx for reading
General Steps
That's correct, you'll need to complete the following steps:
Return a token from your authentication API.
Configure your application for JWT Bearer authentication.
Include that token as part of an authorize header on every request to the server.
Require authentication/authorization in your controllers.
There is an excellent ASP.NET Core 2.2 JWT Authentication Tutorial you should check out.
There's too much code involved to post all of it in it's entirety, but here are some key snippets (some code slightly modified for greater clarity out of context from the tutorial):
Some Key Code Snippets
Creating the token
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
// 'user' is the model for the authenticated user
// also note that you can include many claims here
// but keep in mind that if the token causes the
// request headers to be too large, some servers
// such as IIS may reject the request.
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
Configuring JWT Authentication (in Startup.cs ConfigureServices method)
var appSettings = appSettingsSection.Get<AppSettings>();
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
Don't forget to configure the app to actually use authentication in Startup.cs Configure method:
app.UseAuthentication();

Where to store JWT Token in .net core web api?

I am using web api for accessing data and I want to authenticate and authorize web api.For that I am using JWT token authentication. But I have no idea where should I store access tokens?
What I want to do?
1)After login store the token
2)if user want to access any method of web api, check the token is valid for this user,if valid then give access.
I know two ways
1)using cookies
2)sql server database
which one is the better way to store tokens from above?
Alternatively, if you just wanted to authenticate using JWT the implementation would be slightly different
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var user = context.Principal.Identity.Name;
//Grab the http context user and validate the things you need to
//if you are not satisfied with the validation fail the request using the below commented code
//context.Fail("Unauthorized");
//otherwise succeed the request
return Task.CompletedTask;
}
};
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey("MyVeryStrongKeyHiddenFromAnyone"),
ValidateIssuer = false,
ValidateAudience = false
};
});
still applying use authentication before use MVC.
[Please note these are very simplified examples and you may need to tighten your security more and implement best practices such as using strong keys, loading configs perhaps from the environment etc]
Then the actual authentication action, say perhaps in AuthenticationController would be something like
[Route("api/[controller]")]
[Authorize]
public class AuthenticationController : Controller
{
[HttpPost("authenticate")]
[AllowAnonymous]
public async Task<IActionResult> AuthenticateAsync([FromBody]LoginRequest loginRequest)
{
//LoginRequest may have any number of fields expected .i.e. username and password
//validate user credentials and if they fail return
//return Unauthorized();
var claimsIdentity = new ClaimsIdentity(new Claim[]
{
//add relevant user claims if any
}, "Cookies");
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
await Request.HttpContext.SignInAsync("Cookies", claimsPrincipal);
return Ok();
}
}
in this instance I'm using cookies so I'm returning an HTTP result with Set Cookie. If I was using JWT, I'd return something like
[HttpPost("authenticate")]
public IActionResult Authenticate([FromBody]LoginRequest loginRequest)
{
//validate user credentials and if they validation failed return a similar response to below
//return NotFound();
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes("MySecurelyInjectedAsymKey");
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
//add my users claims etc
}),
Expires = DateTime.UtcNow.AddDays(1),//configure your token lifespan and needed
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey("MyVerySecureSecreteKey"), SecurityAlgorithms.HmacSha256Signature),
Issuer = "YourOrganizationOrUniqueKey",
IssuedAt = DateTime.UtcNow
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
var cookieOptions = new CookieOptions();
cookieOptions.Expires = DateTimeOffset.UtcNow.AddHours(4);//you can set this to a suitable timeframe for your situation
cookieOptions.Domain = Request.Host.Value;
cookieOptions.Path = "/";
Response.Cookies.Append("jwt", tokenString, cookieOptions);
return Ok();
}
I'm not familiar with storing your users tokens on your back end app, I'll quickly check how does that work however if you are using dotnet core to authenticate with either cookies or with jwt, from my understanding and experience you need not store anything on your side.
If you are using cookies then you just need to to configure middleware to validate the validity of a cookie if it comes present in the users / consumer's headers and if not available or has expired or can't resolve it, you simply reject the request and the user won't even hit any of your protected Controllers and actions. Here's a very simplified approach with cookies.(I'm still in Development with it and haven't tested in production but it works perfectly fine locally for now using JS client and Postman)
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "yourCookieName";
options.Cookie.SameSite = SameSiteMode.None;//its recommended but you can set it to any of the other 3 depending on your reqirements
options.Events = new Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents
{
OnRedirectToLogin = redirectContext =>//this will be called if an unauthorized connection comes and you can do something similar to this or more
{
redirectContext.HttpContext.Response.StatusCode = 401;
return Task.CompletedTask;
},
OnValidatePrincipal = context => //if a call comes with a valid cookie, you can use this to do validations. in there you have access to the request and http context so you should have enough to work with
{
var userPrincipal = context.Principal;//I'm not doing anything with this right now but I could for instance validate if the user has the right privileges like claims etc
return Task.CompletedTask;
}
};
});
Obviously this would be placed or called in the ConfigureServices method of your startup to register authentication
and then in your Configure method of your Startup, you'd hookup Authentication like
app.UseAuthentication();
before
app.UseMvc()

Azure AD B2C with Angular4 and WebAPI Core2 token validation issue [duplicate]

This question already has an answer here:
Azure AD B2C error - IDX10501: Signature validation failed
(1 answer)
Closed 5 years ago.
Doesn't seem Azure documentation can give a clear example hot to do it right.
There are Angular4 (WebApp) and WebAPI Core 2.0 back-end.Two application configured in Azure B2C. WebApp has WebAPI app in its API access.
Web app gets redirected to https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize. There, credentials provided and then AAD B2C calls back WebApp page with access_token, token_type, expires_in, id_token url parameters.
Then, WebApp makes a request to a protected endpoint of the back-end with access_token in Authorization header. MessageReceivedAsync is called when request hits the back-end and goes all the way through validating the token.
However, when process exits the method next step it goes into is AuthenticationFailed with error.
"IDX10501: Signature validation failed. Unable to match 'kid': 'Base64_kid',
token: '{"alg":"RS256","typ":"JWT","kid":"Base64_kid"}.{"iss":"number of claims"}'."
My understanding that Audience is the WebAPI application id. I have only a SingIn/Up policy.
What am I missing here to complete jwt manual validation w/o errors? Another question, when claimsPrincipal is created when token validated, how does it go into request context to be able to access protected endpoint?
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.Authority = string.Format("https://login.microsoftonline.com/{0}/v2.0/",
Configuration["Authentication:AzureAd:ida:Tenant"], Configuration["Authentication:AzureAd:ida:Policy"]);
options.Audience = Configuration["Authentication:AzureAd:ida:ClientId"];
options.Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed,
OnMessageReceived = MessageReceivedAsync,
OnChallenge = Challenge,
OnTokenValidated = TokenValidated
};
});
...
}
private Task MessageReceivedAsync(MessageReceivedContext arg)
{
string jwtToken = null;
var aadInstance = Configuration["Authentication:AzureAd:ida:AADInstance"];
var tenant = Configuration["Authentication:AzureAd:ida:Tenant"];
var audience = Configuration["Authentication:AzureAd:ida:Audience"];
var policy = Configuration["Authentication:AzureAd:ida:Policy"];
var authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
string _issuer = string.Empty;
List<SecurityKey> _signingTokens = null;
var authHeader = arg.HttpContext.Request.Headers["Authorization"];
// 7 = (Bearer + " ").Length
var token = authHeader.ToString().Substring(7);
try
{
string stsDiscoveryEndpoint = string.Format("{0}/v2.0/.well-known/openid-configuration?p={1}", authority, policy);
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint,
new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config = null;
var openIdConfigTask = Task.Run(async () => {
config = await configManager.GetConfigurationAsync();
});
openIdConfigTask.Wait();
_issuer = config.Issuer;
_signingTokens = config.SigningKeys.ToList();
}
catch(Exception ex)
{
...
}
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidAudience = audience,
ValidIssuer = _issuer,
IssuerSigningKeys = _signingTokens
};
var claimsPrincipal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
//Thread.CurrentPrincipal = claimsPrincipal; ?
//var ticket = new AuthenticationTicket(claimsPrincipal, arg.Scheme.Name); ?
//arg.HttpContext.User = claimsPrincipal; ?
return Task.FromResult(0);
}
The options.Audience property is correct (i.e. the application identifier for the Web API application) but the JWT bearer authentication middleware is downloading the wrong signing keys because you don't seem to be setting the options.Authority property to the right value.
It must include the Azure AD B2C policy.
You should be setting it to:
https://login.microsoftonline.com/tfp/{tenant}/{policy}/v2.0/'
such as:
https://login.microsoftonline.com/tfp/{Configuration["Authentication:AzureAd:ida:Tenant"]}/{Configuration["Authentication:AzureAd:ida:Policy"]}/v2.0/.
As result of the token validation, the HttpContext.User object contains the claims from the token, so you can control access for example via scopes.

Authorize via JWT Token

ASP.NET Core 5 with ASP.NET Identity 3.0, I'm using both web pages and apis. I am using OpenIddict to issue a JWT token and to authenticate. My code looks as such:
X509Certificate2 c = new X509Certificate2(#"tokensign.p12", "MyCertificatePassword");
services.AddOpenIddict<WebUser, IdentityRole<int>, WebDbContext, int>()
.EnableTokenEndpoint("/api/customauth/login")
.AllowPasswordFlow()
.UseJsonWebTokens()
.AddSigningCertificate(c);
If I disable UseJsonWebTokens(), I can generate a token and authorise successfully. However, I am not sure that my certificate is validating the returned tokens.
And when enable UseJsonWebTokens, I am able to issue a JWT token at this end point. However, I can't authenticate any request!
I am using the following code in the app configuration:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false,
Authority = "http://localhost:60000/",
Audience = "http://localhost:60000/",
});
app.UseOAuthValidation();
app.UseIdentity();
app.UseOpenIddict();
app.UseMvcWithDefaultRoute();
How can I enforce the request to be validated with my certificate to make sure the JWT token is not tampered with.
What are the correct settings that will allow validation and authorisation of my JWT token, given that if I am not using JWT, I am getting authorised successfully.
If I disable UseJsonWebTokens(), I can generate a token and authorise successfully. However, I am not sure that my certificate is validating the returned tokens.
In ASOS (the OpenID Connect server framework behind OpenIddict), there are 2 different built-in serialization mechanisms to create and protect tokens:
One that uses IdentityModel (a library developed by Microsoft) and produces standard tokens verifiable by third parties:
Identity tokens (JWT by definition) are always created using this process and you can call UseJsonWebTokens() to force OpenIddict to issue access tokens that use the same serialization process.
The certificate you specify when calling AddSigningCertificate() is always used to sign these tokens.
One that uses the ASP.NET Core Data Protection stack (also developed by Microsoft):
This stack exclusively produces "proprietary" tokens that are not meant to be read or verified by a third-party, as the token format is not standard and necessarily relies on symmetric signing and encryption.
It's the mechanism we use for authorization codes and refresh tokens, that are only meant to be consumed by OpenIddict itself. It's also used for access tokens when you use the default token format.
In this case, the certificate you specify when calling AddSigningCertificate() is not used.
Instead, these tokens are always encrypted by the Data Protection stack using an Authenticated Encryption algorithm (by default, AES-256-CBC with HMACSHA256), that provides authenticity, integrity and confidentiality. For that, 2 keys (one for encryption, one for validation) are derived by the Data Protection stack from one of the master keys stored in the key ring.
How can I enforce the request to be validated with my certificate to make sure the JWT token is not tampered with.
What are the correct settings that will allow validation and authorisation of my JWT token, given that if I am not using JWT, I am getting authorised successfully.
To answer these questions, it would help if you enabled logging and shared your traces.
Creating JWT Token based authentication in ASP.NET Core is very very simple. Please follow below link you will get more idea.
How to Create JWT Token in Asp NET Core
Sample Code
public static class AuthenticationConfig
{
public static string GenerateJSONWebToken(string user)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("730F046B1ADF1555FF0C80149B47B38CD7C0A146AAFA34870E863CAA25B585C3"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[] {
new Claim("UserName", user),
new Claim("Role", "1"),
};
var token = new JwtSecurityToken("http://localhost:30972",
"http://localhost:30972",
claims,
DateTime.UtcNow,
expires: DateTime.Now.AddMinutes(10),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
//ConfigureJwtAuthentication
internal static TokenValidationParameters tokenValidationParams;
public static void ConfigureJwtAuthentication(this IServiceCollection services)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("730F046B1ADF1555FF0C80149B47B38CD7C0A146AAFA34870E863CAA25B585C3"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
tokenValidationParams = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidIssuer = "http://localhost:30972",
ValidateLifetime = true,
ValidAudience = "http://localhost:30972",
ValidateAudience = true,
RequireSignedTokens = true,
// Use our signing credentials key here
// optionally we can inject an RSA key as
//IssuerSigningKey = new RsaSecurityKey(rsaParams),
IssuerSigningKey = credentials.Key,
ClockSkew = TimeSpan.FromMinutes(10)
};
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = tokenValidationParams;
#if PROD || UAT
options.IncludeErrorDetails = false;
#elif DEBUG
options.RequireHttpsMetadata = false;
#endif
});
}
}
Add this line in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureJwtAuthentication();
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build();
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
Add these lines in Authentication Controller
[Route("api/[controller]")]
public class AuthenticationController : Controller
{
// GET: api/<controller>
[HttpGet]
public string Get(string user, string pass)
{
if (user == "admin")
{
return AuthenticationConfig.GenerateJSONWebToken(user);
}
else
{
return "";
}
}
// POST api/<controller>
[Authorize]
[HttpPost]
public string Post()
{
var identity = HttpContext.User.Identity as ClaimsIdentity;
IEnumerable<Claim> claim = identity.Claims;
var UserName = claim.Where(c => c.Type == "UserName").Select(c => c.Value).SingleOrDefault();
return "Welcome to " + UserName + "!";
}
}