Update Okta IDP adapter in portal (#3647)

Why:

* After reviewing the Okta docs closer, in order for an OAuth token to
have Okta API scopes attached to it, the Okta org authorization server
must be used, not a custom authorization server (which includes the
'default' authorization server). This means that the OAuth Authorization
URI that was previously being asked for in the Okta Adapter form won't
work for IDP sync to Firezone. This commit updates the form to accept
the Okta Account Domain (i.e. `<company>.okta.com`)
This commit is contained in:
Brian Manifold
2024-02-14 13:42:39 -05:00
committed by GitHub
parent e47c1766bf
commit 1939b9c3f9
9 changed files with 78 additions and 61 deletions

View File

@@ -15,7 +15,7 @@ defmodule Domain.Auth.Adapters.Okta.Settings do
field :client_id, :string
field :client_secret, :string
field :discovery_document_uri, :string
field :oauth_uri, :string
field :okta_account_domain, :string
field :api_base_url, :string
end

View File

@@ -7,7 +7,7 @@ defmodule Domain.Auth.Adapters.Okta.Settings.Changeset do
response_type
client_id client_secret
discovery_document_uri
oauth_uri
okta_account_domain
api_base_url]a
def changeset(%Settings{} = settings, attrs) do

View File

@@ -58,7 +58,7 @@ defmodule Domain.Auth.Adapters.OktaTest do
client_secret: ["can't be blank"],
discovery_document_uri: ["can't be blank"],
api_base_url: ["can't be blank"],
oauth_uri: ["can't be blank"]
okta_account_domain: ["can't be blank"]
}
}
end
@@ -67,7 +67,7 @@ defmodule Domain.Auth.Adapters.OktaTest do
account = Fixtures.Accounts.create_account()
bypass = Domain.Mocks.OpenIDConnect.discovery_document_server()
discovery_document_url = "http://localhost:#{bypass.port}/.well-known/openid-configuration"
oauth_url = "http://localhost:#{bypass.port}/.well-known/oauth-authorization-server"
okta_account_domain = "http://localhost:#{bypass.port}"
api_base_url = "http://localhost:#{bypass.port}"
attrs =
@@ -77,7 +77,7 @@ defmodule Domain.Auth.Adapters.OktaTest do
client_id: "client_id",
client_secret: "client_secret",
discovery_document_uri: discovery_document_url,
oauth_uri: oauth_url,
okta_account_domain: okta_account_domain,
api_base_url: api_base_url
}
)
@@ -107,7 +107,7 @@ defmodule Domain.Auth.Adapters.OktaTest do
"client_id" => "client_id",
"client_secret" => "client_secret",
"discovery_document_uri" => discovery_document_url,
"oauth_uri" => oauth_url,
"okta_account_domain" => okta_account_domain,
"api_base_url" => api_base_url
}
end

View File

@@ -121,13 +121,13 @@ defmodule Domain.Fixtures.Auth do
def start_and_create_okta_provider(attrs \\ %{}) do
bypass = Domain.Mocks.OpenIDConnect.discovery_document_server()
api_base_url = "http://localhost:#{bypass.port}"
adapter_config =
openid_connect_adapter_config(
api_base_url: "http://localhost:#{bypass.port}",
oauth_uri: "http://localhost:#{bypass.port}/.well-known/oauth-authorization-server",
discovery_document_uri:
"http://localhost:#{bypass.port}/.well-known/openid-configuration",
api_base_url: api_base_url,
okta_account_domain: api_base_url,
discovery_document_uri: "#{api_base_url}/.well-known/openid-configuration",
scope: Domain.Auth.Adapters.Okta.Settings.scope() |> Enum.join(" ")
)

View File

@@ -83,14 +83,14 @@ defmodule Web.Settings.IdentityProviders.Okta.Components do
<div>
<.input
label="OAuth Authorization Server URI"
label="Okta Account Domain"
autocomplete="off"
field={adapter_config_form[:oauth_uri]}
placeholder="https://<company>.okta.com/.well-known/oauth-authorization-server"
field={adapter_config_form[:okta_account_domain]}
placeholder="<company>.okta.com"
required
/>
<p class="mt-2 text-xs text-neutral-500">
The Metadata URI of the Authorization Server for your Okta Application.
Your Okta account domain.
</p>
</div>
@@ -102,7 +102,7 @@ defmodule Web.Settings.IdentityProviders.Okta.Components do
placeholder=".well-known/openid-configuration URL"
/>
<p class="mt-2 text-xs text-neutral-500">
The OIDC Configuration URI. This field is derived from the value in the OAuth Authorization Server URI field.
The OIDC Configuration URI. This field is derived from the value in the Okta Account Domain field.
</p>
</div>
</.inputs_for>
@@ -123,7 +123,7 @@ defmodule Web.Settings.IdentityProviders.Okta.Components do
|> Enum.join("\n")
end
def visible?(value) do
defp visible?(value) do
case value do
nil -> false
"" -> false

