172

I'm working with ASP.NET Core application. I'm trying to implement Token Based Authentication but can not figure out how to use new Security System for my case. I went through examples but they didn't help me much, they are using either cookie authentication or external authentication (GitHub, Microsoft, Twitter).

What my scenario is: angularjs application should request /token url passing username and password. WebApi should authorize user and return access_token which will be used by angularjs app in following requests.

I've found great article about implementing exactly what I need in current version of ASP.NET - Token Based Authentication using ASP.NET Web API 2, Owin, and Identity. But it is not obvious for me how to do the same thing in ASP.NET Core.

My question is: how to configure ASP.NET Core WebApi application to work with token based authentication?

cuongle
  • 74,024
  • 28
  • 151
  • 206
Grant
  • 2,295
  • 3
  • 14
  • 13

4 Answers4

147

Update for .Net Core 3.1:

David Fowler (architect for the ASP .NET Core team) has put together an incredibly simple set of task applications, including a simple application demonstrating JWT. I'll be incorporating his updates and simplistic style to this post soon.

Updated for .Net Core 2:

Previous versions of this answer used RSA; it's really not necessary if your same code that is generating the tokens is also verifying the tokens. However, if you're distributing the responsibility, you probably still want to do this using an instance of Microsoft.IdentityModel.Tokens.RsaSecurityKey.

  1. Create a few constants that we'll be using later; here's what I did:

    const string TokenAudience = "Myself";
    const string TokenIssuer = "MyProject";
    
  2. Add this to your Startup.cs's ConfigureServices. We'll use dependency injection later to access these settings. I'm assuming that your authenticationConfiguration is a ConfigurationSection or Configuration object such that you can have a different config for debug and production. Make sure you store your key securely! It can be any string.

    var keySecret = authenticationConfiguration["JwtSigningKey"];
    var symmetricKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(keySecret));
    
    services.AddTransient(_ => new JwtSignInHandler(symmetricKey));
    
    services.AddAuthentication(options =>
    {
        // This causes the default authentication scheme to be JWT.
        // Without this, the Authorization header is not checked and
        // you'll get no results. However, this also means that if
        // you're already using cookies in your app, they won't be 
        // checked by default.
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    })
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters.ValidateIssuerSigningKey = true;
            options.TokenValidationParameters.IssuerSigningKey = symmetricKey;
            options.TokenValidationParameters.ValidAudience = JwtSignInHandler.TokenAudience;
            options.TokenValidationParameters.ValidIssuer = JwtSignInHandler.TokenIssuer;
        });
    

    I've seen other answers change other settings, such as ClockSkew; the defaults are set such that it should work for distributed environments whose clocks aren't exactly in sync. These are the only settings you need to change.

  3. Set up Authentication. You should have this line before any middleware that requires your User info, such as app.UseMvc().

    app.UseAuthentication();
    

    Note that this will not cause your token to be emitted with the SignInManager or anything else. You will need to provide your own mechanism for outputting your JWT - see below.

  4. You may want to specify an AuthorizationPolicy. This will allow you to specify controllers and actions that only allow Bearer tokens as authentication using [Authorize("Bearer")].

    services.AddAuthorization(auth =>
    {
        auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
            .AddAuthenticationTypes(JwtBearerDefaults.AuthenticationType)
            .RequireAuthenticatedUser().Build());
    });
    
  5. Here comes the tricky part: building the token.

    class JwtSignInHandler
    {
        public const string TokenAudience = "Myself";
        public const string TokenIssuer = "MyProject";
        private readonly SymmetricSecurityKey key;
    
        public JwtSignInHandler(SymmetricSecurityKey symmetricKey)
        {
            this.key = symmetricKey;
        }
    
        public string BuildJwt(ClaimsPrincipal principal)
        {
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
            var token = new JwtSecurityToken(
                issuer: TokenIssuer,
                audience: TokenAudience,
                claims: principal.Claims,
                expires: DateTime.Now.AddMinutes(20),
                signingCredentials: creds
            );
    
            return new JwtSecurityTokenHandler().WriteToken(token);
        }
    }
    

    Then, in your controller where you want your token, something like the following:

    [HttpPost]
    public string AnonymousSignIn([FromServices] JwtSignInHandler tokenFactory)
    {
        var principal = new System.Security.Claims.ClaimsPrincipal(new[]
        {
            new System.Security.Claims.ClaimsIdentity(new[]
            {
                new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Name, "Demo User")
            })
        });
        return tokenFactory.BuildJwt(principal);
    }
    

    Here, I'm assuming you already have a principal. If you are using Identity, you can use IUserClaimsPrincipalFactory<> to transform your User into a ClaimsPrincipal.

  6. To test it: Get a token, put it into the form at jwt.io. The instructions I provided above also allow you to use the secret from your config to validate the signature!

  7. If you were rendering this in a partial view on your HTML page in combination with the bearer-only authentication in .Net 4.5, you can now use a ViewComponent to do the same. It's mostly the same as the Controller Action code above.

