Edit: added demo gif below

As a serial self-hoster, I've spent the last many years building my homelab and becoming as self-reliant as possible for my personal and professional digital services. But lately, I've been toying with the idea of opening up my home network so I can share the apps and infrastructure I have with my friends and family. Naturally, this implies some kind of VPN access with an identity layer.

Tailscale has been my default answer for this ever since I found out it existed about half a decade ago, since it's pretty much networking magic but also with a great product experience. Traditional VPNs (like OpenVPN) proxy all traffic through the VPN server (hub-and-spoke), whereas Tailscale uses a server solely for coordination of any two devices that can be in any sort of network, private or public (mesh networking). Once the devices can form a connection with each other, they can go on to form a secure tunnel using the power of WireGuard. This way, the only role of the coordination server is to help devices find each other and maintain an ACL so we ensure that only devices bound to a permitted identity can enter the network.

This is pretty high level tl;dr, for a full technical deep dive refer to this definitive guide

This is great for corporate IT departments, since they already maintain some sort of Active Directory, and can tie this straight into the coordination server. The trick to doing this is the Open ID Connect (OIDC) standard , which is supported by applications for the purpose of integrating your custom corporate identity layer. For example, if my corporation uses Entra AD, I can use it to connect to my Github enterprise and make sure access control is seamless.

While this works fine for corporations which have a clear way of identity provisioning, since a company has a fixed set of employees. It doesn't quite work for users of the internet who have all kinds of disparate identities. Social identities are more correctly shaped for this, and Atproto's Decentralised Identity (DID) has all the required tools (and social benefits) for this. Specifically, DIDs are self-sovereign because they aren't minted by a specific corporation (such as signing in with Github, Google, etc.) but rather by a Personal Data Server that you can choose a provider for or self-host, like I do. Crucially, Atproto identity is portable so long as you maintain your cryptographic identity.

So instead of a fixed set of corporate identities, we can use a generic identity that everyone on the internet has (hopefully soon when everyone has an Atmosphere account). Now any authentication that needs to happen for a typical Atproto application occurs through the PDS, which uses OAuth DPoP. However, OAuth and OIDC serve different needs, since OAuth is a tool for granting applications access to protected resources or performing actions on behalf of the user whereas OIDC lets an application verify a user's identity by relying on a trusted identity provider's (IdP) login, without having to handle credentials itself.

Atproto PDS' do not typically act as an OIDC IdP, but they have everything that's required to act in that capacity. Hence I built at-oidc, an OIDC shim for the PDS that negotiates OAuth with the PDS, but translates it to OIDC for applications that require it for sign in, such as Tailscale. It provides a whitelist for the admin to add approved identities. Additionally, it creates a WebFinger resource for the identity. Webfinger is a protocol used for discovering information about people and things identified by a URI. It serves as the discovery protocol for OIDC, where an application can use just an email to find out about their identity issuer, i.e. the server that provides auth tokens.

Note that I'm handwaving the role of the browser in these flows, and rejection paths are omitted

Now let's tie it all together:

  • Setup

    • the administrator sets up the at-oidc server https://at-oidc.example.io, and registers some client applications

    • they add the DIDs of the accounts they want to approve into the whitelist

    • the whitelist generates an email that has a webfinger resource attached to it

  • Login

    • a user uses the at-oidc email to login to one of the connected applications

    • the application resolves the email to a Webfinger discovery request, for e.g. https://at-mesh.example.io/.well-known/webfinger?resource=acct:john.bsky.social@at-mesh.example.io

    • at-oidc responds with the issuer details

    • the client application redirects the user to the issuer auth server, https://at-mesh.example.io, with the client ID, user email, and PKCE details to prove client provenance

    • at-oidc presents a page to the user where they can enter their Atproto handle, john.bsky.social, which can be resolved into the corresponding DID

    • if the DID exists in the whitelist, the user is sent to their PDS login page so they can authorise

    • when that succeeds, at-mesh unpacks the session data to ensure that the did and corresponding handle matches the whitelist

    • finally, this information is used to generate an OIDC auth code, which is sent back to client application using the registered redirect url

So now we're able to create what resembles an organisation for OIDC purposes and use it to invite people using their social identity. at-oidc comes with sample configs for both Tailscale and Headscale (for which you have to manually specify issuer in the config instead of using WebFinger).

Future work involves adding access management tools for the admin, such as expiring access, scopes for access to apps, and so on. Crucially, evicting someone from the whitelist doesn't remove them from connected applications, which has to be done manually!

I do wonder about having this capability directly within the PDS, so that you can register apps to it and provide limited access to users outside that PDS. Sort of like inviting someone to your digital home for a visit.

Code available here.

Attributions

Diagrams made with the very awesome Lexidraw.

Employees icons by Vector Stall, GitHub icons by Dave Gandy, Slack chat icons by Enamo Studios, Phone/Computer icons by Magnific, Computer icons by Vectors Market, and Video game icons by Flat Icons, all via Flaticon.