Managing the Identity Entry Point for Self-Hosted Services with Rauthy

Disclaimer: The translated content below was generated by a large language model.

After gradually deploying services like Matrix, Forgejo, Crow CI, and GoToSocial on my server, a very natural question arose: each service has its own set of accounts, but is it really a good thing for every service to manage accounts on its own?

If it is just one service, the built-in account system is of course the simplest. But once there are many services, accounts, passwords, two-factor authentication, registration switches, user disabling, and permission changes end up scattered across different admin panels. For personal self-hosted services, this complexity comes from the number of systems and the differences in the management logic of each service itself. So I started needing a separate identity entry point to gradually converge these applications from “each service manages its own accounts” into “the application handles its own business, and the identity service handles login and authentication,” while using a single identity entry point to provide relatively consistent authentication strength.

The tool I currently chose is Rauthy. It is not like Keycloak, which emphasizes enterprise-grade directories, complex organizational structures, and extensive integration capabilities. Instead, it is more like an OIDC identity provider suited to individuals, small teams, and lightweight services.

 

A Few Related Concepts

Before deploying an identity service, the terms most easily confused with one another are OAuth2, OIDC, and IdP. They often appear together, but they do not solve exactly the same problems.

OAuth2 mainly addresses authorization. A typical example is: an application wants to access some of your resources in another service, such as reading your avatar, reading your email, or accessing a certain API. OAuth2 does not require you to give your password to this application; instead, the authorization server issues a token with limited scope and time validity. This token usually only represents “this client is allowed to do certain things,” and is not directly equivalent to “who this person is.”

OIDC, or OpenID Connect, is the identity layer built on top of OAuth2. It adds a standardized “login” and “identity claims” mechanism on top of the authorization flow, such as concepts like id_token, userinfo, issuer, subject, and claims. After an application integrates OIDC, it can hand over “who the user is, what the email is, what the display name is, and whether this login is trustworthy” to an external identity service.

An IdP is an Identity Provider, that is, an identity provider. For self-hosted services, it is the unified login entry point. Users log in to the IdP first, and then applications trust the identity information returned by the IdP. Services like Forgejo, Crow CI, Matrix, or GoToSocial that support OIDC can delegate their account entry point to the same IdP.

So when these three terms are put together, they can be roughly understood like this:

  1. OAuth2 is responsible for authorization, allowing clients to obtain access.
  2. OIDC is responsible for authentication on top of OAuth2, letting the client know who logged in.
  3. An IdP is the service itself that provides authentication capabilities.

In this relationship, Rauthy plays the role of the IdP. It issues tokens for other applications, provides user information, manages the login flow, and strives to make these things happen in a standard OIDC/OAuth2 way.

What Rauthy Is

The official description of Rauthy is that it is an identity and access management system that provides single sign-on through OpenID Connect, OAuth2, and PAM. Simply put, it is a self-hosted SSO service: users log in to Rauthy once, and then connect to different applications through OIDC.

What attracts me most is that its goals are very clear. It is not a huge system trying to replicate a full enterprise identity platform, but rather focuses on lightweight deployment, secure defaults, Passkey, an admin interface, and support for common protocols. By default, Rauthy can use its own embedded Hiqlite storage, or it can be switched to PostgreSQL. For my deployment environment, which already has a PostgreSQL container, the latter is easy to connect to; for someone who just wants to maintain one less database, the default option is also straightforward enough.

In principle, Rauthy sits between the user and the application:

  1. The user accesses Forgejo, Crow CI, or another OIDC-compatible application.
  2. The application redirects the user to Rauthy’s authorization endpoint.
  3. Rauthy completes login, MFA, or Passkey verification.
  4. Rauthy returns the authorization code to the application.
  5. The application exchanges the authorization code for a token, and then confirms the user’s identity based on the token and userinfo.

The advantage of this process is that the application does not need to handle passwords itself, nor does it need to implement Passkey on its own. It only needs to trust Rauthy as the issuer and correctly validate the token to obtain a relatively standard login capability.