Matt DeKrey
  • 11,582
  • 5
  • 54
  • 69
  • can you provide an additional sample of your Api Controller that generates Tokens? My OAuthBearerAuthenticationOptions are not being resolved. Is the `services.Configure` method call enough to register with the built in DI? Thanks – cleftheris May 07 '15 at 15:27
  • 1
    You'll need to actually inject `IOptions` to use the Options; using an Options object directly is not supported due to the named configuration that is supported by the Options Model framework. – Matt DeKrey May 07 '15 at 21:09
  • @MattDeKrey I agree, generating signing credentials via the new ASP.NET 5 Data Protection block would be ideal. I had a chat with Barry Dorrans (from the ASP.NET team) a few days ago to see if I couldn't use its nice key management capabilities to store a generated RSA key and use it as the signing key for AspNet.Security.OpenIdConnect.Server. Sadly, once it is stored, there is no way to export a key and its material using the new DP block. I've been told the ASP.NET team had no intention to change that, so creating your own certificate or RSA key is probably your best bet. – Kévin Chalet May 23 '15 at 18:27
  • I did that myself, but was hoping for something more user-friendly from the ASP.NET team. I'll add that to my answer once my conversion to beta-5 is working again! – Matt DeKrey May 23 '15 at 18:49
  • @MattDeKrey Thanks for your quick update! In case you can share the full solution once it's ready, you can answer my question here :) http://stackoverflow.com/questions/30546542/token-based-authentication-in-asp-net-5-vnext-refreshed – ebvtrnog May 30 '15 at 13:05
  • @MattDeKrey FYI, I opened a ticket at aspnet/DataProtection: https://github.com/aspnet/DataProtection/issues/86. Feel free to upvote it ;) – Kévin Chalet Jun 24 '15 at 18:51
  • @MattDeKrey What's this `bearerOptions.SecurityTokenValidators` list? (It's null within my code. Am I overseeing something?) – Corstian Boerman Jun 29 '15 at 12:58
  • @CorstianBoerman bearerOptions.SecurityTokenValidators = new List(); bearerOptions.SecurityTokenValidators.Add(new JwtSecurityTokenHandler()); – Smartkid Jul 20 '15 at 09:58
  • 6
    #5 has since been changed to the following in Microsoft.AspNet.Authentication.OAuthBearer - beta 5 - 6 and possibly earlier betas but haven't confirmed those. auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder() .AddAuthenticationSchemes(OAuthBearerAuthenticationDefaults.AuthenticationScheme).RequireAuthenticatedUser().Build()); – dynamiclynk Aug 07 '15 at 16:18
  • Just for my understanding - this (great) answer lists what you need to change from the (also great) article linked in the OP? – Astaar Aug 19 '15 at 09:54
  • The answer should be able to be used stand-alone as the article is for previous versions of .Net; however, the article gives lots of background information that is not included in this answer and might be helpful anyway! – Matt DeKrey Aug 19 '15 at 16:32
  • Thank you @MattDeKrey! This is by far the best post on the subject on the web today. The part that I am not understanding is the ClaimsIdentity. How a user can pass securely a username and password which will result in a token? Have you also considered IdentityServer (https://identityserver.github.io/)? – Martin Aug 19 '15 at 17:31
  • Step 6 details building the token; you'll need to authenticate the user yourself on your own, though, preferably using SSL. – Matt DeKrey Aug 19 '15 at 17:34
  • In beta6: Putting IOptions in the construction as the injection requires you to make a small change to parts of code: "var handler = bearerOptions.SecurityTokenValidators..." "becomes var handler = bearerOptions.Options.SecurityTokenValidators..." – damccull Aug 24 '15 at 05:36
  • How long does the token last for and how can we refresh it? – damccull Aug 24 '15 at 06:19
  • I'd recommend you do the `bearerOptions.Options` in the constructor rather than preserving a reference to the entire `IOptions<>`, but that's style. The token as given in the sample does not expire, but the `handler.CreateToken` takes additional parameters to either post-date a token or set an expiration date: `... DateTime? notBefore = default(DateTime?), DateTime? expires = default(DateTime?), ...` – Matt DeKrey Aug 24 '15 at 15:16
  • @MattDeKrey Thanks for the reply. I'll move the options object, that's a good idea. When the token expires or is about it, would we just generate a new one and pass it back if they need a new one? Also, how can we revoke an issued token if they don't expire? I'm planning to write up an article on how I implemented your solution, but I want to be thorough. – damccull Aug 24 '15 at 17:15
  • Just like in the article mentioned by the OP (Part 3), you'll need to refresh the tokens manually - I'd pass in the old token and get a new one out just before expiration. You ultimately can't expire a token itself if you don't set an expiration time; you'd need to handle that within your own data store if you need that functionality. – Matt DeKrey Aug 24 '15 at 20:55
  • The article the OP mentioned uses a OAuthValidateClientAuthenticationContext and a subclass of OAuthAuthorizationServerProvider in order to handle refresh tokens. Will these work with your approach or do I need to come up with something else? – damccull Aug 27 '15 at 19:34
  • I believe those classes are gone; generating a new token should be using the code from Step 6 again in a subsequent call and getting the client to use the new token. – Matt DeKrey Aug 28 '15 at 19:26
  • Thanks, I've figured out how to do that. Next problem I'm having is when the access token expires, I'm getting a SecurityTokenExpiredException: IDX10223: Lifetime validation failed. The token is expired. This is no good, because I need to return a 401 or some other code. Any idea where to catch this and act on it? – damccull Sep 02 '15 at 04:36
  • I solved it by adding my ExceptionHandlerMiddleware wrapping the _next call like this try { await _next(context); } catch(SecurityTokenExpiredException stex) { _logger.LogDebug("An expired token was provided: " + stex.Message, stex); context.Response.StatusCode = StatusCodes.Status401Unauthorized; context.Response.Headers.Clear(); await context.Response.WriteAsync(JsonConvert.SerializeObject(new { Exception = stex.GetType().FullName, ErrorMessage = stex.Message })); } – Maxim Balaganskiy Sep 03 '15 at 02:06
  • You can also use OAuthBearerAuthenticationOptions.Notifications.AuthenticationFailed delegate – Maxim Balaganskiy Sep 03 '15 at 04:12
  • I get "System.InvalidOperationException: No SecurityTokenValidator available for token: {JWT token I gave to client}" exception when I pass "Authorization: Bearer {Token}". Is there any solution? – Burak Tamtürk Sep 23 '15 at 13:35
  • I noticed that, too, with the latest version. I haven't had a chance to find a solution yet. I will come back with another update when I get there! – Matt DeKrey Sep 23 '15 at 20:15
  • 5
    @MattDeKrey I've used this answer as a starting point for an example of simple token based auth, and updated it to work against beta 7 - see https://github.com/mrsheepuk/ASPNETSelfCreatedTokenAuthExample - also incorporates a few of the pointers from these comments. – Mark Hughes Sep 24 '15 at 10:40
  • @MarkHughes - looks great - thanks for putting that together! A gotcha when going to beta8 is that you need to replace `OAuthBearer` with `JwtBearer`; I'll update my answer once beta8 is fully released. (Soon!) @BurakTamurk - I'm no longer getting that exception; perhaps it was a specific version of the framework? – Matt DeKrey Sep 28 '15 at 04:02
  • @MattDeKrey Good point, I'd noticed that somewhere when trying to find the source code for OAuthBearer - I've added comments in the places it'll need to change. – Mark Hughes Sep 28 '15 at 09:13
  • @MattDeKrey would you consider including a link to MarkHughes' example in your answer? I found your answer extremely useful, but couldn't quite work out how everything hung together until I found his example, and it took me a good while to notice it down here at the bottom of the comments! Many thanks to you both in any case. – nickspoon Oct 18 '15 at 22:09
  • 1
    @nickspoon I've updated my GitHub project and posted a working beta8 example in [a separate answer](http://stackoverflow.com/a/33217122/789529) - let me know if that works for you. – Mark Hughes Oct 19 '15 at 14:35
  • 2
    [Updated again for RC1](http://stackoverflow.com/a/33217122/789529) - old versions for [Beta7](https://github.com/mrsheepuk/ASPNETSelfCreatedTokenAuthExample/tree/beta7) and [Beta8](https://github.com/mrsheepuk/ASPNETSelfCreatedTokenAuthExample/tree/beta8) available in branches on GitHub. – Mark Hughes Nov 19 '15 at 22:54
85

Working from Matt Dekrey's fabulous answer, I've created a fully working example of token-based authentication, working against ASP.NET Core (1.0.1). You can find the full code in this repository on GitHub (alternative branches for 1.0.0-rc1, beta8, beta7), but in brief, the important steps are:

Generate a key for your application

In my example, I generate a random key each time the app starts, you'll need to generate one and store it somewhere and provide it to your application. See this file for how I'm generating a random key and how you might import it from a .json file. As suggested in the comments by @kspearrin, the Data Protection API seems like an ideal candidate for managing the keys "correctly", but I've not worked out if that's possible yet. Please submit a pull request if you work it out!

Startup.cs - ConfigureServices

Here, we need to load a private key for our tokens to be signed with, which we will also use to verify tokens as they are presented. We're storing the key in a class-level variable key which we'll re-use in the Configure method below. TokenAuthOptions is a simple class which holds the signing identity, audience and issuer that we'll need in the TokenController to create our keys.

// Replace this with some sort of loading from config / file.
RSAParameters keyParams = RSAKeyUtils.GetRandomKey();

// Create the key, and a set of token options to record signing credentials 
// using that key, along with the other parameters we will need in the 
// token controlller.
key = new RsaSecurityKey(keyParams);
tokenOptions = new TokenAuthOptions()
{
    Audience = TokenAudience,
    Issuer = TokenIssuer,
    SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.Sha256Digest)
};

// Save the token options into an instance so they're accessible to the 
// controller.
services.AddSingleton<TokenAuthOptions>(tokenOptions);

// Enable the use of an [Authorize("Bearer")] attribute on methods and
// classes to protect.
services.AddAuthorization(auth =>
{
    auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌​)
        .RequireAuthenticatedUser().Build());
});

