AccessPass - a opinionated authorization library

authentication
Tags: #<Tag:0x00007fcf98029eb8>

#1

I spent the last couple weeks writing my own authorization library that provides all the basics like login,logout,register,forgot password,forgot username and email confirmation. I found myself re implementing auth every time I started a project because I did not quite like the options out there(hence why the lib is quite opinionated).

AccessPass is token based(kind of actually you send id’s in place of tokens) that is backed by a genserver for refresh tokens / access tokens.

This is my first hex package published so I would love some feedback.

The one thing left to do for me is write tests.

https://hexdocs.pm/access_pass/introduction.html

Thanks!


Authentication Systems!
#2

@jordiee AccessPass looks cool. I have been looking for an authentication library, and this one looks promising. I would be curious to read about how it’s opinionated – what aspects of exising auth libraries you have borrowed and which ones you didn’t like. On a different note, does it support JWT tokens?


#3

Hi anandtrex. As for how it is opinionated as of right now it locks you into using postgres as your database. It currently does not support JWT but I may change it in the future to add your own token generation. Right now “tokens” in essence are base64 uuids that are sent back and used as lookup keys for a session. The defaults out of the box may be different for some people but most things are overridable.

As for what I took from other libraries I really liked addict for elixir but wanted things to work slightly different in the sense that I wanted it to be token based and I wanted tokens to be revokable. I did some research on best methods of security a while back and have for quite some time now been doing short lived access_tokens(5 minute default) and long lived refresh tokens(default no expire). That way if someone did manage to get their hands on an access token it would only be valid for a couple more minutes.

The goals for accessPass were actually quite simple. I wanted a “token” based authentication system that could be dropped into applications and gave all the basics(login, logout, register, forgot password, confirmation email, ect) while keeping the library very fast and very secure.


#4

^ for any suggesting/issues/pull requests. They are always welcome!


#5

Hi @jordiee I guess just having a dependency on postgres is not too opinionated :slight_smile: . I think having one lightweight library that works out of the box for APIs is good for the ecosystem.

If I understand from your documentation correctly, you store the auth and refresh tokens in a GenServer and serve it from there. This means that whenever the service is restarted, the user would have to authenticate again. And I’m guessing it wouldn’t be too easy to scale it horizontally. I wonder if it would be a good idea to persist the refresh token in the database, so that other instances of the service can be spun up, which would have access to the refresh token through the database. As long as the reverse proxy in front of the service is sticky enough, it would be possible to have many instances of the service running with the ability to switch the user from one to the other.
I’m interested in hearing your thoughts on this. I don’t have a lot of experience with auth so I might be missing something here.


#6

That is correct to some extent. I actually made some changes recently so that the primary storage is ETS but all writes to ets are serialized through a genserver. I did this so that doing access_token checks was not a bottleneck and getting serialized. As for losing all tokens when servers is restarted this is correct in that if the service is restarted or crashes all tokens will also go with it. If you do not want tokens to go when restarting the server maybe look into hot updates but as for crashes…when designing the system. I weighed out if it would be worth it to back up the ets table on crash but decided against it. I decided against it because something that leads to a crash…in all honesty is most likely a bug that needs to be fixed. The storage should not crash if it is bug free. So I would rather it crash and I/or some else put a pull request in to fix the bug.

As for having it backed by a db. I decided against this because speed was a huge concern for me. I have implemented auth systems before that stored tokens in a db and it was noticeable to have to hit a database on every call that is authorized. I ended up switching them to redis in the end but with elixir and ets it does not seem needed.

As for scaling horizontally right now you are correct in that it would be hard to do. I am potentially going to look into mnesia rather then ets to have a shared table of tokens. But for now if you are worried about scaling applications horizontally separate of AccessPass I suggest setting up a normal phoenix app and just having accesspass run on it. From their all of your services that need auth can delegate calls via http to your “auth server” as for authorizing individual routes this way you can use the /check endpoint in much of the same way the plug is used. The downside to doing it all this way is it is slower because it goes over the network but the pro is you can scale your application separate of accesspass


#7

#8

Good news… as of 0.4.0 accesspass supports distributed environments thanks to Mnesia.


#9

How do you handle mnesia netsplits or corruption and state rebuilding (which can pretty well freeze access to it for a LONG time)?

Also, mnesia/ets/etc… are still databases, and as such those tokens are not quite revokeable without hitting them, which is not possible if they are passed externally.


#10

Hey thanks for the questions. I will update the post to say external databases as that is obviously more correct.

As for handling NetSplit, that is not something currently handled by AccessPass right now but will in the future be added to the SyncM library that goes hand and hand with using Mnesia(for me). I intent to keep adding to both libraries to handle as many use cases as I can(within reason). As for state building that is also not something currently supported(it also was not supported in earlier versions when I used ETS only). I may look at some solutions in the future but…in the case of AccessPass the state being gone is not the worst thing in the world as it should not happen often… In the case that is does happen the user would just be “logged out” so to speak and need to log back in. Also for that matter right now even netsplit is not the worst thing in the world as it is state easily rebuild(user logs back in). I am using AccessPass in a couple distributed projects currently and will be able to better gauge the likely hood of these events. But as always if you have any ideas on how best to handle this please feel free to put up an issue on github about it so we can continue discussion!


#11

Updated in version 0.5.0 to allow for any email provider supported by bamboo. Slowly but surely becoming less opinionated.


#12

Updated to version 0.5.4.

This version is an overhaul on how to override things like email templates/add hooks. The previous way I was doing it felt very hacky. This new way relies on AccessPassBehavior allowing you to override many functions that AccessPass uses(https://hexdocs.pm/access_pass/AccessPassBehavior.html)

Version also includes password confirmation validation for both register and change password(I can’t believe I forgot to add this originally)

Other then that it includes more hooks like after_confirmation, after_login ect. Look at the link above for the full current list.

As always most of these changes were due to github issues / user pull requests. So if you see something missing that you need don’t be shy to open an issue so we can discuss.


#13

Also I should mention AccessPass no longer uses mnesia because it did not work to well when it came to dynamic node membership. It now uses a weak replication style of replicating ets state.


#14

0.5.6:
base_url added to config to override email href links for stuff like update password / confirm email.

login_return override added to allow for stuffing additional data in the default /login /register return.

Docs have been updated to be more accurate thanks to pull requests.