View File

@@ -44,7 +44,7 @@ defmodule Web.Settings.IdentityProviders.Okta.Edit do
def handle_event("change", %{"provider" => attrs}, socket) do
attrs =
attrs
|> put_discovery_document_uri()
|> Map.update("adapter_config", %{}, &put_discovery_document_uri/1)
changeset =
Auth.change_provider(socket.assigns.provider, attrs)
@@ -56,6 +56,7 @@ defmodule Web.Settings.IdentityProviders.Okta.Edit do
def handle_event("submit", %{"provider" => attrs}, socket) do
attrs =
attrs
|> Map.update("adapter_config", %{}, &put_discovery_document_uri/1)
|> Map.update("adapter_config", %{}, &put_api_base_url/1)
with {:ok, provider} <-
@@ -74,22 +75,32 @@ defmodule Web.Settings.IdentityProviders.Okta.Edit do
end
defp put_api_base_url(adapter_config) do
uri = URI.parse(adapter_config["discovery_document_uri"])
Map.put(adapter_config, "api_base_url", "#{uri.scheme}://#{uri.host}")
api_base_url = create_api_base_url(adapter_config["okta_account_domain"])
Map.put(adapter_config, "api_base_url", api_base_url)
end
defp put_discovery_document_uri(attrs) do
config = attrs["adapter_config"]
defp put_discovery_document_uri(adapter_config) do
api_base_url = create_api_base_url(adapter_config["okta_account_domain"])
oidc_uri =
String.replace_suffix(
config["oauth_uri"],
"oauth-authorization-server",
"openid-configuration"
)
Map.put(
adapter_config,
"discovery_document_uri",
"#{api_base_url}/.well-known/openid-configuration"
)
end
config = Map.put(config, "discovery_document_uri", oidc_uri)
# This is done for easier testing. Production should only use 'https' and Okta domains,
# but in dev and test there are times when putting an explicit URI is useful.
if Mix.env() in [:dev, :test] do
defp create_api_base_url(okta_account_domain) do
uri = URI.parse(okta_account_domain)
Map.put(attrs, "adapter_config", config)
if uri.scheme, do: okta_account_domain, else: "https://#{okta_account_domain}"
end
else
defp create_api_base_url(okta_account_domain) do
"https://#{okta_account_domain}"
end
end
end

View File

@@ -54,7 +54,7 @@ defmodule Web.Settings.IdentityProviders.Okta.New do
attrs =
attrs
|> Map.put("adapter", :okta)
|> put_discovery_document_uri()
|> Map.update("adapter_config", %{}, &put_discovery_document_uri/1)
changeset =
Auth.new_provider(socket.assigns.account, attrs)
@@ -66,6 +66,7 @@ defmodule Web.Settings.IdentityProviders.Okta.New do
def handle_event("submit", %{"provider" => attrs}, socket) do
attrs =
attrs
|> Map.update("adapter_config", %{}, &put_discovery_document_uri/1)
|> Map.update("adapter_config", %{}, &put_api_base_url/1)
|> Map.put("id", socket.assigns.id)
|> Map.put("adapter", :okta)
@@ -95,22 +96,32 @@ defmodule Web.Settings.IdentityProviders.Okta.New do
end
defp put_api_base_url(adapter_config) do
uri = URI.parse(adapter_config["discovery_document_uri"])
Map.put(adapter_config, "api_base_url", "#{uri.scheme}://#{uri.host}")
api_base_url = create_api_base_url(adapter_config["okta_account_domain"])
Map.put(adapter_config, "api_base_url", api_base_url)
end
defp put_discovery_document_uri(attrs) do
config = attrs["adapter_config"]
defp put_discovery_document_uri(adapter_config) do
api_base_url = create_api_base_url(adapter_config["okta_account_domain"])
oidc_uri =
String.replace_suffix(
config["oauth_uri"],
"oauth-authorization-server",
"openid-configuration"
)
Map.put(
adapter_config,
"discovery_document_uri",
"#{api_base_url}/.well-known/openid-configuration"
)
end
config = Map.put(config, "discovery_document_uri", oidc_uri)
# This is done for easier testing. Production should only use 'https' and Okta domains,
# but in dev and test there are times when putting an explicit URI is useful.
if Mix.env() in [:dev, :test] do
defp create_api_base_url(okta_account_domain) do
uri = URI.parse(okta_account_domain)
Map.put(attrs, "adapter_config", config)
if uri.scheme, do: okta_account_domain, else: "https://#{okta_account_domain}"
end
else
defp create_api_base_url(okta_account_domain) do
"https://#{okta_account_domain}"
end
end
end

