View Source Kloak (kloak v0.2.1)

This module is as a simple wrapper around OAuth2, which helps building a valid configuration for authorizing with a Keycloak server.

Examples

Phoenix controller

@doc "Login controller action which redirects to Keycloak for authentication."
def login(conn, _) do
  with {:ok, client} <- Kloak.Client.new(),
       {:ok, nonce} <- Kloak.generate_nonce(),
       {:ok, redirect_url} <- Kloak.authorize_url(client, scope: "openid", state: nonce, redirect_uri: url(~p"/auth/callback")) do
    conn
    |> Kloak.put_oidc_state(nonce)
    |> redirect(external: redirect_url)
  end
end

@doc "Callback controller action which is called when a users is redirected from Keycloak."
def callback(conn, %{"code" => code, "state" => state}) do
  with {:ok, true} <- Kloak.verify_oidc_state(conn, state),
       {:ok, client} <- Kloak.Client.new(),
       {:ok, token} <- Kloak.get_token(client, code: code, redirect_uri: url(~p"/auth/callback")),
       {:ok, client} <- Kloak.Client.new(token: token),
       {:ok, user_information} <- Kloak.user_information(client) do
    # Do something with the user information
    IO.inspect(user_information)

    # Authentication was successful
    conn
    |> put_session(:token, token)
    |> put_flash(:info, gettext("You have successfully logged in."))
    |> redirect(to: ~p"/dashboard")
  end
end

Summary

Functions

Build the autorization URL, which is required in the authentication flow. This built URL is used for redirecting to Keycloak.

Delete the OpenID Connect state from the session of the current user.

Generate a unique, secure random nonce of the given size.

Try to get the access token from Keycloak with the given, preconfigured OAuth2.Client.

Put the OpenID Connect state into the session in order to be able to verify that an authentication is requested by the actual user.

Fetches the user information of the authenticated user from the Keycloak userinfo endpoint. The passed OAuth2.Client must be properly configured and authorized to perform this action.

Verify the OpenID Connect state from the redirect with the state stored in the session of the current user.

Functions

Link to this function

authorize_url(client, params \\ [])

View Source
@spec authorize_url(
  OAuth2.Client.t(),
  keyword()
) :: {:ok, binary()} | {:error, binary()}

Build the autorization URL, which is required in the authentication flow. This built URL is used for redirecting to Keycloak.

Examples

iex> authorize_url(%OAuth2.Client{...})
{:ok, "https://localhost:4000/..."}

iex> authorize_url(%OAuth2.Client{...})
{:error, "Building the authorization URL failed with an invalid URL"}

iex> authorize_url(%OAuth2.Client{...})
{:error, "Building the authorization URL failed with an unknown error"}
Link to this function

delete_oidc_state(conn, oidc_state_key \\ :oidc_state)

View Source
@spec delete_oidc_state(conn :: Plug.Conn.t(), oidc_state_key :: atom()) ::
  Plug.Conn.t()

Delete the OpenID Connect state from the session of the current user.

Examples

iex> delete_oidc_state(%Plug.Conn{})
%Plug.Conn{...}

iex> delete_oidc_state(%Plug.Conn{}, :extra_oidc_state_key)
%Plug.Conn{...}
Link to this function

generate_nonce(size \\ 32)

View Source
@spec generate_nonce(non_neg_integer()) :: {:ok, binary()} | {:error, binary()}

Generate a unique, secure random nonce of the given size.

Examples

iex> generate_nonce()
{:ok, "aas7d8ads8789asd7981aas7d8ads87d"}

iex> generate_nonce(8)
{:ok, "aas7d8ad"}

iex> generate_nonce(8)
{:error, "Generating nonce failed with an invalid result"}

iex> generate_nonce(nil)
{:error, "Generating nonce failed due to an invalid given size"}
Link to this function

get_token(client, params \\ [], headers \\ [], opts \\ [])

View Source
@spec get_token(OAuth2.Client.t(), keyword(), keyword(), keyword()) ::
  {:ok, OAuth2.AccessToken.t()} | {:error, binary()}

Try to get the access token from Keycloak with the given, preconfigured OAuth2.Client.

Examples

iex> get_token(%OAuth2.Client{...})
{:ok, %OAuth2.AccessToken{...}}

iex> get_token(%OAuth2.Client{...})
{:error, "Getting the access token from Keycloak failed"}
Link to this function

put_oidc_state(conn, oidc_state, oidc_state_key \\ :oidc_state)

View Source
@spec put_oidc_state(
  conn :: Plug.Conn.t(),
  oidc_state :: binary(),
  oidc_state_key :: atom()
) ::
  Plug.Conn.t()

Put the OpenID Connect state into the session in order to be able to verify that an authentication is requested by the actual user.

Examples

iex> put_oidc_state(%Plug.Conn{}, "somestate")
%Plug.Conn{...}

iex> put_oidc_state(%Plug.Conn{}, "somestate", :extra_oidc_state_key)
%Plug.Conn{...}
Link to this function

user_information(client)

View Source
@spec user_information(OAuth2.Client.t()) :: {:ok, map()} | {:error, binary()}

Fetches the user information of the authenticated user from the Keycloak userinfo endpoint. The passed OAuth2.Client must be properly configured and authorized to perform this action.

Examples

iex> user_information(valid_client)
{:ok, %{"given_name" => "John", ...}}

iex> user_information(invalid_client)
{:error, "Retriving user information from Keycloak failed with an error"}

iex> user_information(invalid_client)
{:error, "Retriving user information failed due to an invalid realm configuration"}
Link to this function

verify_oidc_state(conn, oidc_state, oidc_state_key \\ :oidc_state)

View Source
@spec verify_oidc_state(
  conn :: Plug.Conn.t(),
  oidc_state :: binary(),
  oidc_state_key :: atom()
) ::
  {:ok, boolean()} | {:error, binary()}

Verify the OpenID Connect state from the redirect with the state stored in the session of the current user.

Examples

iex> verify_oidc_state(%Plug.Conn{}, "statefromredirection")
{:ok, true}

iex> verify_oidc_state(%Plug.Conn{}, "invalidstatefromredirection")
{:ok, false}

iex> verify_oidc_state(%Plug.Conn{}, "somestate", :extra_oidc_state_key)
{:error, "Unable to retrieve OIDC state from session"}

iex> verify_oidc_state(%Plug.Conn{}, nil, nil)
{:error, "Unable to verify invalid OIDC state with invalid attributes"}