Sphyrnidae Common Library  2.0.1
Shared Utilities/Library
Authentication

Overview

Authentication is done without the need for an overly complex architecture containing OAuth. Instead, this package relies on the presense of a JWT. It will be up to your application to handle user authentication/registration, and that endpoint should return a JWT in it's response. This JWT should be saved by the client and provided in every subsequent HTTP request.

Interface: IIdentityHelper

Default Implementation / Mock: IdentityHelper

Setup

All methods needed to act on an Identity object are provided in IIdentityHelper. The registered implementation is to use IdentityHelper with the type of BasicIdentity. You can always extend the Identity by inheriting and adding custom properties. If you do extend the identity, you should register (in startup.cs) the new implementation. You can additionally modify the default behaviors present in the IdentityHelper implementation (or provide a different implementation class).

Please note that the IIdentityHelper will generally work off a BaseIdentity. You can send anything that inherits from BaseIdentity into this class, and if your implementation returns an inherited class, you will need to cast the result to your inherited type.

JWT Creation

To generate a JWT, you should first generate an Identity object. Eg. new() up an inherited class from BaseIdentity You can ignore the 'Expires' attribute, as that will be set in the next step. All roles that the user has will be populated into the Roles property.

After you have created your identity object, you can convert this to a JWT via the ToJwt() method on the IIdentityHelper interface. This will set the Expires property based on ExpirationMinutes. The standard implementation utilizes serialization and encryption to convert your identity object to a string.

The JWT can then be placed into your HTTP response.

JWT Consumption

The JWT is a self-contained encrypted object which contains all identity information needed for Authentication and Authorization. The client will be unable to parse/alter the JWT, as this is only a server-side ability. When the client makes an HTTP request to your application, it should place the JWT in the Authorization header. If you need to return the actual identity object (or any of it's properties), you will need to convert the JWT back into the Identity object. This is done using the Current property on the IIdentityHelper interface. The interface implementation should be registered as request-scoped, meaning that the object will be cached for the entirety of the request.

There is also the GetIdentity() method, but this is not cached and will always do the full decryption/deserialization. Behind the scenes, the Current property will call the GetIdentity() method if it does not already have the identity. If you need to convert a JWT that is not the Current one, you can pass that JWT directly to the GetIdentity() method. By default, the retrieval of the JWT comes from the JwtHeader.

Authentication and Authorization

By utilizing the API Pipeline, authentication and authorization will automatically occur in your API request. There is nothing else you have to do - if you have guarded your API endpoint with an Authentication attribute, then you can be assured that only requests that are authenticated and authroized make it through to your API. For more information on this setup, please refer to the Authentication section of the API documentation.

Refresh Token

A common scenario is to give a JWT a short expiration time (eg. 20 minutes), but this time should be reset on every interaction with your application. To help accomplish this, the JwtMiddleware component will automatically put a refreshed JWT (with a new expiration date) into the HTTP response (will utilize the same Authorization header.) This middleware component is included in the default pipeline, but can be removed or altered per configuration. If you would like to change any properties of the JWT/Identity, this can be done by 'setting' a new Current Identity.

Please note that this implementation does leave a security hole open in your application. If an attacker is able to steal a JWT, it will be valid only until it expires. However, if you are always sending a refresh token, the attacker can continue to use a refreshed token indefinitely. It is our recommendation that you have a JWT invalidation scheme. Eg. Update (replace) the JwtMiddleware component with another component which will lookup validity of a user's logon.

Examples

    IIdentityHelper helper; // Should be injected

    // Building new identity and setting the JWT
    var identity = new BasicIdentity {
        Id = 1,
        Username = "MyUsername",
        FirstName = "First",
        LastName = "Last",
        Email = "me@foo.com",
        Roles = new List<string> {"role1","role2","role3"}
    };
    var jwt = helper.ToJwt(identity);

    // Take any jwt and convert it to an identity
    identity = helper.GetIdentity(jwt);

    // Getting the identity back from the request
    identity = helper.Current;

    // See if user has a given role
    var hasRole = identity.SearchableRoles.Has("role1"); // true
    hasRole = identity.SearchableRoles.Has("role18"); // false