2

I added Token based authentication to my OWIN middleware and can generate the token. But while using, the token for an API call with Authorize attribute I always get "Authorization has been denied for this request." It works fine though without Authorize attribute. Here is my startup.cs and controller method. Any thoughts , what is wrong?

startup.cs

    public void Configuration(IAppBuilder app)
            {
                var issuer = ConfigurationManager.AppSettings["issuer"];
                var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);
                app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
                {
                    AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
                    AllowInsecureHttp = true,
                    TokenEndpointPath = new PathString("/token"),
                    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
                    Provider = new SimpleAuthProvider(),
                    AccessTokenFormat = new JwtFormat(issuer)
                });
                app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
                {
                    AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
                    AuthenticationMode = AuthenticationMode.Active,
                    AllowedAudiences = new[] { "*" },
                    IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
                    {
                        new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
                    }
                });
                container = BuildDI();
                var config = new HttpConfiguration();
                config.Formatters.XmlFormatter.UseXmlSerializer = true;
                config.MapHttpAttributeRoutes();
                config.SuppressDefaultHostAuthentication();
                config.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ExternalBearer));
                config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
                app.UseCors(CorsOptions.AllowAll);
                app.UseSerilogRequestContext("RequestId");
                app.UseAutofacMiddleware(container);
                app.UseAutofacWebApi(config);
                app.UseWebApi(config);
                RegisterShutdownCallback(app, container);
            }

 public class SimpleAuthProvider: OAuthAuthorizationServerProvider
        {
            public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
            {

                if (context.UserName != context.Password)
                {
                    context.SetError("invalid_grant", "The user name or password is incorrect");
                    context.Rejected();
                    return Task.FromResult<object>(null);
                }

                var ticket = new AuthenticationTicket(SetClaimsIdentity(context), new AuthenticationProperties());
                context.Validated(ticket);

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

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

            private static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context)
            {
                var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ExternalBearer);
                identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
                return identity;
            }
        }

API Controller Method:

 [HttpGet]
        [Route("sampleroute")]
        [Authorize]
        public async Task<HttpResponseMessage> GetSamples(string search)
        {
            try
            {

                HttpResponseMessage response;
                using (HttpClient client = new HttpClient(Common.CreateHttpClientHandler()))
                {
                     response = await client.GetAsync("test url");
                }
                var result = response.Content.ReadAsStringAsync().Result;
                Samples[] sampleArray = JsonConvert.DeserializeObject<Samples[]>(result);
                var filteredSamples = sampleArray .ToList().Where(y => y.NY_SampleName.ToUpper().Contains(search.ToUpper())).Select(n=>n);
                log.Information("<==========Ended==========>");
                return  Request.CreateResponse(HttpStatusCode.OK,filteredSamples);

            }
            catch (Exception ex)
            {
                log.Error($"Error occured while pulling the Samples:  {ex.ToString()}");
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex.ToString());
            }
        }
user3154990
  • 555
  • 4
  • 13
  • 27
  • Just to be sure. In the Authorization attribute did you include the token and bearer? I.E. Authorization: Bearer "token" – Alin Jun 06 '17 at 19:26
  • @allencage No I haven't. How do I do that? – user3154990 Jun 06 '17 at 19:33
  • @RuardvanElburg I am testing in postman...I get the token by calling localhost/api/token and using it for API calls with Authorize attribute. lcoalhost/api is my host url. – user3154990 Jun 06 '17 at 19:35
  • You'll need to add the token to the header of your request. –  Jun 06 '17 at 19:38
  • @RuardvanElburg I am doing that, I see the token in my context.request.headers too – user3154990 Jun 06 '17 at 19:39
  • I do not have a clear picture of your design, but I wonder if you need jwt: https://forums.asp.net/t/rss/2013277 Are you using an external authentication service? –  Jun 06 '17 at 20:13

2 Answers2

2

It's probably a problem with the allowed audiences. Here

 app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
 {
     ...     
     AllowedAudiences = new[] { "*" },
     ...
 }

you set the allowed audiences. The tokens audclaim will be checked against the list of AllowedAudiences. But you never add any audience to the token.

In our project I used a CustomJwtFormat based on the code shown in http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/

The token will be generated with a call to

var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey);

the second parameter is responsible for the aud claim in the JWT:

From https://msdn.microsoft.com/en-us/library/dn451037(v=vs.114).aspx :

audience Type: System.String

If this value is not null, a { aud, 'audience' } claim will be added.

After setting the aud claim in the token authorization should work fine.

jps
  • 20,041
  • 15
  • 75
  • 79
1

From what I understood, you need to add the header: Authorization: Bearer "token". If you have not modified the default implementation of the authorization request the steps are these:

  1. Register user at the endpoint:

    /api/Account/Register
    
  2. Post to /token the following items:
    • grant_type: password
    • username: "the username you registered"
    • password: "the password you registered for the user"
  3. You will receive a token in the Response
  4. Copy that token and create a Request to the method you secured with the [Authorize] filter of type:

     Authorization: Bearer "the_token_you_copied_earlier"
    

    Needless to say, it could be pretty easy for you if you used Postman or Fiddler to make and receive Requests because it shows you how everything works.

Alin
  • 394
  • 5
  • 14
  • @allenceage I am doing exact same way and ran out of ideas before posting here. :) – user3154990 Jun 07 '17 at 13:15
  • @user3154990 my solution works for OAuth out of the box, from what I saw in your code you are not using the default implementation. If the authorization that you are trying to build is not mandatory try the default method. – Alin Jun 07 '17 at 13:55