docs: Documentation refresh for Ash 3.0. (#665)

* docs: Update README to new format.

* docs: tidy up docus some more.

* docs: More documentation tweaks.

* docs: Tweak readme.
This commit is contained in:
James Harton 2024-05-08 11:50:57 +12:00 committed by GitHub
parent 3a3cc98fce
commit bc1e590bca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 525 additions and 283 deletions

View file

@ -1,50 +1,79 @@
# AshAuthentication
<img src="https://github.com/ash-project/ash/blob/main/logos/ash-auth-logo.svg?raw=true" alt="Ash Authentication Logo" width="250"/>
![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only)
![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only)
![Elixir CI](https://github.com/team-alembic/ash_authentication/workflows/Elixir%20Library/badge.svg)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Hex version badge](https://img.shields.io/hexpm/v/ash_authentication.svg)](https://hex.pm/packages/ash_authentication)
[![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](https://hexdocs.pm/ash_authentication)
AshAuthentication provides drop-in support for user authentication for users of
the [Ash framework](https://ash-hq.org). It is designed to be highly
configurable, with sensible defaults covering the most common use-cases.
# Ash Authentication
## Installation
Welcome! Here you will find everything you need to know to get started with and use Ash Authentication. This documentation is best viewed on [hexdocs](https://hexdocs.pm/ash_authentication).
The package can be installed by adding `ash_authentication` to your list of
dependencies in `mix.exs`:
## About the Documentation
```elixir
def deps do
[
{:ash_authentication, "~> 4.0.0-rc.6"}
]
end
```
[**Tutorials**](#tutorials) walk you through a series of steps to accomplish a goal. These are **learning-oriented**, and are a great place for beginners to start.
## Documentation
---
See the [official documentation](https://ash-hq.org/docs/guides/ash_authentication/latest/tutorials/getting-started-with-authentication) for more.
[**Topics**](#topics) provide a high level overview of a specific concept or feature. These are **understanding-oriented**, and are perfect for discovering design patterns, features, and tools related to a given topic.
Additionally, documentation for the latest release will be [available on
hexdocs](https://hexdocs.pm/ash_authentication) and for the [`main`
branch](https://team-alembic.github.io/ash_authentication).
---
## Contributing
[**How-to**](#how-to) guides are **goal-oriented** recipes for accomplishing specific tasks. These are also good to browse to get an idea of how Ash Authentication works and what is possible with it.
- To contribute updates, fixes or new features please fork and open a
pull-request against `main`.
- To regenerate cheat sheets for the DSLs, run `mix spark.cheat_sheets`. For new strategies ensure you've added them to the extensions and documentation groups in `mix.exs`.
- Please use [conventional
commits](https://www.conventionalcommits.org/en/v1.0.0/) - this allows us to
dynamically generate the changelog.
- Feel free to ask any questions on out [GitHub discussions
page](https://github.com/team-alembic/ash_authentication/discussions).
---
## Licence
[**Reference**](#reference) documentation is produced automatically from our source code. It comes in the form of module documentation and DSL documentation. This documentation is **information-oriented**. Use the sidebar and the search bar to find relevant reference information.
`AshAuthentication` is licensed under the terms of the [MIT
license](https://opensource.org/licenses/MIT). See the [`LICENSE` file in this
repository](https://github.com/team-alembic/ash_authentication/blob/main/LICENSE)
for details.
## Tutorials
- [Get Started](documentation/tutorials/get-started.md)
---
## Topics
- [Custom Strategies](documentation/topics/custom-strategy.md)
- [Policies on Authenticated Resources](documentation/topics/policies-on-authentication-resources.md)
- [Testing](documentation/topics/testing.md)
- [Tokens](documentation/topics/tokens.md)
- [Upgrade guides](documentation/topics/upgrading.md)
---
## How To
- [Authenticate with Auth0](documentation/how-to/auth0.md)
- [Authenticate with GitHub](documentation/how-to/github.md)
- [Authenticate with Google](documentation/how-to/google.md)
- [Authenticate with Magic Links](documentation/how-to/magic-links.md)
- [Confirmation](documentation/how-to/confirmation.md)
---
## Reference
- [AshAuthentication DSL](documentation/dsls/DSL:-AshAuthentication.md)
- [AshAuthentication.AddOn.Confirmation DSL](documentation/dsls/DSL:-AshAuthentication.AddOn.Confirmation.md)
- [AshAuthentication.Strategy.Auth0](documentation/dsls/DSL:-AshAuthentication.Strategy.Auth0.md)
- [AshAuthentication.Strategy.Github DSL](documentation/dsls/DSL:-AshAuthentication.Strategy.Github.md)
- [AshAuthentication.Strategy.Google DSL](documentation/dsls/DSL:-AshAuthentication.Strategy.Google.md)
- [AshAuthentication.Strategy.MagicLink DSL](documentation/dsls/DSL:-AshAuthentication.Strategy.MagicLink.md)
- [AshAuthentication.Strategy.OAuth2 DSL](documentation/dsls/DSL:-AshAuthentication.Strategy.OAuth2.md)
- [AshAuthentication.Strategy.Oidc DSL](documentation/dsls/DSL:-AshAuthentication.Strategy.Oidc.md)
- [AshAuthentication.Strategy.Password DSL](documentation/dsls/DSL:-AshAuthentication.Strategy.Password.md)
- [AshAuthentication.TokenResource DSL](documentation/dsls/DSL:-AshAuthentication.TokenResource.md)
- [AshAuthentication.UserIdentity DSL](documentation/dsls/DSL:-AshAuthentication.UserIdentity.md)
- For other reference documentation, see the sidebar & search bar
## Related packages
- [Ash Framework](https://hexdocs.pm/ash)
- [Ash Authentication Phoenix](https://hexdocs.pm/ash_authentication_phoenix) | Integrates Ash Authentication into your Phoenix application
---
[![Alembic](logos/alembic.png)](https://alembic.com.au)
Proudly written and maintained by the team at [Alembic](https://alembic.com.au) for the Ash community.

View file

@ -15,8 +15,9 @@ In order to use Auth0 you need to provide the following minimum configuration:
- `client_secret`
- `site`
See the [Auth0 quickstart guide](/documentation/tutorials/auth0-quickstart.md)
for more information.
## More documentation:
- The [Auth0 Tutorial](/documentation/tutorial/auth0.md).
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
@ -31,8 +32,9 @@ Provides a pre-configured authentication strategy for [Auth0](https://auth0.com/
This strategy is built using the `:oauth2` strategy, and thus provides all the same
configuration options should you need them.
For more information see the [Auth0 Quick Start Guide](/documentation/tutorials/auth0-quickstart.md)
in our documentation.
###### More documentation:
- The [Auth0 Tutorial](/documentation/tutorial/auth0.md).
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
###### Strategy defaults:

View file

@ -14,8 +14,9 @@ In order to use GitHub you need to provide the following minimum configuration:
- `redirect_uri`
- `client_secret`
See the [GitHub quickstart guide](/documentation/tutorials/github-quickstart.html)
for more information.
## More documentation:
- The [GitHub Tutorial](/documentation/tutorial/github.md).
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
@ -30,8 +31,9 @@ Provides a pre-configured authentication strategy for [GitHub](https://github.co
This strategy is built using the `:oauth2` strategy, and thus provides all the same
configuration options should you need them.
For more information see the [Github Quick Start Guide](/documentation/tutorials/github-quickstart.md)
in our documentation.
###### More documentation:
- The [GitHub Tutorial](/documentation/tutorial/github.md).
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
###### Strategy defaults:

View file

@ -15,8 +15,10 @@ In order to use Google you need to provide the following minimum configuration:
- `client_secret`
- `site`
See the [Google OAuth 2.0 Overview](https://developers.google.com/identity/protocols/oauth2)
for Google setup details.
## More documentation:
- The [Google OAuth 2.0 Overview](https://developers.google.com/identity/protocols/oauth2).
- The [Google Tutorial](/documentation/tutorial/google.md)
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
@ -31,8 +33,10 @@ Provides a pre-configured authentication strategy for [Google](https://google.co
This strategy is built using the `:oauth2` strategy, and thus provides all the same
configuration options should you need them.
See the [Google OAuth 2.0 Overview](https://developers.google.com/identity/protocols/oauth2)
for Google setup details.
#### More documentation:
- The [Google OAuth 2.0 Overview](https://developers.google.com/identity/protocols/oauth2).
- The [Google Tutorial](/documentation/tutorial/google.md)
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
###### Strategy defaults:

View file

@ -95,6 +95,8 @@ Dispatching to plugs directly:
...> signed_in_user.id == user.id
true
See the [Magic Link Tutorial](/documentation/tutorial/magic-links.md) for more information.
## authentication.strategies.magic_link

View file

@ -48,6 +48,9 @@ A random value generator can look like this:
AshAuthentication will dynamically generate one for the session if `nonce` is
set to `true`.
## More documentation:
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
## authentication.strategies.oidc
@ -61,6 +64,9 @@ Provides an OpenID Connect authentication strategy.
This strategy is built using the `:oauth2` strategy, and thus provides
all the same configuration options should you need them.
###### More documentation:
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)

View file

@ -104,6 +104,7 @@ password name \\ :password
Strategy for authenticating using local resources as the source of truth.
### Nested DSLs
* [resettable](#authentication-strategies-password-resettable)

View file

@ -59,8 +59,14 @@ Currently supported strategies:
- authenticate users against your local database using a unique identity
(such as username or email address) and a password.
2. `AshAuthentication.Strategy.OAuth2`
- authenticate using local or remote [OAuth 2.0](https://oauth.net/2/)
compatible services.
- authenticate using local or remote [OAuth 2.0](https://oauth.net/2/) compatible services.
- also includes:
- `AshAuthentication.Strategy.Auth0`
- `AshAuthentication.Strategy.Github`
- `AshAuthentication.Strategy.Google`
- `AshAuthentication.Strategy.Oidc`
3. `AshAuthentication.Strategy.MagicLink`
- authenticate by sending a single-use link to the user.
## Add-ons

View file

@ -1,7 +0,0 @@
# Confirmation
## Inhibiting Updates
Inhibiting updates can be done with `d:AshAuthentication.AddOn.Confirmation.**authentication**.add_ons.confirmation.inhibit_updates?`.
If a change to a monitored field is detected, then the change is stored in the token resource and the changeset updated to not make the requested change. When the token is confirmed, the change will be applied. This could be potentially weird for your users, but useful in the case of a user changing their email address or phone number where you want to verify that the new contact details are reachable.

View file

@ -3,6 +3,8 @@
AshAuthentication allows you to bring your own authentication strategy without
having to change the Ash Authentication codebase.
> #### Add-on vs Strategy? {:.info}
>
> There is functionally no difference between "add ons" and "strategies" other
> than where they appear in the DSL. We invented "add ons" because it felt
> weird calling "confirmation" an authentication strategy.
@ -93,9 +95,7 @@ here's a brief overview of what each field we've set does:
- `target` is the name of the module which defines our entity struct. We've
set it to `__MODULE__` which means that we'll have to define the struct on
this module.
- `schema` is a keyword list that defines a `NimbleOptions` schema. Spark
provides a number of additional types over the default ones though, so check
out `Spark.OptionsHelpers` for more information.
- `schema` is a keyword list that defines an options schema. See `Spark.Options`.
> By default the entity is added to the `authentication / strategy` DSL, however
> if you want it in the `authentication / add_ons` DSL instead you can also pass
@ -148,22 +148,9 @@ The Strategy protocol is used to introspect the strategy so that it can
seamlessly fit in with the rest of Ash Authentication. Here are the key
concepts:
- "phases" - in terms of HTTP, each strategy is likely to have many phases (eg
OAuth 2.0's "request" and "callback" phases). Essentially you need one
phase for each HTTP endpoint you wish to support with your strategy. In our
case we just want one sign in endpoint.
- "actions" - actions are exactly as they sound - Resource actions which can
be executed by the strategy, whether generated by the strategy (as in the
password strategy) or typed in by the user (as in the OAuth 2.0 strategy).
The reason that we wrap the strategy's actions this way is that all the
built-in strategies (and we hope yours too) allow the user to customise the
name of the actions that it uses. At the very least it should probably
append the strategy name to the action. Using `Strategy.action/4` allows us
to refer these by a more generic name rather than via the user-specified one
(eg `:register` vs `:register_with_password`).
- "routes" - `AshAuthentication.Plug` (or `AshAuthentication.Phoenix.Router`)
will generate routes using `Plug.Router` (or `Phoenix.Router`) - the
`routes/1` callback is used to retrieve this information from the strategy.
- "phases" - in terms of HTTP, each strategy is likely to have many phases (eg OAuth 2.0's "request" and "callback" phases). Essentially you need one phase for each HTTP endpoint you wish to support with your strategy. In our case we just want one sign in endpoint.
- "actions" - actions are exactly as they sound - Resource actions which can be executed by the strategy, whether generated by the strategy (as in the password strategy) or typed in by the user (as in the OAuth 2.0 strategy). The reason that we wrap the strategy's actions this way is that all the built-in strategies (and we hope yours too) allow the user to customise the name of the actions that it uses. At the very least it should probably append the strategy name to the action. Using `Strategy.action/4` allows us to refer these by a more generic name rather than via the user-specified one (eg `:register` vs `:register_with_password`).
- "routes" - `AshAuthentication.Plug` (or [`AshAuthentication.Phoenix.Router.html`](`e:ash_authentication_phoenix:AshAuthentication.Phoenix.Router.html`)) will generate routes using `Plug.Router` (or [`Phoenix.Router`](`e:phoenix:Phoenix.Router.html`)) - the `routes/1` callback is used to retrieve this information from the strategy.
Given this information, let's implement the strategy. It's quite long, so I'm
going to break it up into smaller chunks.

View file

@ -1,9 +1,17 @@
# Upgrading
## Upgrading to version 4.0.0
Version 4.0.0 of AshAuthentication adds support for Ash 3.0 and in line with [a number of changes in Ash](`e:ash:upgrading-to-3.0.html`) there are some corresponding changes to Ash Authentication:
- Token generation is enabled by default, meaning that you will have to explicitly set [`authentication.tokens.enabled?`](documentation/dsls/DSL:-AshAuthentication.md#authentication-tokens-enabled?) to `false` if you don't need them.
- Sign in tokens are enabled by default in the password strategy. What this means is that instead of returning a regular user token on sign-in in the user's metadata, we generate a short-lived token which can be used to actually sign the user in. This is specifically to allow live-view based sign-in UIs to display an authentication error without requiring a page-load.
## Upgrading to version 3.6.0.
As of version 3.6.0 the `TokenResource` extension adds the `subject` attribute
which allows us to more easily match tokens to specific users. This unlocks
which allows us to more easily match tokens to specific users. This unlocks
some new use-cases (eg sign out everywhere).
This means that you will need to generate new migrations and migrate your
@ -15,9 +23,9 @@ database.
>
> If you already have tokens stored in your database then the migration will
> likely throw a migration error due to the new `NOT NULL` constraint on
> `subject`. If this happens then you can either delete all your tokens or
> `subject`. If this happens then you can either delete all your tokens or
> explicitly add the `subject` attribute to your resource with `allow_nil?` set
> to `true`. eg:
> to `true`. eg:
>
> ```elixir
> attributes do

View file

@ -1,16 +1,8 @@
# Auth0 Quick Start Guide
# Auth0 Tutorial
This is a _very quick_ tutorial on how to configure your application to use
Auth0 for authentication.
This is a quick tutorial on how to configure your application to use Auth0 for authentication.
Before you start this tutorial, skip the Token resource while following the
[AshAuthenticationPhoenix guide](https://hexdocs.pm/ash_authentication_phoenix/getting-started-with-ash-authentication-phoenix.html))
> [!WARNING]
> Make sure that your `ash_postgres` dependency is `~> 1.3.64`. A bug in previous versions prevents the action shown below from working correctly.
Next, you need to configure an application in [the Auth0
dashboard](https://manage.auth0.com/) using the following steps:
First, you need to configure an application in [the Auth0 dashboard](https://manage.auth0.com/) using the following steps:
1. Click "Create Application".
2. Set your application name to something that identifies it. You will likely
@ -18,10 +10,8 @@ dashboard](https://manage.auth0.com/) using the following steps:
keep that in mind.
3. Select "Regular Web Application" and click "Create".
4. Switch to the "Settings" tab.
5. Copy the "Domain", "Client ID" and "Client Secret" somewhere safe - we'll
need them soon.
6. In the "Allowed Callback URLs" section, add your callback URL. The
callback URL is generated from the following information:
5. Copy the "Domain", "Client ID" and "Client Secret" somewhere safe - we'll need them soon.
6. In the "Allowed Callback URLs" section, add your callback URL. The callback URL is generated from the following information:
- The base URL of the application - in development that would be
`http://localhost:4000/` but in production will be your application's
@ -59,11 +49,7 @@ defmodule MyApp.Accounts.User do
end
```
Because all the configuration values should be kept secret (ie the
`client_secret`) or are likely to be different for each environment we use the
`AshAuthentication.Secret` behaviour to provide them. In this case we're
delegating to the OTP application environment, however you may want to use a
system environment variable or some other secret store (eg Vault).
Because all the configuration values should be kept secret (ie the `client_secret`) or are likely to be different for each environment we use the `AshAuthentication.Secret` behaviour to provide them. In this case we're delegating to the OTP application environment, however you may want to use a system environment variable or some other secret store (eg Vault).
```elixir
defmodule MyApp.Secrets do
@ -97,25 +83,16 @@ end
The values for this configuration should be:
- `client_id` - the client ID copied from the Auth0 settings page.
- `redirect_uri` - the URL to the generated auth routes in your application
(eg `http://localhost:4000/auth`).
- `redirect_uri` - the URL to the generated auth routes in your application (eg `http://localhost:4000/auth`).
- `client_secret` the client secret copied from the Auth0 settings page.
- `base_url` - the "domain" value copied from the Auth0 settings page prefixed
with `https://` (eg `https://dev-yu30yo5y4tg2hg0y.us.auth0.com`).
- `base_url` - the "domain" value copied from the Auth0 settings page prefixed with `https://` (eg `https://dev-yu30yo5y4tg2hg0y.us.auth0.com`).
Lastly, we need to add a register action to your user resource. This is defined
as an upsert so that it can register new users, or update information for
returning users. The default name of the action is `register_with_` followed by
the strategy name. In our case that is `register_with_auth0`.
Lastly, we need to add a register action to your user resource. This is defined as an upsert so that it can register new users, or update information for returning users. The default name of the action is `register_with_` followed by the strategy name. In our case that is `register_with_auth0`.
The register action takes two arguments, `user_info` and the `oauth_tokens`.
- `user_info` contains the [`GET /userinfo` response from
Auth0](https://auth0.com/docs/api/authentication#get-user-info) which you
can use to populate your user attributes as needed.
- `oauth_tokens` contains the [`POST /oauth/token` response from
Auth0](https://auth0.com/docs/api/authentication#get-token) - you may want
to store these if you intend to call the Auth0 API on behalf of the user.
- `user_info` contains the [`GET /userinfo` response from Auth0](https://auth0.com/docs/api/authentication#get-user-info) which you can use to populate your user attributes as needed.
- `oauth_tokens` contains the [`POST /oauth/token` response from Auth0](https://auth0.com/docs/api/authentication#get-token) - you may want to store these if you intend to call the Auth0 API on behalf of the user.
```elixir
defmodule MyApp.Accounts.User do

View file

@ -0,0 +1,173 @@
# Confirmation Tutorial
This is a quick tutorial on how to configure your application to enable confirmation.
In this tutorial we'll assume that you have a `User` resource which uses `email` as it's user identifier. We'll show you how to confirm a new user on sign-up and also require them to confirm if they wish to change their email address.
Here's the user resource we'll be starting with:
```elixir
defmodule MyApp.Accounts.User do
use Ash.Resource,
extensions: [AshAuthentication],
domain: MyApp.Accounts
attributes do
uuid_primary_key :id
attribute :email, :ci_string, allow_nil?: false, public?: true, sensitive?: true
attribute :hashed_password, :string, allow_nil?: false, public?: false, sensitive?: true
end
authentication do
strategies do
password :password do
identity_field :email
hashed_password_field :hashed_password
end
end
end
identities do
identity :unique_email, [:email]
end
end
```
## Confirming newly registered users
First we start by adding the confirmation add-on to your existing authentication DSL:
```elixir
defmodule MyApp.Accounts.User do
# ...
authentication do
# ...
add_ons do
confirmation :confirm_new_user do
monitor_fields [:email]
confirm_on_create? true
confirm_on_update? false
confirm_action_name :confirm_new_user
sender MyApp.NewUserConfirmationSender
end
end
end
end
```
Next we will define our "sender" module using `Swoosh`:
```elixir
defmodule MyApp.NewUserConfirmationSender do
use AshAuthentication.Sender
def send(user, token, _opts) do
new()
|> to(user.email)
|> from({"MyApp Admin", "support@myapp.inc"})
|> subject("Confirm your email address")
|> html_body("""
<p>
Hi!<br />
Someone has tried to register a new account at <a href="https://myapp.inc">MyApp</a>.
If it was you, then please click the link below to confirm your identity. If you did not initiate this request then please ignore this email.
</p>
<p>
<a href="https://myapp.inc/auth/user/confirm_new_user?#{URI.encode_query(token: @token)}">Click here to confirm your account</a>
</p>
""")
|> MyApp.Mailer.deliver()
end
end
```
Provided you have your authentication routes hooked up either via `AshAuthentication.Plug` or [`AshAuthentication.Phoenix.Router`](`e:ash_authentication_phoenix:AshAuthentication.Phoenix.Router.html`) then the user will be confirmed when the token is submitted.
## Confirming changes to monitored fields
You may want to require a user to perform a confirmation when a certain field changes. For example if a user changes their email address we can send them a new confirmation request.
First, let's start by defining a new confirmation add-on in our resource:
```elixir
defmodule MyApp.Accounts.User do
# ...
authentication do
# ...
add_ons do
confirmation :confirm_change do
monitor_fields [:email]
confirm_on_create? false
confirm_on_update? true
confirm_action_name :confirm_change
sender MyApp.EmailChangeConfirmationSender
end
end
end
end
```
> #### Why two confirmation configurations? {: .info}
>
> While you can perform both of these confirmations with a single confirmation add-on, in general the Ash philosophy is to be more explicit. Each confirmation will have it's own URL (based on the name) and tokens for one will not be able to be used for the other.
Next, let's define our new sender:
```elixir
defmodule MyApp.NewUserConfirmationSender do
use AshAuthentication.Sender
def send(user, token, _opts) do
new()
|> to(user.email)
|> from({"MyApp Admin", "support@myapp.inc"})
|> subject("Confirm your new email address")
|> html_body("""
<p>
Hi!<br />
You recently changed your email address on <a href="https://myapp.inc">MyApp</a>. Please confirm it.
</p>
<p>
<a href="https://myapp.inc/auth/user/confirm_change?#{URI.encode_query(token: @token)}">Click here to confirm your new email address</a>
</p>
""")
|> MyApp.Mailer.deliver()
end
end
```
> #### Inhibiting changes {: .tip}
>
> Depending on whether you want the user's changes to be applied _before_ or _after_ confirmation, you can enable the [`inhibit_updates?` DSL option](documentation/dsls/DSL:-AshAuthentication.AddOn.Confirmation.md#authentication-add_ons-confirmation-inhibit_updates?).
>
> When this option is enabled, then any potential changes to monitored fields are instead temporarily stored in the [token resource](documentation/dsls/DSL:-AshAuthentication.TokenResource.md) and applied when the confirmation action is run.
## Customising the confirmation action
By default Ash Authentication will generate an update action for confirmation automatically (named `:confirm` unless you change it). You can manually implement this action in order to change it's behaviour and AshAuthentication will validate that the required changes are also present.
For example, here's an implementation of the `:confirm_change` action mentioned above, which adds a custom change that updates a remote CRM system with the user's new address.
```elixir
defmodule MyApp.Accounts.User do
# ...
actions do
# ...
update :confirm_change do
argument :confirm, :string, allow_nil?: false, public?: true
change AshAuthentication.AddOn.Confirmation.ConfirmChange
change AshAuthentication.GenerateTokenChange
change MyApp.UpdateCrmSystem, only_when_valid?: true
end
end
end
```

View file

@ -1,4 +1,4 @@
# Getting started with Ash Authentication
# Get started with Ash Authentication
If you haven't already, read [the getting started guide for
Ash](https://ash-hq.org/docs/guides/ash/latest/tutorials/get-started.md). This

View file

@ -1,10 +1,8 @@
# GitHub Quick Start Guide
# GitHub Tutorial
This is a _very quick_ tutorial on how to configure your application to use
GitHub for authentication.
This is a quick tutorial on how to configure your application to use GitHub for authentication.
First you need to configure an application in your [GitHub developer
settings](https://github.com/settings/developers):
First you need to configure an application in your [GitHub developer settings](https://github.com/settings/developers):
1. Click the "New OAuth App" button.
2. Set your application name to something that identifies it. You will likely

View file

@ -1,6 +1,6 @@
# Google Quick Start Guide
# Google Tutorial
This is a _very quick_ tutorial on how to configure Google authentication in your application using the default oauth2 strategy in ash.
This is a quick tutorial on how to configure Google authentication.
First you'll need a registered application in [Google Cloud](https://console.cloud.google.com/welcome), in order to get your OAuth 2.0 Client credentials.
@ -23,9 +23,9 @@ defmodule MyApp.Accounts.User do
authentication do
strategies do
oauth2 :google do
client_id "123abc..."
redirect_uri {:ok, "123abc..."}
client_secret fn -> {:ok, "123abc..."} end
client_id MyApp.Secrets
redirect_uri MyApp.Secrets
client_secret MyApp.Secrets end
base_url MyApp.Secrets
end
end
@ -53,19 +53,17 @@ defmodule MyApp.Accounts.User do
change AshAuthentication.GenerateTokenChange
# Required if you have the `identity_resource` configuration enabled.
# change AshAuthentication.Strategy.OAuth2.IdentityChange
change AshAuthentication.Strategy.OAuth2.IdentityChange
change fn changeset, _ctx ->
change fn changeset, _ ->
user_info = Ash.Changeset.get_argument(changeset, :user_info)
changeset
|> Ash.Changeset.change_attribute(:google_info, user_info)
# you could upsert custom user attributes from the given google's user_info
# |> Ash.Changeset.change_attribute(:email, user_info["email"])
# |> Ash.Changeset.change_attribute(:name, user_info["name"])
# |> Ash.Changeset.change_attribute(:portrait, user_info["picture"])
Ash.Changeset.change_attributes(changeset, Map.take(user_info, ["email"]))
end
end
end
# ...
end
```

View file

@ -1,3 +0,0 @@
# Integrating Ash Authentication and Phoenix
This guide is now located in the [AshAuthenticationPhoenix documentation](https://hexdocs.pm/ash_authentication_phoenix/getting-started-with-ash-authentication-phoenix.html)

View file

@ -1,4 +1,4 @@
# Magic Links Quick Start Guide
# Magic Links Tutorial
This is a quick tutorial to get you up and running on Magic Links.
This assumes you've set up `ash_authentication` and [password reset](https://ash-hq.org/docs/guides/ash_authentication_phoenix/latest/tutorials/getting-started-with-ash-authentication-phoenix) in your Phoenix project.
@ -75,4 +75,3 @@ end
# ...
```

View file

@ -58,8 +58,14 @@ defmodule AshAuthentication do
- authenticate users against your local database using a unique identity
(such as username or email address) and a password.
2. `AshAuthentication.Strategy.OAuth2`
- authenticate using local or remote [OAuth 2.0](https://oauth.net/2/)
compatible services.
- authenticate using local or remote [OAuth 2.0](https://oauth.net/2/) compatible services.
- also includes:
- `AshAuthentication.Strategy.Auth0`
- `AshAuthentication.Strategy.Github`
- `AshAuthentication.Strategy.Google`
- `AshAuthentication.Strategy.Oidc`
3. `AshAuthentication.Strategy.MagicLink`
- authenticate by sending a single-use link to the user.
## Add-ons

View file

@ -117,7 +117,8 @@ defmodule AshAuthentication.AddOn.Confirmation.Transformer do
Transformer.build_entity!(Resource.Dsl, [:actions, :update], :argument,
name: :confirm,
type: Type.String,
allow_nil?: false
allow_nil?: false,
public?: true
)
]

View file

@ -1,5 +1,5 @@
defmodule AshAuthentication.Sender do
@moduledoc ~S"""
@moduledoc ~S'''
A module to implement sending of a token to a user.
Allows you to glue sending of instructions to
@ -16,7 +16,7 @@ defmodule AshAuthentication.Sender do
```elixir
defmodule MyApp.PasswordResetSender do
use AshAuthentication.PasswordReset.Sender
use AshAuthentication.Sender
import Swoosh.Email
def send(user, reset_token, _opts) do
@ -24,7 +24,7 @@ defmodule AshAuthentication.Sender do
|> to({user.name, user.email})
|> from({"Doc Brown", "emmet@brown.inc"})
|> subject("Password reset instructions")
|> html_body("
|> html_body("""
<h1>Password reset instructions</h1>
<p>
Hi #{user.name},<br />
@ -32,10 +32,10 @@ defmodule AshAuthentication.Sender do
Someone (maybe you) has requested a password reset for your account.
If you did not initiate this request then please ignore this email.
</p>
<a href=\"https://example.com/user/password/reset?#{URI.encode_query(reset_token: reset_token)}\">
<a href="https://example.com/user/password/reset?#{URI.encode_query(reset_token: reset_token)}\">
Click here to reset
</a>
")
""")
|> MyApp.Mailer.deliver()
end
end
@ -78,7 +78,7 @@ defmodule AshAuthentication.Sender do
end
end
```
"""
'''
alias Ash.Resource

View file

@ -14,8 +14,9 @@ defmodule AshAuthentication.Strategy.Auth0 do
- `client_secret`
- `site`
See the [Auth0 quickstart guide](/documentation/tutorials/auth0-quickstart.md)
for more information.
## More documentation:
- The [Auth0 Tutorial](/documentation/tutorial/auth0.md).
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
"""
alias AshAuthentication.Strategy.{Custom, OAuth2}

View file

@ -16,8 +16,9 @@ defmodule AshAuthentication.Strategy.Auth0.Dsl do
This strategy is built using the `:oauth2` strategy, and thus provides all the same
configuration options should you need them.
For more information see the [Auth0 Quick Start Guide](/documentation/tutorials/auth0-quickstart.md)
in our documentation.
#### More documentation:
- The [Auth0 Tutorial](/documentation/tutorial/auth0.md).
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
#### Strategy defaults:

View file

@ -2,7 +2,7 @@ defmodule AshAuthentication.Strategy.Custom do
@moduledoc """
Define your own custom authentication strategy.
See [the Custom Strategies guide](/documentation/topics/custom-strategy.html)
See [the Custom Strategies guide](/documentation/topics/custom-strategy.md)
for more information.
"""

View file

@ -13,8 +13,9 @@ defmodule AshAuthentication.Strategy.Github do
- `redirect_uri`
- `client_secret`
See the [GitHub quickstart guide](/documentation/tutorials/github-quickstart.html)
for more information.
## More documentation:
- The [GitHub Tutorial](/documentation/tutorial/github.md).
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
"""
alias AshAuthentication.Strategy.{Custom, OAuth2}

View file

@ -16,8 +16,9 @@ defmodule AshAuthentication.Strategy.Github.Dsl do
This strategy is built using the `:oauth2` strategy, and thus provides all the same
configuration options should you need them.
For more information see the [Github Quick Start Guide](/documentation/tutorials/github-quickstart.md)
in our documentation.
#### More documentation:
- The [GitHub Tutorial](/documentation/tutorial/github.md).
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
#### Strategy defaults:

View file

@ -14,8 +14,10 @@ defmodule AshAuthentication.Strategy.Google do
- `client_secret`
- `site`
See the [Google OAuth 2.0 Overview](https://developers.google.com/identity/protocols/oauth2)
for Google setup details.
## More documentation:
- The [Google OAuth 2.0 Overview](https://developers.google.com/identity/protocols/oauth2).
- The [Google Tutorial](/documentation/tutorial/google.md)
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
"""
alias AshAuthentication.Strategy.{Custom, OAuth2}

View file

@ -16,8 +16,10 @@ defmodule AshAuthentication.Strategy.Google.Dsl do
This strategy is built using the `:oauth2` strategy, and thus provides all the same
configuration options should you need them.
See the [Google OAuth 2.0 Overview](https://developers.google.com/identity/protocols/oauth2)
for Google setup details.
## More documentation:
- The [Google OAuth 2.0 Overview](https://developers.google.com/identity/protocols/oauth2).
- The [Google Tutorial](/documentation/tutorial/google.md)
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
#### Strategy defaults:

View file

@ -93,6 +93,8 @@ defmodule AshAuthentication.Strategy.MagicLink do
...> {_conn, {:ok, signed_in_user}} = Plug.Helpers.get_authentication_result(conn)
...> signed_in_user.id == user.id
true
See the [Magic Link Tutorial](/documentation/tutorial/magic-links.md) for more information.
"""
defstruct identity_field: :username,

View file

@ -46,6 +46,9 @@ defmodule AshAuthentication.Strategy.Oidc do
AshAuthentication will dynamically generate one for the session if `nonce` is
set to `true`.
## More documentation:
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
"""
alias AshAuthentication.Strategy.{Custom, Oidc}

View file

@ -17,6 +17,9 @@ defmodule AshAuthentication.Strategy.Oidc.Dsl do
This strategy is built using the `:oauth2` strategy, and thus provides
all the same configuration options should you need them.
#### More documentation:
- The [OAuth2 documentation](`AshAuthentication.Strategy.OAuth2`)
""",
auto_set_fields: [assent_strategy: Assent.Strategy.OIDC, icon: :oidc],
schema: patch_schema()

View file

@ -13,7 +13,9 @@ defmodule AshAuthentication.Strategy.Password.Dsl do
def dsl do
%Entity{
name: :password,
describe: "Strategy for authenticating using local resources as the source of truth.",
describe: """
Strategy for authenticating using local resources as the source of truth.
""",
examples: [
"""
password :password do

BIN
logos/alembic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

8
logos/alembic.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
logos/ash-auth-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

19
logos/ash-auth-logo.svg Normal file
View file

@ -0,0 +1,19 @@
<svg width="939" height="741" viewBox="0 0 939 741" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M470.482 294L743.966 741H729.078L394.43 418.305L470.482 294Z" fill="#FF5757"/>
<path d="M296.391 741H328.49L500.904 533.333L392.216 428.527L296.391 741Z" fill="#FF914D"/>
<path d="M431.037 741H340.052L432.58 629.553L431.037 741Z" fill="#FFBD59"/>
<path d="M439.934 741H606.641L441.62 619.199L439.934 741Z" fill="#FF5757"/>
<path d="M625.59 741L513.772 545.742L716.263 741H625.59Z" fill="#FF914D"/>
<path d="M610.739 732.969L447.037 612.141L502.967 544.774L610.739 732.969Z" fill="#FFBD59"/>
<path d="M287.086 741H196.999L377.62 445.78L287.086 741Z" fill="#FFBD59"/>
<path d="M470.482 294L766.5 741H729.078L394.43 418.305L470.482 294Z" fill="#FF5757"/>
<path d="M610.739 732.969L447.037 612.141L502.967 544.774L610.739 732.969Z" fill="#FFBD59"/>
<path d="M196.999 725.5L218.5 689L366 448.5H269.5L196.999 725.5Z" fill="#FF5757"/>
<path d="M197 688.5L197 294L260.5 441.5L197 688.5Z" fill="#FFBD59"/>
<path d="M763.305 317.27L763.305 690.5L710.805 510.77L763.305 317.27Z" fill="#FF5757"/>
<path d="M764.5 720L481 294L633.5 294L764.5 720Z" fill="#FFBD59"/>
<path d="M218.5 295L458.5 295L380 427.5L218.5 295Z" fill="#FFBD59"/>
<path d="M205 294L378.56 439.5L270.5 439.5L205 294Z" fill="#FF914D"/>
<path d="M641.439 294L763 294L705 500L641.439 294Z" fill="#FF914D"/>
<path d="M321 251.125V189.375C321 148.432 337.264 109.166 366.215 80.2154C395.166 51.2645 434.432 35 475.375 35C516.318 35 555.584 51.2645 584.535 80.2154C613.486 109.166 629.75 148.432 629.75 189.375V251.125" stroke="#FF5757" stroke-width="70" stroke-linecap="square" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

256
mix.exs
View file

@ -2,143 +2,32 @@ defmodule AshAuthentication.MixProject do
@moduledoc false
use Mix.Project
@description """
Authentication extension for the Ash Framework.
"""
@version "4.0.0-rc.6"
def project do
[
app: :ash_authentication,
version: @version,
description: "User authentication support for Ash",
elixir: "~> 1.13",
start_permanent: Mix.env() == :prod,
preferred_cli_env: [ci: :test],
aliases: aliases(),
deps: deps(),
package: package(),
elixirc_paths: elixirc_paths(Mix.env()),
consolidate_protocols: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
elixirc_paths: elixirc_paths(Mix.env()),
package: package(),
deps: deps(),
dialyzer: [
plt_add_apps: [:mix, :ex_unit],
plt_core_path: "priv/plts",
plt_file: {:no_warn, "priv/plts/dialyzer.plt"}
],
docs: [
main: "readme",
extras: [
{"README.md", name: "READ ME"},
"documentation/tutorials/getting-started-with-authentication.md",
"documentation/tutorials/auth0-quickstart.md",
"documentation/tutorials/github-quickstart.md",
"documentation/tutorials/google-quickstart.md",
"documentation/tutorials/integrating-ash-authentication-and-phoenix.md",
"documentation/tutorials/magic-links-quickstart.md",
"documentation/topics/custom-strategy.md",
"documentation/topics/policies-on-authentication-resources.md",
"documentation/topics/testing.md",
"documentation/topics/tokens.md",
"documentation/topics/confirmation.md",
"documentation/topics/upgrading.md",
"documentation/dsls/DSL:-AshAuthentication.md",
"documentation/dsls/DSL:-AshAuthentication.AddOn.Confirmation.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Auth0.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Github.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Google.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.MagicLink.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.OAuth2.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Oidc.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Password.md",
"documentation/dsls/DSL:-AshAuthentication.TokenResource.md",
"documentation/dsls/DSL:-AshAuthentication.UserIdentity.md"
],
groups_for_extras: [
Tutorials: ~r'documentation/tutorials',
"How To": ~r'documentation/how_to',
Topics: ~r'documentation/topics',
DSLs: ~r'documentation/dsls'
],
extra_section: "GUIDES",
formatters: ["html"],
before_closing_head_tag: fn type ->
if type == :html do
"""
<script>
if (location.hostname === "hexdocs.pm") {
var script = document.createElement("script");
script.src = "https://plausible.io/js/script.js";
script.setAttribute("defer", "defer")
script.setAttribute("data-domain", "ashhexdocs")
document.head.appendChild(script);
}
</script>
"""
end
end,
filter_modules: ~r/^Elixir.AshAuthentication/,
source_url_pattern:
"https://github.com/team-alembic/ash_authentication/blob/main/%{path}#L%{line}",
nest_modules_by_prefix: [
AshAuthentication.Strategy,
AshAuthentication.AddOn,
AshAuthentication.Plug,
AshAuthentication.Validations
],
groups_for_modules: [
Extensions: [
AshAuthentication,
AshAuthentication.TokenResource,
AshAuthentication.UserIdentity
],
Strategies: [
AshAuthentication.Strategy,
AshAuthentication.Strategy.Auth0,
AshAuthentication.Strategy.Github,
AshAuthentication.Strategy.Google,
AshAuthentication.Strategy.MagicLink,
AshAuthentication.Strategy.OAuth2,
AshAuthentication.Strategy.Password
],
CustomStrategies: [
~r/AshAuthentication.Strategy.Custom/
],
"Add ons": [
AshAuthentication.AddOn.Confirmation
],
Cryptography: [
AshAuthentication.HashProvider,
AshAuthentication.BcryptProvider,
AshAuthentication.Jwt
],
Introspection: [
AshAuthentication.Info,
AshAuthentication.TokenResource.Info,
AshAuthentication.UserIdentity.Info
],
Utilities: [
AshAuthentication.Debug,
AshAuthentication.Secret,
AshAuthentication.Sender,
AshAuthentication.Supervisor,
~r/.*Actions$/,
AshAuthentication.Strategy.Password.Actions,
AshAuthentication.TokenResource.Expunger
],
Plugs: [~r/^AshAuthentication\.Plug.*/, AshAuthentication.Strategy.MagicLink.Plug],
"Reusable Components": [
AshAuthentication.GenerateTokenChange,
AshAuthentication.Strategy.Password.HashPasswordChange,
AshAuthentication.Strategy.Password.PasswordConfirmationValidation,
AshAuthentication.Strategy.Password.PasswordValidation,
AshAuthentication.Checks.AshAuthenticationInteraction,
AshAuthentication.Password.Plug,
~r/AshAuthentication.Validations/
],
Errors: ~r/AshAuthentication.Errors/,
"DSL Transformers": [
~r/Transformer/,
~r/Verifier/
]
]
]
docs: docs(),
aliases: aliases(),
description: @description,
preferred_cli_env: [ci: :test],
consolidate_protocols: Mix.env() == :prod
]
end
@ -170,6 +59,125 @@ defmodule AshAuthentication.MixProject do
defp extra_applications(:test), do: [:logger, :bcrypt_elixir]
defp extra_applications(_), do: [:logger]
defp docs do
[
main: "readme",
source_ref: "v#{@version}",
logo: "logos/ash-auth-small-logo.png",
extra_section: ["GUIDES"],
extras: [
{"README.md", name: "Home"},
"documentation/tutorials/get-started.md",
"documentation/tutorials/auth0.md",
"documentation/tutorials/github.md",
"documentation/tutorials/google.md",
"documentation/tutorials/magic-links.md",
"documentation/tutorials/confirmation.md",
"documentation/topics/custom-strategy.md",
"documentation/topics/policies-on-authentication-resources.md",
"documentation/topics/testing.md",
"documentation/topics/tokens.md",
"documentation/topics/upgrading.md",
"documentation/dsls/DSL:-AshAuthentication.md",
"documentation/dsls/DSL:-AshAuthentication.AddOn.Confirmation.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Auth0.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Github.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Google.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.MagicLink.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.OAuth2.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Oidc.md",
"documentation/dsls/DSL:-AshAuthentication.Strategy.Password.md",
"documentation/dsls/DSL:-AshAuthentication.TokenResource.md",
"documentation/dsls/DSL:-AshAuthentication.UserIdentity.md",
"CHANGELOG.md"
],
groups_for_extras: [
"Start Here": [
"documentation/home.md",
"documentation/tutorials/get-started.md"
],
Tutorials: ~r"documentation/tutorials",
Topics: ~r"documentation/topics",
"How To": ~r"documentation/how-to",
Reference: ~r"documentation/dsls"
],
skip_undefined_reference_warnings_on: [
"CHANGELOG.md"
],
nest_modules_by_prefix: [],
before_closing_head_tag: fn type ->
if type == :html do
"""
<script>
if (location.hostname === "hexdocs.pm") {
var script = document.createElement("script");
script.src = "https://plausible.io/js/script.js";
script.setAttribute("defer", "defer")
script.setAttribute("data-domain", "ashhexdocs")
document.head.appendChild(script);
}
</script>
"""
end
end,
filter_modules: ~r/^Elixir.AshAuthentication/,
source_url_pattern:
"https://github.com/team-alembic/ash_authentication/blob/main/%{path}#L%{line}",
groups_for_modules: [
Extensions: [
AshAuthentication,
AshAuthentication.TokenResource,
AshAuthentication.UserIdentity
],
Strategies: [
AshAuthentication.Strategy,
AshAuthentication.AddOn.Confirmation,
AshAuthentication.Strategy.Auth0,
AshAuthentication.Strategy.Custom,
AshAuthentication.Strategy.Github,
AshAuthentication.Strategy.Google,
AshAuthentication.Strategy.MagicLink,
AshAuthentication.Strategy.OAuth2,
AshAuthentication.Strategy.Oidc,
AshAuthentication.Strategy.Password
],
Cryptography: [
AshAuthentication.HashProvider,
AshAuthentication.BcryptProvider,
AshAuthentication.Jwt
],
Introspection: [
AshAuthentication.Info,
AshAuthentication.TokenResource.Info,
AshAuthentication.UserIdentity.Info
],
Utilities: [
AshAuthentication.Debug,
AshAuthentication.Secret,
AshAuthentication.Sender,
AshAuthentication.Supervisor
],
Plugs: [
AshAuthentication.Plug,
AshAuthentication.Plug.Helpers
],
"Reusable Components": [
AshAuthentication.GenerateTokenChange,
AshAuthentication.Strategy.Password.HashPasswordChange,
AshAuthentication.Strategy.Password.PasswordConfirmationValidation,
AshAuthentication.Strategy.Password.PasswordValidation,
AshAuthentication.Checks.AshAuthenticationInteraction,
AshAuthentication.Password.Plug,
~r/AshAuthentication.Validations/
],
Errors: [
~r/^AshAuthentication\.Errors/
],
Internals: ~r/.*/
]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[