We've also set up an authorization policy to allow us to use [Authorize("Bearer")] on the endpoints and classes we wish to protect.

Startup.cs - Configure

Here, we need to configure the JwtBearerAuthentication:

app.UseJwtBearerAuthentication(new JwtBearerOptions {
    TokenValidationParameters = new TokenValidationParameters {
        IssuerSigningKey = key,
        ValidAudience = tokenOptions.Audience,
        ValidIssuer = tokenOptions.Issuer,

        // When receiving a token, check that it is still valid.
        ValidateLifetime = true,

        // This defines the maximum allowable clock skew - i.e.
        // provides a tolerance on the token expiry time 
        // when validating the lifetime. As we're creating the tokens 
        // locally and validating them on the same machines which 
        // should have synchronised time, this can be set to zero. 
        // Where external tokens are used, some leeway here could be 
        // useful.
        ClockSkew = TimeSpan.FromMinutes(0)
    }
});

TokenController

In the token controller, you need to have a method to generate signed keys using the key that was loaded in Startup.cs. We've registered a TokenAuthOptions instance in Startup, so we need to inject that in the constructor for TokenController:

[Route("api/[controller]")]
public class TokenController : Controller
{
    private readonly TokenAuthOptions tokenOptions;

    public TokenController(TokenAuthOptions tokenOptions)
    {
        this.tokenOptions = tokenOptions;
    }
...

Then you'll need to generate the token in your handler for the login endpoint, in my example I'm taking a username and password and validating those using an if statement, but the key thing you need to do is create or load a claims-based identity and generate the token for that:

public class AuthRequest
{
    public string username { get; set; }
    public string password { get; set; }
}

/// <summary>
/// Request a new token for a given username/password pair.
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
[HttpPost]
public dynamic Post([FromBody] AuthRequest req)
{
    // Obviously, at this point you need to validate the username and password against whatever system you wish.
    if ((req.username == "TEST" && req.password == "TEST") || (req.username == "TEST2" && req.password == "TEST"))
    {
        DateTime? expires = DateTime.UtcNow.AddMinutes(2);
        var token = GetToken(req.username, expires);
        return new { authenticated = true, entityId = 1, token = token, tokenExpires = expires };
    }
    return new { authenticated = false };
}

private string GetToken(string user, DateTime? expires)
{
    var handler = new JwtSecurityTokenHandler();

    // Here, you should create or look up an identity for the user which is being authenticated.
    // For now, just creating a simple generic identity.
    ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user, "TokenAuth"), new[] { new Claim("EntityID", "1", ClaimValueTypes.Integer) });

    var securityToken = handler.CreateToken(new Microsoft.IdentityModel.Tokens.SecurityTokenDescriptor() {
        Issuer = tokenOptions.Issuer,
        Audience = tokenOptions.Audience,
        SigningCredentials = tokenOptions.SigningCredentials,
        Subject = identity,
        Expires = expires
    });
    return handler.WriteToken(securityToken);
}

