16

This is the page where I "learned" how to do it: https://stormpath.com/blog/token-authentication-asp-net-core

But for me this is not working (doesn't work with Fiddler, too) There is this controller for my ApplicationUser-model:

[Authorize] //works when it's not set, doesn't work when it's set
[Route("api/[controller]")]
public class ApplicationUserController : Controller
{
    private IRepository<ApplicationUser> _applicationUserRepository;

    public ApplicationUserController(IRepository<ApplicationUser> applicationUserRepository)
    {
        _applicationUserRepository = applicationUserRepository;
    }

    [HttpGet("{id}")]
    public ApplicationUser Get(int id)
    {
        return _applicationUserRepository.Get(id);
    }
}

and there's my wrapper for RestSharp to get all applicationusers:

public Task<T> GetResponseContentAsync<T>(string resource, int id) where T : new()
{
    RestRequest request = new RestRequest($"{resource}/{{id}}", Method.GET);
    request.AddUrlSegment("id", id);
    if (!AuthenticationToken.IsNullOrEmpty(true))
    {
        request.AddHeader("Authorization", string.Format("Bearer {0}", AuthenticationToken));
        _client.Authenticator = new JwtAuthenticator(AuthenticationToken);
        _client.Authenticator.Authenticate(_client, request);
    }

    TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
    _client.ExecuteAsync<T>(request, response =>
    {
        tcs.SetResult(response.Data);
    });
    return tcs.Task;
}

From my web-client application I want to login with JWT (Token-Authentication) what works. After login I get e.g. this access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJURVNUIiwianRpIjoiZTBjYjE0NjgtYzBmOS00ZTM4LTg4ZjgtMGM4ZjNmYjMyNjZmIiwiaWF0IjoxNDcwOTUwMTA0LCJuYmYiOjE0NzA5NTAxMDQsImV4cCI6MTQ3MDk1MDQwNCwiaXNzIjoiRXhhbXBsZUlzc3VlciIsImF1ZCI6IkV4YW1wbGVBdWRpZW5jZSJ9.a9_JK2SG3vzc6NSOB0mZXqHlM9UAEXUHHrrijAQUsX0

without the Authorize-attribute I get the ApplicationUser, but when setting the Attribute, the result is null (since the web-api is not getting called)

the wrapper-call looks like this:

//this works, token-value is set
string token = new RepositoryCall("http://localhost:54008/").Login("token", "TEST", "TEST123");

string accessToken = JsonConvert.DeserializeObject<Dictionary<string, string>>(token)["access_token"];
ViewData["Result"] = accessToken;

ApplicationUser userAfterLogin = await new RepositoryCall("http://localhost:54008/api") 
    { AuthenticationToken = accessToken }
    .GetResponseContentAsync<ApplicationUser>("ApplicationUser", 2);

and here userAfterLogin is null.

I'm trying to get the login since two weeks but I still don't get it right..

Any idea what I'm doing wrong? Maybe a wrong request-header-value for authorization?

Update

this is my Startup.Configure where I configured to use the Bearer / JWT:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseIdentity();
    var secretKey = "mysupersecret_secretkey!123";
    var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));

    // Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
    var options = new TokenProviderOptions
    {
        Audience = "ExampleAudience",
        Issuer = "ExampleIssuer",
        SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
    };


    var tokenValidationParameters = new TokenValidationParameters
    {
        // The signing key must match!
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = signingKey,

        // Validate the JWT Issuer (iss) claim
        ValidateIssuer = true,
        ValidIssuer = "ExampleIssuer",

        // Validate the JWT Audience (aud) claim
        ValidateAudience = true,
        ValidAudience = "ExampleAudience",

        // Validate the token expiry
        ValidateLifetime = true,

        // If you want to allow a certain amount of clock drift, set that here:
        ClockSkew = TimeSpan.Zero
    };


    app.UseJwtBearerAuthentication(new JwtBearerOptions
    {
        AutomaticAuthenticate = true,
        AutomaticChallenge = true,
        TokenValidationParameters = tokenValidationParameters
    });

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AutomaticAuthenticate = true,
        AutomaticChallenge = true,
        AuthenticationScheme = "Cookie",
        CookieName = "access_token",
        TicketDataFormat = new CustomJwtDataFormat(
            SecurityAlgorithms.HmacSha256,
            tokenValidationParameters)
    });

    app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}
Ralf Bönning
  • 14,515
  • 5
  • 49
  • 67
