Ash Fieldnotes: Ash Authentication

AshAuthentication gives you an easy to use batteries included authentication framework compatible with the Phoenix, and Ash framework.

The AshAuthentication logo - a cool looking padlock
Honestly, shoutout to Theo Harris (@dino_coder) for the sweet logo for AshAuthentication!

Kicking off the series on the Ash Framework, we've got AshAuthentication & AshAuthenticationPhoenix.

For the purposes of this post, I'm going to treat them as one and the same. Your mileage may vary.

Though AshAuthentication is a relative newcomer to the Ash ecosystem, we're starting with it because any time I start a new project I assume it's going to have users and I want to start with getting them up and running.

So, off to the races!

Getting started

Local authentication (username and password)

AshAuthentication's getting started guide will quickly get you up to speed. Getting local authentication up and running, complete with password reset is as easy as following the docs, as described.

So far, so good.

Adding confirmation

But wait. You probably want to confirm the user's email address.

Ash's got you covered.

Jump on over to the confirmation add on docs.

On discovering the confirmation docs, which are as easy to find as searching AshHQ for confirm it's easy to add, although it wasn't clear why this is an AddOn or what that means from a framework patterns perspective.

At the point of first implementation this was missing a note about identities, and needing a sender, which I put up a patch for.

The sender wasn't too hard, as you can build that off the sender example in the password reset section.

The identities bit wasn't too hard to figure out how to fix, and when I looked at the docs for what exactly an Identity is, it was straight forward and made sense.  (It's essentially representations of unique indices, composite or not, for the resource if you didn't want to click the link.)

The eager_check_with that the compiler was yelling at me about, nested inside the identities section was a bit harder to figure out.

Initially AshHq's docs search were pointing me to the attribute on the resource, which didn't tell me that all I needed to do was point the macro to MyApp.Accounts and I'm done.

When I returned to the identity section of the docs though it made that very explicit, which was nice, and points out two important things.

  1. Ash's compiler is actually really user friendly, at least based on what I've experienced so far. It tells you exactly where in the DSL you need to go to fix something.
  2. Reading the docs is still important.

The compiler friendliness is great, because it keeps with the common theme in Elixir of having actually useful stack traces, which can be something that takes getting used to for folks coming from other languages and ecosystems.

To highlight this, here's a paste of one of the compiler errors

Compiling 6 files (.ex)

== Compilation error in file lib/my_app/accounts/resources/user.ex ==
** (Spark.Error.DslError) [MyApp.Accounts.User]
 identities -> identity:
  The email identity on the resource `MyApp.Accounts.User` needs the `eager_check_with` property set so that inhibited changes are still validated.
    (spark 0.4.5) lib/spark/dsl/extension.ex:605: Spark.Dsl.Extension.raise_transformer_error/2
    (elixir 1.14.2) lib/enum.ex:4751: Enumerable.List.reduce/3
    (elixir 1.14.2) lib/enum.ex:2514: Enum.reduce_while/3
    /Users/brittonbroderick/code/my_app/lib/my_app/accounts/resources/user.ex:1: (file)
    (stdlib 4.1.1) erl_eval.erl:748: :erl_eval.do_apply/7
    (stdlib 4.1.1) erl_eval.erl:961: :erl_eval.expr_list/7
    (stdlib 4.1.1) erl_eval.erl:454: :erl_eval.expr/6

Reading the docs is still pretty important, although I feel like they behave weirdly for me sometimes on AshHq.

Same as the initial login, registration, password reset flow. Grab the docs. And just copy, paste, and tweak to use your app modules and atoms.

Easy.

Senders and email or phone number

An important note for this one, is that the flexibility of the sender and the package allows you to easily implement confirmation on either an email address or phone number.

While this may not be a detail you necessarily care about, it's an interesting consideration that your auth layer isn't necessarily limited to email, as we're seeing a shift to using phone numbers in some use cases. (Acknowledging either case has different tradeoffs that may be relevant for you.)

Adding OAuth2

Now, we've covered all the basics.

But what about when you want to hand off all of those responsibilities to someone like Google?

Once again, Ash has got you covered. Head on over to the OAuth2 docs.

This one the hardest part was finding in what the values ought to have been for the OAuth2 required fields

  • authorization_params
  • site
  • authorize_url
  • client_id
  • redirect_uri
  • client_secret
  • token_url
  • user_url

Full disclosure, that isn't actually Ash's fault, and you just end up having to dig through the provider's docs, or another package's hard coding of these fields.

After you've got those in place you can fire it up and be good to go. If you're using Google they won't let you use localhost as the redirect URL even in dev, so you'll have to fire up something like Ngrok, but otherwise you're good to go.

Backing way up now, but you probably want your app to use your logo, rather than Ash's.

You can configure an Overrides module.

Now I did this piece a few days ago, and I remember struggling to find the overrides module a bit, but can't recall why, so as long as you find the docs here, it's a trivial affair as well. I ended up finding how to configure the Override through a combination of browsing the Discord and spelunking in the AshAuthenticationPhoenix.

Conclusion

That's all there is to it.

Both to implement authentication with AshAuthentication, which is easy, and to the available functionality. I've only left some finer configuration you can do with all of this uncovered.

If you're me, at this point the only thing left on your authentication wish list is multi-factor authentication.

As far as I know that's not on the roadmap yet. but given the extensibility of Ash, which I'll cover in a later post, you (and I) have the opportunity to be the change we want to see in the world.

I'll keep it going in the next post in the series.