Our.Umbraco.AuthU

An Umbraco plugin to add an OAuth API endpoint to allow authenticated Members/Users via OAuth


Keywords
umbraco, oauth, oauth2
License
MIT
Install
Install-Package Our.Umbraco.AuthU -Version 2.0.0

Documentation

AuthU

AuthU is an add-on library to Umbraco providing a simplified OAuth2 endpoint from which you can authenticate Umbraco Members/Users and use it to protect your API controllers. Ideal for developing web/mobile apps.

Installation

Nuget

PM> Install-Package Our.Umbraco.AuthU

Configuration

In order to configure AuthU you will first of all need to create an Umbraco composer + compontent combination like so:

    public class AuthUConfigComponent : IComponent
    {
        public void Initialize()
        {
            // Configuration goes here
        }

        public void Terminate() { }
    }

    public class AuthUConfigComposer : ComponentComposer<AuthUConfigComponent>
    { }

From within the Initialize method, you can then configure your endpoint(s) via the OAuth.ConfigureEndpoint helper like so:

    ...
    OAuth.ConfigureEndpoint(...);
    ...

Basic Configuration

For the most basic OAuth implementation, the following minimal configuration is all that is needed:

    OAuth.ConfigureEndpoint("/oauth/token", new OAuthOptions {
        UserService = new UmbracoMembersOAuthUserService(),
        SymmetricKey = "856FECBA3B06519C8DDDBC80BB080553",
        AccessTokenLifeTime = 20, // Minutes
        AllowInsecureHttp = true // During development only
    });

This will create an endpoint at the path /oauth/token, authenticating requests against the Umbraco members store, issuing access tokens with a lifespan of 20 minutes.

Advanced Configuration

For a more advanced OAuth implementation, the following configuration shows all the supported options.

    OAuth.ConfigureEndpoint("realm", "/oauth/token", new OAuthOptions {
        UserService = new UmbracoMembersOAuthUserService(),
        SymmetricKey = "856FECBA3B06519C8DDDBC80BB080553",
        AccessTokenLifeTime = 20, // Minutes
        ClientStore = new UmbracoDbOAuthClientStore(),
        RefreshTokenStore = new UmbracoDbOAuthRefreshTokenStore(),
        RefreshTokenLifeTime = 1440, // Minutes (1 day)
        AllowedOrigin = "*",
        AllowInsecureHttp = true // During development only
    });

This will create an endpoint the same as the basic configuration with added support of refresh tokens and a client store.

Configuration Options

  • Realm : string
    [optional, default:"default"]
    A unique alias for the configuration, allowing you to configure multiple endpoints.
  • Path : string
    [optional, default:"/oauth/token"]
    The path of the endpoint (IMPORTANT! Be sure to add the base of the path to the umbracoReservedPaths app setting, ie ~/oauth/)
  • UserService : IOAuthUserService
    [optional, default:new UmbracoMembersOAuthUserService()]
    The service from which to validate authentication requests against. Out of the box AuthU comes with 2 implementations, UmbracoMembersOAuthUserService and UmbracoUsersOAuthUserService which authenticate against the Umbraco members and users store respectively. Custom sources can be configured by implementing the IOAuthUserService interface yourself.
  • SymmetricKey : string
    [required]
    A symmetric key used to sign the generated access tokens. Must be a string, 32 characters long, BASE64 encoded.
  • AccessTokenLifeTime : int
    [optional, default:20]
    Sets the lifespan, in minutes, of an access token before re-authentication is required. Should be short lived.
  • ClientStore : IOAuthClientStore
    [optional, default:null]
    Defines a store for OAuth client information. If not null, all authentication requests are required to pass valid client credentials in order for authentication to be successful. If null, client crendetials are not required to authenticate. Out of the box AuthU comes with 2 implementations, InMemoryOAuthClientStore which stores a fixed list of client credentials in memory and UmbracoDbOAuthClientStore which stores the client credentials in a custom database table in the Umbraco database (AuthU does not provide an api for creating clients, so you'll need to configure these manually in the database, or write your own CRUD layer). Alternative implementations can be configured by implementing the IOAuthClientStore interface.
  • RefreshTokenStore : IOAuthRefreshTokenStore
    [optional, default:null]
    Defines a store for OAuth refresh token information. If not null, authentication responses will include a refresh_token parameter which can be used to re-authenticate a user without needing to use their username / password credentials, allowing you to extend the lifespan of an access token. If null, refresh tokens will not be issued. Out of the box, AuthU comes with 1 implementation, UmbracoDbOAuthRefreshTokenStore, which stores the refresh tokens in a custom database table in the Umbraco database. Alternative implementations can be configured by implementing the IOAuthRefreshTokenStore interface.
  • RefreshTokenLifeTime : int
    [optional, default:1440]
    Sets the lifespan, in minutes, of a refresh token before it can no longer be used. Can be long lived. If a client store is configured, this will get overridden by the client settings.
  • AllowedOrigin : string
    [optional, default:"*"]
    Sets the allowed domain from which authentication requests can be made. If developing a web application, it is strongly recommended to set this to the domain from which your app is hosted at to prevent access from unwanted sources. If developing a mobile app, it can be set to wildcard "*" which will allow any source to access it, however it is strongly recommended you use a client store which requires a secret key to be passed. If a client store is configured, this will get overridden by the client settings. If you are managing CORS headers yourself and you don't want AuthU to set the allowed origins header for you, you will need to explicitly set this to null.
  • AllowInsecureHttp : bool
    [optional, default:false]
    Sets whether the api should allow requests over insecure HTTP. You'll probably want to set this to true during development, but it is strongly advised to disable this in the live environment.

Usage

Authenticating

With an endpoint configured, initial authentication can be performed by sending an POST request to the endpoints url with a body of content type application/x-www-form-urlencoded, containing the following key values:

  • grant_type = "password"
  • username = The users username
  • password = The users password
  • client_id = A valid client id (Only required if a client store is configured)
  • client_secret = A valid client secret (Only required if a client store is configured, and the client is "secure")
  • device_id = An optional device id to associate the token with, allowing login from multiple devices

Example (with client store and refresh token stores configured):

Request URL:
  POST https://mydomain.com/oauth/token
Request Headers:
  Content-Type: application/x-www-form-urlencoded
Request POST Body:
  grant_type=password&username=joebloggs&password=password1234&client_id=myclient&client_secret=myclientsecret&device_id=edfb6f01-2342-47f8-a5ee-a520969539d0
Response:
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1laWQiOiIxMDgxIiwidW5pcXVlX25hbWUiOiJtZW1iZXIiLCJyb2xlIjoiTWVtYmVyIiwicmVhbG0iOiJkZWZhdWx0IiwiZXhwIjoxNDg3NDk2NzM3LCJuYmYiOjE0ODc0OTU1Mzd9.9uiIxrPggvH5nyLbH4UKIL52V6l5mpOyJ26J12FkXvI",
  "token_type": "bearer",
  "expires_in": 1200,
  "refresh_token": "b3cc9c66b86340c5b743f2a7cec9d2f1"
}