Rauthy’s feature scope is not limited to web applications. It supports OIDC/OAuth2, supports login methods more suitable for CLIs or headless devices such as OAuth2 Device Authorization Grant, and also provides PAM/NSS-related modules for Linux login scenarios. But for my self-hosted services, the core thing is still OIDC.

Passkey Requirements and Trade-offs

Rauthy’s emphasis on Passkey is very obvious. It supports the traditional “password + security key” mode, and also supports Passkey-only accounts. The latter means the account can have no password, and login is completed using an email address and a FIDO2 Passkey.

However, Passkey-only is not something just any WebAuthn device can satisfy. Rauthy requires such Passkeys to provide User Verification. In practice, this usually means a platform passkey, a security key with PIN or biometric capability, or an authenticator that can confirm “this operation was indeed initiated by the current user.” Simply having the security key present, but being unable to verify the user’s identity, is not enough to constitute a complete Passkey-only login.

There is a detail that is easy to overlook: Rauthy does not encourage discoverable credentials. In other words, it tends to have the user enter an email address first, and then log in with the corresponding Passkey, rather than letting the authenticator enumerate available accounts by itself. The result is an extra email input step during login, but the storage slot pressure on the security key will be much lower. For devices like YubiKey, this is very practical, because the number of discoverable passkeys that different models and firmware versions can store is not unlimited.

Passkey deployment is also constrained by browser and TLS requirements. In production, a normally trusted HTTPS certificate should be used; during local testing, if a self-signed certificate is used, some browsers may not allow Passkey registration, or may require the test CA to be added to the trust list. An identity service is not an ordinary internal tool. If the login entry point relies from the start on temporary certificates, incorrect domain names, and browser exceptions, it is very easy for problems to be buried later in all sorts of “I don’t know why this device can’t log in” edge cases.

That said, I still do not think Passkey will immediately replace all two-factor authentication methods. At least in real-world use, the more common ones are still TOTP services like Google Authenticator, Aegis, 1Password, and Bitwarden Authenticator. They are not as phishing-resistant as Passkey, and they depend on shared secrets and time windows, but they win in compatibility, low migration cost, and broad support across almost all services. For self-hosted services, TOTP is still the most realistic MFA baseline; Passkey is more like a high-strength login method worth introducing gradually.

So my attitude is: Rauthy’s Passkey capability is worth using, but I should not push all accounts to Passkey-only right from the start. A more stable approach is to first get the main services working with password plus TOTP or a security key, and then gradually move my primary account to Passkey-only. Once the identity service becomes unavailable, the impact is on the entire self-hosted system, not just a single application.

Why It Is Needed

My self-hosted service setup is increasingly becoming more like a small personal infrastructure, rather than a few unrelated toy services.

Matrix handles chat and notifications, Forgejo handles code hosting, Crow CI handles build pipelines, and GoToSocial handles federated social networking. The next step I also want is to connect Vaultwarden, to store Passkeys, TOTP seeds, and some credentials that are not used often but cannot be lost. Each of them can create accounts on its own, and each of them has its own permissions and user profiles. But if every system maintains accounts separately, several problems will quickly appear.

The first is uncontrolled registration entry points. Matrix may have one registration policy, Forgejo another, and GoToSocial yet another. As long as one service forgets to disable registration, or its configuration does not match expectations, it may expose an entry point that was not meant to be open.

The second is that the account lifecycle is fragmented. Adding a user requires going through multiple admin panels separately; disabling a user also means remembering to handle each system. For public services, this is a security concern; for private services, it is at least a maintenance burden.

The third is that two-factor authentication and password policies are hard to unify. Some services support Passkey, some only support TOTP, and some support it poorly. After unifying the login entry point under Rauthy, at least the main authentication policies can be centralized in one place, instead of being determined by the implementation quality of each application.

The fourth is that future expansion becomes easier. Today it is Matrix, Forgejo, Crow CI, and GoToSocial; tomorrow it may be Vaultwarden, MinIO, Grafana, Paperless, Nextcloud, or other internal tools. As long as the new service supports OIDC, the integration cost stays relatively stable: create a client, set the redirect URI, configure scopes, confirm claim mapping, and then point the application’s login entry point to Rauthy.

