Packages
ash_authentication
5.0.0-rc.11
5.0.0-rc.12
5.0.0-rc.11
5.0.0-rc.10
5.0.0-rc.9
5.0.0-rc.8
5.0.0-rc.7
5.0.0-rc.6
5.0.0-rc.5
5.0.0-rc.4
5.0.0-rc.3
5.0.0-rc.2
5.0.0-rc.1
5.0.0-rc.0
4.14.1
4.14.0
4.13.7
4.13.6
4.13.5
4.13.4
4.13.3
4.13.2
retired
4.13.1
retired
4.13.0
4.12.0
4.11.0
4.10.0
4.9.9
4.9.8
4.9.7
4.9.6
4.9.5
4.9.4
4.9.3
4.9.2
4.9.1
4.9.0
4.8.7
4.8.6
4.8.5
4.8.3
4.8.2
4.8.1
4.8.0
4.7.6
4.7.5
4.7.4
4.7.3
4.7.2
4.7.1
4.7.0
4.6.4
4.6.3
4.6.2
4.6.1
4.6.0
4.5.6
4.5.5
4.5.4
4.5.3
4.5.2
4.5.1
4.5.0
4.4.9
4.4.8
4.4.7
4.4.6
4.4.5
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.12
4.3.11
4.3.10
4.3.9
4.3.8
4.3.7
4.3.6
4.3.5
4.3.4
4.3.3
4.3.2
4.3.1
4.3.0
4.2.7
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.4
4.0.3
4.0.2
4.0.1
4.0.0
4.0.0-rc.6
4.0.0-rc.5
4.0.0-rc.3
4.0.0-rc.2
4.0.0-rc.1
4.0.0-rc.0
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.16
3.11.15
3.11.14
3.11.13
3.11.12
3.11.11
3.11.10
3.11.9
3.11.8
3.11.7
3.11.6
3.11.5
3.11.4
3.11.3
3.11.2
3.11.1
3.11.0
3.10.8
3.10.7
3.10.6
3.10.5
3.10.4
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.0
3.7.9
3.7.8
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.1
3.6.0
3.5.3
3.5.2
3.5.1
3.5.0
3.3.1
3.3.0
3.2.2
3.2.1
3.2.0
3.1.0
3.0.3
Authentication extension for the Ash Framework.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/ash_authentication.add_strategy.oidc.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
# credo:disable-for-this-file Credo.Check.Design.AliasUsage
if Code.ensure_loaded?(Igniter) do
defmodule Mix.Tasks.AshAuthentication.AddStrategy.Oidc do
use Igniter.Mix.Task
@example "mix ash_authentication.add_strategy oidc my_provider"
@shortdoc "Adds a generic OpenID Connect authentication strategy to your user resource"
@moduledoc """
#{@shortdoc}
OIDC auto-discovers provider endpoints from the `.well-known/openid-configuration`
URL, so you only need to provide the base URL of your OIDC provider.
## Example
```bash
#{@example}
```
## Options
* `--user`, `-u` - The user resource. Defaults to `YourApp.Accounts.User`
* `--accounts`, `-a` - The accounts domain. Defaults to `YourApp.Accounts`
* `--identity-field`, `-i` - The field used to identify the user. Defaults to `email`
* `--base-url` - If provided, set as a literal in the strategy DSL instead of via env var
"""
def info(_argv, _composing_task) do
%Igniter.Mix.Task.Info{
group: :ash,
example: @example,
extra_args?: false,
only: nil,
positional: [{:name, optional: true}],
composes: [],
schema: [
accounts: :string,
user: :string,
identity_field: :string,
name: :string,
base_url: :string
],
aliases: [a: :accounts, u: :user, i: :identity_field],
defaults: [identity_field: "email"]
}
end
# sobelow_skip ["DOS.BinToAtom"]
def igniter(igniter) do
options = parse_options(igniter)
name_string = igniter.args.positional[:name] || options[:name]
unless name_string do
raise ArgumentError, """
A provider name is required for the generic OIDC strategy.
Via positional argument:
mix ash_authentication.add_strategy.oidc my_provider
Via flag:
mix ash_authentication.add_strategy oidc --name my_provider
"""
end
name = String.to_atom(name_string)
env_prefix = name |> to_string() |> String.upcase()
case Igniter.Project.Module.module_exists(igniter, options[:user]) do
{true, igniter} ->
identity_resource =
Module.concat(AshAuthentication.Igniter.parent_module(options[:user]), UserIdentity)
secrets_module = Igniter.Project.Module.module_name(igniter, "Secrets")
secret_pairs = [
{:client_id, "#{env_prefix}_CLIENT_ID"},
{:client_secret, "#{env_prefix}_CLIENT_SECRET"},
{:redirect_uri, "#{env_prefix}_REDIRECT_URI"}
]
{base_url_line, secret_pairs} =
if options[:base_url] do
{"base_url \"#{options[:base_url]}\"", secret_pairs}
else
{"base_url #{inspect(secrets_module)}",
secret_pairs ++ [{:base_url, "#{env_prefix}_BASE_URL"}]}
end
igniter
|> Ash.Resource.Igniter.add_new_attribute(options[:user], options[:identity_field], """
attribute #{inspect(options[:identity_field])}, :ci_string do
allow_nil? false
public? true
end
""")
|> AshAuthentication.Igniter.ensure_identity(options[:user], options[:identity_field])
|> AshAuthentication.Igniter.ensure_user_identity_resource(
options[:user],
identity_resource
)
|> AshAuthentication.Igniter.add_oauth_register_action(
options[:user],
name,
identity_field: options[:identity_field],
identity_resource: identity_resource
)
|> AshAuthentication.Igniter.add_oauth_secrets(
secrets_module,
options[:user],
name,
secret_pairs
)
|> AshAuthentication.Igniter.add_new_strategy(options[:user], :oidc, name, """
oidc :#{name} do
client_id #{inspect(secrets_module)}
client_secret #{inspect(secrets_module)}
redirect_uri #{inspect(secrets_module)}
#{base_url_line}
identity_resource #{inspect(identity_resource)}
end
""")
|> AshAuthentication.Igniter.codegen_for_strategy(name)
|> Igniter.add_notice("""
OIDC strategy "#{name}" setup:
Set these environment variables:
#{env_prefix}_CLIENT_ID=<your_client_id>
#{env_prefix}_CLIENT_SECRET=<your_client_secret>
#{env_prefix}_REDIRECT_URI=http://localhost:4000/auth
#{unless options[:base_url], do: " #{env_prefix}_BASE_URL=<your_oidc_provider_base_url>"}
The provider's callback URL should be set to:
http://localhost:4000/auth/#{name}/callback
""")
{false, igniter} ->
Igniter.add_issue(igniter, """
User module #{inspect(options[:user])} was not found.
Perhaps you have not yet installed ash_authentication?
""")
end
end
defp parse_options(igniter) do
options =
igniter.args.options
|> Keyword.put_new_lazy(:accounts, fn ->
Igniter.Project.Module.module_name(igniter, "Accounts")
end)
options
|> Keyword.put_new_lazy(:user, fn -> Module.concat(options[:accounts], User) end)
|> Keyword.update(:identity_field, :email, &String.to_atom/1)
|> Keyword.update!(:accounts, &AshAuthentication.Igniter.maybe_parse_module/1)
|> Keyword.update!(:user, &AshAuthentication.Igniter.maybe_parse_module/1)
end
end
else
defmodule Mix.Tasks.AshAuthentication.AddStrategy.Oidc do
@shortdoc "Adds a generic OpenID Connect authentication strategy to your user resource"
@moduledoc @shortdoc
use Mix.Task
def run(_argv) do
Mix.shell().error("The task 'ash_authentication.add_strategy.oidc' requires igniter.")
exit({:shutdown, 1})
end
end
end