And that should be it. Just add [Authorize("Bearer")] to any method or class you want to protect, and you should get an error if you attempt to access it without a token present. If you want to return a 401 instead of a 500 error, you'll need to register a custom exception handler as I have in my example here.

Community
  • 1
  • 1
Mark Hughes
  • 7,264
  • 1
  • 33
  • 37
  • 1
    This is a really excellent example, and included all the missing pieces I needed to get @MattDeKrey's example to work, thanks so much! Note that anyone still targetting beta7 instead of beta8 can still find that example in the [github history](https://github.com/mrsheepuk/ASPNETSelfCreatedTokenAuthExample/tree/4a36dd0a78f15ab25616c85e236ea2e79443d142) – nickspoon Oct 19 '15 at 21:22
  • Do you have to use your own manually generated signing key? Is there any way to just tap into the keys already generated and managed by the data protection libraries? Isn't that how the cookie auth tokens work? – kspearrin Oct 20 '15 at 18:37
  • I suspect there is a way of doing that @kspearrin - if you know how let me know and I'll add it in! I will take a look tomorrow to see if I can work that out. – Mark Hughes Oct 20 '15 at 18:53
  • Take a look at the [Data Protection docs](https://docs.asp.net/en/latest/security/data-protection/index.html). I have found them to be very well written and complete, so I'm sure all that we need is in there. My guess is that the [IKeyManager](https://docs.asp.net/en/latest/security/data-protection/implementation/key-management.html) might expose what is needed. – kspearrin Oct 20 '15 at 19:27
  • @MarkHughes - I can't get the post to take the authrequest, its always null. I'm calling it with application/x-www-form-urlencoded and the body is username=TEST&password=TEST. Just wondering if it might be a bug in beta 8. – Paul Speranza Oct 21 '15 at 16:05
  • @PaulSperanza It works for me using application/json encoding for the post - I've not used form-urlencoded against Beta8 myself, so can't comment on that. – Mark Hughes Oct 21 '15 at 16:10
  • @MarkHughes - yes I got it to work as json too. I did notice, when I inspected the Request object, that there were errors on some properties so maybe it is due to being a beta. Anyway, thank you so much for this example. – Paul Speranza Oct 21 '15 at 16:14
  • 2
    Thanks for this, however I don't quite understand why something which worked out of the box in ASP.Net 4 Web API now requires quite a bit of configuration in ASP.Net 5. Seems like a step backwards. – JMK Jan 06 '16 at 13:46
  • 2
    I think they're really pushing "social auth" for ASP.NET 5, which makes some sense I suppose, but there are applications that isn't appropriate for so I'm not sure I agree with their direction @JMK – Mark Hughes Jan 07 '16 at 10:58
  • 1
    Updated for dotnet core 1.0.1, for anyone who is interested. – Mark Hughes Dec 04 '16 at 21:41
4

You can have a look at the OpenId connect samples which illustrate how to deal with different authentication mechanisms, including JWT Tokens:

https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Samples

If you look at the Cordova Backend project, the configuration for the API is like so:

           // Create a new branch where the registered middleware will be executed only for non API calls.
        app.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api")), branch => {
            // Insert a new cookies middleware in the pipeline to store
            // the user identity returned by the external identity provider.
            branch.UseCookieAuthentication(new CookieAuthenticationOptions {
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                AuthenticationScheme = "ServerCookie",
                CookieName = CookieAuthenticationDefaults.CookiePrefix + "ServerCookie",
                ExpireTimeSpan = TimeSpan.FromMinutes(5),
                LoginPath = new PathString("/signin"),
                LogoutPath = new PathString("/signout")
            });

            branch.UseGoogleAuthentication(new GoogleOptions {
                ClientId = "560027070069-37ldt4kfuohhu3m495hk2j4pjp92d382.apps.googleusercontent.com",
                ClientSecret = "n2Q-GEw9RQjzcRbU3qhfTj8f"
            });

            branch.UseTwitterAuthentication(new TwitterOptions {
                ConsumerKey = "6XaCTaLbMqfj6ww3zvZ5g",
                ConsumerSecret = "Il2eFzGIrYhz6BWjYhVXBPQSfZuS4xoHpSSyD9PI"
            });
        });