This is also the core reason I chose Rauthy. It is not just “deploying another login page,” but rather extracting the authentication method of future self-hosted services into a layer of shared infrastructure. That way, every time a new service is added, I do not need to rethink the account system from scratch.

Combining It with Cloudflare Access

There is another usage pattern worth calling out separately: instead of connecting every application directly to Rauthy, Cloudflare Access can connect to Rauthy first, and then some web services can sit behind Cloudflare’s proxy and Access rules.

Cloudflare One supports generic OIDC identity providers. According to Cloudflare’s documentation, the setup starts by creating an OIDC client for Cloudflare in Rauthy. The redirect URI should be Cloudflare Access’s callback:

https://<your-team-name>.cloudflareaccess.com/cdn-cgi/access/callback

Then, in Cloudflare Zero Trust under Identity providers, choose OpenID Connect and fill in the authorization endpoint, token endpoint, JWKS endpoint, client ID, and client secret from Rauthy’s OIDC discovery information. When someone visits a domain protected by Cloudflare Access, Cloudflare intercepts the request first. The user is sent to Rauthy for login and MFA, and Cloudflare then evaluates the Access policy before allowing the request through to the origin service.

In effect, this is somewhat like a Rauthy Forward Auth setup: web services that do not have built-in OIDC support, or whose login pages I do not want to expose directly, can be protected by a front authentication layer. The difference is that this layer is handled by Cloudflare Access instead of a custom forward-auth setup in my own reverse proxy. That makes it fit more naturally with Cloudflare Tunnel, Access policies, audit logs, device posture, session policies, and Gateway policies, while Rauthy continues to focus on being the upstream IdP.

This combination also works better with Cloudflare One Client. For private web applications accessed through Cloudflare One Client, the same Rauthy identity source and Access policies can be reused: whether the user is logged in, whether the session has expired, and whether the device satisfies the policy can all be handled at the Cloudflare layer. Applications that directly support OIDC can still log in through Rauthy on their own; internal panels, temporary tools, and admin entry points that are not worth modifying can be placed behind Cloudflare Access first.

This is not about replacing Rauthy with Cloudflare. More precisely, Rauthy remains responsible for “who this user is,” while Cloudflare Access is responsible for “whether this request can enter this doorway.” For personal self-hosted services, that boundary is useful: core applications can trust Rauthy directly, while edge entry points and applications without OIDC support can use Cloudflare as a front gate.

Relationship with Existing Services

Different services connect to OIDC in slightly different ways, but the overall idea is similar.

A code hosting platform like Forgejo is suitable for using Rauthy as an OAuth2/OIDC login source. In this way, users can enter the code platform through a unified identity entry point, while Forgejo still handles repository permissions, organizations, issues, pull requests, and other business permissions.

Crow CI’s relationship is better treated as an extension after Forgejo, rather than becoming another independent identity entry point directly. The current flow is more like: Rauthy first provides login for Forgejo, and then Crow CI relies on Forgejo for authentication and repository authorization. In this way, Crow CI’s user identity can be reused from Forgejo, and a Forgejo user can also link a GitHub account. This design is very important for a small personal self-hosted setup, because it does not require me to migrate all code and historical projects to Forgejo immediately just to use Crow CI. Forgejo can first become the self-hosted control plane, while content that has not yet moved off GitHub can also be gradually connected to the pipeline.

Matrix is a bit more complicated. I originally used Synapse’s built-in account and admin methods, but if the upstream identity is to be handed over to Rauthy, I need to migrate to Matrix Authentication Service. MAS itself is the OAuth2/OIDC service used while Matrix is migrating to the OIDC authentication layer. It can serve as the authentication layer between Matrix clients and the homeserver, and it can also connect to an upstream OIDC Provider like Rauthy through upstream_oauth2.providers.

