This is an Elixir Plug for adding basic authentication into an application.
Add the package as a dependency in your Elixir project using something along the lines of:
defp deps do
[{:basic_auth, "~> 1.0.0"}]
end
Add into the top of a controller, or into a router pipeline a plug declaration like:
plug BasicAuth, realm: "Admin Area", username: "admin", password: "secret"
Easy as that!
The above example is great to get going with basic auth, and depending on your use case, might be everything you need. Generally speaking, we don't want to store credentials in version control for security reasons.
Instead of passing credentials into the plug directly, we can pass a use_config
option to the plug to tell
it we want to use some variables we've stored in config files:
# inside router or controller file
plug BasicAuth, Application.get_env(:the_app, :basic_auth)
And then we can setup some configuration using something like the following:
# dev.exs, test.exs
config :the_app, :basic_auth, [
realm: "Admin Area",
username: "sample",
password: "sample"
]
# config/prod.secret.exs
config :the_app, :basic_auth, [
realm: "Admin Area",
username: System.get_env("BASIC_AUTH_USER"),
password: System.get_env("BASIC_AUTH_PASSWORD")
]
The example above for config/prod.exs
makes use of system ENV vars. You could use String objects
if your config/prod.exs
is outside of version control, but for environments like Heroku, it's easier
to use ENV vars for storing configuration.
If you're storing credentials within configuration files, we can reuse them within our test files
directly using snippets like Application.get_env(:basic_auth)[:username]
.
Any controller that makes use of basic authentication, will need an additional header injected into the connection in order for your tests to continue to work. The following is a brief snippet of how to get started. There is a more detailed blog post that explains a bit more about what needs to be done.
At the top of my controller test I have something that looks like:
@username Application.get_env(:the_app, :basic_auth)[:username]
@password Application.get_env(:the_app, :basic_auth)[:password]
defp using_basic_auth(conn, username, password) do
header_content = "Basic " <> Base.encode64("#{username}:#{password}")
conn |> put_req_header("authorization", header_content)
end
Then for any tests, I can simply pipe in this helper method to the connection process:
test "GET / successfully renders when basic auth credentials supplied" do
conn = conn
|> using_basic_auth(@username, @password)
|> get("/admin/users")
assert html_response(conn, 200) =~ "Users"
end
And a test case without basic auth for completeness:
test "GET / without basic auth credentials prevents access" do
conn = conn
|> get("/admin/users")
assert response(conn, 401) =~ "401 Unauthorized"
end