The logic in /Providers/AuthorizationProvider.cs and the RessourceController of that project are also worth having a look at ;).

Alternatively, you can also use the following code to validate tokens (there is also a snippet to make it work with signalR):

        // Add a new middleware validating access tokens.
        app.UseOAuthValidation(options =>
        {
            // Automatic authentication must be enabled
            // for SignalR to receive the access token.
            options.AutomaticAuthenticate = true;

            options.Events = new OAuthValidationEvents
            {
                // Note: for SignalR connections, the default Authorization header does not work,
                // because the WebSockets JS API doesn't allow setting custom parameters.
                // To work around this limitation, the access token is retrieved from the query string.
                OnRetrieveToken = context =>
                {
                    // Note: when the token is missing from the query string,
                    // context.Token is null and the JWT bearer middleware will
                    // automatically try to retrieve it from the Authorization header.
                    context.Token = context.Request.Query["access_token"];

                    return Task.FromResult(0);
                }
            };
        });

For issuing token, you can use the openId Connect server packages like so:

        // Add a new middleware issuing access tokens.
        app.UseOpenIdConnectServer(options =>
        {
            options.Provider = new AuthenticationProvider();
            // Enable the authorization, logout, token and userinfo endpoints.
            //options.AuthorizationEndpointPath = "/connect/authorize";
            //options.LogoutEndpointPath = "/connect/logout";
            options.TokenEndpointPath = "/connect/token";
            //options.UserinfoEndpointPath = "/connect/userinfo";

            // Note: if you don't explicitly register a signing key, one is automatically generated and
            // persisted on the disk. If the key cannot be persisted, an exception is thrown.
            // 
            // On production, using a X.509 certificate stored in the machine store is recommended.
            // You can generate a self-signed certificate using Pluralsight's self-cert utility:
            // https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip
            // 
            // options.SigningCredentials.AddCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75");
            // 
            // Alternatively, you can also store the certificate as an embedded .pfx resource
            // directly in this assembly or in a file published alongside this project:
            // 
            // options.SigningCredentials.AddCertificate(
            //     assembly: typeof(Startup).GetTypeInfo().Assembly,
            //     resource: "Nancy.Server.Certificate.pfx",
            //     password: "Owin.Security.OpenIdConnect.Server");

            // Note: see AuthorizationController.cs for more
            // information concerning ApplicationCanDisplayErrors.
            options.ApplicationCanDisplayErrors = true // in dev only ...;
            options.AllowInsecureHttp = true // in dev only...;
        });

