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 🙂
Nice article.
Thanks Taiseer!
You welcome Joy, glad you liked it 🙂
Excellent article! Very good follow-up to the article on Token Based Authentication using ASP.NET Web API 2, Owin, and Identity. I’m actually using Ember and not Angular, but your examples across a few of your articles provided enough information for me to implement simple authentication in an Ember app.
Hello David, glad you liked the post and the previous series, thanks for taking the time to read it and comment.
Taiseer,
Wow, you just keep plowing along! Thanks for the updated post. I like the idea of using JWT instead of the shared machien keys. Can this be combined with your other 5 posts so that I have a Token based authentication that supports external authentication? It would be nice if you extended your 5 part series and added JWT to that.
Bob
Hi Taiseer,
I try to validate a JWT produced by Auth0 (https://auth0.com/) and constantly receive 401 error.
The same for JWT produced by Google OAuth 2.0 Playground (https://developers.google.com/oauthplayground)
What instruments are available to diagnose the problem?
Thanks!
Hi, what do you mean by validate? to decode it and see the claims inside it? If this is the case I’ll use http://jwt.io. As well you need to know what is the signing algorithm used to sign this token.
Hope this answers your question.
Thanks! Jwt.io is definitely a good tool. I googled for “jwt validate online” and found some more.
Regarding my problem I just needed to read and debug more 🙂
Hey Taiseer,
First of all, thanks for this amazing tutorial.
Is it possible to get a JWT token from a ExternalLogin provider ? Can you please point me a direction in order to achieve this goal ?
Thanks!
Hi Maraues,
Well it depends on the external provider access token format, you can’t control this for sure. For example FB access token format is not JWT and you have no control on this.
Thanks for your answer Taiseer!
I follow your tutorial, but instead of doing it from scratch I started using the WebApi template. So the tokens retrieved from the CustomProvider works just fine. But after the implementation of JWT, the ExternalLogin Endpoint stops working, and allways retrieve 404 response. Is this related to the JWT ?
Once more, thanks a lot for your amazing articles
Let me explain my question a litle better.In the OAuthAuthorizationServer i’m using a provider for issue tokens and set its format as JWT (like your explanation) , and i also set the AuthorizeEndPointPath to handle ExternalLogins. So my provider Options are:
OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString(“/oauth2/token”),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(“Teste”),
AuthorizeEndpointPath = new PathString(“/api/Account/ExternalLogin”),
};
This means that when I request an external token, it will try to set a jwt format for the external token , and this will fail.
So is it possible to exclude the JWT format only for external login tokens ?
This is the same case I faced when I used the default Web API template, I recommend you to read this post where I implement the external authentication in different way.
I do not think that JWT has anything to do with the AuthorizationEndPoint and receiving 404, try to return it to default access token format and you might receive the same issue.
Sorry if I was not able to fully help you here, if you find something it will be great to share it with the readers here.
In fact, i´m considering now to implement the Authorization Server without External Login functionality. This will be handled directly on Ressource Servers, so i’m leaving the Authorization Server only with user accounts and jwt tokens management. Do you agree with this aproach ?
Well, the AuthZ server should be your only server for managing authentication and authorization, the Resource servers should rely on it and not be responsible for external logins. So…my recommendation is not to go with approach. As i told you you can check this post which shows how to use external logins and you can use JWT on same time.
Thanks for the excellent tutorials and the detailed information in each one of them (and in the comments!). I was wondering, is it possible to combine the Auth and Resource servers? Or is there a specific reason to split them?
And what about refresh tokens? Do we need them? What happens when a ticket is expired?
Thanks for reading the articles and taking the time to comment 🙂
Well I’ve answered all your questions on different parts of the posts.
For combining AuthZ and Res. server, yes you can do this for sure, nothing forces you to separate them, read the into for this post again carfeuly.
For refresh tokens please read this again because all have been answered carefully. Refresh tokens are not mandatory but they are good to keep user logged in without issuing a very long lived non revocable access token.
In the end, that was exactly what I did. I mixed everything from the articles into one. According to the JWT specification, we should use the access token to request a new refresh token and that is how I’ve implemented it now. Thanks again!
I’d love to see a sample project that implements both OAuth 2 Refresh Tokens and JWT. I have an implementation of the Refresh Token base on your post about it and now I would like to add in support for JWT. I’d love to see a post for how to modify that code with the existing Client entity to use JWT.
Hi Chris, I’m working on this on a different project, hopefully if all is good I will share this with you.
Taiseer have you finished that project i would like to have a look also :p
Btw greate series of tutorials
Hi Taiseer,
A big thanks for saving days of research on Token based app authentication using basic token, refresh token and now JWT. Initially, I spent almost two weeks looking for the right article to explain how to’s before came across your posts. Feeling blessed now. 🙂 Thank you!
Could you please advise if project that implements both Refresh Tokens and JWT is available for learning purpose? Meantime, I will try to merge your two separate posts and make sense out of it.
Will hugely appreciate a working example by you to make sure I and other people like me on the right path.
Awaiting your favourable reply.
Many thanks
Sudhir
I am also interested in how the Refresh Token would work with JWT in this tutorial.
I am also interested in how to make refresh token work with JWT. I assume , all mobile devices can handle jwt without any issues ?
Great job Taiseer, your posts have been very helpful as we get up to speed on this stuff.
I do have a question. Can you give the explanation on the reasoning for using the approach of using the JwtBearerAuthenticationOptions implementation of verifying the JWT tokens versus implementing it in the Unprotect in the CustomJwtFormat class? It seems a little disjoint and just opposite of what Microsoft did of implementing the Unprotect and not the protect.
Thanks.
Hi Kent, glad it was useful.
I’m not sure if I understood your question 100% but I will try to answer it.
MS doesn’t have ready made class which helps you to create JWT tokens out of the box, so I need to create my own “CustomJwtFormat” and implement the Protect method and use it in the Authorization server.
On the other hand, and on the Resource server, MS has out of the box implementation for the Unprotect method in class (JwtBearerAuthenticationOptions) which is responsible to Unprotect the JWT token and check the validity of the signature. So there is no need to re-implement this. If this class was not available you will see that I’ll be writing the concrete implementation for the Unprotect method. You can check the source code of the Unprotect method here.
Hope this answers your question.
HI Taiseer,
Using the Unprotect() of the Microsoft.Owin.Jwt.JwtFormat that you pointed out, I implemented the Unprotect() under my CustomJwtFormat class.
Unfortuntely the error expection was thrown while extracting the Token/protectedText using the Unprotect() of JwtFormat.
This is what the error says:
> Audience validation failed. Audiences: ‘8b7cf74424034fb180c902d090ca556a’. Did not match: validationParameters.ValidAudience: ‘http://khict.com’ or validationParameters.ValidAudiences: ‘null'”
Could you please advise what possible to resolve this error?
Thanks in Advance!
======================================================
== My Unprotect() function in CustomJwtFormat class
======================================================
public AuthenticationTicket Unprotect(string protectedText)
{
//throw new NotImplementedException();
string issuer = “http://khict.com”;
string clientId = “8b7cf74424034fb180c902d090ca556a”;
var secret = TextEncodings.Base64Url.Decode(“OikD2sWtM0gZQGjONy3e9zkdRpbSvbVDhkd3tql4mZ8”);
IIssuerSecurityTokenProvider iis = new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret);
var jf = new JwtFormat(issuer, iis);
//Error Thrown here
var result = jf.Unprotect(protectedText);
return result;
}
======================================================
== Error Exception thrown
======================================================
{
“Message”: “An error has occurred.”,
“ExceptionMessage”: “IDX10214: Audience validation failed. Audiences: ‘8b7cf74424034fb180c902d090ca556a’. Did not match: validationParameters.ValidAudience: ‘http://khict.com’ or validationParameters.ValidAudiences: ‘null'”,
“ExceptionType”: “System.IdentityModel.Tokens.SecurityTokenInvalidAudienceException”,
“StackTrace”: ” at System.IdentityModel.Tokens.Validators.ValidateAudience(IEnumerable
1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)\r\n at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateAudience(IEnumerable
1 audiences, SecurityToken securityToken, TokenValidationParameters validationParameters)\r\n at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateToken(String securityToken, TokenValidationParameters validationParameters, SecurityToken& validatedToken)\r\n at Microsoft.Owin.Security.Jwt.JwtFormat.Unprotect(String protectedText)\r\n at GeneralPOS.Formats.CustomJwtFormat.Unprotect(String protectedText) in z:\\Vichou\\developments\\khictposserver\\GeneralPOS\\POSServer-JWT-Bearer-Token-Authorizer\\Formats\\CustomJwtFormat.cs:line 82\r\n at GeneralPOS.ApiAuthorizeAttribute.IsAuthorized(HttpActionContext actionContext) in z:\\Vichou\\developments\\khictposserver\\GeneralPOS\\POSServer-JWT-Bearer-Token-Authorizer\\Auth.cs:line 65\r\n at System.Web.Http.AuthorizeAttribute.OnAuthorization(HttpActionContext actionContext)\r\n at GeneralPOS.ApiAuthorizeAttribute.OnAuthorization(HttpActionContext actionContext) in z:\\Vichou\\developments\\khictposserver\\GeneralPOS\\POSServer-JWT-Bearer-Token-Authorizer\\Auth.cs:line 51\r\n at System.Web.Http.Filters.AuthorizationFilterAttribute.OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)\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.GetResult()\r\n at System.Web.Http.Filters.AuthorizationFilterAttribute.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.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n at System.Web.Http.Controllers.AuthenticationFilterResult.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.Runtime.CompilerServices.TaskAwaiter
1.GetResult()\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()”}
Hi Taiseer, Thanks for the great article. I have been following this series and they have helped me learn many concepts. On this article though I have some basic confusion, would appreciate if you can take some time to answer:
We have three concepts – Auth Server, Resource server and the (real) client. Based upon your previous post, I understand that the client will first get the auth token from Auth Server (after providing client and user credentials) and then supply that token for future requests to Resource server. There may not be direct communication between Auth server and Resource server if we are using JWTs.
In this article, resource server seems to be acting like a client, providing its id and secret to auth server. How does a true client (e.g. Android App) fit in here? Once we bring in this android app in picture how do we make sure:
1. The token created by auth server works only for the resource server (there’s no communication between the two) and also created after validating the client id of android app.
2. The resource server can validate the JWT token (created by our auth server and mean only for this resource server etc) that it receives from the android app on subsequent requests.
Thanks in advance,
Sandeep
I’m similarly confused. Is the client application responsible for passing the audienceId of the resource server? In which case, does this setup support one auth server and multiple resource servers? Should all resource servers beconfigured with the same audience id so they can respond to this token?
Resource servers should register with the Auth server and obtain unique clientid/secret. If you give them the same clientId/secret then you an consider them one resource server.
So:
Client (in my case your ngAuthApp from earlier articles)
Resource Server A (registered with my auth server as audience A)
Resource Server B (registered with my auth server as audience B)
I want the client to send their credentials to the auth server and get back a token. This token needs to give my client access to resources on the two audiences A and B. I can make my auth server issue a ticket that gives access to a single audience/resource server.
Where I’m confused is the ‘Protect(AuthenticationTicket data)’ in CustomJwtFormat.cs creates a token with a single audience id. So as far as I can tell this token couldn’t give you access to multiple resource servers? The JwtSecurityToken class has a ‘public IEnumerable Audiences { get; }’ property but not a clear way of setting it. Can you give any pointers as to how this set-up might work with multiple resource servers?
By the way, thank you for this excellent series! I’ve followed it all the way up to this point, and now have got a bit bamboozled!
Hi Matthew,
With my current implementation the token is issued for only one Audience, so in you case and if you want to separate your audiences for design reason you might give them the same clientid and secret, this will work fine for sure.
Regarding the IEnumerable Audiences { get; } property I recommend you to check the source code for this middleware on github, currently I’ve no answer on how you can use it.
Hope this answers your question.
Hi Sandeep,
You can’t store secret on your Android App, it is non-confidential client, so in your case the image here represents your situation.
Your resource server will store the clientId/secret which is received from Auth server so it can use it to validate that token is issued from trusted Auth server. You client application (android) is responsible to just ask the token from Auth server and pass it to the resource server, no validation for token should be done on the android app. Hope this answers your question.
Hi Taiseer, thanks for reply.
My question was more related to what change would the architecture in this article undergo if a real client (such as an android app for example) is brought into picture.
Hi Sandeep,
There should be no change and your Android App should act like AngularJS app (Both are non-confidential clients). I believe there are few users who posted comments here telling me that they were able to integrate Android App with the current setup successfully, even including the external providers logins.
If you have specific question or you are suspicious that something will not work when you try to build Android app and integrate with the API; then please let me know, always happy to help.
Thanks Taiseer for reply and much appreciate your help.
This is the best resource on securing Web APIs that I have found. Thanks for your efforts! A quick question to verify my understanding, if you please:
When a resource server receives a request with a JWT from a non-confidential client (such as a mobile app), will the resource server need to communicate directly with the auth server which issued the JWT in order to validate the token? Or, can the resource server validate the JWT programmatically (by itself) by using the client secret that was issued to it by the auth server?
Hi Phil, glad to hear that post is useful:)
Basically the resource server will be able to validate the JWT based on the client secret issued by the AuthZ server, there is no need to communicate with the AuthZ server to validate a JWT token.
Hope this answers your question.
Thanks Taiseer. Yes, this answers my question perfectly.
It occurs to me that the contents and validation of the JWT could be a proprietary (custom) implementation, provided that each supported resource server implemented the JWT validation algorithm in the same way. Am I characterizing this accurately?
Hello Taiseer,
Again, excellent article. I am just a little bit confused. I followed your five part tutorial and I have have an authorization server and a resource server that are decoupled and using the same machine key. And, since I followed your tutorial, it supports a native login with refresh tokens as well as external facebook and google authentication.
Unfortunately for me, this article started fresh instead of continuing from post number 4 or from post number 5. If I merge the changes you have outlined in this post into my existing authorization and resource servers, can I still support all of the features, native login with refresh tokens and external authoritzation?
I have a few specific questions:
1. From the first four posts I have a ClientId table that was being used to hold the external clients, which are the web and mobile applications. And, now I also have an audience table. Do those represent the same enttiy? Can they be combined?
2. My SimpleRefreshTokenProvider is throwing an error since there is no as:client_id in the context.ticket.properties.dictionary. I think I should just return null in this situation?
But, the more I think about this, it seems like I am no longer authenticating with an external id but with a client id.
Bob
Hi Bob,
Apologies for the late reply, well the idea for this post was to find another way to decouple AuthZ server from resource server without unifying the machineKeys, so the use of JWT is suitable here, and yes you should be able to use refresh tokens along with external providers with JWT, we are only changing the format of the access token.
Regarding your first question, the clients are piece of software used to to communicate with both your AuthZ server to get tokens from and Recourse server (Audience) to get data from, so the audience is different entity and usually it represents server side component (Web API) which relies on AuthZ server to protect it.
For the second question I need to check it but with little re-factoring you can handle this, I do not have clear answer now.
Hope this answers your questions and let me know if you need further help.
This is a great tutorial. I’m running into an issue when trying to expand it though.
I would like to have multiple audiences be able to access the resource server. So in ConfigureOAuth() of startup.cs (of the resource server), I see that app.UseJwtBearerAuthentication() .AllowedAudiences property and .IssuerSecurityTokenProviders property are arrays, so I can easily pass in an array of my multiple audiences names[] and SymmetricKeyIssuerSecurityTokenProvider[].
However, since ConfigureOAuth() is only run when the server starts, I’m not sure how to add/remove audiences from the allowed audiences of the app. Is there any way to do this? Are there any resources you could point me to?
Many thanks, and again, this is a fantastic tutorial!
Cheers,
Brett
Brett, thanks for your comment, I do not have answer right now but I’ll check it the use for those arrays and get back to you if I have an answer.
Hi Taiseer,
Just following up with a little code. This code is working and allows JWTs to be authenticated against multiple audiences:
https://gist.github.com/brspurri/9dfeed196f5bf539189c
But the question remains, is it possible to dynamically append audiences? Restarting the server after adding new audiences works of course – because ConfigureOAuth() is rerun, but I’d like to avoid a forced restart.
Thanks again! Your tutorials are some of the best I’ve read!
Brett
Hi Taiseer,
Excellent article! I’m actually having the same problem that Brett is having, regarding audiences being bound to a database and when it changes I’m having to restart the app pool. Any update on this?
Thanks!
Dave
Thanks Taiseer about this greate article!
I wonder how can we extend TokenExpiration when client call/access to resource server, and should we do that, is it best practice?
Hi David, thanks for you comment.
Sorry I din’t get your question, what do you mean by extending the TokenExpiration property? The only property you can use to set the token expiry time is “AccessTokenExpireTimeSpan” and this is set on the AuthZ server not on the resource server. If you are referring to something else please let me know.
Can you show an example of issuing a request with the acquired jwt? How do I structure the REST request to hit the Resource server (where does the JWT go?) Something like the POST examples you have above would be great!
Hi Rod,
It is exactly like the default access token format, you need to send the JWT in the Authorization header of your request using Bearer scheme, the header will be something like this: [Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlx…….]
Hope this answers your question.
Hello! Great arcticles! I’m following carefully all of them so I can understand better those mechanisms. There’s a lot of things here that I’ve never used (and some even heard) before.
I would like to ask you onething: I’m trying to create an api to receive calls only from my application. They’ll be hosted on the same domain. Following your example, I didn’t enable CORS. And when sending the “Access-Control-Allow-Origin” header, I send only my application domain.
But still, anyone would be able to issue a request against “oauth2/token” and check if a certain username and password matches (ie, brute-force), is that correct? Or by having CORS disabled that wouldn’t be a problem?
I’m testing this with Postman sending “Origin” header with “http://foo.example/” and I’m still able to request the token generation endpoint. Sure, I’m testing this locally only so far. But should this work? Should I be checking “manually” the header “Access-Control-Allow-Origin” against a string with my domain?
As I said, many things here are new to me, so, could you please point out how can I test this?
And furthermore, would you say that the JWT way is the better approach on this case?
Thanks in advance! I look forward more posts!
Hi Rodrigo, glad to hear that posts are useful.
Well if your API is hosted withing your Front end application, I assume that it is an application will be running in a browser, then you do not need to enable CORS for sure. As well you shouldn’t send any extra headers such as “Access-Control-Allow-Origin” with your request.
Keep in your mind that same origin policy and CORS are only applicable on browsers, if you are calling from PostMan, native application, or exe, then there is no CORS involved at all.
JWT is more standard especially if your one of your resource servers is not built using .NET, maybe Node.js then tokens using JWT format can work between different server platforms.
Hope this answers you questions.
is it possible to use JWT in vNext project? For instance I can’t add references to Microsoft.Owin.Security.Jwt to ResourceProject
As far as I know, currently you can’t because this middleware has not been ported to ASP.NET 5 yet, you can check this issue here
I believe you can create a resource server that understands JWT with vnext. Just add the middleware app.UseOAuthBearerAuthentication(…). Not sure about Authentication server though.
Amazing set of tutorials Taiseer!
I have a quick question:
In your previous tutorial with separating the resource server and auth server you used the existing project from earlier tutorials. (refresh tokens, access tokens, etc) With this tutorial being stand alone, I’m not sure if the JWT, that is used to secure the APIs, also replaces the refresh token and access token formats? Or does all that still stay the same and the JWT is JUST used to secure the API infrastructure?
Any clarification would be great! 🙂
Hi Matt,
Glad to hear that posts were useful for you. Well the JWT will be used as new format for the access token only, the refresh token identifier which you send to the user will remain the same, it will stay the as Guid.
As well the ProtectedTicket stored in table RefreshTokens will not be affected too, so the only one changed is the access token format.
Hope this answers your question.
Thanks Taiseer.
Would you recommend using JSON Web Encryption then? Or is that overkill for this solution? (using your previous tutorials for reference)
Hi Matt, are you going to encode any sensitive information in your JWT or just set of claims? If there is sensitive information then consider encryption, but you need to mange decryption on client too. Usually JWT should not contain sensitive information such as CC number or Social security numbers, etc…
It will just contain claims and a userid… any sensitive data will be retrieved from subsequent calls to the resource server. So encryption will probably be too much.
The audience entity in this tutorial… does that replace much of the ‘client’ entity from your earlier tutorials?
Sorry for all the questions! This is really great stuff! 🙂
To try and clarify my previous question a bit further…
In your first set of tutorials the client_id that is passed to the Auth server is ‘ngAuthApp’ which is describing the angular.js application requesting the token. This is used to set AllowedOrigin and RefreshTokenLifeTime in the AuthorizationServerProvider component in the middleware.
In this tutorial, the client_id is now the resource server client id. This comes from registering it as an ‘audience’.
It seems the context of client_id is different in both cases… 1st being the javascript application requesting the token and 2nd being the service you want to make your request to. (resource server)
Or have I not fully understood? 🙂
Hi Matt, apologies for the late reply.
You are correct, in the first posts the client id is an identification for the client (application/software) which attempt to get access to the user’s account, and in the first posts I’m not trying to get access to the user account, I’m using only the client id to get some settings from DB for this client (application) such as the (allowed origin for building redirect URIs, Refresh token life time, etc..).
I this post the client id is little bit different, it is the Id for the Resource server which will rely on this authorization server to get tokens from, maybe we should consider calling it audience_id instead of client_id, but the default implementation for the context “OAuthValidateClientAuthenticationContext ” of “OAuthAuthorizationServerProvider” contains strongly types properties for reading the client_id from (audience_id in your case).
Hope this answers your concerns.
Thanks Taiseer. Don’t apologise… thanks for taking the time to reply to every single question people ask!
I think a common thing people are getting confused about (or have trouble with) is requesting a token from a client application (mobile / website / etc) AND then still having some way of coupling an auth server and a resource server.
I have seen a lot of questions that is similar to that. Your first tutorial you solve the client app requesting an auth token flow and then in this tutorial you solve the RS and Auth Server coupling flow. Putting it together seems to be the harder part.
I have seen a few posts about how to solve this (as the spec just says ‘the resource server should check with the auth server to make sure the token is valid… but how to do that is outside of purpose of the spec’) and they recommend implementing a custom grant type, one that would allow a resource server to send a bearer token, that it has received, to the auth server for validation.
I am going to attempt at putting together a working scenario on github over the next couple of weeks when I can fit it in and see if I can implement such a thing. I will reply here if I am successful if you would like to have a look at it 🙂
You are welcome, yes please if you find better solution please share it here. Good luck 🙂
Hello,
I have problem access from rest client.
{
Message: “Authorization has been denied for this request.”
}
I dont understand, lep me please
This is means your access token is invalid, make sure your are sending the right access token as well using bearer scheme
I have the problem of Luis as well. I followed all the steps in the tutorial and when access the resource server by putting the token in the header in bearer scheme, I got the error saying “Authorization has been denied for this request.”. Do I missing anything here, and need to provide the secret as well, but how?
Also, when I stepped into the process of token generation, I got error saying that HmacSigningCredentials.cs not found, although I double checked the Thinktecure package was installed properly. Please help.
Can I get help on this? I used the clientId statically configured in the tutorial to generate the token, but still got access denied error. Thanks.
First of all, fantastic article. Then my question is, do you know how to add additional information to the jwt token response. The current format is { access_token : xxxx, token_type: “bearer”, expires_in: 1799}. Is there a way of including the issued date in the token or is this the standard way the token must be?
Hi Werner,
I guess you need override the method “TokenEndpointResponse” in class “CustomOAuthProvider” and include the properties needed there, I didn’t try it but it should be like the below:
Let me know if it works 🙂
public override Task TokenEndpointResponse(OAuthTokenEndpointContext context) property in context.Properties.Dictionary)
{
foreach (KeyValuePair
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult
Looking at the OAuth 2 documentation, shouldn’t the grant_type be “urn:ietf:params:oauth:grant-type:jwt-bearer” instead of “password”? Then the the authorization header in requests for resources should just contain the JTW, right?
See http://openid.net/specs/draft-jones-oauth-jwt-bearer-03.html section 2.1. Hopefully I’m understanding it correctly.
Thanks Taiseer for the great article.
I’ve read your series on Token Based Authentication with Angular and now this particular article has me asking questions:
I would like to combined the RefreshToken logic with JWT. I want a user on a particular application(client) to claim access to a resource(audience).
So I changed my OAthServerOptions to this:
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() {
AllowInsecureHttp = true,
TokenEndpointPath = new PathString(“/token”),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new JWTAuthorizationServerProvider(),
AccessTokenFormat = new CustomJwtFormat(“http://dev-maui-04/SOCCSAuthService”)
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
My trouble lies in the ValidateClientAuthentication and GrantResourceOwnerCredentials:
Since both JWT and RefreshToken are each accessing the ClientId property for different values, how do I elegantly hack this?
Perhaps you can show me an example of how these two methods may look for returning a RefreshToken with a JWT Token as its Ticket?
How would the resource server’s conversation with the Auth server change with a RefreshToken paradigm?
Also, would this idea be forgery proof, considering Access-Control-Allow-Origin is set to *?
Hi and thanks for your comment, I guess you need to add new property named “audience_id” in once you request the token, this property will hold the value for the resource server and the validation will take place in method “ValidateClientAuthentication” as it is in step 1.5. To read the value of the new parameter in method “ValidateClientAuthentication” you can do the following:
dynamic formCollection = (Microsoft.Owin.FormCollection)context.Parameters;
audience_id = formCollection.Get(“audience_id”);
I didn’t try it before, so in summary the client_id/client_secret will be used to validate the client(i.e mobile app) and the audience_id property will be used to validate the Resource server.
Let me know if this works with you.
Thanks again Taiseer. Its working out just right.
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string audiencId = string.Empty;
string clientSecret = string.Empty;
string symmetricKeyAsBase64 = string.Empty;
Client client = null;
Audience audience = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
context.SetError(“invalid_clientId”, “ClientId should be sent.”);
return Task.FromResult(null);
}
// protected method below. Gets parameter from context.Parameters.
if (!TryGetFormParameter(context, “audience_id”, out audiencId))
{
context.Validated();
context.SetError(“invalid_audienceId”, “AudienceId should be sent.”);
}
using (var repo = TinyIoC.TinyIoCContainer.Current.Resolve())
{
client = repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError(“invalid_clientId”, string.Format(“Client ‘{0}’ is not registered in the system.”,
context.ClientId));
return Task.FromResult(null);
}
if (client.ApplicationType == Models.ApplicationTypes.NativeConfidential)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError(“invalid_clientId”, “Client secret should be sent.”);
return Task.FromResult(null);
}
else
{
if (client.Secret != Helper.GetHash(clientSecret))
{
context.SetError(“invalid_clientId”, “Client secret is invalid.”);
return Task.FromResult(null);
}
}
}
if (!client.Active)
{
context.SetError(“invalid_clientId”, “Client is inactive.”);
return Task.FromResult(null);
}
using (var repo = TinyIoC.TinyIoCContainer.Current.Resolve())
{
audience = repo.FindAudience(audiencId);
}
if (audience == null)
{
context.SetError(“invalid_audienceId”, string.Format(“Invalid audience_id ‘{0}'”, audiencId));
return Task.FromResult(null);
}
context.OwinContext.Set(“as:clientAllowedOrigin”, client.AllowedOrigin);
context.OwinContext.Set(“as:clientRefreshTokenLifeTime”, client.RefreshTokenLifeTime.ToString());
context.OwinContext.Set(“as:audienceId”, audiencId);
context.Validated();
return Task.FromResult(null);
}
Hi @Ra,
Have you got working copy of an entire solution (JWT + Refresh token + multiple ClientIds + multiple AudienceIds)? If yes, please share here. It will greatly resolve 90% of an existing and future comments here.
Thank you!
Sudhir
I’m also really interested in the multiple ClientIds + multiple AudienceIds
Hello again, silly question – I am saving these tokens in SessionStorage but issue is if I open a subpage in new link I need to login again? is there any way out?
Hi Bilal, session storage is per tab, right? Why you not use localStorage to store the token?
Thanks.
So I managed to combine what I learned in your post about refresh tokens with this post on using JWT’s. However, I’m not sure I’m clear on one thing. Right now, I have my code populating the “aud” claim in the JWT with the clientId. Is that correct? Or is the “aud” claim supposed to represent something other than the client?
If you would like share the code you implemented with me and I’ll take a look. Thanks
I’m also looking to implement external authentication like you did in your post about external logins with Facebook and Google. However, I’m trying to do it strictly via the Owin middleware. From what I have found, it looks like all I need to do differently is replace the AccountController.ObtainLocalAccessToken action by overriding the GrantCustomExtension method in my custom implementation of OAuthAuthorizationServerProvider. Then, in that method, check if the grant_type is “urn:ietf:params:oauth:grant-type:google” (I made up this new custom grant_type). If so, then follow the same verification process that the ObtainLocalAccessToken method followed and call context.Validate(ticket) where ticket is a new AuthenticationTicket object based on the authenticated ClaimsUser. Does this sound right? Or am I missing some steps?
Hi to all,
when I fail the request token (login by username and password not correct) is right that the tool returns a 400 error ?
Thank you very much
greetings
James
Hi James, yes it’s correct to revive bad request, because technically you are (user) is sending wrong password and username so there server validate it and it needs to indicate that there is something wrong with user input, it should not return 401 because you are not authenticated yet. Hope this answers your question.
Hi Taiseer,
I think my question is similar to Chris’: I have three resource servers(MapLayers, RSSProxy and Reports). My roles are creator and viewer. A creator can access every server to build a map where on which messages will reside as icons. The viewer can see the resulting report data the the reports server handles.
I want the creator to login one time and get a token that allows role based access to every server.
How would the custom JWT look? Would I sent up three audience_ids (one for each server) or could I add claims to the token?
Hi Taiseer,
Thanks for this excellent article. But I have some questions in regards to mobile client for these api’s:
1. After getting JWT everything’s fine. But how to make this process of fetching JWT from Authorization server secure? As we send credentials in this step and hence it seems to be very critical data, so should I just rely on SSL for that or try additional steps.Can you please throw some light here?
2. In case of mobile clients, such as an Android app, once i authenticate and receive JWT I have to persist JWT somewhere inside the phone to make further calls, lets say sqllite or shared preferences in case of Android. Is it recommended to persist JWT’s inside the client machine. If not then, is there any alternative?
Hi Kartik,
Glad to hear that this post is useful, please find my answers below:
1- I’m implementing here an OAuth 2.0 flow named “Resource owner password credential flow” which should only be used with client application that you trust 100% or your have built, what I mean by this you need to make sure that this native login screen appears in your android App where the user provides his username/password to it is not storing it locally or invoking a malicious API where it logs those credentials. Those credentials should be sent the /token endpoint directly over HTTPS only and your receive the access token over HTTPS too, not if this is not the case you need to consider using another OAuth 2.0 flow named implicit grant.
2- Yes you need to store the access token securely in your device so you present it with each call, usually what you can do is to issue short lived access token 1 hours and you keep refresh it using refresh token grant. Access token transmission should be done over HTTPs only.
Hope this answers your question.
Hi Taiseer,
Maybe a stupid question but how does the client (Angular app for example) know about the client_id (audience_id)?
Is it “hard coded” in for example the app.js file just like the rest? So it is stored in 3 places namely in the web.config of the Resource Server, in the database of the Authentication Server and in the app.js file of the Angular app?
If you want access to multiple Resource servers they are “stored” in the app.js file?
Thanks!
I see it working a little like this (if you can understand my terrible hand writing)
https://www.dropbox.com/s/9shtau0dlpkep32/File-12-06-2015-11-02-24.jpg?dl=0
Client -> Resource Server -> Auth Server
So to login, your js passes the client name & user credentials, the resource server appends on its resource credentials (completely privately, nothing to do with the client) then the resource server just passes on the JWT it receives from the auth server back to the client
(Taiseer if I’m wrong here please let me know!)
i’m using fiddler for testing API’s. when i tried to test the token using http://localhost:18303/api/protected “Get”
User-Agent: Fiddler
authorization: bearer [ACCESS TOKEN]
Host: localhost:18303
the result always 401 Unauthorized.
note: i can create new token and all of them Unauthorized
please need help
Make sure that you implementing step 2.3 correctly and the values for issuer, audience and secret are exactly the same as your AuthZ server.
i downloaded your source code and that what i’m working on it and i didn’t changed in it. it always gives me 401 Unauthorized.
That strange, many others used it without any issues, I can’t think of cause of this issue 🙁
I think he did not change the JWT issuer to his local IP?
Taiseer: Really great article! I have followed all your blogs and especially this one very carefully. And I have the exactly same issue as the above comment. I am getting Authorization has been denied for this request.(401) whenever I try to call a resource server api method from angularjs client with a valid token. Token is valid, I checked it multiple times on http://jwt.io/. I am not sure if I am missing something very simple.
Hi Priya,
Are you sure the same AudienceId and Secret are used to generate the token and the same to validate it in (app.UseJwtBearerAuthentication)?
As well what is the the value you receive in the WWW-Authenticate header? Is it bearer or something else?
Thanks for the reply Taiseer. I finally figured out the for JWT issuer url needed a “/” in the end. 401 error makes you think that error is somewhere else. Again thanks for these great posts.
Hi Taiseer,
Thanks for a great article! I am a bit new to JWT’s and WebAPI after having used mostly WCF services up to now. However, I do have a little experience regarding using JWT’s working with Angular and Node.js.
I mostly followed your tutorial to the T, except that in the Audience store I am using cache to persist the user data as the API am working on will only be used for an intermediary for other services.
My front end uses Angular to do get/posts to the API and after logging in and getting the token form “oauth2/token”, I set it in sessionStorage and subsequently make sure the “Authetication” header is present equal to “Bearer xXxTokenxXx…”.
At the moment when I am trying to access a endpoint protected by the [Authorize] decorator, I get a 401 reply. However, I have made sure on endpoints using [AllowAnonymous] that the “Authetication” header is present by inspecting System.Web.HttpContext.Current.Request.Headers[“Authorization”].
Do you have any idea what I might be doing wrong or if you can point me in the right direction where to start debugging my issue? I currently have breakpoints all over the show in the code I got copied from your example, but not hitting any of them when trying to access the endpoint protected by [Authorize].
As an example, I get the sure issue when trying to call the endpoint from Postman. You can see the example here: http://imgur.com/a/WoCeL#0
Any help would be appreciated. Thanks!
Hi Eben,
Once you receive 401, and in the response header (www-authenticate) what is the value returned? I’m afraid that your resource API is not configured to be protected by bearer tokens and it is using cookies.
As well double check that issuer value used are identical in your Authz and Resource API.
Hi Taiseer
Thanks for a great article again.
I am just wondering if I have a project which is both the Authorization Server and the Resource Server, whether it is possible to get the AllowedAudiences in the JwtBearerAuthenticationOptions object to work on a more dynamic fashion? Realistically in a real world scenario one cannot have only one allowed audience and one cannot restart the server every time a new audience is added to the Audience Store.
In my case I changed the Audience Store to be persisted and when recognized users logs in, I add them to the Audience Store. Then in JwtBearerAuthenticationOptions I would like AllowedAudiences to get it’s values from my Audience Store.
However, if I do it this way and the Audience Store is empty when I start up the server for the first time an error is thrown because the AllowedAudiences in JwtBearerAuthenticationOptions needs to have at least one entry.
If I hard code at least one entry into the Audience store as you did, then it works fine, obviously. However, after that point I can add as my users to the Audience Store as I want, they won’t be recognized until the AllowedAudiences in JwtBearerAuthenticationOptions gets refreshed – aka restarting the server.
I was hoping you could help me get around this?
Kind regards
Eben
I got the similar problem as above from Luis. I followed the steps in your tutorial to add audience, generate token, secret, then, as you mentioned, pasted the token to the jwt.io, it shows the signature is invalid with a red bar. I can put the base64secret in the field as indicated in jwt.io, but should I use only the token, or both token and secret in the header to get to protected area on the resource server, and how? Excuse me if this is a stupid question. I tried with token, and I got
{
Message: “Authorization has been denied for this request.”
}
but I tried again, I got 500 error.
Also, when I was in debug mode to step-in the token generation process, I got an error that HmacSigningCredentials.cs is not found. I did installed Thinktecture Nuget package as the instruction in the tutorial.
Thanks and really appreciate your help..
Never mind about the 500 error, it was something else, I already fixed that. The error is “Authorization has been denied for this request.”, please help. Thanks.
Hi Taiseer,
Great Article!! I am having a scenario where the OAuth server and the resource server are the same. It would be really great if you can guide me what needs to be done in this scenario.
Thanks,
Koundinya
Hi Koundinya,
Glad that this post is useful, you can find the exact scenario you are looking for on my other post here where the Authz and Resource server are the same. Good luck 🙂
This is a great tutorial!
Thank you very much Taiseer Joudeh…
You welcome, glad you liked it!
Could you pls respond to my question left under the comment of Kent F.?
Thanks in advance
I will hopefully by end of today, hopefully I will find answer.
Hi, Taiseer Joudeh
Am sorry to have ask for this question but i have search for resource with any help, I need to convert this part of the code to the pattern of Repository and Controller method but without any solution, please can you point me to resources or solution to help fixing this
public static class AudiencesStore
{
public static ConcurrentDictionary AudiencesList = new ConcurrentDictionary();
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;
}
}
And this Controller
[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(newAudience);
}
}
Am new to web api, and your blog is my first resources in Web API,
Thanks
Do you have an example client or http request that gets a token and then requests from the protected resource? I can get the token but the call to the api/protected fails on authentication.
I have the same question
Hi Rich, I’m using postman to call the resource server end points, any http clients can do the same as long you pass the correct token in the authorization header using bearer scheme. If it is not working then there is issue with configuring the resource server with the authorization server.
hi,Taiseer
I can do this following you hlep
but I have a question:
when I get the access token from Authroization Server,why can’t I use the Authroization’s Protect API?
I means,I add an WebAPI in Authroization Server name Protected3Controller below [Authorize].
I get the access token as your sample and return the right token which can use all of the action in Resource Server,
but I can’t use any action in Protected3Controller .
why? and can you tell me how to do this?
I am sending a post with Fiddler to the Audience Controller. My audienceModel is always null. Am I missing a step?
I found the issue. It was USER ERROR! Boy I feel stupid. Thanks for this excellent post!
You are welcome, glad you find it useful.
I see that you are using the Authorize filter on you WebApi methods. As you know this filter has a Roles parameter so access to the api methods can be restricted to specific roles [Authorize(Roles = “Admin”)] for example. I also see you added roles to the JWT. My question is will the Authorize filter inspect the JWT for roles if they are provided in the filter on the api method? If not would I need to implement that in the custom provider?
Thanks!
Hi Chris,
You are right, the Authorize filter will check for Roles provided in the token, so if you have a method which is attribute by [Authorize(Roles = “Admin”)] only access token which contains the role “Admin” will be authorized, so no need to create custom provider.
Thanks! I can’t tell you how much this series of blog posts have helped me in understanding a fairly confusing process. Great job!
Good day, why?They use the line AccessTokenFormat = new CustomJwtFormat (“http://jwtauthzsrv.azurewebsites.net”) why must serguir that format? there is something more generic?
Great article – can I ask what tool you are using to test the endpoint? I’ve been using Fiddler but yours looks a bit friendlier!
Hi Brett,
It is called PostMan, Google Chrome Extension, just search for “PostMan Packaged App”
Hi Taiseer,
Thanks a lot for explaining such a complicated thing with easiness, simple words and working examples. Thank you!
What is the best way to incorporate 3rd party apps accessing multiple in-house APIs? Can you please guide?
Any help here would be much appreciated.
Thank you!
Sudhir
Great article, thanks a lot for these beneficial blogs, but I have a little confuse about the difference between client ID and resource server ID,
1- should I add hard coded resource ID???
2- Isn’t the client ID should be generated when register to application??
3- when a requesting an access token, the client ID passed is that of client and not the resource server Id?
Hi,
Great! thanks a lot for all these beneficial blogs, but I have a little bit confused about the difference between Resource server ID and client ID,
1- Should I hard coded Resource server ID?
2- The Client ID is the one got upon app registration, isn’t it?
3- when requesting an access token, using post, the client Id entered is that of the client or of the resource server?
please advice ,
Best Regards.
Thank you for the detailed instructions here. This is exactly what I was looking for.
I’m have a little problem, however. The code compiles, but I’m getting a run-time error “ServiceLocationProvider must be set.” at the OAuthServerOptions. Any idea what that is about?
Oh, it just occurred to me, is this something to do with the CommonServiceLocator? If so, I’ll figure it out.
I’m trying this out step by step and I’m getting the following response when using the client_id, username, password, and grant_type = password:
{
“error”: “unsupported_grant_type”
}
Please help me figure this out. I haven’t the slightest idea as to why this is happening and no one has mentioned this yet.
Your request should look as the below, i guess you missed setting the content-type header correctly
POST /consumerappapi/oauth2/token HTTP/1.1
Host: ws.sandbox.aramex.net
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
username=XXX&password=XXX&grant_type=password&client_Id=XXX&client_secret=XXX&device_id=XXXX
Thanks for the help. It turned out that I fat fingered the word “grant_type”. Would it be possible to update this tutorial? I noticed a few inaccuracies:
Your tutorial says:
Install-Package Thinktecture.IdentityModel.Core Version 1.2.0
It should say:
Install-Package Thinktecture.IdentityModel.Core -Version 1.2.0
The missing hyphen on the version threw me off a little. Last, for the Resource Server’s startup.cs, there’s a difference between what you put in this tutorial and what’s on github. In this tutorial, you’re missing the following (within JwtBearerAuthenticationOptions) :
,
Provider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = context =>
{
context.Ticket.Identity.AddClaim(new System.Security.Claims.Claim(“newCustomClaim”, “newValue”));
return Task.FromResult(null);
}
}
Adding those two updates will make things a bit easier to follow when you’re not downloading the source. Without the Provider being set, you’ll get:
{
“Message”: “Authorization has been denied for this request.”
}
Good catch, I will check it and update. Thanks Leroy
Great post!
Just want to see what I should do if I want to keep the roles under the resource server instead of Authorization server.
Because I thing that every systems should have the specific roles, right?
Thanks!
I have the same question. Really hope that you can help on this.
Hi Taiseer,
Very wonderful article, I’m new in Web API, I just follow your steps. I can register the new audience and get the access_token, but when trying to get the protected api(Resources Server), it always return
{
“Message”: “Authorization has been denied for this request.”
}
I just download the source code from GitHub, and modify the issuer to localhost with port, and the behavior still the same.
Thanks again for the great post.
Hi Derek,
Make sure that issues is the exact thing, its case sensitive and trailing slash will affect the validation of the token
Thanks for the reply Taiseer. It’s my careless, the issuer not exactly the same, the resource server miss a trailing slash.
Realy great tutorial.
Thanks for your time to explain it nice and clear.
You are welcome Mitja.
Excuse me, I downloaded your solution, I can generate JWT by AuthorizeServer, but when I request with this JWT, I received status code 401 unauthorized, I dont understand. I’m no change anything in your project, please help me, thank you.
Realy great tutorial
i have issue
1. http://localhost:21972/oauth2/token
2. http://localhost:21972/api/audience
The first one works fine, so I can get token back. I have issues with the second one try to add new audience. It gives back 404, always. how to fix this issue
Thanks for your help
Make sure that you are setting the correct [RoutePrefix] attribute on this controller.
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)
You can not decrypt the jwt created by google since you do not have the secret key it was encrypted with.
That is correct, but you can decode it is content and see the claims inside it.
Great post.
How do you combine that with social logins ?
How will the flow look like ?
I’m guessing the resource server will be responsible to provide a social provider access token?
But how will the authorization server validate it? should it be validates on the auth server ?
What grant_type will be used on such flow ? not the ‘password’ since the user won’t provide any username \ password.
Will appreciate your help figuring out the flow.
Thanks !
Hello,
Did you have the chance to take a look at the following blog where I cover social logins? You might be able to take some hints as I do not have a dedicated blog post for this.
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.
Would this include the port number and/or path used to retrieve the token?
>> 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”
Hai Taiseer,
I have set the authorization header as you said. Should I send the issuer name with the request? If so how to set that ? Should I send it as json in the request body. I am getting the Unauthorized error.
Thanks,
Ganesh
Hi Ganesh,
No you should not send the issuer in the request, but you need to make sure that you used the same issuer in resource server and Authz server.
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?
Did you find the solution for 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
Hi,
Andrew, did you find any thing. Can you help me ? I do to use asymmetric key and have multiple resource Servers.
Let me know if you can help me.
Thanks,
Jawand Singh
Why symmetric key is associated with Client? What if we have more than one client who uses same resource server, how to configure resource server settings in this case?
Great post! I have just one question that I cannot seem to solve. Based on client id, I am setting a different ExpiresUtc datetime in AuthenticationProperties in GrantResourceOwnerCredentials like so:
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
{
{
“audience”, (context.ClientId == null) ? string.Empty : context.ClientId
}
});
prop.ExpiredUtc = DateTime.UtcNow.AddMonths(1);
//prop.ExpiredUtc = DateTime.UtcNow.AddMonths(3);
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
When it gets to my Protect method, it reverts back to the standard expiration from the OAuthAuthorizationServerOptions init. Any ideas why this is doing it and what I can do to do what I would like? Thanks!
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,
This is a very useful article. I have a question around how can this be used to provide SSO between a number of Resource Servers?
Thanks
Ali
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”
Hey Taiseer, I keep calling your end point (with Postman) http://jwtauthzsrv.azurewebsites.net/api/protected after adding an audience and successfully generating a token. I am getting a 404. Do you still have the site on Azure? great article!
Hi Rob,
I’m afraid not, the azure subscription I used to host apps on it is expired now, sorry about this.
Hai Taiseer,
I have passed the data as,
{“client_id”:”099153c2625149bc8ecb3e85e03f0022″,”username”:”myApp”,”password”:”myApp”,”grant_type”:”password”}
to the url,
http://localhost:18292/oauth2/token
content-type: application/xxx-www-form-urlencoded
For the above requests it is giving 403 response. It is not giving token. Am I doing right?? Can u help me, Pls.
Regards,
Ganesh. K
Hi,
You are receiving 403 when you invoke the Api from postman? If it is not working you should receive 401 not 403.
Hey Taiseer,
After adding an audience and successfully generating a token. With token i’ve successfully accessed ‘api/protected’.
Is token(ResourceServer A) is readable to another ResourceServer(B) server that authorized with same AuthorizationServer ?
great article!
Hi, it should not be accessible if they are using different Audience ID, each token will be issued for certain audience.
Hi Taiseer,
Can you please describe why the client_id in the request is set to the audience id ?
If i have only one Resource server should i include the audience id ?
Thank you.
Regards
Hi Taiseer,
Very insightful I must say. Everything works perfect when I followed your post. however, I need to make the authentication server read audiences from the database where they are stored instead one single constant audience. How do archive this. Thank you.
Hi Taiseer!
I am developing a project that needs to use SSO (Single Sign-On) strategy, but, as I saw, I cant use your example (great, by the way) in my solution or, if I use it, I will need to define the same key to all audiances. So, in this case, nothing changes between this and the machine key solution. Am I right?
Thank you!
Hi Maicon,
You are right, if you need to enable SSO you need to unify the key, my post was about creating a very simple Authorization server where multiple resource servers can rely on. If you ae looking for SSO service, my recommendation is to take a look at the ThinkTecture Identity Server.
Hi Taiseer. This article is really useful.
Pls, tell me. There is anyway to add more audience to resource server in run time?
Thank you!
Hello,
How I will get audience (Client id) in below mentioned code dynamically rather than hard-coded. Once I will audience client than i get secret-key from database and can dynamically pass it to below mentioned code. I observed that you are adding clientid in AuthenticationProperties of JWT but I dont know how to get this clientId.
Your quick reply will highly appreciated
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)
}
});
}
Hi Taiseer,
HmacSigningCredentials cannot convert to SigningCredentials, After upgrading System.IdentityModel.Tokens.Jwt to 5.0
Please help.
Regards
JwtSecurityToken use a microsoft.identityModel.token.signningcredentials not HmacSigningCredentials. please help me
error at: var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
What is the exact error please?
When using the latest versions of
Install-package System.IdentityModel.Tokens.Jwt
Install-package Thinktecture.IdentityModel.Core
Then you get the following error:
Error BC30311 Value of type ‘HmacSigningCredentials’ cannot be converted to ‘SigningCredentials’
Hello Oliver,
Thanks for the update, to be honest I didn’t check them with latest version, but I will try them soon. meanwhile If you found a fix for the issue please share it here so others can benefit from it. Thank you.
Hi Oliver,
As Taiseer mentioned, if you use the versions he indicated in the article you’ll be fine. If you want to upgrade though, here’s what happened.
Microsoft incorporated the Microsoft.IdentityModel.Tokens members into the .NET framework, effectively as System.IdentityModel.Tokens. See here:
https://msdn.microsoft.com/en-us/library/microsoft.identitymodel.tokens.aspx
So what’s happening is you’re caught in the middle of this change:
– Thinktecture.IdentityModel.Core (1.2.0) extends class Microsoft.IdentityModel.Tokens.SigningCredentials
– Thinktecture.IdentityModel.Core (1.3.0+) extends class System.IdentityModel.Tokens.SigningCredentials
– System.IdentityModel.Tokens.Jwt (4.0.0) references Microsoft.IdentityModel.Tokens.SigningCredentials
– System.IdentityModel.Tokens.Jwt (4.0.3) references System.IdentityModel.Tokens.SigningCredentials
So basically you need to make sure you have versions that match the namespace. If you’re using Thinktecture 1.3.0, than update System.IdentityModel.Tokens.Jwt to 4.0.3 (do NOT update to 5.0.0)
Best,
Hi Taiseer,
very helpful article. Thanks. I have a question though. How do we refresh the token before it expires. It would be nice if you post an article in continuation to this article.
Thanks
Manav
Please check this post, I believe it will help answering your questions.
How do I convert the Thinktecture.IdentityModel.Tokens.HmacSigningCredentials to Microsoft.IdentityModel.Tokens.SigningCredentials?
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey); gives the below error:
Error CS1503 Argument 6 signingKey: cannot convert from ‘Thinktecture.IdentityModel.Tokens.HmacSigningCredentials’ to ‘Microsoft.IdentityModel.Tokens.SigningCredentials’ in AuthorizationServer.Api
I think you are using the latest version of the NuGet packages, somehow it is not compatible with the current post, I do not have an answer for this now but if you used the same packages version used in the post then it should work.
Fix for latest nuget packages:
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(keyByteArray), SecurityAlgorithms.Sha256Digest);
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 man, this single comment made me stop tearing my hair out!
FYI you can also remove the need for the Thinktecture package by doing this stuff.
Send my best wishes to your family also.
Hi,
When I try with URL http://jwtauthzsrv.azurewebsites.net/oauth2/token, it seems working fine and provided me with the correct result. But I am doing the same with my ADFS server (http://my_ADFS_server/oauth2/token ) and not able to pass the username and password.
I search a bit and came to know that
“AD FS 3.0 (2012 R2) DOES NOT support grant_type=password for OAuth 2.0 but it supports grant_type=authorization_code and grant_type=refresh_token only”
“AD FS provides WS-Trust endpoints and you could use them instead of OAuth 2.0 endpoint for issuing and exchanging tokens”
So, is there any way to make it work or how do I made above code working with “authorization_code”.
If you can guide me for the same because I want to make SSO with multiple intranet applications based on JWT.
Regards,
Sanvid