plug_signature v0.7.0 PlugSignature View Source

Server side implementation of IETF HTTP signature draft (https://tools.ietf.org/html/draft-cavage-http-signatures-11), as a reusable Plug.

Supports the following algorithms:

  • "hs2019", using ECDSA, RSASSA-PSS or HMAC (all with SHA-512)
  • "rsa-sha256", using RSASSA-PKCS1-v1_5
  • "ecdsa-sha256"
  • "hmac-sha256"
  • "rsa-sha1", using RSASSA-PKCS1-v1_5

ECDSA signatures with hs2019/ecdsa-sha256 are accepted in ASN.1 or raw r/s format, for maximum interoperability.

Signature validity time window

Requests signed according to the "hs2019" algorithm should include a 'created' and/or 'expires' timestamp. When present in the Authorization header, the 'created' parameter is checked against the configured validity (unless it is set to :infinity) and the 'expires' parameter is checked against the current time.

At least one of these parameters should be included in the signature calculation, through the respecive pseudo-header. The validity check based on 'created' and/or 'expires' takes place regardless of whether the parameters were signed.

For legacy algorithms, signature validity should be checked using the HTTP 'Date' header. This happens for all algorithms (including "hs2019") if and only if when the header value is included in the signature.

Signing the request body

The HTTP 'Digest' header can be used to protect the integrity of the request body and include it in the signature. The PlugSignature Plug treats the 'Digest' header as any other header, i.e. it verifies the integrity of the header value if included in the signature, but it does not verify the integrity of the request body itself.

Use a Plug such as PlugBodyDigest to handle the processing of the 'Digest' header.

Options

  • :callback_module (mandatory) - the name of a callback module implementing the PlugSignature.Callback behaviour; this module must implement the PlugSignature.Callback.client_lookup/3 callback

  • :algorithms - the signature algorithms, as defined in the IETF specification; a list containing one or more of:

    • "hs2019" (default)

    Legacy algorithms:

    • "rsa-sha256"
    • "rsa-sha1"
    • "ecdsa-sha256"
    • "hmac-sha256"

    The first algorithm in the list is considered the default algorithm: if a client does not specify an algorithm the request is assumed to be signed using this algorithm

  • :headers - the minimum set of (pseudo-)headers that need to be signed; defaults to the request timestamp, taken from the 'created' signature parameter or the HTTP 'Date' header, depending on the selected algorithm

  • :validity - a Range defining the timeframe (in seconds) after signing during which the signature is considered valid; set to :infinity to disable, relying solely on the :expires parameter; defaults to -300..30, meaning a signature can be up to 5 minutes old, or up to 30 seconds in the future

  • :legacy - a keyword list, used to override the :headers and/or :validity options for legacy algorithms (those other than "hs2019"); this may be necessary when these options are set to values not supported by the legacy algorithms; see the examples below

  • :on_success - an optional callback for updating the Plug.Conn state upon success; possible values include:

    • nil (the default) - do nothing
    • {PlugSignature, :assign_client, [key]} - assign the client, as returned by the PlugSignature.Callback.client_lookup/3 callback, in the Plug.Conn struct to the specified key
    • {m, f, a} - call the function identified by the atom f in module m; the function receives the current Plug.Conn struct and the client returned by the c:PlugSignature.Callbacks.client_lookup/3 callback, along with any additional parameters in the list a, and is expected to return the updated Plug.Conn struct
  • :on_failure - an optional callback for updating the Plug.Conn state upon failure; possible values include:

    • {PlugSignature, :failure, []} (the default) - halt the connection with an appropriate response; see failure/3 below
    • {m, f, a} - call the function identified by the atom f in module m; the function receives the current Plug.Conn struct, the error reason (see t:error_reason/0),the selected algorithm (a string) and a list of required headers (strings, for possible use in a 'WWW-Authenticate' response header), along with any additional parameters in the list a; it is expected to return the updated Plug.Conn struct; see the implementation of failure/4 for an example
    • nil - do nothing

Examples

# Minimal example relying on defaults: "hs2019" algorithm only, with
# minimal "(created)" headers and default validity:
plug PlugSignature, callback_module: MyApp.SignatureAuth

# More realistic example with custom header configuration; "hs2019"
# only:
plug PlugSignature,
  callback_module: MyApp.SignatureAuth,
  headers: "(request-target) (created) host digest"

# Using legacy algorithms only, with custom header set:
plug PlugSignature,
  callback_module: MyApp.SignatureAuth,
  algorithms: ["ecdsa-sha256", "rsa-sha256"],
  headers: "(request-target) date host"

# Mix of "hs2019" and legacy algorithms, using 'expires' rather than
# 'created' to verify validity for "hs2019"; the `:legacy` option is
# necessary since the `:headers` and `:validity` values are not valid
# for legacy algoritms:
plug PlugSignature,
  callback_module: MyApp.SignatureAuth,
  headers: "(request-target) (expires) host digest",
  validity: :infinity,
  legacy: [
    headers: "(request-target) date host digest",
    validity: -300..30,
  ]

Link to this section Summary

Link to this section Functions

Link to this function

assign_client(conn, client, field_name)

View Source
assign_client(Plug.Conn.t(), any(), atom()) :: Plug.Conn.t()

Success function that assigns the authenticated client under the specified key in the Plug.Conn struct.

Link to this function

failure(conn, reason, algorithm, headers)

View Source
failure(Plug.Conn.t(), String.t(), String.t(), String.t()) :: Plug.Conn.t()

The default failure function.

It logs the failure reason, returns a 401 'Unauthorized' response with a 'WWW-Authenticate' response header listing the supported algorithms, and halts the connection.

Link to this function

verify_signature_expiry(signature_opts)

View Source
Link to this function

verify_signature_timestamp(signature_opts, validity \\ -300..30)

View Source