I have implemented a single page application with token based authentication implementation using the Aurelia front end framework and ASP.NET core. There is also a signal R persistent connection. However, I have not done any DB implementation. Code here: https://github.com/alexandre-spieser/AureliaAspNetCoreAuth

Pang
  • 9,564
  • 146
  • 81
  • 122
Darxtar
  • 2,022
  • 22
  • 21
1

Have a look at OpenIddict - it's a new project (at the time of writing) that makes it easy to configure the creation of JWT tokens and refresh tokens in ASP.NET 5. The validation of the tokens is handled by other software.

Assuming you use Identity with Entity Framework, the last line is what you'd add to your ConfigureServices method:

services.AddIdentity<ApplicationUser, ApplicationRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders()
    .AddOpenIddictCore<Application>(config => config.UseEntityFramework());

In Configure, you set up OpenIddict to serve JWT tokens:

app.UseOpenIddictCore(builder =>
{
    // tell openiddict you're wanting to use jwt tokens
    builder.Options.UseJwtTokens();
    // NOTE: for dev consumption only! for live, this is not encouraged!
    builder.Options.AllowInsecureHttp = true;
    builder.Options.ApplicationCanDisplayErrors = true;
});

You also configure the validation of tokens in Configure:

// use jwt bearer authentication
app.UseJwtBearerAuthentication(options =>
{
    options.AutomaticAuthenticate = true;
    options.AutomaticChallenge = true;
    options.RequireHttpsMetadata = false;
    options.Audience = "http://localhost:58292/";
    options.Authority = "http://localhost:58292/";
});

There are one or two other minor things, such as your DbContext needs to derive from OpenIddictContext.

You can see a full length explanation on this blog post: http://capesean.co.za/blog/asp-net-5-jwt-tokens/

A functioning demo is available at: https://github.com/capesean/openiddict-test

Sean
  • 14,359
  • 13
  • 74
  • 124