This is the third part of Building Simple Membership system using ASP.NET Identity 2.1, ASP.NET Web API 2.2 and AngularJS. The topics we’ll cover are:
- Configure ASP.NET Identity with ASP.NET Web API (Accounts Management) – Part 1.
- ASP.NET Identity 2.1 Accounts Confirmation, and Password/User Policy Configuration – Part 2.
- Implement JSON Web Tokens Authentication in ASP.NET Web API and Identity 2.1 – (This Post)
- ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4
- ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
- AngularJS Authentication and Authorization with ASP.NET Web API and Identity 2.1 – Part 6
The source code for this tutorial is available on GitHub.
Implement JSON Web Tokens Authentication in ASP.NET Web API and and Identity 2.1
Currently our API doesn’t support authentication and authorization, all the requests we receive to any end point are done anonymously, In this post we’ll configure our API which will act as our Authorization Server and Resource Server on the same time to issue JSON Web Tokens for authenticated users and those users will present this JWT to the protected end points in order to access it and process the request.
I will use step by step approach as usual to implement this, but I highly recommend you to read the post JSON Web Token in ASP.NET Web API 2 before completing this one; where I cover deeply what is JSON Web Tokens, the benefits of using JWT over default access tokens, and how they can be used to decouple Authorization server from Resource server. In this tutorial and for the sake of keeping it simple; both OAuth 2.0 roles (Authorization Server and Recourse Server) will live in the same API.
Step 1: Implement OAuth 2.0 Resource Owner Password Credential Flow
We are going to build an API which will be consumed by a trusted client (AngularJS front-end) so we only interested in implementing a single OAuth 2.0 flow where the registered user will present username and password to a specific end point, and the API will validate those credentials, and if all is valid it will return a JWT for the user where the client application used by the user should store it securely and locally in order to present this JWT with each request to any protected end point.
The nice thing about this JWT that it is a self contained token which contains all user claims and roles inside it, so there is no need to do any extra DB queries to fetch those values for the authenticated user. This JWT token will be configured to expire after 1 day of its issue date, so the user is requested to provide credentials again in order to obtain new JWT token.
If you are interested to know how to implement sliding expiration tokens and how you can keep the user logged in; I recommend you to read my other post Enable OAuth Refresh Tokens in AngularJS App which covers this deeply, but adds more complexity to the solution. To keep this tutorial simple we’ll not add refresh tokens here but you can refer to the post and implement it.
To implement the Resource Owner Password Credential flow; we need to add new folder named “Providers” then add a new class named “CustomOAuthProvider”, after you add then paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public class CustomOAuthProvider : OAuthAuthorizationServerProvider { public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { context.Validated(); return Task.FromResult<object>(null); } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { var allowedOrigin = "*"; context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin }); var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); if (user == null) { context.SetError("invalid_grant", "The user name or password is incorrect."); return; } if (!user.EmailConfirmed) { context.SetError("invalid_grant", "User did not confirm email."); return; } ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT"); var ticket = new AuthenticationTicket(oAuthIdentity, null); context.Validated(ticket); } } |
This class inherits from class “OAuthAuthorizationServerProvider” and overrides the below two methods:
- As you notice the “ValidateClientAuthentication” is empty, we are considering the request valid always, because in our implementation our client (AngularJS front-end) is trusted client and we do not need to validate it.
- The method “GrantResourceOwnerCredentials” is responsible for receiving the username and password from the request and validate them against our ASP.NET 2.1 Identity system, if the credentials are valid and the email is confirmed we are building an identity for the logged in user, this identity will contain all the roles and claims for the authenticated user, until now we didn’t cover roles and claims part of the tutorial, but for the mean time you can consider all users registered in our system without any roles or claims mapped to them.
- The method “GenerateUserIdentityAsync” is not implemented yet, we’ll add this helper method in the next step. This method will be responsible to fetch the authenticated user identity from the database and returns an object of type “ClaimsIdentity”.
- Lastly we are creating an Authentication ticket which contains the identity for the authenticated user, and when we call “context.Validated(ticket)” this will transfer this identity to an OAuth 2.0 bearer access token.
Step 2: Add method “GenerateUserIdentityAsync” to “ApplicationUser” class
Now we’ll add the helper method which will be responsible to get the authenticated user identity (all roles and claims mapped to the user). The “UserManager” class contains a method named “CreateIdentityAsync” to do this task, it will basically query the DB and get all the roles and claims for this user, to implement this open class “ApplicationUser” and paste the code below:
1 2 3 4 5 6 7 |
//Rest of code is removed for brevity public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType) { var userIdentity = await manager.CreateIdentityAsync(this, authenticationType); // Add custom user claims here return userIdentity; } |
Step 3: Issue JSON Web Tokens instead of Default Access Tokens
Now we want to configure our API to issue JWT tokens instead of default access tokens, to understand what is JWT and why it is better to use it, you can refer back to this post.
First thing we need to installed 2 NueGet packages as the below:
1 2 |
Install-package System.IdentityModel.Tokens.Jwt -Version 4.0.1 Install-package Thinktecture.IdentityModel.Core -Version 1.3.0 |
There is no direct support for issuing JWT in ASP.NET Web API, so in order to start issuing JWTs we need to implement this manually by implementing the interface “ISecureDataFormat” and implement the method “Protect”.
To implement this add new file named “CustomJwtFormat” under folder “Providers” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket> { private readonly string _issuer = string.Empty; public CustomJwtFormat(string issuer) { _issuer = issuer; } public string Protect(AuthenticationTicket data) { if (data == null) { throw new ArgumentNullException("data"); } string audienceId = ConfigurationManager.AppSettings["as:AudienceId"]; string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["as:AudienceSecret"]; var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64); var signingKey = new HmacSigningCredentials(keyByteArray); var issued = data.Properties.IssuedUtc; var expires = data.Properties.ExpiresUtc; var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey); var handler = new JwtSecurityTokenHandler(); var jwt = handler.WriteToken(token); return jwt; } public AuthenticationTicket Unprotect(string protectedText) { throw new NotImplementedException(); } } |
What we’ve implemented in this class is the following:
- The class “CustomJwtFormat” implements the interface “ISecureDataFormat<AuthenticationTicket>”, the JWT generation will take place inside method “Protect”.
- The constructor of this class accepts the “Issuer” of this JWT which will be our API. This API acts as Authorization and Resource Server on the same time, this can be string or URI, in our case we’ll fix it to URI.
- Inside “Protect” method we are doing the following:
- As we stated before, this API serves as Resource and Authorization Server at the same time, so we are fixing the Audience Id and Audience Secret (Resource Server) in web.config file, this Audience Id and Secret will be used for HMAC265 and hash the JWT token, I’ve used this implementation to generate the Audience Id and Secret.
- Do not forget to add 2 new keys “as:AudienceId” and “as:AudienceSecret” to the web.config AppSettings section.
- Then we prepare the raw data for the JSON Web Token which will be issued to the requester by providing the issuer, audience, user claims, issue date, expiry date, and the signing key which will sign (hash) the JWT payload.
- Lastly we serialize the JSON Web Token to a string and return it to the requester.
- By doing this, the requester for an OAuth 2.0 access token from our API will receive a signed token which contains claims for an authenticated Resource Owner (User) and this access token is intended to certain (Audience) as well.
Step 4: Add Support for OAuth 2.0 JWT Generation
Till this moment we didn’t configure our API to use OAuth 2.0 Authentication workflow, to do so open class “Startup” and add new method named “ConfigureOAuthTokenGeneration” as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
private void ConfigureOAuthTokenGeneration(IAppBuilder app) { // Configure the db context and user manager to use a single instance per request app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() { //For Dev enviroment only (on production should be AllowInsecureHttp = false) AllowInsecureHttp = true, TokenEndpointPath = new PathString("/oauth/token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays(1), Provider = new CustomOAuthProvider(), AccessTokenFormat = new CustomJwtFormat("http://localhost:59822") }; // OAuth 2.0 Bearer Access Token Generation app.UseOAuthAuthorizationServer(OAuthServerOptions); } |
What we’ve implemented here is the following:
- The path for generating JWT will be as :”http://localhost:59822/oauth/token”.
- We’ve specified the expiry for token to be 1 day.
- We’ve specified the implementation on how to validate the Resource owner user credential in a custom class named “CustomOAuthProvider”.
- We’ve specified the implementation on how to generate the access token using JWT formats, this custom class named “CustomJwtFormat” will be responsible for generating JWT instead of default access token using DPAPI, note that both format will use Bearer scheme.
Do not forget to call the new method “ConfigureOAuthTokenGeneration” in the Startup “Configuration” as the class below:
1 2 3 4 5 6 7 8 9 |
public void Configuration(IAppBuilder app) { HttpConfiguration httpConfig = new HttpConfiguration(); ConfigureOAuthTokenGeneration(app); //Rest of code is removed for brevity } |
Our API currently is ready to start issuing JWT access token, so test this out we can issue HTTP POST request as the image below, and we should receive a valid JWT token for the next 24 hours and accepted only by our API.
Step 5: Protect the existing end points with [Authorize] Attribute
Now we’ll visit all the end points we have created earlier in previous posts in the “AccountsController” class, and attribute the end points which need to be protected (only authenticated user with valid JWT access token can access it) with the [Authorize] attribute as the below:
– GetUsers, GetUser, GetUserByName, and DeleteUser end points should be accessed by users enrolled in Role “Admin”. Roles Authorization is not implemented yet and for now we will only allow any authentication user to access it, the code change will be as simple as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[Authorize] [Route("users")] public IHttpActionResult GetUsers() {} [Authorize] [Route("user/{id:guid}", Name = "GetUserById")] public async Task<IHttpActionResult> GetUser(string Id) {} [Authorize] [Route("user/{username}")] public async Task<IHttpActionResult> GetUserByName(string username) { } [Authorize] [Route("user/{id:guid}")] public async Task<IHttpActionResult> DeleteUser(string id) { } |
– CreateUser and ConfirmEmail endpoints should be accessed anonymously always, so we need to attribute it with [AllowAnonymous] as the below:
1 2 3 4 5 6 7 8 9 10 11 12 |
[AllowAnonymous] [Route("create")] public async Task<IHttpActionResult> CreateUser(CreateUserBindingModel createUserModel) { } [AllowAnonymous] [HttpGet] [Route("ConfirmEmail", Name = "ConfirmEmailRoute")] public async Task<IHttpActionResult> ConfirmEmail(string userId = "", string code = "") { } |
– ChangePassword endpoint should be accessed by the authenticated user only, so we’ll attribute it with [Authorize] attribute as the below:
1 2 3 4 5 |
[Authorize] [Route("ChangePassword")] public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model) { } |
Step 6: Consume JSON Web Tokens
Now if we tried to obtain an access token by sending a request to the end point “oauth/token” then try to access one of the protected end points we’ll receive 401 Unauthorized status, the reason for this that our API doesn’t understand those JWT tokens issued by our API yet, to fix this we need to the following:
Install the below NuGet package:
1 |
Install-Package Microsoft.Owin.Security.Jwt -Version 3.0.0 |
The package “Microsoft.Owin.Security.Jwt” is responsible for protecting the Resource server resources using JWT, it only validate and de-serialize JWT tokens.
Now back to our “Startup” class, we need to add the below method “ConfigureOAuthTokenConsumption” as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
private void ConfigureOAuthTokenConsumption(IAppBuilder app) { var issuer = "http://localhost:59822"; string audienceId = ConfigurationManager.AppSettings["as:AudienceId"]; byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]); // Api controllers with an [Authorize] attribute will be validated with JWT app.UseJwtBearerAuthentication( new JwtBearerAuthenticationOptions { AuthenticationMode = AuthenticationMode.Active, AllowedAudiences = new[] { audienceId }, IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[] { new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret) } }); } |
This step will configure our API to trust tokens issued by our Authorization server only, in our case the Authorization and Resource Server are the same server (http://localhost:59822), notice how we are providing the values for audience, and the audience secret we used to generate and issue the JSON Web Token in step3.
By providing those values to the “JwtBearerAuthentication” middleware, our API will be able to consume only JWT tokens issued by our trusted Authorization server, any other JWT tokens from any other Authorization server will be rejected.
Lastly we need to call the method “ConfigureOAuthTokenConsumption” in the “Configuration” method as the below:
1 2 3 4 5 6 7 8 9 10 11 |
public void Configuration(IAppBuilder app) { HttpConfiguration httpConfig = new HttpConfiguration(); ConfigureOAuthTokenGeneration(app); ConfigureOAuthTokenConsumption(app); //Rest of code is here } |
Step 7: Final Testing
All the pieces should be in place now, to test this we will obtain JWT access token for the user “SuperPowerUser” by issuing POST request to the end point “oauth/token”
Then we will use the JWT received to access protected end point such as “ChangePassword”, if you remember once we added this end point, we were not able to test it directly because it was anonymous and inside its implementation we were calling the method “User.Identity.GetUserId()”. This method will return nothing for anonymous user, but after we’ve added the [Authorize] attribute, any user needs to access this end point should be authenticated and has a valid JWT.
To test this out we will issue POST request to the end point “/accounts/ChangePassword”as the image below, notice he we are sitting the Authorization header using Bearer scheme setting its value to the JWT we received for the user “SuperPwoerUser”. If all is valid we will receive 200 OK status and the user password should be updated.
The source code for this tutorial is available on GitHub.
In the next post we’ll see how we’ll implement Roles Based Authorization in our Identity service.
Follow me on Twitter @tjoudeh
References
- Understanding OWIN/Katana Authentication/Authorization Part I: Concepts by John Atten
- Featured Image Source
Getting this error while posting my question.
Notice: Trying to get property of non-object in /home3/bitoftec/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_wp.class.php on line 717
Warning: Cannot modify header information – headers already sent by (output started at /home3/bitoftec/public_html/wp-content/plugins/crayon-syntax-highlighter/crayon_wp.class.php:717) in /home3/bitoftec/public_html/wp-includes/comment.php on line 517
The CustomJwtFormat breaks when targeting .NET 4.6.1 and update all my packages:
“The type or namespace name ‘JwtSecurityToken’ could not be found (are you missing a using directive or an assembly reference?)”
These are the references I have for that class:
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Web;
using Thinktecture.IdentityModel.Tokens;
Any idea how to make it work for 4.6.1?
Hi Elizabet,
Well, I didn’t try to upgrade to .NET 4.6.1 framework. But is there a certain package which is not compatible with the 4.6.1? I will try to take a look at it and if you find a solution please share it here.
Hi Taiseer,
I am having the same issue “The type or namespace name ‘JwtSecurityToken’ could not be found “. Any thoughts? Thank you.
I resolved it by changing the using statement to
using System.IdentityModel.Tokens.Jwt;
Has this been resolved?
Thank you so much for taking the time to share your knowledge! This article has helped me out a great deal in understanding OAuth and tokens. I am confused on how to properly generate the client_id and the client_secret. You provided a link to source code when talking about the client_id and the client_secret. Is this code used to generate these values? Thank you!
Hello David,
You can find the code used to generate the clientid and secret in the Seed method for EntityFramework Configuration class.
I am confused by this answer. Your link for the implementation above pointed to a class named AudiencesStore. Is this supposed to be added to the project?
Same question here, I don’t understand how to generate the Audience id and secret.
Concerning the implementation file you provided (AudienceStore.cs), shall we create a console app next to this project and use it to generate Audience id/secret ?
Hi Christophe,
I allow myself to answer instead of Taiseer. I’ve used the whole solution with success and I confirm, that’s what I did (create a console app) to generate the Audiences id/secret
By the way, thanks a bunch Taiseer for your incredibly helpful work 😉
You are right Nicky, I used a test project to generate this, sorry for not being clear describing this step.
Hi,
using Taiseer’s suggested AudienceStore class I wrote a simple unit test to get the desired audience id and secret:
[TestClass]
public class AudienceStoreTest
{
[TestMethod]
public void WhenGeneratingClientIdAndClientSecretTheyAreGenerated()
{
var audience = AudiencesStore.AddAudience(“ANY_NAME_YOU_WANT”);
}
}
I set a break point at the end of the unit test and used the watch window to copy-paste the required data.
Of cource, I added his class and a helper class “Audience” into my project.
Hope this is correct and helps,
Torsten
Get AudiencesStore and Audience class from here:
https://github.com/tjoudeh/JWTAspNetWebApi/tree/master/AuthorizationServer.Api
Get an “unsupported_grant_type” error when posting to “http://localhost:58604/oauth/token” as demonstrated?
You are facing the same issue if you cloned the GitHub code?
I am!!!
Assalam Walaykum, Taiseer.
Thanks a lot for this great tutorial.
About the invalid grant type, content type should be application/x-www-form-urlencoded , type of request should be POST and in Raw Payload enter grant_type=password&password=PASSWORD&username=USERNAME
This I tried on ARC. This request works fine.
Thanks!
Apparently Postman doesn’t clear the Raw field when changing the radio to
form-data
, therefore the body sent (still the Raw) is wrong and the grant_type isn’t known.Hi, thank you for your tutorial first. I have a question.
There are username and password is display in the url parameters and how can we Protected user info?
Looking forward to your answer.
What worked for me on this:
Make sure you’re putting the username/password/grant_type in the “Body” tab of PostMan. First, check the radio button for x-www-form-urlencoded and then enter the info in the below fields right below the radio button… It looks identical to the “Headers” section, but it’s not.
I have followed your full tutorial, but at this point the authorization is not working. Any protected endpoint generates
“Authorization has been denied for this request.” error. I have generated the token and passed it in the authorization key in Header and also Added the bearer key. Can anyone help me?
Hello Taieb,
Can you make sure that you are using the same issuer without any trailing slashes?
Muito bom!! Me ajudou!
that was my problem too. Working now.
Thank you for taking your time and writing these tutorials.
I was getting the same error and I had not set the issuer variable properly either indeed.
Thank you Taiseer!
Hi Taieb,
I had the same problem. It was my mistake – My localhost app was running on different port and i forgot change it for issuer in Startup.ConfigureOAuthTokenConsumption(IAppBuilder app) method.
@Taiseer: Thank You very very much for this tutorial 🙂
You are welcome, happy to help 🙂
Getting the same error. Checked the issuer and it’s the same.
Getting this error:
I am using the same code from Git. I guess I am missing something in Step:3 to generate the AudenceID and AudenceSecret key. Using same code I am able to get users, delete users but not able to reset password.
Please guide me to.
{
“message”: “An error has occurred.”,
“exceptionMessage”: “UserId not found.”,
“exceptionType”: “System.InvalidOperationException”,
“stackTrace”: ” at Microsoft.AspNet.Identity.UserManager
2.d__2a.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter
1.GetResult()\r\n at AspNetIdentity.WebApi.Controllers.AccountsController.d__5.MoveNext() in C:\\Users\\ssinha01\\Desktop\\MVC\\AspNetIdentity.WebApi-master\\AspNetIdentity.WebApi-master\\AspNetIdentity.WebApi\\Controllers\\AccountsController.cs:line 139\r\n— End of stack trace from previous location where exception was thrown —\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Threading.Tasks.TaskHelpersExtensions.d__3`1.MoveNext()\r\n— End of stack trace from previous location where exception was thrown —\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n— End of stack trace from previous location where exception was thrown —\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n— End of stack trace from previous location where exception was thrown —\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()”}
It is resolved now. I missed to call the function ‘ConfigureOAuthTokenConsumption’ from Configuration in Startup.cs file.
I am having a lot of trouble following this tutorial because I am not using the versions of the packages that you are using. Like when I am write this line:
var token = new JwtSecurityToken(issuer, plaintext, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signKey);
I am getting this error: ‘cannot convert from ThinkTecture.IdentityModel.Tokens.HmacSigningCredentials to Microsoft.IdentityModel.Tokens.SigningCredentials.
And in your git repo, you are using this namespace using System.IdentityModel.Tokens;
and my version of Visual Studio is making me using System.IdentityModel.Tokens.Jwt
And for some reason, I can’t let the type convert.
okay, so I narrowed down the problem. I am using Version 5.0.xxxxx
and you are using an earlier version like version 4.0.xxxxx
The newer version is using different references. So the older version uses
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
The newer version uses
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
I narrowed down the problem, but I don’t know where to go from here
Hi TJ,
Sorry for the late reply, I understand your frustration but sometimes NuGet packages for certain libraries tend to introduce breaking compatibilities, the best thing to do is to check the GitHub documentation and release notes for each package and see what has been changed and try to implement in the new version.
Sorry for not being helpful but I can’t reproduce your issue now.
Hi,
I had the same issue and was able to fix it thanks to that post :
http://stackoverflow.com/questions/38338580/conflict-between-system-identitymodel-tokens-and-microsoft-identitymodel-tokens
In the end, in \Providers\CustomJwtFormat.cs , I have (excerpt) :
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using System;
using System.Configuration;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
…
public string Protect(AuthenticationTicket data)
{
…
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyByteArray);
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingCredentials);
…
}
Thanks for your reply ! I had the same problem !
Hello,
Which Value we need to set For Below Key in Web.config file
as:AudienceId
as:AudienceSecret
In AudienceId anything (I tried with an empty string), but in AudienceSecret any Sha256, Sha384 or Sha512 signature.
Actually, The Sha512 one does not work. The exception is “Invalid Key Length”. I then tried a Sha256 and it worked. I then tried Sha385 out of interest and it also did not work. Same error. Obviously I might have done something wrong / different that yourself (Lug Velez Schmitz) so I leave it open to user error on my part. Just wanted to post here to help others who might get this same error.
Thanks Brent for sharing this.
Hi!
Great tutorial.
Nevertheless I’ve got some issue and asking U for help:
Argument 6: cannot convert from ‘Thinktecture.IdentityModel.Tokens.HmacSigningCredentials’ to ‘Microsoft.IdentityModel.Tokens.SigningCredentials’
(I’ve got system.identitymodel.tokens.jwt 5.0.0. installed)
Hi Simon, this is due to different NuGet packages with breaking compatibility, unfortunately i do not have enough time to check how to upgrade them, but if you used the same NuGet packages I’m using the tutorial then you are good and you will not face this issue.
Do not use System.IdentityModel.Tokens.Jwt 5.0.0, I have 2 reasons to prevent you from update to version 5.x.
1. Because there are change in initalizing JwtSecurityToken() object you will get this error : “cannot convert from ThinkTecture.IdentityModel.Tokens.HmacSigningCredentials to Microsoft.IdentityModel.Tokens.SigningCredentials”
but can fixed with this code [CustomJwtFormat.cs] – work fine, get access_token normally :
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException(“data”);
}
string audienceId = ConfigurationManager.AppSettings[“as:AudienceId”];
string symmetricKeyAsBase64 = ConfigurationManager.AppSettings[“as:AudienceSecret”];
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
//var signingKey = new HmacSigningCredentials(keyByteArray);
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(keyByteArray);
var signingKey = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
2. Even if you pass #1 you will face this error : “Could not load type ‘System.IdentityModel.Tokens.TokenValidationParameters’ from assembly ‘System.IdentityModel.Tokens.Jwt, Version=5.0.0.127, Culture=neutral, PublicKeyToken=31bf3856ad364e35” at function app.UseJwtBearerAuthentication() because Microsoft.Owin.Security.Jwt require System.IdentityModel.Tokens.Jwt branch 4.x
Thank you very much, Taiseer Joudeh, This is a great tutorial series and it save my life. Thank you very much again.
You are most welcome, happy to help.
Hi Taiseer,
Once again loving your style of detailed explanation.
Would you be pointing me to how or will you have another article about revoking an issued JWT token?
Cheers,
Vinh
Hello Vinh,
Revoking an issued refresh token is all about deleting it from the DB, you need to delete the identifier for it and that’s it.
Just wanted to add a warning about using “TextEncodings.Base64Url.Decode(..)”. We were having some problems with some of our values that had ‘+’ or ‘/’ characters in them. We followed Brock Allen’s advice at https://brockallen.com/2014/10/17/base64url-encoding/ where he said to use his custom decoder that would handle these characters correctly. Which is basically the same as changing the code above to “TextEncodings.Base64.Decode(..)” (NOTE: without the “Url” after the “Base64”).
Both absolutely incredible and impossible.
Incredible that it works and impossible to do it yourself from scratch.
After following your first tutorial series, I now jump from series to series picking what I need, and it really works out very well.
Thank you very much once again,
Torsten
Hi Torsten, thanks for your comment and I’m glad to know that all the posts are helping you building your solution.
Still working on authentication – still enthousiastic about your tutorials
🙂
Hey Taiseer, I looked at this tutorial a while back and got it to work the first time. However, when I decided to migrate the code and make some name changes to the project and solution, I am no longer able to do the post call for /oauth/token
I keep getting a 404 error and can’t figure out why that is the case. Why would I not be able to do the post call anymore? It’s like that route does not exist anymore and I can’t figure out why.
Excellent articles. Do you know if you can change Policies(and the claims/roles that belong to them) AFTER the application has loaded? My Executive wants to be able to configure “Groups” by using a UI/web page. I think I can consider the “Groups” to be Policies, but Policies load at startup. I’m not sure they can be changed after the app has loaded. Do you know if this is true or not and if not, is there another way to do this? Thanks(ASP.NET Core)
Hello Mark,
The bearer tokens are self-contained tokens, you can’t update them at run time. I recommend you to check “Reference Tokens” where you can achieve this, but the drawback that you will hit some store to get updated policies from.
I am generating JWT just fine. The problem is when I test it on jwt.io it fails because something is apparently setting the alg property to “http://www.w3.org/2001/04/xmldsig-more#hmac-sha384”. jwt.io is expecting HMACSHA256.
Hi Jeff, that is strange, are you using the same code base shared or you have changed the signing approach?
I figured it out. My original secret was created by Auth0. The secret was more than 32 bytes, that caused the ThinkTecture HmacSigningCredentials method to create the token using HMACSHA384. HmacSigningCredentials has a switch statement that sets the
alg
property based on the length of the secret.After creating a 32 byte secret which equated to HMACSHA256, which is compatible with jwt.io everything worked fine.
Hi Taiseer
I followed your tutorial with my sample application. I am getting the token but when I try to access one of the protected API’s I get “Authorization has been denied for this request.” error.
I am using the same issuer without any trailing slashes. Can you please help me??
Seems like using AudienceId as empty string does not work. To resolve this issue I had to put AudienceId as “Any”.
Thanks for this great tutorial.
Hello Aman,
Can you try to debug the token using http://jwt.io and check the issuer matching what you have used, as well check the signature in jwt.io.
Yes I can find the same issuer and signature is also verified. I guess issue was AudienceId was not assigned.
powerful Article . Thank you….!!!
Hi Taiseer,
This is a great tutorial, it is really helpful. The thing is, I’m facing a problem in startup file in configuration method. It says that
“The inline constraint resolver of type ‘DefaultInlineConstraintResolver’ was unable to resolve the following inline constraint: ‘uid”.
Everything was working fine before I applied Authorize
I look forward to hear from you.
Thank You!
Best Regards,
Sandeep Apsingekar
Hi,
My bad I did a very simple typo error in the attribute routing. I fixed it now.
Thank you!
Best Regards,
Sandeep
No problem, happy you found a solution 🙂
Thank You! Great tutorial I have learnt alot from this. Look forward to follow new tutorials from you.
You are welcome, happy to hear this 🙂
Hi Taiseer,
now that I have this working, by using the same token mechanism, what would it take to expose the rest of the api (no credentials included) to the outside?
never mind, starting part 4 made me realize the answer is there.
Thank you
Alex
Hi,
Can you assist me in getting the expiry and not before times validated. I have attempted to add TokenValidationParameters to:
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
},
TokenValidationParameters = validationParameters
});
Where
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateLifetime = true
};
However this results in all requests being rejected. Why?
Thanks for the articles, they have been very informative.
You are welcome, but could you elaborate more? I didn;t get what you are trying to achieve.
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[] {
new SymmetricKeyIssuerSecurityKeyProvider(issuer, audienceSecret)
}
});
Hi, I cant seem to work out how to get the Authorize attribute to validate the Token expiry and not before date time. I must be missing something simple.
I have a error “XMLHttpRequest cannot load http://localhost:51860/oauth/token. Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:3000’ is therefore not allowed access. The response had HTTP status code 400.” when I try send request from domain localhost:3000 to URL localhost:51860/oauth/token?
This is CORS issue, make sure that the server you send request to contains the client “JS” URL in the “Access-Control-Allow-Origin” of the server.
Hi Taiseer,
Thank you for your Job.
Can I add a determinate list of URL into “Access-Control-Allow-Origin” instead of “*” ?
Thanks for putting this together. It has saved me weeks of work! But, I am also having the same problem. Every endpoint within the owin controllers and my own work fine. The only one that doesn’t is the magical /oauth/token endpoint. I’ll be honest and say I do not understand how this special endpoint works, but it does very nicely within the same domain. Cross domain, I get CORS errors.
Thank you very much Taiseer Joudeh.
This is a great tutorial !!!
You are welcome Rahul, happy to hear it was useful 🙂
Hi
I am getting following error. Please help
{“error”:”unsupported_grant_type”}
Hi,
Awesome work, Great tutorial.
Thanks for your message, but what is the grant type you are using?
It was pointing to differenting database. Thank you very much very nice tutorial, I happy with your tutorial. why don’t you publish part – 6 please
I think the same, maybe the next part could be in Angular 4.
I have 2 web apps one worked as authorization server and also have some resources and another one contains some resources. I tried here to follow thw JWT Authentication but it didn’t work:
http://stackoverflow.com/questions/43156228/authorization-has-been-denied-for-this-request-using-jwt-everytime
I may forgot some steps or misunderstand others but i tried to follow the tutorial steps
Hi Marzouk, dropped you a comment on SO.
Getting the same error any help?
{
“message”: “Authorization has been denied for this request.”
}
The issuer for the TokenConsumer method is different from my localhost would that cause any problems? I am getting errors similar to https://stackoverflow.com/questions/43156228/authorization-has-been-denied-for-this-request-using-jwt-everytime
Please how to generate the Audience Id and Audience Secret ???
Hi Stephan,
Generating those values are up to you, you can use GUID for Audience ID and use whatever secret you want
Taiseer Joudeh i didnt really understand hwo to generate the Audience ID and Audience Secret with this project , can you provide us how to do so ?
thank you !
For Audience ID you can use :
var audienceId = Guid.NewGuid().ToString(“N”);
HI,
you could also use a password generator website:
https://passwordsgenerator.net/
The site doesn’t load, maybe try this one.
https://passwords-generator.org
Hi Taiseer,
Great articles but it seems that this approach will not work with the latest Visual Studio 2017 Community version and latest version of WebApi. Always get “Authorization has been denied for this request.” when using the [Authorize] attribute. Any ideas? Thanks
I got an issue about “Could not load type ‘System.IdentityModel.Tokens.TokenValidationParameters'”
and I found System.IdentityModel.Tokens.Jwt 5.0 is not compatible with Katana 3,
So need to downgrading to v4
see this post…https://github.com/IdentityServer/IdentityServer3/issues/3017
Great write up. Thanks for taking the time to do it.
I am following your post and found it really useful. Currently I got stuck to generate “as:AudienceId” and “as:AudienceSecret” keys. Can you please provide block of code of this implementation. Its difficult to understand the ‘implementation’ https://github.com/tjoudeh/JWTAspNetWebApi/blob/master/AuthorizationServer.Api/AudiencesStore.cs .
I have generated the values using below code (snippet from https://github.com/tjoudeh/JWTAspNetWebApi/blob/master/AuthorizationServer.Api/):
using Microsoft.Owin.Security.DataHandler.Encoder;
using System;
using System.Security.Cryptography;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
var clientId = Guid.NewGuid().ToString(“N”);
var key = new byte[32];
RNGCryptoServiceProvider.Create().GetBytes(key);
var base64Secret = TextEncodings.Base64Url.Encode(key);
Console.WriteLine(“CliendID : ” + clientId);
Console.WriteLine(“Base64 : ” + base64Secret);
Console.ReadLine();
}
}
}
Thanks.
Nice explanation. I am having doubt related to my project architecture. I want to create 3 Layer architecture with presentation layer(AngularJs), business logic layer (Web API) and data access layer(DAL) which would use the same DB for the authentication as for the other data.
Is there any way to separate my data access layer from business logic layer with OWIN (JWT) authentication ?
Please help.
HI Taiseer Joudeh,
After following your tutorial the access token that was generated does not contain three parts so it shows invalid on jwt.io
this is an example of the access token that was generated
{“access_token”:”d0eHEjrCu7AoX1nKnyTM-IHYi2R2sD_YjozMzFKPYnFFMr2v8zTZX_fUaCIxkhB0Y6N-FoTxN9J67wQqRvQNrrkL2WxfyE_lC2h3OsflPosVRHQtRk42P7jqAMakpfYRk721fkW0PpKYYThQjkiqT3Y0pksg1z0K2qZpB0pQPh–upTmhWsUfAluxJiStsI7n_qtNveo1me8gl5tmkstAD0GuJrY6zDlJKWtATcNiSy-eJuXTAjUR2ApqP_FpYhXrssAim6QZUMsG_5uatbfVA”,”token_type”:”bearer”,”expires_in”:1799,”as:client_id”:””,”userName”:”SuperPowerUser”,”.issued”:”Thu, 10 Aug 2017 16:08:48 GMT”,”.expires”:”Thu, 10 Aug 2017 16:38:48 GMT”}
Hi Taiseer,
Thanks for your time and your knowledge you are sharing with us , may GOD blessed You for that.
you said ” As you notice the “ValidateClientAuthentication” is empty, we are considering the request valid always, because in our implementation our client (AngularJS front-end) is trusted client and we do not need to validate it.”
In case we want to validate the request if our client is not trusted, can you provide the code for it or a link that deals with it.
Thanks
Djama from Ivory coast
Hi Taiseer,
I run demo not successfully, sql database script not found from your solution.
Please give me a database script.
Thanks
Tuan Nguyen.
I have just found in issue config, we used Entity Data Migration.
Thanks Taiseer.
hi,
its a great article for me to start with web api. can you clarify what is the use if audience in this project? Is the audience values changes for the each user try to login
Hi, fantastic article indeed! I followed it all the way and it’s worked perfectly well.
I would however love to implement a Whitelist of tokens against the User, I have created a table to host a JTI Claim value as well as the ClientId (Audience) and also the User it is against, I have a Unique Constraint to allow only one set of ClientId/ApplicationId and User at any one time.
I would now love to know where to add the additional logic so that the [Authorize] attribute also checks against this table to ensure that the JTI claim within the JWT is listed against the Specified User and for this Audience…
I suspect it needs to be added to the JwtBearerAuthenticationOptions somewhere but I am unsure of where..?
Please help!
Thanks
Even if i am not putting [Authorize] on my Controller it will throw “Authorization has been denied for this request.” Can you help me in this.? I don’t want to authenticate whole controller. Just want to authenticate some actions only. How i can do that?
When user is incorrect, a Bad Request is received instead of the invalid grant.
I have this code in GrantResourceOwnerCredentials of my custom OAuthAuthorizationServerProvider
var userManager = context.OwinContext.GetUserManager();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError(“invalid_grant”, “El nombre de usuario o la contraseña no son correctos.”);
return;
}
if (user.Locked)
{
context.SetError(“invalid_grant”, “El usuario está bloqueado. Contáctese con el administrador.”);
return;
}
When user is invalid, user is null and the method
context.SetError(“invalid_grant”, “El nombre de usuario o la contraseña no son correctos.”);
is executed. After that, client receives a Bad Request response with this header:
Cabeceras de la respuesta (346 B)
Cache-Control
private
Content-Length
4901
Content-Type
text/html; charset=utf-8
Date
Fri, 30 Nov 2018 23:49:44 GMT
Expires
-1
Pragma
no-cache
Server
Microsoft-IIS/10.0
X-Powered-By
ASP.NET
X-SourceFiles
=?UTF-8?B?QzpcV29ya2luZ0ZvbGRl…Edlc3RvckRvY1dlYlxUb2tlbg==?=
any help on this, please?
Thanks
Jaime
{
“Message”: “Authorization has been denied for this request.”
}
{“access_token”:”HGDowgl4pJky_h—n-Bfl7OoTTmsP5vwS2-ZfZRvRTJRP_WZkI1SqRez111DISwBXY_FJqMZJSIw3Luvx08nBoU8-qkWRFKinRmZUsJ6II-4sXfYM7rbDnt3vqy51oI5FPKHsPx-lulb3rLFwQpm272czDVjy4oynpzaFVKQJ5HiZhJp0fUyc3394PzL4hvSSmb93dieCJ6xdm3pXWVkntA-KJXLiwDzf2wrmsKFHdhospygQA1q9n41Ycf1x4eJ4tCNgmtzA79nX0kf9cogIaTp-AG4VJtQRdS_7rGH9Kv3lHTMa3rd6Czdhhki40R”,”token_type”:”bearer”,”expires_in”:1799}
http://localhost:18292/api/audience/ChangePassword
[Authorize]
[Route(“ChangePassword”)]
[HttpPost]
public IHttpActionResult ChangePassword()
{
return Ok();
}
{“access_token”:”HGDowgl4pJky_h—n-Bfl7OoTTmsP5vwS2-ZfZRvRTJRP_WZkI1SqRez111DISwBXY_FJqMZJSIw3Luvx08nBoU8-qkWRFKinRmZUsJ6II-4sXfYM7rbDnt3vqy51oI5FPKHsPx-lulb3rLFwQpm272czDVjy4oynpzaFVKQJ5HiZhJp0fUyc3394PzL4hvSSmb93dieCJ6xdm3pXWVkntA-KJXLiwDzf2wrmsKFHdhospygQA1q9n41Ycf1x4eJ4tCNgmtzA79nX0kf9cogIaTp-AG4VJtQRdS_7rGH9Kv3lHTMa3rd6Czdhhki40R”,”token_type”:”bearer”,”expires_in”:1799}
Error
{
“Message”: “Authorization has been denied for this request.”
}
I am using chrome postman
[{“key”:”Authorization”,”value”:”bearer U2f5tusgwRRRkW4fYk82oOLk1Y8d_H8wWg9m5tEmufWuOG5F1HPIa4ckB_Le3BLvorkEZYVjun__rBSutge9WuOm-D1x1IIGFTWSPMB9m3-55gmouQldKgxtHdVzc6b1ZtH0AlxMm8QdLgfyzNUIQ5p1qtwmd-fmqCcHauJmkUEG5IlEaxlqQ9EJGzQdJrxgKh7Hwd88eBJh0APZkv9Aka2eDjE9Wuel4mo4VYXDD95z0GUjlZvMJ3rmSuBiwjyti6g26GhOHeibFw4YRq6B_PNUXUXC6F-3JCzcBCQq0H5ahGIQwKwzeYH2-RtYZXk7″,”description”:””}]
Superb! Thank you Thank you. Implemented ok, working perfectly !!
I have been following this tutorial from the first part. At the start of this tutorial you mention:
“I will use step by step approach as usual to implement this, but I highly recommend you to read the post JSON Web Token in ASP.NET Web API 2 before completing this one”
Had initially ignored that part in order to stay on track because everything was working fine. Up until the final part of testing.
It just wont authorise for me. I keep getting the “message”: “Authorization has been denied for this request.” So i then decided to go and implement the part you recommended and came back and still faced the same issue.
I have looked in every corner for help to get past this hurdle and no success. To the point that I am now confused which part should come before or after the other because the code from the other part doesn’t tie in with this one at all. Leaving me as a beginner on this topic completely confused and no lead on how to get over this hurdle
I have the same problem and ı can not find any response at all. There are some pages which respond it but they are not relevant with my problem. I think it is related to installed packages version’s.
I have the same problem and ı can not find any response at all. There are some pages which respond it but they are not relevant with my problem. I think it is related to installed packages version’s.
Thank you so much for taking the time to share your knowledge! This article has helped me out a great deal in understanding OAuth and tokens. Actually I cant providing the values for audience, and the audience secret
please help me
hi all,
i had this error:
{
“message”: “Permission was denied for this request.”
}
any help
Make sure “app.UseWebApi(config)” is last line in “Configuration(IAppBuilder app)” method
Hi,
I am getting this error on the CustomJwtFormat class:
Argument 6: cannot convert from ‘Thinktecture.IdentityModel.Tokens.HmacSigningCredentials’ to ‘Microsoft.IdentityModel.Tokens.SigningCredentials’
at the line:
var signingKey = new HmacSigningCredentials(keyByteArray);
….
—>var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
Can you help pls.
Please how can I consume this api using mvc5 fronted with jquery ajax?
HI,
How can use this doc for .net 5 web api development? I read about there is no alternative of OAuthAuthorizationServerMiddleware in ASP.NET Core, the ASP.NET team simply decided not to port it.
Can I implement OAuth authorization along with JWT ?