Your music, beautifully tracked. All yours. (coming soon) teal.fm
teal-fm atproto

oauth changes!

+101 -36
+12
apps/amethyst/assets/client-metadata.json
··· 1 + { 2 + "redirect_uris": ["https://alpha.teal.fm/oauth/callback"], 3 + "response_types": ["code"], 4 + "grant_types": ["authorization_code", "refresh_token"], 5 + "scope": "atproto transition:generic", 6 + "token_endpoint_auth_method": "none", 7 + "application_type": "web", 8 + "client_id": "https://alpha.teal.fm/client-metadata.json", 9 + "client_name": "teal", 10 + "client_uri": "https://alpha.teal.fm", 11 + "dpop_bound_access_tokens": true 12 + }
+38 -35
apps/amethyst/lib/atp/oauth.tsx
··· 19 19 } 20 20 let meta: ClientMetadata; 21 21 22 - const isWeb = Platform.OS === "web"; 23 - const u = new URL(baseUrl); 24 - let hostname = u.hostname; 25 - if (hostname == "localhost") { 26 - hostname = "127.0.0.1"; 22 + const isWeb = Platform.OS === "web"; 23 + const u = new URL(baseUrl); 24 + let hostname = u.hostname; 25 + if (hostname == "localhost") { 26 + hostname = "127.0.0.1"; 27 + } 28 + let redirect = `${u.protocol}//${hostname}`; 29 + if (u.port !== "") { 30 + redirect = `${redirect}:${u.port}`; 31 + } 32 + if (isWeb) { 33 + redirect = `${redirect}/auth/callback`; 34 + } else { 35 + const scheme = Constants.expoConfig?.scheme; 36 + if (!scheme) { 37 + throw new Error("unable to resolve scheme for oauth redirect"); 27 38 } 28 - let redirect = `${u.protocol}//${hostname}`; 29 - if (u.port !== "") { 30 - redirect = `${redirect}:${u.port}`; 31 - } 32 - if (isWeb) { 33 - redirect = `${redirect}/auth/callback`; 34 - } else { 35 - const scheme = Constants.expoConfig?.scheme; 36 - if (!scheme) { 37 - throw new Error("unable to resolve scheme for oauth redirect"); 38 - } 39 - redirect = `${redirect}/app-return/${scheme}`; 40 - } 41 - const queryParams = new URLSearchParams(); 42 - queryParams.set("scope", "atproto transition:generic"); 43 - queryParams.set("redirect_uri", redirect); 44 - meta = { 45 - client_id: `http://localhost?${queryParams.toString()}`, 46 - redirect_uris: [redirect as any], 47 - scope: "atproto transition:generic", 48 - token_endpoint_auth_method: "none", 49 - client_name: "Loopback client", 50 - response_types: ["code"], 51 - grant_types: ["authorization_code", "refresh_token"], 52 - // > There is a special exception for the localhost development workflow [ ... ] 53 - // > These clients use web URLs, but have application_type set to native in the generated client metadata. 54 - application_type: "native", 55 - dpop_bound_access_tokens: true, 56 - }; 39 + redirect = `${redirect}/app-return/${scheme}`; 40 + } 41 + const queryParams = new URLSearchParams(); 42 + queryParams.set("scope", "atproto transition:generic"); 43 + queryParams.set("redirect_uri", redirect); 44 + meta = { 45 + client_id: 46 + hostname === "localhost" 47 + ? `http://localhost?${queryParams.toString()}` 48 + : `https://${hostname}/client-metadata.json`, 49 + redirect_uris: [redirect as any], 50 + scope: "atproto transition:generic", 51 + token_endpoint_auth_method: "none", 52 + client_name: "Amethyst", 53 + response_types: ["code"], 54 + grant_types: ["authorization_code", "refresh_token"], 55 + // > There is a special exception for the localhost development workflow [ ... ] 56 + // > These clients use web URLs, but have application_type set to native in the generated client metadata. 57 + application_type: hostname === "localhost" ? "native" : "web", 58 + dpop_bound_access_tokens: true, 59 + }; 57 60 clientMetadataSchema.parse(meta); 58 61 return new ReactNativeOAuthClient({ 59 62 handleResolver: "https://bsky.social", // backend instances should use a DNS based resolver ··· 63 66 // "client_id" endpoint (except when using a loopback client) 64 67 clientMetadata: meta, 65 68 }); 66 - } 69 + }
+48
apps/amethyst/postbuild.py
··· 1 + import json 2 + import os 3 + import sys 4 + 5 + def update_metadata(): 6 + # Get CF_PAGES_URL from environment 7 + cf_pages_url = os.environ.get('CF_PAGES_URL') 8 + 9 + if not cf_pages_url: 10 + print("CF_PAGES_URL environment variable not found") 11 + sys.exit(1) 12 + 13 + # Remove 'https://' if present 14 + if cf_pages_url.startswith('https://'): 15 + cf_pages_url = cf_pages_url[8:] 16 + 17 + # Path to metadata file 18 + metadata_path_pre = 'assets/client-metadata.json' 19 + metadata_path = 'dist/client-metadata.json' 20 + 21 + try: 22 + # Read the JSON file 23 + with open(metadata_path_pre, 'r') as file: 24 + metadata = json.load(file) 25 + 26 + # Replace all instances of 'alpha.teal.fm' with CF_PAGES_URL 27 + metadata_str = json.dumps(metadata) 28 + updated_metadata_str = metadata_str.replace('alpha.teal.fm', cf_pages_url) 29 + updated_metadata = json.loads(updated_metadata_str) 30 + 31 + # Write the updated JSON back to file 32 + with open(metadata_path, 'w') as file: 33 + json.dump(updated_metadata, file, indent=2) 34 + 35 + print(f"Successfully updated {metadata_path} with {cf_pages_url}") 36 + 37 + except FileNotFoundError: 38 + print(f"Error: {metadata_path} not found") 39 + sys.exit(1) 40 + except json.JSONDecodeError: 41 + print(f"Error: Invalid JSON in {metadata_path}") 42 + sys.exit(1) 43 + except Exception as e: 44 + print(f"Error: {str(e)}") 45 + sys.exit(1) 46 + 47 + if __name__ == "__main__": 48 + update_metadata()
+3 -1
apps/amethyst/stores/authenticationSlice.tsx
··· 34 34 set, 35 35 get, 36 36 ) => { 37 - const initialAuth = createOAuthClient("http://localhost:8081"); 37 + // check if we have CF_PAGES_URL set. if not, use localhost 38 + const baseUrl = process.env.CF_PAGES_URL || "http://localhost:8081"; 39 + const initialAuth = createOAuthClient(baseUrl); 38 40 39 41 console.log("Auth client created!"); 40 42