1
votes

I have followed the tutorials up till this point in the series. I am using one project in the solution that acts as both the token issuing authority as well as the resource server.

The JWT is generated using the endpoint mentioned in the startup class and I validated it on jwt.io as well. However when I pass this JWT using Postman on Chrome to the resource API end point secured with an Authorize attribute, I always find it returning

{ "message": "Authorization has been denied for this request." }

The other api method as in the api controller class below works when called thru Postman on Chrome.

I have used the latest versions of all dlls required from the nuget console

Code in the startup class

  public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();
        ConfigureOAuthTokenGeneration(app);
        ConfigureOAuthTokenConsumption(app);
        WebApiConfig.Register(config);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);
    }


    private void ConfigureOAuthTokenGeneration(IAppBuilder app)
    {
        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            //For Dev enviroment only (on production should be AllowInsecureHttp = false)
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/oauth/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new CustomOAuthProvider(),
            AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["Issuer"]),
        };

        // OAuth 2.0 Bearer Access Token Generation
        app.UseOAuthAuthorizationServer(OAuthServerOptions);
    }

    private void ConfigureOAuthTokenConsumption(IAppBuilder app)
    {
        string issuer = ConfigurationManager.AppSettings["Issuer"]; 
        string audienceId = ConfigurationManager.AppSettings["AudienceId"];
        byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);

        // Api controllers with an [Authorize] attribute will be validated with JWT
        app.UseJwtBearerAuthentication(
            new JwtBearerAuthenticationOptions
            {
                AuthenticationMode = AuthenticationMode.Active,
                AllowedAudiences = new[] { audienceId },
                IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                {
                    new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
                }
            });
    }

Code in the Custom OAuthProvider

  public class CustomOAuthProvider : OAuthAuthorizationServerProvider
    {
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
            return Task.FromResult<object>(null);
        }

        public override Task MatchEndpoint(OAuthMatchEndpointContext context)
        {
            //avoid pre-flight calls
            if (context.OwinContext.Request.Method == "OPTIONS" && context.IsTokenEndpoint)
            {
                context.OwinContext.Response.Headers.Add("Access-Control-Allow-Methods", new[] { "POST" });
                context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "accept", "authorization", "content-type" });
                context.OwinContext.Response.StatusCode = 200;
                context.RequestCompleted();

                return Task.FromResult<object>(null);
            }

            return base.MatchEndpoint(context);       
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

            //setting up claims in the constructor of class UserDetails 
            UserDetails user = new UserDetails();
            user.UserName = context.UserName;
            user.FirstName = "Dummy First";
            user.LastName = "Dummy Last";

            ClaimsIdentity identity = new ClaimsIdentity("JWT-BearerAuth-Test");
            identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
            foreach (string claim in user.Claims)
            {
                identity.AddClaim(new Claim(ClaimTypes.Role, claim));    
            }
            var ticket = new AuthenticationTicket(identity, null);

            context.Validated(ticket);

        }
    }

The custom JWT class

   public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
    {
        private readonly string _issuer = string.Empty;

        public CustomJwtFormat(string issuer)
        {
            _issuer = issuer;
        }

        public string Protect(AuthenticationTicket data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            string audienceId = ConfigurationManager.AppSettings["AudienceId"];

            string symmetricKeyAsBase64 = ConfigurationManager.AppSettings["AudienceSecret"];

            var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);

            var signingKey = new HmacSigningCredentials(keyByteArray);
            var issued = data.Properties.IssuedUtc;
            var expires = data.Properties.ExpiresUtc;
            var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);
            var handler = new JwtSecurityTokenHandler();
            var jwt = handler.WriteToken(token);
            return jwt;
        }

    }

The Resource server's Api controller

   public class AdminController : ApiController
    {
        //This call works
        public IHttpActionResult ReadData(string id)
        {
            return Ok("ID sent in:" + id);
        }

        //[Authorize(Roles="EditRecord")]  //doesnt work
        [Authorize] //doesnt work either
        public IHttpActionResult EditData(string id)
        {
            return Ok("Edited ID:" + id);
        }
    }

My environment is VS2013 with Framework 4.5 using OAuth2 with Web Api 2. Please excuse the long post.

1
Hi, were you able to get this working? I'm facing the same issue. Searching but couldn't find any solutions - Charanraj Golla

1 Answers

0
votes

You need to make sure that values for issuer, audienceId, and audienceSecret used in method "ConfigureOAuthTokenConsumption" are the same values used when you generated the JWT token, take care of trailing slashes "/".

This is the only thing comes to my mind right now.