View File

@@ -55,7 +55,7 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.EditTest do
"provider[adapter_config][client_id]",
"provider[adapter_config][client_secret]",
"provider[adapter_config][discovery_document_uri]",
"provider[adapter_config][oauth_uri]",
"provider[adapter_config][okta_account_domain]",
"provider[name]"
]
end
@@ -67,11 +67,10 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.EditTest do
conn: conn
} do
bypass = Domain.Mocks.OpenIDConnect.discovery_document_server()
api_base_url = "http://localhost:#{bypass.port}"
adapter_config_attrs =
Fixtures.Auth.openid_connect_adapter_config(
oauth_uri: "http://localhost:#{bypass.port}/.well-known/oauth-authorization-server"
)
Fixtures.Auth.openid_connect_adapter_config(okta_account_domain: api_base_url)
adapter_config_attrs =
Map.drop(adapter_config_attrs, [
@@ -100,8 +99,7 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.EditTest do
|> render_submit(%{
provider: %{
adapter_config: %{
"discovery_document_uri" =>
"http://localhost:#{bypass.port}/.well-known/openid-configuration"
"discovery_document_uri" => "#{api_base_url}/.well-known/openid-configuration"
}
}
})
@@ -119,11 +117,10 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.EditTest do
assert provider.adapter_config["client_id"] == adapter_config_attrs["client_id"]
assert provider.adapter_config["client_secret"] == adapter_config_attrs["client_secret"]
assert provider.adapter_config["oauth_uri"] ==
"http://localhost:#{bypass.port}/.well-known/oauth-authorization-server"
assert provider.adapter_config["okta_account_domain"] == api_base_url
assert provider.adapter_config["discovery_document_uri"] ==
"http://localhost:#{bypass.port}/.well-known/openid-configuration"
"#{api_base_url}/.well-known/openid-configuration"
end
test "renders changeset errors on invalid attrs", %{
@@ -133,11 +130,10 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.EditTest do
conn: conn
} do
bypass = Domain.Mocks.OpenIDConnect.discovery_document_server()
api_base_url = "http://localhost:#{bypass.port}"
adapter_config_attrs =
Fixtures.Auth.openid_connect_adapter_config(
oauth_uri: "http://localhost:#{bypass.port}/.well-known/oauth-authorization-server"
)
Fixtures.Auth.openid_connect_adapter_config(okta_account_domain: api_base_url)
adapter_config_attrs =
Map.drop(adapter_config_attrs, [

View File

@@ -46,7 +46,7 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.NewTest do
"provider[adapter_config][_persistent_id]",
"provider[adapter_config][client_id]",
"provider[adapter_config][client_secret]",
"provider[adapter_config][oauth_uri]",
"provider[adapter_config][okta_account_domain]",
"provider[name]"
]
end
@@ -57,11 +57,10 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.NewTest do
conn: conn
} do
bypass = Domain.Mocks.OpenIDConnect.discovery_document_server()
api_base_url = "http://localhost:#{bypass.port}"
adapter_config_attrs =
Fixtures.Auth.openid_connect_adapter_config(
oauth_uri: "http://localhost:#{bypass.port}/.well-known/oauth-authorization-server"
)
Fixtures.Auth.openid_connect_adapter_config(okta_account_domain: api_base_url)
adapter_config_attrs =
Map.drop(adapter_config_attrs, [
@@ -90,8 +89,7 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.NewTest do
|> render_submit(%{
provider: %{
adapter_config: %{
"discovery_document_uri" =>
"http://localhost:#{bypass.port}/.well-known/openid-configuration"
"discovery_document_uri" => "#{api_base_url}/.well-known/openid-configuration"
}
}
})
@@ -162,7 +160,8 @@ defmodule Web.Live.Settings.IdentityProviders.Okta.NewTest do
assert form_validation_errors(form) == %{
"provider[name]" => ["should be at most 255 character(s)"],
"provider[adapter_config][client_id]" => ["can't be blank"],
"provider[adapter_config][oauth_uri]" => ["can't be blank"]
"provider[adapter_config][okta_account_domain]" => ["can't be blank"],
"provider[adapter_config][discovery_document_uri]" => ["is not a valid URL"]
}
end)
end