Matthias Burger
  • 5,549
  • 7
  • 49
  • 94
  • Have you tried to invoke your controller method manually (i.e. using Postman/Fiddler)? – mateudu Aug 12 '16 at 16:07
  • i tried, but how can i set the token in fiddler when executing the request? – Matthias Burger Aug 12 '16 at 20:10
  • Use Postman instead. It is much easier. – mateudu Aug 13 '16 at 05:29
  • 1
    In fiddler you can compose the request and set the `Authorization` header eg `Authorization: Bearer ` – Nkosi Aug 14 '16 at 03:34
  • Okay so I tested with fiddler and it doesn't work.. (With postman i need to add a login-page I guess, which for now I don't want.) - the request of /api/ApplicationUser redirects me to /Account/Login (so a login-page), I think because the token Is not valid?! – Matthias Burger Aug 14 '16 at 09:07
  • You need to show how the authentication is configured in the API. – juunas Aug 14 '16 at 17:52
  • Hi @juunas, what exactly do you need? My Startup.Configure() ? – Matthias Burger Aug 14 '16 at 17:58
  • Well, somewhere you have configured the API to use JWT authentication right? – juunas Aug 14 '16 at 18:02
  • @juunas, yep in my startup.cs -> updated :) – Matthias Burger Aug 14 '16 at 18:06
  • Hmm, I would have to test that myself to figure out what might be the problem. But judging from what is happening, the JWT middleware is not authenticating the request, since AuthorizeAttribute is throwing the request back. – juunas Aug 14 '16 at 18:12
  • Why do you use `UseIdentity`? And what is the status code you received when used `Authorize` attribute? – adem caglin Aug 16 '16 at 08:17
  • @ademcaglin since this is asp.net core you can use the webservice and the application in the same project. There's already the possibility of using the application with the default identity-login. We want to give customers the possibility of writing their own front-end if they don't want to use ours: so we give them the possibility to authenticate with a JWT Token. Statuscode is 404 (I think because `[Authorize]` redirects to a login-page that is not existent) – Matthias Burger Aug 16 '16 at 09:07
  • Can you try `[Authorize(ActiveAuthenticationSchemes = "Bearer")]`? – adem caglin Aug 16 '16 at 09:11
  • tested it, but still the same result, but thanks for the tip.. I think i'll check the web again for another solution of authorization - still no progress after 3 weeks in one problem is so much frustrating... – Matthias Burger Aug 17 '16 at 07:55
  • I have done something similar but with no JWT. I used a custom process, however as I read from the blog you mention the required claims have to be inside the token. Have you tried to decrypt it with an external utility to see if the JSON is encrypted correctly? Other than that your request headers are looking good, I am using restSharp like this: .Method = RestSharp.Method.GET .Resource = "rest/Users/CurrentProfile" .AddHeader("Authorization", "Bearer " & mClient.BearerToken) and the Authorize attribute works. It looks to me either as a decryption problem or a claim isn't there? – ChD Computers Aug 18 '16 at 03:38
  • I will check decrypting the token later on, seems to be a good attempt. thx! – Matthias Burger Aug 18 '16 at 07:11
  • Why do you also call "UseCookieAuthentication" ? Looking at the symptoms it's actually the thing that's bouncing your requests. Cookie authentication seems redundant to me if you don't need browsers to use your API. – pkmiec Aug 20 '16 at 22:07
  • @pkmiec but i need the Cookie Authentication since the application has also its own frontend :) – Matthias Burger Aug 20 '16 at 22:18
  • Ok. Could you also post your ConfigureServices method? And could you please confirm that what you try to implement is "Modern Token based authentication" figure from https://stormpath.com/blog/token-authentication-scalable-user-mgmt ? – pkmiec Aug 20 '16 at 22:42
  • Also, the code of Login method which is called here would be useful to see: ( new RepositoryCall("http://localhost:54008/").Login("token", "TEST", "TEST123") ) – pkmiec Aug 20 '16 at 22:50
  • ... I mean the server-side part of it – pkmiec Aug 20 '16 at 22:57

3 Answers3

1

If you recive authorizacion error or using postman you realize that you are being asked to redirect to login just decorate your class with:

[Authorize(AuthenticationSchemes = "Bearer")]

By default .Net uses cookie based auth, with that annotation you swicht to token based one

jeprato
  • 61
  • 1
  • 6
0

In Fiddler you would see, if you are redirected to login page (it would report 2 Results, one with 302 (redirect) and then the 404 - is that the case?

You have DebugLogger activated, so try AddDebug(LogLevel.Trace) and view the Debug output window, it is very helpful in analysing which of authentication steps fail. It also shows if authentication fails or authorization, and if has a valid token etc. So it points to the direction to look for problems.

Kirsten
  • 492
  • 4
  • 11
-1

So you are using 2 middlewares for identity. One provided by asp.net identity (cookie based) and another token based. Now both of the middleware use the same attribute for handling the request [Authorize]. More precisely look at the code here

https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs

for JWTBearer

and

https://github.com/aspnet/Security/blob/dev/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs

for Cookie

Because both are activated in middleware pipeline the principal will have the data when you send auth token or cookie.

But because both of them are active either of them will return Unauthorized for the request that doesnt have cookie or JwtBearer.

For the solution you are looking for you need to create a middleware on top of existing cookie and token based to route the request to either based on if authorization header is present.

lazy
  • 531
  • 2
  • 4