In the previous post Decouple OWIN Authorization Server from Resource Server we saw how we can separate the Authorization Server and the Resource Server by unifying the “decryptionKey” and “validationKey” key values in machineKey node in the web.config file for the Authorization and the Resource server. So once the user request an access token from the Authorization server, the Authorization server will use this unified key to encrypt the access token, and at the other end when the token is sent to the Resource server, it will use the same key to decrypt this access token and extract the authentication ticket from it.
The source code for this tutorial is available on GitHub.
This way works well if you have control on your Resource servers (Audience) which will rely on your Authorization server (Token Issuer) to obtain access tokens from, in other words you are fully trusting those Resource servers so you are sharing with them the same “decryptionKey” and “validationKey” values.
But in some situations you might have big number of Resource servers rely on your Authorization server, so sharing the same “decryptionKey” and “validationKey” keys with all those parties become inefficient process as well insecure, you are using the same keys for multiple Resource servers, so if a key is compromised all the other Resource servers will be affected.
To overcome this issue we need to configure the Authorization server to issue access tokens using JSON Web Tokens format (JWT) instead of the default access token format, as well on the Resource server side we need to configure it to consume this new JWT access tokens, as well you will see through out this post that there is no need to unify the “decryptionKey” and “validationKey” key values anymore if we used JWT.
What is JSON Web Token (JWT)?
JSON Web Token is a security token which acts as a container for claims about the user, it can be transmitted easily between the Authorization server (Token Issuer), and the Resource server (Audience), the claims in JWT are encoded using JSON which make it easier to use especially in applications built using JavaScript.
JSON Web Tokens can be signed following the JSON Web Signature (JWS) specifications, as well it can be encrypted following the JSON Web Encryption (JWE) specifications, in our case we will not transmit any sensitive data in the JWT payload, so we’ll only sign this JWT to protect it from tampering during the transmission between parties.
JSON Web Token (JWT) Format
Basically the JWT is a string which consists of three parts separated by a dot (.) The JWT parts are: <header>.<payload>.<signature>.
The header part is JSON object which contains 2 nodes always and looks as the following: { "typ": "JWT", "alg": "HS256" } The “type” node has always “JWT” value, and the node “alg” contains the algorithm used to sign the token, in our case we’ll use “HMAC-SHA256” for signing.
The payload part is JSON object as well which contains all the claims inside this token, check the example shown in the snippet below:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "unique_name": "SysAdmin", "sub": "SysAdmin", "role": [ "Manager", "Supervisor" ], "iss": "http://myAuthZServer", "aud": "379ee8430c2d421380a713458c23ef74", "exp": 1414283602, "nbf": 1414281802 } |
All those claims are not mandatory in order to build JWT, you can read more about JWT claims here. In our case we’ll always use those set of claims in the JWT we are going to issue, those claims represent the below:
- The “sub” (subject) and “unique_claim” claims represent the user name this token issued for.
- The “role” claim represents the roles for the user.
- The “iss” (issuer) claim represents the Authorization server (Token Issuer) party.
- The “aud” (audience) claim represents the recipients that the JWT is intended for (Relying Party – Resource Server). More on this unique string later in this post.
- The “exp” (expiration time) claim represents the expiration time of the JWT, this claim contains UNIX time value.
- The “nbf” (not before) claim represent the time which this JWT must not be used before, this claim contains UNIX time vale.
Lastly the signature part of the JWT is created by taking the header and payload parts, base 64 URL encode them, then concatenate them with “.”, then use the “alg” defined in the <header> part to generate the signature, in our case “HMAC-SHA256”. The resulting part of this signing process is byte array which should be base 64 URL encoded then concatenated with the <header>.<payload> to produce a complete JWT.
JSON Web Tokens support in ASP.NET Web API and Owin middleware.
There is no direct support for issuing JWT in ASP.NET Web API or ready made Owin middleware responsible for doing this, so in order to start issuing JWTs we need to implement this manually by implementing the interface “ISecureDataFormat” and implement the method “Protect”. More in this later. But for consuming the JWT in a Resource server there is ready middleware named “Microsoft.Owin.Security.Jwt” which understands validates, and and de-serialize JWT tokens with minimal number of line of codes.
So most of the heavy lifting we’ll do now will be in implementing the Authorization Server.
What we’ll build in this tutorial?
We’ll build a single Authorization server which issues JWT using ASP.NET Web API 2 on top of Owin middleware, the Authorization server is hosted on Azure (http://JwtAuthZSrv.azurewebsites.net) so you can test it out by adding new Resource servers. Then we’ll build a single Resource server (audience) which will process JWTs issued by our Authorization server only.
I’ll split this post into two sections, the first section will be for creating the Authorization server, and the second section will cover creating Resource server.
The source code for this tutorial is available on GitHub.
Section 1: Building the Authorization Server (Token Issuer)
Step 1.1: Create the Authorization Server Web API Project
Create an empty solution and name it “JsonWebTokensWebApi” then add a new ASP.NET Web application named “AuthorizationServer.Api”, the selected template for the project will be “Empty” template with no core dependencies. Notice that the authentication is set to “No Authentication”.
Step 1.2: Install the needed NuGet Packages:
Open package manger console and install the below Nuget packages:
|
1 2 3 4 5 6 7 |
Install-Package Microsoft.AspNet.WebApi -Version 5.2.2 Install-Package Microsoft.AspNet.WebApi.Owin -Version 5.2.2 Install-Package Microsoft.Owin.Host.SystemWeb -Version 3.0.0 Install-Package Microsoft.Owin.Cors -Version 3.0.0 Install-Package Microsoft.Owin.Security.OAuth -Version 3.0.0 Install-Package System.IdentityModel.Tokens.Jwt -Version 4.0.0 Install-Package Thinktecture.IdentityModel.Core Version 1.2.0 |
You can refer to the previous post if you want to know what each package is responsible for. The package named “System.IdentityModel.Tokens.Jwt” is responsible for validating, parsing and generating JWT tokens. As well we’ve used the package “Thinktecture.IdentityModel.Core” which contains class named “HmacSigningCredentials” which will be used to facilitate creating signing keys.
Step 1.3: Add Owin “Startup” Class:
Right click on your project then add a new class named “Startup”. It will contain 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 |
public class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); // Web API routes config.MapHttpAttributeRoutes(); ConfigureOAuth(app); app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); app.UseWebApi(config); } public void ConfigureOAuth(IAppBuilder app) { OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() { //For Dev enviroment only (on production should be AllowInsecureHttp = false) AllowInsecureHttp = true, TokenEndpointPath = new PathString("/oauth2/token"), AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30), Provider = new CustomOAuthProvider(), AccessTokenFormat = new CustomJwtFormat("http://jwtauthzsrv.azurewebsites.net") }; // OAuth 2.0 Bearer Access Token Generation app.UseOAuthAuthorizationServer(OAuthServerOptions); } } |
Here we’ve created new instance from class “OAuthAuthorizationServerOptions” and set its option as the below:
- The path for generating JWT will be as :”http://jwtauthzsrv.azurewebsites.net/oauth2/token”.
- We’ve specified the expiry for token to be 30 minutes
- We’ve specified the implementation on how to validate the client and Resource owner user credentials 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 are using bearer scheme.
We’ll come to the implementation of both class later in the next steps.
Step 1.4: Resource Server (Audience) Registration:
Now we need to configure our Authorization server to allow registering Resource server(s) (Audience), this step is very important because we need a way to identify which Resource server (Audience) is requesting the JWT token, so the Authorization server will be able to issue JWT token for this audience only.
The minimal needed information to register a Recourse server into an Authorization server are a unique Client Id, and shared symmetric key. For the sake of keeping this tutorial simple I’m persisting those information into a volatile dictionary so the values for those Audiences will be removed from the memory if IIS reset toke place, for production scenario you need to store those values permanently on a database.
Now add new folder named “Entities” then add new class named “Audience” inside this class paste the code below:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Audience { [Key] [MaxLength(32)] public string ClientId { get; set; } [MaxLength(80)] [Required] public string Base64Secret { get; set; } [MaxLength(100)] [Required] public string Name { get; set; } } |
Then add new folder named “Models” the add new class named “AudienceModel” inside this class paste the code below:
|
1 2 3 4 5 6 |
public class AudienceModel { [MaxLength(100)] [Required] public string Name { get; set; } } |
Now add new class named “AudiencesStore” 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 |
public static class AudiencesStore { public static ConcurrentDictionary<string, Audience> AudiencesList = new ConcurrentDictionary<string, Audience>(); static AudiencesStore() { AudiencesList.TryAdd("099153c2625149bc8ecb3e85e03f0022", new Audience { ClientId = "099153c2625149bc8ecb3e85e03f0022", Base64Secret = "IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw", Name = "ResourceServer.Api 1" }); } public static Audience AddAudience(string name) { var clientId = Guid.NewGuid().ToString("N"); var key = new byte[32]; RNGCryptoServiceProvider.Create().GetBytes(key); var base64Secret = TextEncodings.Base64Url.Encode(key); Audience newAudience = new Audience { ClientId = clientId, Base64Secret = base64Secret, Name = name }; AudiencesList.TryAdd(clientId, newAudience); return newAudience; } public static Audience FindAudience(string clientId) { Audience audience = null; if (AudiencesList.TryGetValue(clientId, out audience)) { return audience; } return null; } } |
Basically this class acts like a repository for the Resource servers (Audiences), basically it is responsible for two things, adding new audience and finding exiting one.
Now if you take look on method “AddAudience” you will notice that we’ve implemented the following:
- Generating random string of 32 characters as an identifier for the audience (client id).
- Generating 256 bit random key using the “RNGCryptoServiceProvider” class then base 64 URL encode it, this key will be shared between the Authorization server and the Resource server only.
- Add the newly generated audience to the in-memory “AudiencesList”.
- The “FindAudience” method is responsible for finding an audience based on the client id.
- The constructor of the class contains fixed audience for the demo purpose.
Lastly we need to add an end point in our Authorization server which allow registering new Resource servers (Audiences), so add new folder named “Controllers” then add new class named “AudienceController” and paste the code below:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[RoutePrefix("api/audience")] public class AudienceController : ApiController { [Route("")] public IHttpActionResult Post(AudienceModel audienceModel) { if (!ModelState.IsValid) { return BadRequest(ModelState); } Audience newAudience = AudiencesStore.AddAudience(audienceModel.Name); return Ok<Audience>(newAudience); } } |
This end point can be accessed by issuing HTTP POST request to the URI http://jwtauthzsrv.azurewebsites.net/api/audience as the image below, notice that the Authorization server is responsible for generating the client id and the shared symmetric key. This symmetric key should not be shared with any party except the Resource server (Audience) requested it.
Note: In real world scenario the Resource server (Audience) registration process won’t be this trivial, you might go through workflow approval. Sharing the key will take place using a secure admin portal, as well you might need to provide the audience with the ability to regenerate the key in case it get compromised.
Step 1.5: Implement the “CustomOAuthProvider” Class
Now we need to implement the code responsible for issuing JSON Web Token when the requester issue HTTP POST request to the URI: http://jwtauthzsrv.azurewebsites.net/oauth2/token the request will look as the image below, notice how we are setting the client id for for the Registered resource (audience) from the previous step using key (client_id).
To implement this we need to add new folder named “Providers” then add new class named “CustomOAuthProvider”, paste the code snippet 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
public class CustomOAuthProvider : OAuthAuthorizationServerProvider { public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { string clientId = string.Empty; string clientSecret = string.Empty; string symmetricKeyAsBase64 = string.Empty; if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) { context.TryGetFormCredentials(out clientId, out clientSecret); } if (context.ClientId == null) { context.SetError("invalid_clientId", "client_Id is not set"); return Task.FromResult<object>(null); } var audience = AudiencesStore.FindAudience(context.ClientId); if (audience == null) { context.SetError("invalid_clientId", string.Format("Invalid client_id '{0}'", context.ClientId)); return Task.FromResult<object>(null); } context.Validated(); return Task.FromResult<object>(null); } public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" }); //Dummy check here, you need to do your DB checks against memebrship system http://bit.ly/SPAAuthCode if (context.UserName != context.Password) { context.SetError("invalid_grant", "The user name or password is incorrect"); //return; return Task.FromResult<object>(null); } var identity = new ClaimsIdentity("JWT"); identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); identity.AddClaim(new Claim("sub", context.UserName)); identity.AddClaim(new Claim(ClaimTypes.Role, "Manager")); identity.AddClaim(new Claim(ClaimTypes.Role, "Supervisor")); var props = new AuthenticationProperties(new Dictionary<string, string> { { "audience", (context.ClientId == null) ? string.Empty : context.ClientId } }); var ticket = new AuthenticationTicket(identity, props); context.Validated(ticket); return Task.FromResult<object>(null); } } |
As you notice this class inherits from class “OAuthAuthorizationServerProvider”, we’ve overridden two methods “ValidateClientAuthentication” and “GrantResourceOwnerCredentials”
- The first method “ValidateClientAuthentication” will be responsible for validating if the Resource server (audience) is already registered in our Authorization server by reading the client_id value from the request, notice that the request will contain only the client_id without the shared symmetric key. If we take the happy scenario and the audience is registered we’ll mark the context as a valid context which means that audience check has passed and the code flow can proceed to the next step which is validating that resource owner credentials (user who is requesting the token).
- The second method “GrantResourceOwnerCredentials” will be responsible for validating the resource owner (user) credentials, for the sake of keeping this tutorial simple I’m considering that each identical username and password combination are valid, in read world scenario you will do your database checks against membership system such as ASP.NET Identity, you can check this in the previous post.
- Notice that we are setting the authentication type for those claims to “JWT”, as well we are passing the the audience client id as a property of the “AuthenticationProperties”, we’ll use the audience client id in the next step.
- Now the JWT access token will be generated when we call “context.Validated(ticket), but we still need to implement the class “CustomJwtFormat” we talked about in step 1.3.
Step 1.5: Implement the “CustomJwtFormat” Class
This class will be responsible for generating the JWT access token, so what we need to do is to add new folder named “Formats” then add new class named “CustomJwtFormat” 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 44 45 46 47 |
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket> { private const string AudiencePropertyKey = "audience"; 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 = data.Properties.Dictionary.ContainsKey(AudiencePropertyKey) ? data.Properties.Dictionary[AudiencePropertyKey] : null; if (string.IsNullOrWhiteSpace(audienceId)) throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience"); Audience audience = AudiencesStore.FindAudience(audienceId); string symmetricKeyAsBase64 = audience.Base64Secret; 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 Authorization server, this can be string or URI, in our case we’ll fix it to URI with the value “http://jwtauthzsrv.azurewebsites.net”
- Inside “Protect” method we are doing the following:
- Reading the audience (client id) from the Authentication Ticket properties, then getting this audience from the In-memory store.
- Reading the Symmetric key for this audience and Base64 decode it to byte array which will be used to create a HMAC265 signing key.
- Preparing 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 the JWT payload.
- Lastly we serialize the JSON Web Token to a string and return it to the requester.
- By doing this requester for an access token from our Authorization server will receive a signed token which contains claims for a certain resource owner (user) and this token intended to certain Resource server (audience) as well.
So if we need to request a JWT from our Authorization server for a user named “SuperUser” that needs to access the Resource server (audience) “099153c2625149bc8ecb3e85e03f0022”, all we need to do is to issue HTTP POST to the token end point (http://jwtauthzsrv.azurewebsites.net/oauth2/token) as the image below:
The result for this will be the below JSON Web Token:
|
1 |
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6IlN1cGVyVXNlciIsInN1YiI6IlN1cGVyVXNlciIsInJvbGUiOlsiTWFuYWdlciIsIlN1cGVydmlzb3IiXSwiaXNzIjoiaHR0cDovL2p3dGF1dGh6c3J2LmF6dXJld2Vic2l0ZXMubmV0IiwiYXVkIjoiMDk5MTUzYzI2MjUxNDliYzhlY2IzZTg1ZTAzZjAwMjIiLCJleHAiOjE0MTQzODEyODgsIm5iZiI6MTQxNDM3OTQ4OH0.pZffs_TSXjgxRGAPQ6iJql7NKfRjLs1WWSliX5njSYU |
There is an online JWT debugger tool named jwt.io that allows you to paste the encoded JWT and decode it so you can interpret the claims inside it, so open the tool and paste the JWT above and you should receive response as the image below, notice that all the claims are set properly including the iss, aud, sub,role, etc…
One thing to notice here that there is red label which states that the signature is invalid, this is true because this tool doesn’t know about the shared symmetric key issued for the audience (099153c2625149bc8ecb3e85e03f0022).
So if we decided to share this symmetric key with the tool and paste the key in the secret text box; we should receive green label stating that signature is valid, and this is identical to the implementation we’ll see in the Resource server when it receives a request containing a JWT.
Now the Authorization server (Token issuer) is able to register audiences and issue JWT tokens, so let’s move to adding a Resource server which will consume the JWT tokens.
Section 2: Building the Resource Server (Audience)
Step 2.1: Creating the Resource Server Web API Project
Add a new ASP.NET Web application named “ResourceServer.Api”, the selected template for the project will be “Empty” template with no core dependencies. Notice that the authentication is set to “No Authentication”.
Step 2.2: Installing the needed NuGet Packages:
Open package manger console and install the below Nuget packages:
|
1 2 3 4 5 |
Install-Package Microsoft.AspNet.WebApi -Version 5.2.2 Install-Package Microsoft.AspNet.WebApi.Owin -Version 5.2.2 Install-Package Microsoft.Owin.Host.SystemWeb -Version 3.0.0 Install-Package Microsoft.Owin.Cors -Version 3.0.0 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.
Step 2.3: Add Owin “Startup” Class:
Right click on your project then add a new class named “Startup”. It will contain 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 |
public class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); ConfigureOAuth(app); app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll); app.UseWebApi(config); } public void ConfigureOAuth(IAppBuilder app) { var issuer = "http://jwtauthzsrv.azurewebsites.net"; var audience = "099153c2625149bc8ecb3e85e03f0022"; var secret = TextEncodings.Base64Url.Decode("IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw"); // Api controllers with an [Authorize] attribute will be validated with JWT app.UseJwtBearerAuthentication( new JwtBearerAuthenticationOptions { AuthenticationMode = AuthenticationMode.Active, AllowedAudiences = new[] { audience }, IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[] { new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret) } }); } } |
This is the most important step in configuring the Resource server to trust tokens issued by our Authorization server (http://jwtauthzsrv.azurewebsites.net), notice how we are providing the values for the issuer, audience (client id), and the shared symmetric secret we obtained once we registered the resource with the authorization server.
By providing those values to JwtBearerAuthentication middleware, this Resource server will be able to consume only JWT tokens issued by the trusted Authorization server and issued for this audience only.
Note: Always store keys in config files not directly in source code.
Step 2.4: Add new protected controller
Now we want to add a controller which will serve as our protected resource, this controller will return list of claims for the authorized user, those claims for sure are encoded within the JWT we’ve obtained from the Authorization Server. So add new controller named “ProtectedController” under “Controllers” folder and paste the code below:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[Authorize] [RoutePrefix("api/protected")] public class ProtectedController : ApiController { [Route("")] public IEnumerable<object> Get() { var identity = User.Identity as ClaimsIdentity; return identity.Claims.Select(c => new { Type = c.Type, Value = c.Value }); } } |
Notice how we attribute the controller with [Authorize] attribute which will protect this resource and only will authentic HTTP GET requests containing a valid JWT access token, with valid I mean:
- Not expired JWT.
- JWT issued by our Authorization server (http://jwtauthzsrv.azurewebsites.net).
- JWT issued for the audience (099153c2625149bc8ecb3e85e03f0022) only.
Conclusion
Using signed JSON Web Tokens facilitates and standardize the way of separating the Authorization server and the Resource server, no more need for unifying machineKey values nor having the risk of sharing the “decryptionKey” and “validationKey” key values among different Resource servers.
Keep in mind that the JSON Web Token we’ve created in this tutorial is signed only, so do not put any sensitive information in it 
The source code for this tutorial is available on GitHub.
That’s it for now folks, hopefully this short walk through helped in understanding how we can configure the Authorization Server to issue JWT and how we can consume them in a Resource Server.
If you have any comment, question or enhancement please drop me a comment, I do not mind if you star the GitHub Repo too 





Hi Taiseer,
As usual you are doing a great job and contribution to community.
Just one question Instead of using the custom json formatter class I am thinking of using the https://github.com/jwt-dotnet/jwt nuget. What are your thoughts on it?
Regards,
Atul(Big Fan)
Hi Atul,
Thanks for comment, the JWT is becoming standard for access token format, so I guess you can use this library, I read the source code of it but never tried it before. Let me know the outcome after you implement it.
Taiseer,
You posts have been a great help.
I have followed you tutorial to configure the resource server. Jwt token is issued by Google (on Android). At the resource server side I get the following error,
– System.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException was unhandled by user code
– Message=IDX10500: Signature validation failed. Unable to resolve SecurityKeyIdentifier: ‘SecurityKeyIdentifier
I’m unable to find a valid solution. I am on .NET version 4.5 (IdentityModel 4.0.0)
Like tuananh above I also get 401 unauthorized.
First off, great job, loved all your oauth/owin related articles, thanks a lot for sharing all this wonderful information with us.
Here is my situation:
i separated the two projects into two separate solutions running respectively in http://localhost:18292 and http://localhost:18303
I am getting the token for that Auth Server [port 18292] fine:
access_token : xxxx
expires_in : 1799
token_type : bearer
But calling the protected [Authorize] method in the Resource Server with these headers gives me the 401:
GET http://localhost:18303/api/protected
Host: localhost:18303
User-Agent: Mozilla/5.0 (Windows NT 6.3; rv:39.0) Gecko/20100101 Firefox/39.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Bearer xxxx
Referer: http://localhost:18303/Login.html
Connection: keep-alive
1, Should the Resource Server by secure (https) ?
2. Are the headers ok? any missing?
3. I left the issuer to: http://jwtauthzsrv.azurewebsites.net, should this be changed to the real Authorize Server url? How do you do that with localhost?
4. Is there any way to get more detailed errors at least during development? or a way to step through the validation code in the Resource Server?
Really appreciate any help you could provide
Hi,
Just make sure that the issuer value in your resource api has the same value of the issuer (authorization server) which generated the JWT, no trailing slashes, case sensitive, same http protocol.
>> Should the Resource Server by secure (https)
YES. If not everybody could steal the token with ease…
Correct, with OAuth2, you should go TLS all the way.
I get the exact same error. I followed your post to the letter, but still get a 401 error. When I run the project you posted on GitHub I also get this issue. Even though the issuer is still set to http://jwtauthzsrv.azurewebsites.net.
Scratch that… What I am actually asking is how do I provide the JWT in my GET request to the /api/protected?
Hi Jason,
You need to set the token in the Authorization header using Bearer scheme, it will be like this “Authorization: Bearer Your_JWT_Token”
Hi Taiseer, this post is very helpfull indeed, but can i ask you after the test in postman with the auth server, how to test the resource server with postman too?
Thanks a lot for sharing your knowledge.
Hi Carlos,
The authorization server will be hosted on different domain and the resource server will be on another domain, so once you obtain the token from the authorization server, you need to send a request to the resource server domain and use the token in the authorization header using Bearer scheme.
Hope this answers your question.
Thanks a lot Taiseer! It worked now, im brand new with oauth and web api security aspects and your posts of oauth and web api 2 helped me a los =D
Can you furthur describe the bearer scheme. I’m new to this and trying to use postman. Thanks.
Hi David, This is a very open question, I recommend to Google this as well I have covered this on the first post.
I followed your set up for section 2 but I’m getting an error when I try to go to the endpoint. It says it can’t find the resource. I followed your suggestion of creating an empty application and then using Nuget to add the proper references. But I can’t use PostMan to hit my protected endpoint. I’ve put a breakpoint inside the Startup class in the ConfigureOAuth method. I can reach that but I can’t seem to reach the controller action. Any suggestions?
Hi Guario,
I guess your problem is with defining Routes in your Web Api controllers, that’s why you are receiving 404. Make sure you are enabling config.MapHttpAttributeRoutes(); if you are using attributes routing.
Hi Taiseer, I would like to know how to configure a resource server that accept many client applications to access the secured resources.
I have a scenario like my Authorization Server and Resource Server(Web API end points) are the same.
Here I need to provide access or authorize many client applications means these resource url’s can be accessed by many other client applications. Please guide me how to authorize the clients using JWT tokens only.
Thanks
Syam
Did you ever figure out a feasible way to do this?
I have followed this tutorial but am getting an “Access is denied for this request”, error when I try and get to the protected API.
1. I am running this locally, must both projects be deployed to separate websites in order for the example to work?
2. When I make a call to the protected controller, it never hits any breakpoints in the Authorization server.
3. Should the issuer be just the base URL or should it also include /token at the end?
The issuer should be only the Base URL not with the “token” end point, I guess this is your only issue.
Hey Taiseer,
Thanks for the post!
I’m facing a little issue here though. I am getting 401 unauthorized.
The issuer, audience and secret are the same for both the AuthenticationServer and ResourceServer. I really can’t place where it’s going wrong. Where else can I look to solve this issue?
Thanks in advance
Hi Srishti,
Usually the issuer will be different, are you sure there is no trailing slash, and you are using same http scheme? (http, https)> Decode the JWT token using http://Jwt.io and make sure that the claims you are looking for is as expected.
This is great. Thanks.
I hope you could help me with a little question,
Right now I’m trying to protect my WCF Service, and my idea is, have a web api project as STS which gives me a token as your example, once I get the token, I pass it to my client (MVC website) and I set a cookie with the token inside,
Is there any way to protect my WCF Service with this mechanism? I mean for example: once I get the token from the web api I like to pass it from my MVC website to my WCF service and validate it, …
I’ll appreciate any example about it.
Best regards.
Is your WCF is exposed to the public (hosted on IIS with Ws-binding enabled) if yes then why not to use Web Api to serve your need? If it is acting like back-end service then it is already protected by your company windows authentication and your company firewall. I’m not sure if I understood the whole picture but I do not know if there is a way to protected the WCF service using JWT tokens.
Yes, my WCF is public, this my scenario
1. WCF on IIS
2. Class Library as Client (With Service Reference to my WCF)
3. MVC Application (this one consumes my class library)
4. Web api as STS
the thing is I don’t want to use web api beacause I will have to change all my services and theres a lot work doing that
Thanks
Thank you for this thorough tutorial Taiseer, worked first time following it. And also for all the others regarding OWIN Auth (only ones I’ve been through). Have a great day.
Regards,
Corne
You are welcome Corne, happy to hear that posts are working directly without any changes
Hi Taiseer, I have a question regarding the CustomOAuthProvider, I see it only support these 2 methods:
TryGetBasicCredentials and TryGetFormCredentials. So how would one handle JSON coming in through request body then ?
If I were to log in by sending through a JSON object rather than forms structured credentials (username=x&password=y&grant_type=password&client_id=bla)
Thanks
Hi Corne,
The standard way to send request to an OAuth 2.0 “token” end point is by setting the content-type to “application/x-www-form-urlencoded” not “application/json”. Please check the specs here.
Got it, thanks Taiseer.
Hi Taisser,
Perhaps my question its too silly, but you add functionality for the user to refresh his token. But in real world this need to be like this? Because i think that common user dont know what means refresh token, so i think i need to refresh on behind. Im correct?
Hi Taiseer,
first of all thank you for share this wonderful article. I have one question why you are using http://jwtauthzsrv.azurewebsites.net in CustomejwtFormat class. Should I use something else.
Hi Sharad, sorry I didnt get your question, can you elaborate more?
Is there any difference from this and this package ? Microsoft.Owin.Security.Jwt ?
Which packages you are comparing?
Like Adolfo I also get 401 unauthorized.
I downloaded your solution, changed the issuer with the localhost server in both webAPIs, it doesn’t change the 401.
I ran a fiddler, the protected webAPI doesn’t even try to connect to the authorization api.
Is there something missing here ? (like some sort of link to specify the oath url ?)
Thanks in advance
Tid, my 401 was due to an issuer inconsistency in my implementation which I realized when I visualized the token in jwt.io.
Your description of your problem is a little vague.
I am not clear what you mean when you say “the protected webAPI doesn’t even try to connect to the authorization api”, because when you are trying to access a protected method in your web.api, the web.api does not need to access the “authentication server”. Your request to access the protected method should come with a token and only if the token is invalid, expired or missing you will get a 401. But this validation does not need to access the authenticating server, it is done in the Startup module of your resource server.
You would never get a 401 from access the authenticating server because the login method is not protected.
So until you clarify your workflow I would say you are trying to access the protected web.api method without a token.
Adolfo, What was your inconsistency and how did jwt.io help you identify it? The first time I implemented this tutorial I got everything to work. The second time, it does not work . Tokens are generated just fine and look great in jwt.io, but I always get an invalid token response on any authorized api resource. Very frustrating! It is almost like the owincontext is not consuming the token.
Hi Taiseer,
Thanks very much for posting this, it’s been very helpful. Do you have any advice on how to use this sort of model with an asymmetric key instead of a symmetric one?
I’m looking at implementing something like this for a microservice-based system — one client (which we control/develop, but is implemented in browser javascript and is therefore untrustable) will log in to our auth server and get a JWT, but that token must be usable by several different resource servers. Obviously I could do this with a symmetric key if I share the key with all the resource servers, but of course that would mean that anyone who managed to hack into any one of our resource servers would be able to get it. So what I’d like to do is use an asymmetric key pair so that the auth server can issue and sign the token with the private key, and the resource servers can verify the token’s signature with only the public key.
As I understand it so far, if I started with your example in this article, I’d need to make changes in two places: in the auth server’s code, alter the class implementing ISecureDataFormat (CustomJwtFormat in your example) to use a different SigningCredentials object which uses asymmetric encryption; and in the resource server’s Web API startup code, use a different implementation of IIssuerSecurityTokenProvider which can verify the signature.
If that’s correct, can you suggest any existing classes or examples for this? For the token provider, Microsoft.Owin.Security.Jwt already offers an X509CertificateSecurityTokenProvider class, but from a quick reading of the MSDN documentation it looks like I would need a full X509 certificate to create an instance of that class — not just the public key from one. Since the whole point is to remove the need to share a private key with the resource servers, that seems like a non-starter.
Thanks again,
–Andrew
Hello, I’m making a request with AngularJS to Web API passing client_id. If I’m understanding correctly it’s securely vulnerable. But I don’t know what should I do in this case… Could you please give me a hint.
Hi Taiseer,
Can you please explain me how can I dynamically add/remove secrets from IssuerSecurityTokenProviders? From how I understand UseJwtBearerAuthentication is fired once on startup.
If I create/store new secret in database, how can I pass that into the IssuerSecurityTokenProviders?
Best,
Zoran
Hi Zoran, good question, and to be honest I have no answer for it yet. If you found a solution please share it here.
Hi Taiseer,
I’m not sure if its still of interest but I added the following to the unprotect mehtod (its not great but ti works)
public AuthenticationTicket Unprotect(string protectedText)
{
if (protectedText == null) throw new ArgumentNullException(“jwt”);
//requires the issuer to send across their audience id in the claim
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
var jwt = tokenHandler.ReadToken(protectedText) as JwtSecurityToken;
var audienceId = jwt.Claims.FirstOrDefault(x => x.Type == “aud”).Value;
if (audienceId == null) throw new InvalidOperationException(“Error the audienceid is not included in the claims”);
//find the client in the repo and validate
Client client = null;
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(audienceId);
}
if (client == null) throw new InvalidOperationException(“ClientId does not exist.”);
string symmetricKeyAsBase64 = client.Secret;
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var signingKey = new HmacSigningCredentials(keyByteArray);
SecurityToken token = null;
var validationParameters = new TokenValidationParameters
{
IssuerSigningKey = signingKey.SigningKey,
ValidAudience = client.Id,
ValidIssuer = _issuer,
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = true
};
//validate the jwt
JwtFormat jwtFormat = new JwtFormat(validationParameters);
var ticket = jwtFormat.Unprotect(protectedText);
if(ticket == null) throw new ArgumentException(“Invalid token”);
return ticket;
}
it works and if theres any improvements that can be done here let me know!
Thanks Matthew for sharing this, I will review it soon and for sure it might be useful for a reader who is looking at the comments, sometimes the comments have more information compared to the original post
Hi Taiseer,
Our AuthorizationServer using mutual ssl in DelegatingHandler.
DelegatingHandler : checking client certificate.
but ,, http://jwtauthzsrv.azurewebsites.net/oauth2/token calling time
DelegatingHandler is not working.
Wow!! Great article. very detailed and helpful as always.
I have AuthServer (web-api) and a ResourceServer (MVC).
The JWT tokens, [Authorize] Attribute and Cookie are all works as expected.
But – I have a Login endpoint (on MVC) which the users can pass his credentials and will be sent to the AuthServer. i’m getting the access_token. then i need to call:Request.GetOwinContext().Authentication.SignIn(identity),
but i don’t know how to extract the Claims from the access_token in order to pass the identity to the SignIn method.
any directions?
Hi TAISEER,
Great tutorial I have followed it and it serves my purposes just great, now i am implementing impersonation in my javascript client any toughts on how to do that ?
I have added an header to the login request on my client saying if we’re impersonating or not someone and in my custom OAuthAuthorizationServerProvider i check for that so i can login without cheking the password, but now i need to check for the authentication of the caller on the GrantResourceOwnerCredentials method however the Owin.OwinContext.Authentication).User is null, as i suspect calls to GrantResourceOwnerCredentials are not authenticated (after all thats what we’re doing in the method) any tips ?
Hi, Taiseer, first off, thanks for the excellent tutorial! I do have one (newbie) question, however. As I understand it, if I have a client app that wants to consume the Web Api of a resource server, I first go to the authorisation server to get a token, and then the client app takes the token and uses it within the request it sends to the resource server, right?
However, as far as I can see from the ValidateClientAuthentication method, the client app would need to pass the resource server’s audience id to the authorisation server in order to get authorised and receive the correct token. How would the client app get that audience id? Simply adding a controller to the resource server that exposes an Api that allows just anybody to retrieve the id doesn’t seem a very secure way of going about it.
How can I revoke a Json Web Token actively?
You can not revoke self-contained tokens, you should wait until they expire
A great article, detailed and very helpful as always. I have one clarification though.
I see you are using client_id to pass identifier for the audience. This may work for AuthorizationCode Grant however for ResourceOwnerCredential Grant and ClientCredentials Grant there seem to a problem of how to pass “audience id” from the client as the client_id will contain the actual client identifier. Any thoughts?
Taiseer, great set of articles.
I have a question with regards to signing the JWT. In practice, should we sign the token using a certificate rather than simply using HmacSigningCredentials? Is this more suited for development purposes or “Good Enough” for production?
Hi Assad,
Certificate is the way yo go with production apps, it is more secure way.
Hi Taiseer thank you for the post, it´s very good.
I have two questions,
How can i update the token? is necesary generate other token? is not?.
How can I close session if the token is active?
thank you for you time.
Hi Diego, thanks for your comment,
Self-contained tokens can’t be updated, any change on it is content will affect the token and it is a signature and invalidate it, so you need to generate a new one when you update the user profile.
Hi Taiseer,
Just to clarify,at resource server (API), If i set these options for UseJwtBearerAuthentication
options.Authority = “”;
options.Audience = “”;
options.AutomaticAuthenticate = true
resource server will make a round trip to Authorization server and validate the access token?
Hi Kirshna,
There should be no direct communication between the resource server and the authorization server, the resource server will validate that the token received is issued by a trusted issuer (Authorization server) using the secret provided by AythZ server upfront when registering the resource server.
How would you envisage an onboarding/first issue scheme by the auth server? Would you be adding some kind of email or token/pin code auth on first request as a callback to then issue the token or how would you generally see that process working?
Hi Tom,
Could you elaborate more, I didn’t get your question.
Hi, thank you for this article, as a beginner for oAuth2 it help me understand the flow of oAuth2 using OWIN and JWT. Quick questions for you, how come I am always getting an “Invalid Client Id” when I tried to request for token from http://jwtauthzsrv.azurewebsites.net/oauth2/token? I was able to register and received a valid client id from http://jwtauthzsrv.azurewebsites.net/api/audience but no success from requesting a token using the client Id that I received.
Thanks,
GhaMe
Hi Grahame,
I’m afraid that you are sending the client id and client secret incorrectly in the request, can you make sure you are using the correct content-type, it should not be JSON, it should be “x-www-form-urlencoded”