apiac_auth_basic v1.0.0 APIacAuthBasic View Source

An APIac.Authenticator plug for API authentication using the HTTP Basic scheme

The HTTP Basic scheme simply consists in transmitting a client and its password in the Authorization HTTP header. It is base64-encoded:

GET /api/accounts HTTP/1.1
Host: example.com
Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9wYXNzd29yZA==
Accept: */*

The decoded value of Y2xpZW50X2lkOmNsaWVudF9wYXNzd29yZA== is client_id:client_password

This scheme is also sometimes called APIKey by some API managers.

Security considerations

The password is transmitted in cleartext form (base64 is not a encryption scheme). Therefore, you should only use this scheme on encrypted connections (HTTPS).

Plug options

  • realm: a mandatory String.t that conforms to the HTTP quoted-string syntax, however without the surrounding quotes (which will be added automatically when needed). Defaults to default_realm
  • callback: a function that will return the password of a client. When a callback is configured, it takes precedence over the clients in the config files, which will not be used. The returned value can be:

    • A cleartext password (String.t)
    • An Expwd.Hashed{} (hashed password)
    • nil if the client is not known
  • set_error_response: function called when authentication failed. Defaults to APIacAuthBasic.send_error_response/3
  • error_response_verbosity: one of :debug, :normal or :minimal. Defaults to :normal

Application configuration

{client_id, client_secret} pairs can be configured in you application configuration files. There will be compiled at compile time. If you need runtime configurability, use the callback option instead.

Storing cleartext password requires special care, for instance: using *.secret.exs files, encrypted storage of these config files, etc. Consider using hashed password instead, such as %Expwd.Hashed{}.

Pairs a to be set separately for each realm in the clients key, as following:

config :apiac_auth_basic,
  clients: %{
    # using Expwd Hashed portable password
    "realm_a" => [
      {"client_1", "expwd:sha256:lYOmCIZUR603rPiIN0agzBHFyZDw9xEtETfbe6Q1ubU"},
      {"client_2", "expwd:sha256:mnAWHn1tSHEOCj6sMDIrB9BTRuD4yZkiLbjx9x2i3ug"},
      {"client_3", "expwd:sha256:9RYrMJSmXJSN4CSJZtOX0Xs+vP94meTaSzGc+oFcwqM"},
      {"client_4", "expwd:sha256:aCL154jd8bNw868cbsCUw3skHun1n6fGYhBiITSmREw"},
      {"client_5", "expwd:sha256:xSE6MkeC+gW7R/lEZKxsWGDs1MlqEV4u693fCBNlV4g"}
    ],
    "realm_b" => [
      {"client_1", "expwd:sha256:lYOmCIZUR603rPiIN0agzBHFyZDw9xEtETfbe6Q1ubU"}
    ],
    # UNSAFE: cleartext passwords set directly in the config file
    "realm_c" => [
      {"client_6", "cleartext password"},
      {"client_7", "cleartext password again"}
    ]
  }

Link to this section Summary

Types

The callback function returns an Expwd.Hashed.t or a client_secret (String.t) so as to prevent developers to unsecurely compare passwords.

Functions

Plug pipeline callback

Plug initialization callback

Saves failure in a Plug.Conn.t()'s private field and returns the conn

Sets the HTTP WWW-authenticate header when no such a scheme is used for authentication.

Link to this section Types

Link to this type

callback_fun()

View Source
callback_fun() ::
  (APIac.realm(), APIac.client() -> Expwd.Hashed.t() | client_secret() | nil)

The callback function returns an Expwd.Hashed.t or a client_secret (String.t) so as to prevent developers to unsecurely compare passwords.

Return nil if the client could not be gound for this realm

Link to this type

client_secret()

View Source
client_secret() :: String.t()

Link to this section Functions

Plug pipeline callback

Link to this function

extract_credentials(conn, opts)

View Source

APIac.Authenticator credential extractor callback

Returns the credentials under the form {client_id, client_secret} where both variables are binaries

Plug initialization callback

Link to this function

save_authentication_failure_response(conn, error, opts)

View Source
save_authentication_failure_response(
  Plug.Conn.t(),
  %APIac.Authenticator.Unauthorized{
    __exception__: term(),
    authenticator: term(),
    reason: term()
  },
  any()
) :: Plug.Conn.t()

Saves failure in a Plug.Conn.t()'s private field and returns the conn

See the APIac.AuthFailureResponseData module for more information.

Link to this function

send_error_response(conn, error, opts)

View Source

Implementation of the APIac.Authenticator callback

Verbosity

The following elements in the HTTP response are set depending on the value of the :error_response_verbosity option:

Error response verbosityHTTP StatusHeadersBody
:debugUnauthorized (401)WWW-Authenticate with Basic scheme and realm paramAPIac.Authenticator.Unauthorized exception's message
:normalUnauthorized (401)WWW-Authenticate with Basic scheme and realm param
:minimalUnauthorized (401)

Note: the behaviour when the verbosity is :minimal may not be conformant to the HTTP specification as at least one scheme should be returned in the WWW-Authenticate header.

Link to this function

set_WWWauthenticate_header(conn, error, opts)

View Source
set_WWWauthenticate_header(
  Plug.Conn.t(),
  %APIac.Authenticator.Unauthorized{
    __exception__: term(),
    authenticator: term(),
    reason: term()
  },
  any()
) :: Plug.Conn.t()

Sets the HTTP WWW-authenticate header when no such a scheme is used for authentication.

Sets the HTTP WWW-Authenticate header with the Basic scheme and the realm name, when the Basic scheme was not used in the request. When this scheme is used in the request, response will be sent by Elixir.APIacAuthBasic.send_error_response/3. This allows advertising that the Basic scheme is available, without stopping the plug pipeline.

Raises a exception when the error response verbosity is set to :minimal since it does not set the WWW-Authenticate header.

Link to this function

validate_credentials(conn, arg, opts)

View Source

APIac.Authenticator credential validator callback