Laravel AT Protocol Client (alpha & unstable)
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Rename metadata_url to client_id and add localhost detection

+110 -16
+21 -7
config/client.php
··· 3 3 return [ 4 4 /* 5 5 |-------------------------------------------------------------------------- 6 - | Client Metadata 6 + | Client Configuration 7 7 |-------------------------------------------------------------------------- 8 8 | 9 - | OAuth client configuration. The metadata URL must be publicly accessible 10 - | and serve the client-metadata.json file. 9 + | OAuth client configuration. The client_id is a URL that serves as the 10 + | unique identifier for your OAuth client. In production, this must be 11 + | an HTTPS URL pointing to your publicly accessible client metadata. 12 + | 13 + | For local development, use 'http://localhost' (no port) as the client_id. 14 + | The redirect_uri for localhost must use 127.0.0.1 with a port. 15 + | 16 + | @see https://atproto.com/specs/oauth#clients 11 17 | 12 18 */ 13 19 'client' => [ 14 20 'name' => env('ATP_CLIENT_NAME', config('app.name')), 15 21 'url' => env('ATP_CLIENT_URL', config('app.url')), 16 - 'metadata_url' => env('ATP_CLIENT_METADATA_URL'), 17 - 'redirect_uris' => [ 18 - env('ATP_CLIENT_REDIRECT_URI', config('app.url').'/auth/atp/callback'), 19 - ], 22 + 23 + // The client_id is the URL to your client metadata document. 24 + // For production: 'https://example.com/oauth/client-metadata.json' 25 + // For localhost: 'http://localhost' (exactly, no port) 26 + 'client_id' => env('ATP_CLIENT_ID'), 27 + 28 + // Redirect URIs for OAuth callback. 29 + // For localhost development, use 'http://127.0.0.1:<port>/callback' 30 + 'redirect_uris' => array_filter([ 31 + env('ATP_CLIENT_REDIRECT_URI'), 32 + ]), 33 + 20 34 'scopes' => ['atproto', 'transition:generic'], 21 35 ], 22 36
+89 -9
src/Auth/ClientMetadataManager.php
··· 2 2 3 3 namespace SocialDept\AtpClient\Auth; 4 4 5 + /** 6 + * Manages OAuth client metadata for AT Protocol authentication. 7 + * 8 + * The client_id in atproto OAuth is a URL that serves as both the unique 9 + * identifier and the location of the client metadata document. 10 + * 11 + * For production: Use an HTTPS URL pointing to your client metadata. 12 + * For localhost: Use exactly 'http://localhost' (no port). 13 + * 14 + * @see https://atproto.com/specs/oauth#clients 15 + */ 5 16 class ClientMetadataManager 6 17 { 7 18 /** 8 - * Get the client ID (typically the client URL) 19 + * Get the client ID (URL to client metadata document). 20 + * 21 + * For production clients, this is an HTTPS URL like: 22 + * 'https://example.com/oauth/client-metadata.json' 23 + * 24 + * For localhost development, this must be exactly 'http://localhost' 25 + * (no port number allowed per atproto spec). 9 26 */ 10 27 public function getClientId(): string 11 28 { 12 - return config('client.client.url'); 29 + $clientId = config('client.client.client_id'); 30 + 31 + if ($clientId) { 32 + return $clientId; 33 + } 34 + 35 + // Fall back to auto-generated client_id based on app URL 36 + return $this->generateClientId(); 13 37 } 14 38 15 39 /** 16 - * Get the client metadata URL 40 + * Check if this is a localhost development client. 17 41 */ 18 - public function getMetadataUrl(): ?string 42 + public function isLocalhost(): bool 19 43 { 20 - return config('client.client.metadata_url'); 44 + return $this->getClientId() === 'http://localhost'; 21 45 } 22 46 23 47 /** 24 - * Get the redirect URIs 48 + * Get the redirect URIs. 49 + * 50 + * For localhost development, redirect URIs must use 127.0.0.1 51 + * (not localhost) and can include a port number. 25 52 * 26 53 * @return array<string> 27 54 */ 28 55 public function getRedirectUris(): array 29 56 { 30 - return config('client.client.redirect_uris', []); 57 + $uris = config('client.client.redirect_uris', []); 58 + 59 + if (! empty($uris)) { 60 + return $uris; 61 + } 62 + 63 + // Default redirect URI based on environment 64 + if ($this->isLocalhost()) { 65 + // For localhost, use 127.0.0.1 66 + return ['http://127.0.0.1']; 67 + } 68 + 69 + // For production, use app URL 70 + return [config('client.client.url').'/auth/atp/callback']; 31 71 } 32 72 33 73 /** 34 - * Get the OAuth scopes 74 + * Get the OAuth scopes. 35 75 * 36 76 * @return array<string> 37 77 */ ··· 41 81 } 42 82 43 83 /** 44 - * Get the client metadata as an array 84 + * Get the client metadata as an array. 85 + * 86 + * This is the structure served at the client_id URL. 45 87 * 46 88 * @return array<string, mixed> 47 89 */ ··· 62 104 'application_type' => 'web', 63 105 'dpop_bound_access_tokens' => true, 64 106 ]; 107 + } 108 + 109 + /** 110 + * Generate client_id from app configuration. 111 + * 112 + * In production, points to the package's client-metadata.json endpoint. 113 + * For localhost detection, checks if app URL contains localhost or .test. 114 + */ 115 + protected function generateClientId(): string 116 + { 117 + $appUrl = config('client.client.url') ?? config('app.url'); 118 + $host = parse_url($appUrl, PHP_URL_HOST); 119 + 120 + // Detect local development environments 121 + if ($this->isLocalDevelopment($host)) { 122 + return 'http://localhost'; 123 + } 124 + 125 + // Production: point to client metadata endpoint 126 + $prefix = config('client.oauth.prefix', '/atp/oauth/'); 127 + 128 + return rtrim($appUrl, '/').rtrim($prefix, '/').'/client-metadata.json'; 129 + } 130 + 131 + /** 132 + * Check if the host indicates a local development environment. 133 + */ 134 + protected function isLocalDevelopment(?string $host): bool 135 + { 136 + if (! $host) { 137 + return false; 138 + } 139 + 140 + return $host === 'localhost' 141 + || $host === '127.0.0.1' 142 + || str_ends_with($host, '.localhost') 143 + || str_ends_with($host, '.test') 144 + || str_ends_with($host, '.local'); 65 145 } 66 146 }