Lazurite's Auth Implementation

owais May 5, 2026
Source

I should be better at implementing auth in my apps by now.

Overview

The short of it is that auth has been a bit of a challenge recently, given OAuth, ATProto, and mobile app links each impose different constraints.

OAuth binds the authorization code to the chosen redirect URI.

ATProto adds decentralized PDS/auth-server discovery plus PAR, PKCE, DPoP, and public client metadata.

Mobile platforms then add messy return-to-app behavior. Custom schemes are reliable but not domain-verified, while Android App Links/iOS Universal Links are cleaner when verified but depend on signing, hosted association files, OS cache state, and browser behavior.

Lazurite has two auth paths

The configured redirect URIs are

Mobile builds prefer the HTTPS callback. The custom scheme remains in client metadata and in native link handling because it is still useful when the hosted HTTPS callback page has loaded in a browser and needs to hand the already-issued query parameters back to the app.

Flow

The authorization-server fallback chain is only valid before browser launch. If PAR fails before the browser launches, trying another candidate is acceptable.

Once PAR succeeds and the user leaves the app, the OAuth attempt is bound to that authorization server, state, PKCE verifier, DPoP nonce, and redirect URI. Callback failure after that point must fail the attempt without starting another browser flow.

Firefox

Firefox is a difficult redirect environment because it is not just the Android OS App Links verifier and has its own "open links in apps" behavior and can choose to keep links inside the browser even when the app is installed. In that state, OAuth redirects can land in limbo.

The "blank screen" is therefore is the browser/OS handoff failing or being repeated at the most fragile part of OAuth, after the authorization code has been issued but before the native app has completed token exchange.

Lazurite addresses this in the following ways

The target here is an entirely bounded failure such that there are no no repeated prompts, no repeated code redemption, no second OAuth launch, and enough UI/logging for a user to retry cleanly.

App-Links

Custom schemes are easy to register and useful as a fallback, but they are not domain verified. Any installed app can attempt to claim many custom schemes. They also leave more behavior up to the browser.

HTTPS App Links and Universal Links are better for OAuth because they bind the app to a domain.

The cost is operational, including signing fingerprints, associated-domain files, CDN/device caching, browser settings, and the line up of install state.

For both platforms

Browser And Hosted Callback Requirements

The hosted HTTPS callback page exists only as a fallback when the browser loads the HTTPS URL instead of the OS sending it directly to Lazurite.

Discussion in the ATmosphere

Loading comments...