A community based topic aggregation platform built on atproto
1package oauth 2 3import ( 4 "encoding/json" 5 "net/http" 6 "os" 7 "strings" 8) 9 10// ClientMetadata represents OAuth 2.0 client metadata (RFC 7591) 11// Served at /oauth/client-metadata.json 12type ClientMetadata struct { 13 ClientID string `json:"client_id"` 14 ClientName string `json:"client_name"` 15 ClientURI string `json:"client_uri"` 16 RedirectURIs []string `json:"redirect_uris"` 17 GrantTypes []string `json:"grant_types"` 18 ResponseTypes []string `json:"response_types"` 19 Scope string `json:"scope"` 20 TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"` 21 TokenEndpointAuthSigningAlg string `json:"token_endpoint_auth_signing_alg"` 22 DpopBoundAccessTokens bool `json:"dpop_bound_access_tokens"` 23 ApplicationType string `json:"application_type"` 24 JwksURI string `json:"jwks_uri,omitempty"` // Only in production 25} 26 27// HandleClientMetadata serves the OAuth client metadata 28// GET /oauth/client-metadata.json 29func HandleClientMetadata(w http.ResponseWriter, r *http.Request) { 30 appviewURL := getAppViewURL() 31 32 // Determine client ID based on environment 33 clientID := getClientID(appviewURL) 34 jwksURI := "" 35 36 // Only include JWKS URI in production (not for loopback clients) 37 if !strings.HasPrefix(appviewURL, "http://localhost") && !strings.HasPrefix(appviewURL, "http://127.0.0.1") { 38 jwksURI = appviewURL + "/oauth/jwks.json" 39 } 40 41 metadata := ClientMetadata{ 42 ClientID: clientID, 43 ClientName: "Coves", 44 ClientURI: appviewURL, 45 RedirectURIs: []string{appviewURL + "/oauth/callback"}, 46 GrantTypes: []string{"authorization_code", "refresh_token"}, 47 ResponseTypes: []string{"code"}, 48 Scope: "atproto transition:generic", 49 TokenEndpointAuthMethod: "private_key_jwt", 50 TokenEndpointAuthSigningAlg: "ES256", 51 DpopBoundAccessTokens: true, 52 ApplicationType: "web", 53 JwksURI: jwksURI, 54 } 55 56 w.Header().Set("Content-Type", "application/json") 57 w.WriteHeader(http.StatusOK) 58 json.NewEncoder(w).Encode(metadata) 59} 60 61// getAppViewURL returns the public URL of the AppView 62func getAppViewURL() string { 63 url := os.Getenv("APPVIEW_PUBLIC_URL") 64 if url == "" { 65 // Default to localhost for development 66 url = "http://localhost:8081" 67 } 68 return strings.TrimSuffix(url, "/") 69} 70 71// getClientID returns the OAuth client ID based on environment 72// For localhost development, use loopback client identifier 73// For production, use HTTPS URL to client metadata 74func getClientID(appviewURL string) string { 75 // Development: use loopback client (http://localhost?...) 76 if strings.HasPrefix(appviewURL, "http://localhost") || strings.HasPrefix(appviewURL, "http://127.0.0.1") { 77 return "http://localhost?redirect_uri=" + appviewURL + "/oauth/callback&scope=atproto%20transition:generic" 78 } 79 80 // Production: use HTTPS URL to client metadata 81 return appviewURL + "/oauth/client-metadata.json" 82}