A subsequent refresh token authentication request can be performed by sending a POST request to the endpoints url with a body of content type application/x-www-form-urlencoded, containing the following key values:

  • grant_type = "refresh_token"
  • refresh_token = The refresh token returned from the original authentication request
  • client_id = A valid client id (Only required if a client store is configured)
  • client_secret = A valid client secret (Only required if a client store is configured, and the client is "secure")
  • device_id = An optional device id to associate the token with, allowing login from multiple devices

Example (with client store and refresh token stores configured):

Request URL:
  POST https://mydomain.com/oauth/token
Request Headers:
  Content-Type: application/x-www-form-urlencoded
Request POST Body:
  grant_type=refresh_token&refresh_token=b3cc9c66b86340c5b743f2a7cec9d2f1&client_id=myclient&client_secret=myclientsecret&device_id=edfb6f01-2342-47f8-a5ee-a520969539d0
Response:
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1laWQiOiIxMDgxIiwidW5pcXVlX25hbWUiOiJtZW1iZXIiLCJyb2xlIjoiTWVtYmVyIiwicmVhbG0iOiJkZWZhdWx0IiwiZXhwIjoxNDg3NDk2NzM3LCJuYmYiOjE0ODc0OTU1Mzd9.9uiIxrPggvH5nyLbH4UKIL52V6l5mpOyJ26J12FkXvI",
  "token_type": "bearer",
  "expires_in": 1200,
  "refresh_token": "b3cc9c66b86340c5b743f2a7cec9d2f1"
}

If any authentication request fails, a status of 401 Unauthorized will be returned containing error information in a JSON object.

Protecting Your API Controllers

With your endpoint working and issuing tokens, you can protect your API controllers by adding the OAuth attribute to your classes, and then use the standard Authorize attribute to restrict action access as follows:

    [OAuth]
    public class MyApiController : UmbracoApiController
    {
        [HttpGet]
        [Authorize]
        public string HelloWorld()
        {
            return "Hello " + Members.GetCurrentMember()?.Name;
        }
    }

The OAuth attribute has a single optional parameter, Realm, which allows you to define which realm this controller is associated with. If a realm is configured, only access tokens that were generated from the associated endpoint will be valid.

Accessing Protected Actions

To access a protected action, an Authorization header should be added to the request with a value of Bearer {access_token} where {access_token} is the value of the access token requested from the authentication endpoint.

Example:

Request URL:
  POST https://mydomain.com/umbraco/api/myapi/helloworld
Request Headers:
  Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1laWQiOiIxMDgxIiwidW5pcXVlX25hbWUiOiJtZW1iZXIiLCJyb2xlIjoiTWVtYmVyIiwicmVhbG0iOiJkZWZhdWx0IiwiZXhwIjoxNDg3NDk2NzM3LCJuYmYiOjE0ODc0OTU1Mzd9.9uiIxrPggvH5nyLbH4UKIL52V6l5mpOyJ26J12FkXvI
Response:
"Hello Joe"

Using javascript

Presist and refreshing the token can be cumbersome. If you are using javascript you can use this small helper to handle and refreshing the token etc: https://github.com/tobbbe/tauth

Contributing To This Project

Anyone and everyone is welcome to contribute. Please take a moment to review the guidelines for contributing.

License

Copyright © 2018 Matt Brailsford, Outfield Digital Ltd

Licensed under the MIT License