/oauth_azure_activedirectory

Elixir Azure Active Directory microsoft Oauth 2.0 wrapper for authentication and authorized (code, access token and id token)

Primary LanguageElixirOtherNOASSERTION

Oauth Azure Activedirectory

Omniauth client for Azure Active Directory using Microsoft identity hybrid authorization code flow

https://hex.pm/packages/oauth_azure_activedirectory

CircleCI status Coverage Status Hex.pm version Hex.pm downloads

Installation

If available in Hex, the package can be installed by adding oauth_azure_activedirectory to your list of dependencies in mix.exs:

def deps do
  [
    {:oauth_azure_activedirectory, "~> 1.0.0"}
  ]
end

Configuration

config :oauth_azure_activedirectory, OauthAzureActivedirectory.Client,
  client_id: System.get_env("AZURE_CLIENT_ID"),
  client_secret: System.get_env("AZURE_CLIENT_SECRET"),
  tenant: System.get_env("AZURE_TENANT"),
  redirect_uri: "http://localhost:4000/auth/azureactivedirectory/callback",
  scope: "openid email profile",
  logout_redirect_url: "http://localhost:4000/users/logout"

Usage

# Add your routes, i.e.

scope "/auth", MyAppWeb do
  pipe_through :browser

  get "/:provider", AuthController, :authorize
  post "/:provider/callback", AuthController, :callback
end

# Adjust your controller actions for authorization and the callback

defmodule MyAppWeb.AuthController do
  use MyAppWeb, :controller

  alias MyApp.User
  alias OauthAzureActivedirectory.Client

  def authorize(conn, _params) do
    redirect conn, external: Client.authorize_url!
  end

  def callback(conn, _params) do
    {:ok, payload} = Client.callback_params(conn)
    email = payload["email"]
    case User.find_or_create(email) do
      {:ok, user} ->
        conn
        |> put_flash(:success, "Successfully authenticated.")
        |> put_session(:current_user, user)
        |> Guardian.Plug.sign_in(user) # if you are using Guardian
        |> redirect(to: "/")
      {:error, reason} ->
        conn
        |> put_flash(:error, reason)
        |> redirect(to: "/")
    end
  end
end

# Add a method to User model to process the data in JWT
def find_or_create(email) do
  query = from u in User, where: u.email == ^email
  case Repo.all(query) do
    [user] -> {:ok, user}
    [] -> create_user(%{email: email, password: SecureRandom.base64(16)})
  end
end

Information

Client.authorize_url!
# will generate a url similar to 
# https://login.microsoftonline.com/9b9eff0c-3e5t-1q2w-3e4r-fe98afcd0299/oauth2/v2.0/authorize?client_id=984ebc2a-4ft5-8ea2-0000-59e43ccd614e&nonce=e22d15fa-853f-4d6a-9215-e2a206f48581&provider=azureactivedirectory&redirect_uri=http%3A%2F%2Flocalhost%3A4000%2Fauth%2Fazureactivedirectory%2Fcallback&response_mode=form_post&response_type=code+id_token

{:ok, payload} = Client.callback_params(conn)
# On a successful callback, jwt variable will return something like below.

%{
  exp: 1515604135,
  family_name: "Allen",
  given_name: "Otis",
  name: "Otis Allen",
  nonce: "e22d15fa-853f-4d6a-9215-e2a206f48581",
  email: "otis.allen@company.com",
  uti: "heXGJdeefedrzEuc1bQNAA",
  ver: "2.0"
  ...
}

# For all attributes, see claims_supported in https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

Useful links

Azure AD token reference

Microsoft OpenID discovery document.

Trusted CA certificates for Azure Cloud Services