Skip to content

Latest commit

 

History

History

wai-middleware-webauthn

wai-middleware-webauthn

This is a WAI middleware which introduces a simple authentication mechanism based on Web Authentication API (WebAuthn).

Configuration

One easy way to configure the middleware is to use YAML.

import qualified Network.Wai.Middleware.WebAuthn as WebAuthn
import qualified Data.Yaml as Yaml
main = do
  config <- Yaml.decodeFileThrow "config.yaml"
  mid <- WebAuthn.mkMiddleware config
  ...
origin: "https://localhost:8080"
endpoint: "webauthn"
authorisedKeys:
  fumieval:
    credentialId: "0IMo2OFRmM903AGEP5/1u5eVGlcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
    publicKey: "pQECAyYgASFYICJwKPYkRYKWH6OIAjp+IDghFnl06S0iSGjxn/arBp0OIlggoJmTH1ZaVWCrn3A2b+wZx4/mVePRFowKujU5xXmafJY="

lib.js

This middleware exposes a JavaScript library in /lib.js:

You must import following scripts in order to make it work:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/cbor.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/base64js.min.js"></script>

Here's the pseudo-code describing the content of the library.

CredentialId = String -- Credential Id
CredentialPublicKey = String -- Public key
Credential =
  { credentialId : CredentialId
  , publicKey : CredentialPublicKey
  }

Token = String -- Token for authorisation
Identifier = String -- Human-readable identifier for a Credential

Endpoint = String -- The prefix of the middleware API e.g. "webauthn"
User = -- Information stored in the authenticator
  { id : String
  , name : String
  , displayName : String
  }

WebAuthnProxy : Endpoint ->
  { register : User -> Promise Credential
  -- Register a user to the authenticator and returns a credential if it's valid.
  -- Once verified, insert the Credential to the list of authorisedKeys into the configuraion.
  , verify : CredentialId -> Promise Token
  -- Verify a credential using the public key stored in the server.
  -- Returns a token if succeeds.
  , lookup : Identifier -> Promise CredentialId
  -- Find a CredentialId associated to the Identifier (provisional).
  }

Authorization

Whenever it receives a request containing Authentication: XXX, it checks if XXX is a valid token generated by verify. It replaces XXX by the associated identifier which can be extracted by requestIdentifier :: Request -> Maybe Identifier.