View Source Oasis.Plug.HMACAuth (oasis v0.5.1)

Functionality for providing an HMAC HTTP authentication.

STATEMENT: Currently there is no standard HMAC authentication definition in the OpenAPI v3.1.0 specification, we implement this function to add an hmac-<algorithm> as the corresponding scheme field to http type of the security scheme, thanks for some public HMAC services or API design as references:

It is recommended to only use this module in production if SSL is enabled and enforced.

usage

Usage

As any other Plug, we can use the hmac_auth/2 plug:

# lib/pre_handler.ex
import Oasis.Plug.HMACAuth

plug(
  :hmac_auth,
  signed_headers: "x-oasis-date;host",
  algorithm: :sha256,
  security: Oasis.Gen.HMACAuth
)

# lib/oasis/gen/hmac_auth.ex
defmodule Oasis.Gen.HMACAuth do
  @behaviour Oasis.HMACToken

  @impl true
  def crypto_config(_conn, _options, _credential) do
    # return `Oasis.HMACToken.Crypto` struct in your preferred way
    %Oasis.HMACToken.Crypto{
      credential: "client id",
      secret: "hmac secret",
    }
  end
end

Or directly plug Oasis.Plug.HMACAuth:

# lib/pre_handler.ex
plug(
  Oasis.Plug.HMACAuth,
  signed_headers: "x-oasis-date;host",
  algorithm: :sha256,
  security: Oasis.Gen.HMACAuth
)

# lib/oasis/gen/HMAC_auth.ex
defmodule Oasis.Gen.HMACAuth do
  @behaviour Oasis.HMACToken

  @impl true
  def crypto_config(_conn, _options, _credential) do
    ...
  end
end

Let's take an example from a YAML specification, we apply a global security to all operation objects:

openapi: 3.1.0

components:
  securitySchemes:
    HMACAuth: # arbitrary name for the security scheme
      type: http
      scheme: hmac-sha256
      x-oasis-signed-headers: x-oasis-date;host
      x-oasis-name-space: MyAuth

security:
  - HMACAuth: []

The above arbitrary name "HMACAuth" for the security scheme will be transferred into a generated module (see the above mentioned "Oasis.Gen.HMACAuth" module) to provide the required crypto-related configuration, and use it as the value to the :security option of hmac_auth/2.

By default, the generated "HMACAuth" module will inherit the module name space in order from the paths object, and then the operation object if they defined an x-oasis-name-space field, or defaults to Oasis.Gen if there are no any specification defined. As an option, we can add an x-oasis-name-space field as a specification extension of the security scheme object to override the generated module's name space, meanwhile, the optional --name-space argument to the mix oas.gen.plug command line is in the highest priority to set the name space of the all generated modules.

components:
  securitySchemes:
    HMACAuth: # arbitrary name for the security scheme
      type: http
      scheme: hmac-sha256
      x-oasis-signed-headers: x-oasis-date;host
      x-oasis-name-space: MyAuth

In the above example, the final generated module name of "HMACAuth" is "MyAuth.HMACAuth" when there is no --name-space argument of mix task input.

After we define the HMAC authentication into the specification, then run mix oas.gen.plug task with this spec file (via --file argument), there will generate the above similar code to the related module file as long as it does not exist, it also follows the name space definition to that module, and the generation does not override it once the file existed, we need to further edit this file to provide a crypto-related configuration in your preferred way.

If we need a customization to verify the HMAC token, we can implement a callback function Oasis.HMACToken.verify/3 to this scenario.

# lib/hmac_auth.ex
defmodule HMACAuth do
  @behaviour Oasis.HMACToken

  @impl true
  def crypto_config(conn, options, credential) do
    ...
  end

  @impl true
  def verify(conn, token, options) do
    # write your rules to verify the token,
    # and return the expected results in:
    #   {:ok, token}, verified
    #   {:error, :expired}, expired token
    #   {:error, :invalid_token}, invalid token
  end
end

supported-algorithms

Supported Algorithms

Here we use :crypto.mac/4 to compute a MAC of type :hmac from data, the completed supported algorithms can be found in the hash algorithm type of :crypto, see Erlang/OTP crypto user's guide - HMAC for more details.

Link to this section Summary

Functions

High-level usage of HMAC HTTP authentication.

Parses the request token from HMAC HTTP authentication.

Link to this section Types

@type algorithm() ::
  :sha
  | :sha224
  | :sha256
  | :sha384
  | :sha512
  | :sha3_224
  | :sha3_256
  | :sha3_384
  | :sha3_512
  | :blake2b
  | :blake2s
  | :md4
  | :md5
  | :ripemd160
@type opts() :: [
  algorithm: algorithm(),
  security: module(),
  signed_headers: String.t()
]

Link to this section Functions

@spec hmac_auth(conn :: Plug.Conn.t(), opts :: opts()) :: Plug.Conn.t()

High-level usage of HMAC HTTP authentication.

See the module docs for examples.

options

Options

  • :algorithm, required, see Algorithms Supported for details.
  • :security, required, a module be with Oasis.HMACToken behaviour.
  • :signed_headers, required, defines HTTP request headers added to the signature, the provided headers are required in the request, and both in client/server side will use them into signature in the explicit definition order. (e.g., "x-oasis-date;host")
Link to this function

parse_hmac_auth(conn, algorithm)

View Source
@spec parse_hmac_auth(conn :: Plug.Conn.t(), algorithm :: algorithm()) ::
  {:ok, Oasis.HMACToken.token()} | {:error, :header_mismatch}

Parses the request token from HMAC HTTP authentication.