Building a user management solution using ORY Oathkeeper and Auth0
I’m part of a team-building platform that requires login and user management functions.
We’re using a combination of Auth0, widely used for user management, and ORY Oathkeeper, an open-source solution.
The combination of Oathkeeper and Auth0 is not a common use case, but it’s an elegant configuration-driven setup. It enables us to decouple our business logic from our user sign-in logic. That’s beneficial for microservice architecture scenarios — as our application scales, we can reuse our user authentication logic across all our services.
We started out with a single service that handles the register, login, authentication, authorization, routing, and business logic. As the application and needs grow, we want to add more features and move our services to a more micro-service-friendly setup. We also want to decouple the business logic from the user authentication.
From the application developer's point of view, it’s also a clean and easy-to-use solution, as it puts all the auth responsibility on Oathkeeper. The application doesn’t need to verify any credentials, it just receives a header added by the proxy. If it exists, the user is logged in. If it doesn’t, they are not.
Using existing tools can give us feature-rich solutions that have been vetted in production and refined over time. Often we don’t realize how many components are in a seemingly simple feature such as login and logout until we start scoping out requirements or even building it. For example, for user-management and surrounding features, you need CSRF protection, password hashing, email verification, securely storing user credentials, social sign-in, token signing, security compliance, and the list goes on.
A solution to keep our backend services all stateless, and keep the user session state outside of the application logic.
Users logging into the application would get a signed JWT token and return to the frontend, saved in a cookie (or localStorage) and passed upstream.
Once a user is logged in, the frontend application passes down the token via header/cookie on every subsequent request, then upstream services would verify and authenticate the request using the JWT token passed along with the requests.
Our tool of choice was Auth0. It’s not open-source software, but there are a plethora of features and great tooling for it. Auth0 is “user-management as a service” software. It has wild adoption and great community support and resources, providing us with many features, such as:
Oathkeeper is a proxy that supports many authentication and authorization features. It has many features to support sessions and allow us to authenticate, mutate, and route requests before reaching the business logic:
A brief explanation of the setup from the ground up:
In our case, we have configured Auth0 following the regular web app flow. While submitting the authorization request, we must include the scope
openId. Then Auth0 will return an ID token when obtaining userInfo, which in this example is the user’s token. This way we don’t need to maintain, rotate or deploy a set of private or public keys, implement code to sign or verify JWT tokens, or figure out how to secure user credentials or sessions in your database.
In the proxy we need to set up three parts:
Here we want to split up the authenticated routes, and routes to Login via `Rules` in Oathkeeper. The Oathkeeper controller (oathkeeper-maester) will pick up `Rules` applied to kubernetes then apply it to Oathkeeper’s config automatically.
Conveniently, Oathkeeper has a suite of plugins to handle common authentication methods. We’re using the id_token from Auth0 as the token and verifying it using the JWT authenticator, configuring the JWT plugin as follows:
Oathkeeper also provides other middleware, such as mutation plugins, which can access the session data and then inject metadata into the request downstream. In this case, we want to remove authentication from the application logic while keeping the authorization and user lookup in the application, so we can pass the user-id and email to the application. This means that by the time the request gets to the application, it can check for the existence of the X-User-Id header. If it exists, the specified user is logged in. If a user isn’t logged in, the request won’t even reach the application.
You will need to implement a few endpoints to handle the interactions with Auth0. Note that you can also implement these in your backend application and route them to the same place, but for clarity the logic is represented as its own service:
Upon success, all three of these methods will redirect the user back to your frontend application.
To let the frontend determine whether a user is logged in or not, we can implement an endpoint like /whoami that checks the headers injected by Oathkeeper and returns the user info. Then you can create stateless API calls that check the user’s logged-in status on the frontend and leverage Auth0 as your logged-in state controller.
Here’s an example in node.js. You could have a middleware to look up the user, such as:
Login: To log in, you can simply redirect the user to the `<ingress>/auth0/login` and it will redirect you to the Auth0 login page, then bring you back to your application with the cookie set.
Check user status: With the endpoint in the backend implemented, in the frontend, you can easily have a function as follows to determine if a user is signed in.
Oathkeeper is a useful open-source tool that can be integrated with Auth0 to create an elegant solution for decoupling user authentication and application logic. This approach allows you to quickly build additional services sharing the user authentication setup. Consider this method when building your next application!
David Cheung is an Engineering Partner at Commit. He has been in software development for over a decade honing his expertise in full-stack development, and is passionate about open source projects.
Also Published Here