-
Notifications
You must be signed in to change notification settings - Fork 0
JWT Token
- add reference to System.IdentityModel.dll
- install the following packages: ** Microsoft.Owin.Host.SystemWeb ** Microsoft.Owin.Security.OAuth ** System.IdentityModel.Tokens.Jwt -Version 4.0.0
- add registered client/audience list:
public class AppConfiguration {
public static string TokenIssuer => "JWTAuthServer";
public static Audience[] Audiences = new Audience[] {
new Audience {
TokenAudienceId = "099153c2625149bc8ecb3e85e03f0022",
TokenAudienceSecret = "IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw",
}
};
}
public class Audience {
public string TokenAudienceId { get; set; }
public string TokenAudienceSecret { get; set; }
}- add the provider that handles the event for requesting access token:
public class SimpleAuthorizationServerProvider : 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 = AppConfiguration.Audiences.FirstOrDefault(a => a.TokenAudienceId == 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) {
if (context.UserName != "test" || context.Password != "test") {
context.SetError("invalid_grant", "The user name or password is incorrect.");
return Task.FromResult<object>(null);
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
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);
}
}- add the class that generate JWT token:
public class JWTFormat : ISecureDataFormat<AuthenticationTicket> {
private readonly string _issuer = string.Empty;
public JWTFormat(string issuer) { _issuer = issuer; }
public string SignatureAlgorithm {
get { return "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"; }
}
public string DigestAlgorithm {
get { return "http://www.w3.org/2001/04/xmlenc#sha256"; }
}
public string Protect(AuthenticationTicket data) {
if (data == null) throw new ArgumentNullException("data");
string audienceId = data.Properties.Dictionary.ContainsKey("audience") ? data.Properties.Dictionary["audience"] : null;
if (string.IsNullOrWhiteSpace(audienceId)) throw new InvalidOperationException("AuthenticationTicket.Properties does not include audience");
var audience = AppConfiguration.Audiences.FirstOrDefault(a => a.TokenAudienceId == audienceId);
var keyByteArray = TextEncodings.Base64Url.Decode(audience.TokenAudienceSecret);
var signingKey = new SigningCredentials(new InMemorySymmetricSecurityKey(keyByteArray),
SignatureAlgorithm, DigestAlgorithm);
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 tokenString) {
throw new NotImplementedException();
}
}- add Startup.cs
public class Startup {
public void Configuration(IAppBuilder app) {
ConfigureOAuth(app);
}
public void ConfigureOAuth(IAppBuilder app) {
var oAuthServerOptions = new OAuthAuthorizationServerOptions() {
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider(),
AccessTokenFormat = new JWTFormat(AppConfiguration.TokenIssuer),
};
app.UseOAuthAuthorizationServer(oAuthServerOptions); //Token Generation
}
}What makes the access token to be in the JWT format is the set to the property AccessTokenFormat, according to https://github.com/aspnet/AspNetKatana/blob/dev/src/Microsoft.Owin.Security.OAuth/OAuthAuthorizationServerOptions.cs:
AccessTokenFormat: The data format used to protect the information contained in the access token. If not provided by the application the default data protection provider depends on the host server. The SystemWeb host on IIS will use ASP.NET machine key data protection, and HttpListener and other self-hosted servers will use DPAPI data protection. If a different access token provider or format is assigned, a compatible instance must be assigned to the OAuthBearerAuthenticationOptions.AccessTokenProvider or OAuthBearerAuthenticationOptions.AccessTokenFormat property of the resource server.
- install the following packages:
** Microsoft.Owin.Host.SystemWeb ** Microsoft.Owin.Security.Jwt ** Microsoft.AspNet.WebApi ** Microsoft.AspNet.WebApi.Owin - store client id and secret:
public class AppConfiguration {
public static string TokenIssuer => "JWTAuthServer";
public static Audience Audience = new Audience {
TokenAudienceId = "099153c2625149bc8ecb3e85e03f0022",
TokenAudienceSecret = "IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw",
};
}
public class Audience {
public string TokenAudienceId { get; set; }
public string TokenAudienceSecret { get; set; }
}- create Startup.cs:
public class Startup {
public void Configuration(IAppBuilder app) {
ConfigureJWTConsumption(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
private void ConfigureJWTConsumption(IAppBuilder app) {
var issuer = AppConfiguration.TokenIssuer;
string audienceId = AppConfiguration.Audience.TokenAudienceId;
byte[] audienceSecret = TextEncodings.Base64Url.Decode(AppConfiguration.Audience.TokenAudienceSecret);
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityKeyProviders = new IIssuerSecurityKeyProvider[] {
new SymmetricKeyIssuerSecurityKeyProvider(issuer, audienceSecret)
}
});
}
}- create WebApiConfig.cs and TestController.cs same as before
- send a post request with the following content to the /token endpoint of authorization server:
grant_type=password&username=test&password=test&client_id=099153c2625149bc8ecb3e85e03f0022
and the response is
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6InRlc3QiLCJzdWIiOiJ0ZXN0Iiwicm9sZSI6InVzZXIiLCJpc3MiOiJKV1RBdXRoU2VydmVyIiwiYXVkIjoiMDk5MTUzYzI2MjUxNDliYzhlY2IzZTg1ZTAzZjAwMjIiLCJleHAiOjE1NjUwNjY2MTksIm5iZiI6MTU2NDk4MDIxOX0.FYnfOYvwMiIfDH-1LC9oZGnb9jEkewk84F4QkHI0uio",
"token_type": "bearer",
"expires_in": 86399
}
- send the following Get request to the /api/test endpoint of the resource server, with the bearer header set to the access_token value got above, and the response is "Hello World".