···1414| `ENABLE_OAUTH_AUTO_REGISTER` | Optional | `false` | Enable automatic OAuth client registration with AIP on server boot |
1515| `OAUTH_CLIENT_ID` | Optional | - | OAuth client ID (auto-registered if `ENABLE_OAUTH_AUTO_REGISTER=true`) |
1616| `OAUTH_CLIENT_SECRET` | Optional | - | OAuth client secret (auto-registered if `ENABLE_OAUTH_AUTO_REGISTER=true`) |
1717-| `OAUTH_REDIRECT_URI` | Optional | `http://localhost:8000/oauth/callback` | OAuth callback URL |
1717+| `EXTERNAL_BASE_URL` | Optional | `http://localhost:8000` | Base URL of your application (used for OAuth redirect: `${EXTERNAL_BASE_URL}/oauth/callback`) |
1818| `AIP_BASE_URL` | Optional | `https://auth.example.com` | AT Protocol Identity Provider URL |
1919| `JETSTREAM_URL` | No | `wss://jetstream2.us-west.bsky.network/subscribe` | Jetstream WebSocket endpoint |
2020| `RELAY_URL` | No | `https://relay1.us-west.bsky.network` | AT Protocol relay URL |
···166166# OAuth - Option A: Auto-registration (recommended)
167167ENABLE_OAUTH_AUTO_REGISTER=true
168168AIP_BASE_URL=https://your-aip-server.com
169169-OAUTH_REDIRECT_URI=https://your-app.up.railway.app/oauth/callback
169169+EXTERNAL_BASE_URL=https://your-app.up.railway.app
170170171171# OAuth - Option B: Manual configuration
172172# OAUTH_CLIENT_ID=your_client_id
173173# OAUTH_CLIENT_SECRET=your_client_secret
174174-# OAUTH_REDIRECT_URI=https://your-app.up.railway.app/oauth/callback
174174+# EXTERNAL_BASE_URL=https://your-app.up.railway.app
175175```
176176177177### 3. Add a volume
+3-3
example/register-oauth-client.sh
···96969797OAUTH_CLIENT_ID=$CLIENT_ID
9898OAUTH_CLIENT_SECRET=$CLIENT_SECRET
9999-OAUTH_REDIRECT_URI=$REDIRECT_URI
10099AIP_BASE_URL=$AIP_BASE
100100+EXTERNAL_BASE_URL=$CLIENT_BASE_URL
101101EOF
102102103103echo "✅ Client registration complete!"
···112112echo "🔧 Environment variables saved to $CONFIG_FILE:"
113113echo " OAUTH_CLIENT_ID"
114114echo " OAUTH_CLIENT_SECRET"
115115-echo " OAUTH_REDIRECT_URI"
116116-echo " OAUTH_AIP_BASE_URL"
115115+echo " AIP_BASE_URL"
116116+echo " EXTERNAL_BASE_URL"
117117echo
118118echo "💡 To use these credentials in your application:"
119119echo " source $CONFIG_FILE"
+4-3
server/.env.example
···1717# OAUTH_CLIENT_ID=your_client_id
1818# OAUTH_CLIENT_SECRET=your_client_secret
19192020-# OAuth Redirect URI (must match the URI registered with your OAuth provider)
2121-# Defaults to {EXTERNAL_BASE}/oauth/callback if not set
2222-# OAUTH_REDIRECT_URI=http://localhost:8000/oauth/callback
2020+# External Base URL - used to construct the OAuth redirect URI
2121+# The redirect URI will be: ${EXTERNAL_BASE_URL}/oauth/callback
2222+# Defaults to http://localhost:8000 if not set
2323+# EXTERNAL_BASE_URL=http://localhost:8000
23242425# Admin DIDs (comma-separated list of DIDs that have admin access to backfill, GraphiQL, etc.)
2526ADMIN_DIDS=did:plc:example1,did:plc:example2
+20-19
server/src/server.gleam
···284284 Error(_) -> "https://auth.example.com"
285285 }
286286287287+ // Get HOST and PORT from environment variables or use defaults
288288+ let host = case envoy.get("HOST") {
289289+ Ok(h) -> h
290290+ Error(_) -> "127.0.0.1"
291291+ }
292292+293293+ let port = case envoy.get("PORT") {
294294+ Ok(p) ->
295295+ case int.parse(p) {
296296+ Ok(port_num) -> port_num
297297+ Error(_) -> 8000
298298+ }
299299+ Error(_) -> 8000
300300+ }
301301+287302 // OAuth configuration - check environment variables first for backwards compatibility
288303 let enable_auto_register = case envoy.get("ENABLE_OAUTH_AUTO_REGISTER") {
289304 Ok(val) -> val == "true" || val == "1"
290305 Error(_) -> False
291306 }
292307293293- // Determine redirect URI from environment or use default
294294- let oauth_redirect_uri = case envoy.get("OAUTH_REDIRECT_URI") {
295295- Ok(uri) -> uri
296296- Error(_) -> "http://localhost:8000/oauth/callback"
308308+ // Determine redirect URI from EXTERNAL_BASE_URL environment variable
309309+ let oauth_redirect_uri = case envoy.get("EXTERNAL_BASE_URL") {
310310+ Ok(base_url) -> base_url <> "/oauth/callback"
311311+ Error(_) ->
312312+ "http://" <> host <> ":" <> int.to_string(port) <> "/oauth/callback"
297313 }
298314299315 let oauth_config = case
···395411 }
396412 }
397413 }
398398- }
399399-400400- // Get HOST and PORT from environment variables or use defaults
401401- let host = case envoy.get("HOST") {
402402- Ok(h) -> h
403403- Error(_) -> "127.0.0.1"
404404- }
405405-406406- let port = case envoy.get("PORT") {
407407- Ok(p) ->
408408- case int.parse(p) {
409409- Ok(port_num) -> port_num
410410- Error(_) -> 8000
411411- }
412412- Error(_) -> 8000
413414 }
414415415416 logging.log(logging.Info, "[server] Using AIP server: " <> auth_base_url)