This migration will encounter a very typical “which came first, the chicken or the egg” problem: inside Rauthy, the MAS OIDC client must be created first; MAS then needs to be configured correctly with its own issuer, redirect URI, homeserver shared secret, and database; after Synapse switches to MAS, the login and registration entry points will change, but the administrator account, existing users, client login state, and rollback path must all be thought through in advance. In other words, it cannot just be treated as “adding an OIDC provider in Synapse’s configuration.” The real chain is that Rauthy manages upstream identity, MAS manages Matrix’s OIDC authentication layer, and Synapse then works with MAS through shared secrets and compatible interfaces.

GoToSocial’s needs are more like a “community entry point.” An account on a federated social service is not a normal admin account; externally, it is the identity itself. The significance of OIDC integration here is not only having one less password to remember, but also moving joining, disabling, and authentication policies to a more forward position.

Vaultwarden is the one that needs the most caution later on. It is very suitable for storing Passkeys, TOTP seeds, recovery codes, backup passwords, and some low-frequency credentials, but there is a natural startup-order problem between it and Rauthy: if Vaultwarden’s login depends on Rauthy, and Rauthy’s recovery credentials exist only in Vaultwarden, then once either side fails to initialize or there is a certificate, callback address, or email configuration problem, you can lock yourself out of the system. A more reasonable approach is to first keep offline recovery materials and at least one break-glass account, and then consider connecting Vaultwarden to unified identity.

These services do not lose their own permission models just because they are connected to Rauthy. Rauthy is responsible for “who you are” and “whether you can log in,” while the application is still responsible for “what you can do in this application.” This boundary is very important. If you try to force all application-level permissions into the IdP, you will eventually turn the identity service into another complex platform.

Shortcomings and Real-World Problems

Rauthy is very suitable for the lightweight self-hosting scenario I currently have, but it is not without issues.

The most obvious shortcoming at the moment is that its registration policy is not fine-grained enough. It supports disabling registration, also supports open registration, and supports a certain degree of domain-based registration restriction. But for personal services or small communities, what is often really needed is an intermediate state: invitation-code registration, admin approval after application, only existing users can invite, new registrations enter a pending-review state first, or different registration policies by client.

Domain restrictions are useful for corporate email or school email, but they are not always suitable for personal domains, small communities, and services among friends. Completely open registration is too risky, while completely closed registration means new users can only be created manually by an administrator. These in-between workflows are exactly what self-hosted services most often need.

The administrator capabilities are also relatively weak. Rauthy has an Admin UI and can manage users, clients, events, and some security options, but compared with mature enterprise IdPs, its admin workflow is still fairly plain. For example, bulk operations, review queues, more detailed invitation flows, clearer user lifecycle states, stronger audit filtering, and recovery tools are not its strongest areas at the moment.

This does not mean Rauthy is unusable. On the contrary, for me its lightweight nature and secure defaults are more important than a complex backend. But it is indeed more like a strong authentication core than a complete community user management system. If it is to be used for services with public registration, the surrounding workflow still needs to be carefully designed, and it may even require an additional layer for invitations or approvals.

Current Positioning

My current expectations for Rauthy are restrained: first let it become the unified login entry point for self-hosted services, instead of asking it from the beginning to carry all imagined user management responsibilities.

In the short term, it is responsible for a few things:

  1. Unify the login entry points for services such as Matrix, Forgejo, Crow CI, and GoToSocial.
  2. Centrally manage my primary account, security keys, and Passkeys.
  3. Provide a stable OIDC integration method for services added later.
  4. Act as the upstream IdP for front access control layers such as Cloudflare Access.
  5. Reduce the need for each application to store its own passwords separately.

In the long term, if its registration and administrator capabilities continue to improve, it may become a more complete personal identity hub. But even if it stays at its current stage, it has already solved a very practical problem: as self-hosted services grow in number, identity should no longer be scattered in the corners of each application.

The value of Rauthy is not that it replaces all account systems, but that it provides these systems with a common entry point. For personal self-hosted services, this is already a key step from “a few separate containers” toward “maintainable infrastructure.”

评论系统尚未配置。请在 .env 中填写 giscus 所需的环境变量。