Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql
at main 111 lines 3.2 kB view raw
1/// Client metadata endpoint for ATProtocol OAuth 2/// GET /oauth-client-metadata.json 3import gleam/json 4import gleam/option.{type Option, None, Some} 5import wisp 6 7/// Client metadata response 8pub type ClientMetadataResponse { 9 ClientMetadataResponse( 10 client_id: String, 11 client_name: String, 12 redirect_uris: List(String), 13 grant_types: List(String), 14 response_types: List(String), 15 scope: String, 16 token_endpoint_auth_method: String, 17 token_endpoint_auth_signing_alg: String, 18 subject_type: String, 19 application_type: String, 20 dpop_bound_access_tokens: Bool, 21 jwks: Option(json.Json), 22 jwks_uri: Option(String), 23 ) 24} 25 26/// Generate client metadata 27pub fn generate_metadata( 28 client_id: String, 29 client_name: String, 30 redirect_uris: List(String), 31 scope: String, 32 jwks: Option(json.Json), 33 jwks_uri: Option(String), 34) -> ClientMetadataResponse { 35 ClientMetadataResponse( 36 client_id: client_id, 37 client_name: client_name, 38 redirect_uris: redirect_uris, 39 grant_types: ["authorization_code", "refresh_token"], 40 response_types: ["code"], 41 scope: scope, 42 token_endpoint_auth_method: "private_key_jwt", 43 token_endpoint_auth_signing_alg: "ES256", 44 subject_type: "public", 45 application_type: "web", 46 dpop_bound_access_tokens: True, 47 jwks: jwks, 48 jwks_uri: jwks_uri, 49 ) 50} 51 52/// Encode client metadata as JSON 53pub fn encode_metadata(metadata: ClientMetadataResponse) -> json.Json { 54 let base_fields = [ 55 #("client_id", json.string(metadata.client_id)), 56 #("client_name", json.string(metadata.client_name)), 57 #("redirect_uris", json.array(metadata.redirect_uris, json.string)), 58 #("grant_types", json.array(metadata.grant_types, json.string)), 59 #("response_types", json.array(metadata.response_types, json.string)), 60 #("scope", json.string(metadata.scope)), 61 #( 62 "token_endpoint_auth_method", 63 json.string(metadata.token_endpoint_auth_method), 64 ), 65 #( 66 "token_endpoint_auth_signing_alg", 67 json.string(metadata.token_endpoint_auth_signing_alg), 68 ), 69 #("subject_type", json.string(metadata.subject_type)), 70 #("application_type", json.string(metadata.application_type)), 71 #("dpop_bound_access_tokens", json.bool(metadata.dpop_bound_access_tokens)), 72 ] 73 74 let with_jwks = case metadata.jwks { 75 Some(jwks) -> [#("jwks", jwks), ..base_fields] 76 None -> base_fields 77 } 78 79 let with_jwks_uri = case metadata.jwks_uri { 80 Some(uri) -> [#("jwks_uri", json.string(uri)), ..with_jwks] 81 None -> with_jwks 82 } 83 84 json.object(with_jwks_uri) 85} 86 87/// Handle GET /oauth-client-metadata.json 88pub fn handle( 89 base_url: String, 90 client_name: String, 91 redirect_uris: List(String), 92 scope: String, 93 jwks: Option(json.Json), 94 jwks_uri: Option(String), 95) -> wisp.Response { 96 let client_id = base_url <> "/oauth-client-metadata.json" 97 let metadata = 98 generate_metadata( 99 client_id, 100 client_name, 101 redirect_uris, 102 scope, 103 jwks, 104 jwks_uri, 105 ) 106 let json_response = encode_metadata(metadata) 107 108 wisp.response(200) 109 |> wisp.set_header("content-type", "application/json") 110 |> wisp.set_body(wisp.Text(json.to_string(json_response))) 111}