Blog Posts
I'm building a cross-platform authentication framework
So I'm building an authentication framework. Why does the world need yet another one? Let me explain.
All web development frameworks come with session management - the ability to use cookies to persist state between requests. Most also come with authentication - user login using said session persistence. If not out-of-the-box for a given web framework, at least available as a plugin.
There are a number of OAuth client libraries, for various frameworks. There are not so many OAuth server frameworks, with Keycloak being the go-to one for the majority of people.
The trouble is, I and my team get pulled into developing with a pretty broad range of web frameworks. I've counted 9 so far, spanning Javascript/Typescript, Python and Java (I'm excluding the R/Shiny which thankfully seems to have dropped off our portfolio). Each one has a different subset of tools that work with them. This means developers having to learn a different stack each time, which is hard enough without also throwing different authentication frameworks into the mix.
Add to this the fact that we develop apps that handle personal medical data, which comes with its own restrictions and fairly rigorous vetting of authentication and authorization technology.
We in academia also have some authentication requirements which are ubiquitous for us not not so well supported by the popular dev frameworks, LDAP, SWITCHaai and Edu-ID being among them.
Parallel to this, I was developing an application in Svelte and, even ignoring OAuth, I couldn't find an authentication framework I liked.
So for the past few months I've been gradually putting together a framework to support at least some of our development frameworks and provide us with authentication OAuth2 authorization that meets our use cases, and is secure enough to confidently use with personal medical data, and with an API that is broadly the same across platforms.
Structure
I'm calling my framework Crossauth. The core of is platform independent. At the moment all in Typescript, but I plan to also create a Python version, then maybe Java. There are five main parts:
- Session-based authentication, with session and CSRF cookies
- API keys
- OAuth2 client
- OAuth2 authentication server
- OAuth2 resource server
Session-based authentication
This is your classic username/password authentication. A user types in his or her password in an HTML form, or as JSON for a REST API call. The server looks them up in a database and, if they match, creates a session ID and sends this back in a cookie. Server-side, the session ID is stored in a database and associated with the user ID.
Session-based authentication opens up Cross-Site Request Forgery (CSRF) vulnerabilites. This is address by sensible cookie settings and with CSRF tokens. I chose the double-submit cookie pattern so that I can also use them when a user is not logged in. To guard against SQL injection and other vulnerabilites, all cookies are signed.
Authenticators
We need to support two-factor authentication (2FA). Our mandated one is Time-based one-time passwords (TOTP). As I don't like this, I am supporting others as well. At the moment I've got TOTP, with Google Authenticator support, and one-time passwords over Email and SMS. Yubikeys will follow. These, by the way, are great. I'll do a separate post about them at some point.
We also need to support LDAP. The result of all this is that I abstracted out the authentication method, with selectable authenticators for one or two factors authentication.
Databases
For the most part, we use PostgreSQL at ETH, but it would hardly be a general-purpose framework if that was all I supported, so that is abstracted out as well. At the moment the only implementation is a Prisma backend (a Javascript/Typescript ORM) but I expect I will add direct support for PostgreSQL and other databases too, and SQL Alchemy when I get around to the Python version.
API Tokens
I don't particularly like API tokens, but they have their place and implementation is easy, so I added that as well. As for cookies, these are signed to defend against SQL injection attacks.
OAuth2
For those not familiar with OAuth, it is a protocol for delegated authorization (and authentication in some circumstances).
OAuth (2.0, 2.1 is in development, 1.0 is deprecated) defines three roles which can be the same server or different ones.
OAuth provides a way for one application (a client) to access resources on another server (the resource server) on behalf of a user, whose account resides on another server (the authorization server, which may or may not be the same as the resource server, or indeed the client).
For example, you have a Gmail account. You have an Email reader which you want to access your Gmail account with. Ideally the app should not ask for your password, as neither you nor Google want random third-party apps to have your Google password. Instead it is Google that asks for your password and sends the email app only a token with limited permissions (it cannot delete your account, for example). This is what OAuth does - it delegates the authorization to the site where the user has an account.
Crossauth has an OAuth client, authorization server and resource server. They can be used together or with other frameworks (my Crossauth client, for example, can be used with Google's authentication server, and my authorization server can be used with other client implementations). I also want the Python and Javascript/Typescript clients and servers to work together. For example, a Javascript client with a Python authorization server and resource server.
Framework support
Everything is framework independent, but of course it need framework integration. So far I have an adapter for Fastify. Next up with be Svelte. Maybe Express but it hasn't been updated in a while and doesn't seem to support async/await
so maybe not. Next, Python, probably FastAPI to start with.
Demo
So how far have I got? All the above is implemented in Typescript with Fastify, with some example apps to demonstrate its use. Here is the OAuth Authorization code flow. There are three servers, all implemented with this framework and the Fastify adapter:
- OAuth client
- OAuth authorization server
- OAuth resource server
What's next?
At ETH, we have a particular need for two-factor authentication on REST APIs. An extension of the OAuth Password Flow is probably a good solution to this, so that will be next, along with the Client Credentials and other flows. I also want to support OpenID Connect (OIDC), particularly as it allows us to authenticate using SWITCHaai (though with less functionality than SAML, apparently - I may have to add that too).
Once this is stable I'll make a Python implementation, followed by Java if there is justification.
Licence
At this stage I am planned to open source it when it is stable, probably under the MIT licence.
Summary
This post covered my embryonic, pre-alpha cross platform authentication framework for Javascript, Typescript, Python and maybe Java applications.
Previous: Where to Store OAuth Access Tokens