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 thePlugSignature.Callback
behaviour; this module must implement thePlugSignature.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
- aRange
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 thePlug.Conn
state upon success; possible values include:nil
(the default) - do nothing{PlugSignature, :assign_client, [key]}
- assign the client, as returned by thePlugSignature.Callback.client_lookup/3
callback, in thePlug.Conn
struct to the specified key{m, f, a}
- call the function identified by the atomf
in modulem
; the function receives the currentPlug.Conn
struct and theclient
returned by thec:PlugSignature.Callbacks.client_lookup/3
callback, along with any additional parameters in the lista
, and is expected to return the updatedPlug.Conn
struct
:on_failure
- an optional callback for updating thePlug.Conn
state upon failure; possible values include:{PlugSignature, :failure, []}
(the default) - halt the connection with an appropriate response; seefailure/3
below{m, f, a}
- call the function identified by the atomf
in modulem
; the function receives the currentPlug.Conn
struct, the error reason (seet: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 lista
; it is expected to return the updatedPlug.Conn
struct; see the implementation offailure/4
for an examplenil
- 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
assign_client(conn, client, field_name)
View Sourceassign_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.
failure(conn, reason, algorithm, headers)
View Sourcefailure(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.