-
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
51168d3
commit cfb52fa
Showing
3 changed files
with
130 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
defmodule PowAssent.Strategy.AzureOAuth2 do | ||
@moduledoc """ | ||
Azure AD OAuth 2.0 strategy. | ||
## Usage | ||
config :my_app, :pow_assent, | ||
providers: | ||
[ | ||
azure: [ | ||
client_id: "REPLACE_WITH_CLIENT_ID", | ||
client_secret: "REPLACE_WITH_CLIENT_SECRET", | ||
strategy: PowAssent.Strategy.AzureOAuth2 | ||
] | ||
] | ||
A tenant id can be set to limit scope of users who can get access (defaults | ||
to "common"): | ||
config :my_app, :pow_assent, | ||
providers: | ||
[ | ||
azure: [ | ||
client_id: "REPLACE_WITH_CLIENT_ID", | ||
client_secret: "REPLACE_WITH_CLIENT_SECRET", | ||
strategy: PowAssent.Strategy.AzureOAuth2, | ||
tenant_id: "8eaef023-2b34-4da1-9baa-8bc8c9d6a490" | ||
] | ||
] | ||
## Setting up Azure AD | ||
Login to Azure, and set up a new application: | ||
https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code#register-your-application-with-your-ad-tenant | ||
* `client_id` is the "Application ID". | ||
* `client_secret` has to be created with a new key for the application. | ||
* The callback URL (http://localhost:4000/auth/azure/callback) should be | ||
added to Reply URL's for the application | ||
* "Sign in and read user profile" permission has to be enabled. | ||
""" | ||
use PowAssent.Strategy.OAuth2.Base | ||
|
||
@spec default_config(Keyword.t()) :: Keyword.t() | ||
def default_config(config) do | ||
tenant_id = Keyword.get(config, :tenant_id, "common") | ||
|
||
[ | ||
site: "https://login.microsoftonline.com", | ||
authorize_url: "/#{tenant_id}/oauth2/authorize", | ||
token_url: "/#{tenant_id}/oauth2/token", | ||
authorization_params: [response_mode: "query", response_type: "code"], | ||
get_user_fn: &get_user/2 | ||
] | ||
end | ||
|
||
@spec normalize(Client.t(), Keyword.t(), map()) :: {:ok, map()} | ||
def normalize(_client, _config, user) do | ||
{:ok, %{ | ||
"uid" => user["sub"], | ||
"name" => user["name"], | ||
"email" => user["email"], | ||
"first_name" => user["given_name"], | ||
"last_name" => user["family_name"]}} | ||
end | ||
|
||
@spec get_user(Keyword.t(), Client.t()) :: {:ok, map()} | ||
def get_user(_config, client) do | ||
user = | ||
client.token.other_params["id_token"] | ||
|> String.split(".") | ||
|> Enum.at(1) | ||
|> Base.decode64!(padding: false) | ||
|> Poison.decode!() | ||
|
||
{:ok, user} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
defmodule PowAssent.Strategy.AzureOAuth2 do | ||
use PowAssent.Test.Phoenix.ConnCase | ||
|
||
import OAuth2.TestHelpers | ||
alias PowAssent.Strategy.AzureOAuth2 | ||
|
||
@access_token "access_token" | ||
|
||
setup %{conn: conn} do | ||
bypass = Bypass.open() | ||
config = [site: bypass_server(bypass)] | ||
|
||
{:ok, conn: conn, config: config, bypass: bypass} | ||
end | ||
|
||
test "authorize_url/2", %{conn: conn, config: config} do | ||
assert {:ok, %{conn: _conn, url: url}} = AzureOAuth2.authorize_url(config, conn) | ||
assert url =~ "/common/oauth2/authorize?client_id=" | ||
|
||
config = Keyword.put(config, :tenant_id, "8eaef023-2b34-4da1-9baa-8bc8c9d6a490") | ||
assert {:ok, %{conn: _conn, url: url}} = AzureOAuth2.authorize_url(config, conn) | ||
assert url =~ "/8eaef023-2b34-4da1-9baa-8bc8c9d6a490/oauth2/authorize?client_id=" | ||
end | ||
|
||
describe "callback/2" do | ||
setup %{conn: conn, config: config, bypass: bypass} do | ||
params = %{"code" => "test", "redirect_uri" => "test"} | ||
|
||
{:ok, conn: conn, config: config, params: params, bypass: bypass} | ||
end | ||
|
||
test "normalizes data", %{conn: conn, config: config, params: params, bypass: bypass} do | ||
Bypass.expect_once(bypass, "POST", "/common/oauth2/token", fn conn -> | ||
id_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctODkwYS0yNzRhNzJhNzMwOWUiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83ZmU4MTQ0Ny1kYTU3LTQzODUtYmVjYi02ZGU1N2YyMTQ3N2UvIiwiaWF0IjoxMzg4NDQwODYzLCJuYmYiOjEzODg0NDA4NjMsImV4cCI6MTM4ODQ0NDc2MywidmVyIjoiMS4wIiwidGlkIjoiN2ZlODE0NDctZGE1Ny00Mzg1LWJlY2ItNmRlNTdmMjE0NzdlIiwib2lkIjoiNjgzODlhZTItNjJmYS00YjE4LTkxZmUtNTNkZDEwOWQ3NGY1IiwidXBuIjoiZnJhbmttQGNvbnRvc28uY29tIiwidW5pcXVlX25hbWUiOiJmcmFua21AY29udG9zby5jb20iLCJzdWIiOiJKV3ZZZENXUGhobHBTMVpzZjd5WVV4U2hVd3RVbTV5elBtd18talgzZkhZIiwiZmFtaWx5X25hbWUiOiJNaWxsZXIiLCJnaXZlbl9uYW1lIjoiRnJhbmsifQ." | ||
|
||
send_resp(conn, 200, Poison.encode!(%{access_token: @access_token, id_token: id_token})) | ||
end) | ||
|
||
expected = %{ | ||
"uid" => "JWvYdCWPhhlpS1Zsf7yYUxShUwtUm5yzPmw_-jX3fHY", | ||
"name" => "Frank Miller", | ||
"given_name" => "Frank", | ||
"family_name" => "Miller", | ||
"email" => "frank@contoso.com", | ||
} | ||
|
||
{:ok, %{user: user}} = AzureOAuth2.callback(config, conn, params) | ||
assert expected == user | ||
end | ||
end | ||
end |