Skip to content

Authentication design

Dominic Monroe edited this page Apr 26, 2019 · 17 revisions

yada authentication

Introduction

Objective

To establish an improved design for yada’s authentication/authorization, which can be validated in the open prior to working on implementation.

Background

Feedback from some projects using 1.3-alpha has led us to think that the new authentication design needs further thought.

Design goals

Dimensions

DMC: I think there’s a few dimensions here, listed below

  • Interaction behavior - how do multiple authentications interact?

    • merging them together can cause "ghost" entities which are mixes of multiple ones

    • Running only one can be confusing to explain why yours didn’t run

      • You also have to either decide to run in linear order (like a sequence) or whether to do provide a lookup-style match which only fires one

        • If you have predicates separate from action, yada can run all predicates meaning that all println checks get a chance to run.

  • Extensibility support - How do you define new authenticators?

    • They should be inspectable to find out:

      • What auth is supported on this resource?

      • e.g. for cookies, what cookie name must be used

      • e.g. for basic, what realms are available

    • It should be possible to support cookie/static authentication under the same key. This allows e.g. the authzn middleware to identify that authentication is present and activate the default authzn. Currently the default authzn doesn’t work for cookies.

    • Custom header support: Bearer tokens need fine-grained control over the headers which are set in response to failure in order to set the correct WWW-Authenticate error to indicate what failure has happened - which comes from a defined set.

Design

Note
MAL: This is a write-up of my current proposed straw-man design

Authenticators

A yada resource declares a collection of authenticators.

{:authenticators [a b c]}

Declaration

Canonically, an authenticator is declared as a map with an optional :type entry.

Example 1. An authenticator
{
:type :my/authenticator
:authenticate (fn [ctx ] …)
}

The :type SHOULD be a keyword. Built-in types lists the types built-in to yada.

Table 1. Built-in types
type HTTP Authentication Scheme

:yada.http-auth-schemes.basic

Basic

:yada.http-auth-schemes.bearer

Bearer

A multi-method is used to dispatch on the :type value of the authenticator. The multimethod is called with ctx, authenticator and authorization (the parsed Authorization header). If there is no Authorization header, this argument is nil.

Nil :type

If the :type entry is missing or nil, the :authenticate entry must be present, being a function of 2-arity.

{:authenticate (fn [ctx authenticator])}

Returned values

The authenticator is called at runtime, returning a value.

Only one authenticator is allowed to succeed. Authenticators are tried in order until one succeeds, or there are no authenticators remaining.

An authenticator’s returned value is interpretted as follows:

nil

the authenticator does not succeed, defering to the next authenticator in the collection

false

the authenticator matches and does not add anything to the context

context

the authenticator succeeds and alters the context

truthy

the authenticator succeeds and associates the truthy value to the :authentication key of the yada context.

Additionally, the authenticator is bound to the :authenticator key of the yada context. Response functions can determine the authenticator type by (get-in ctx [:authenticator :type]).

DMC: I wonder if there’s a use case for augmenting the context from an authenticator but without authenticating? Can’t think of anything right now, but I want to note the limitation of the model

Authenticator protocol

An authenticator may be a 2-arity function that corresponds to the following authenticator:

{:authenticator f}

This is achieved by use of a Clojure protocol. This also means other user-provided records can be used in place of maps, if required.

HTTP Basic Authentication

The defmethod for :yada.http-auth-schemes.basic will extract the user and password encoded with base64 encoding from the Authorization header and expect the map to contain a 3-arity function for it to call.

Note
How do HTTP Authentication authenticators issue WWW-Authenticate challenges?

DMC: Can you clarify when you would issue a challenge? Is it when none of the authenticators match?

Authorizers

Authorizers are functions that take the yada context and can veto the request. An authorizer may be a clojure spec, for example.

Clone this wiki locally
You can’t perform that action at this time.