From 3f8ce57e05069868cafb6b036e5016422dda21ea Mon Sep 17 00:00:00 2001 From: Hayleigh Thompson Date: Sat, 8 Aug 2026 09:52:48 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Make=20sure=20oauth=20clie?= =?UTF-8?q?nt=20and=20oauthorisation=20server=20serde=20is=20consistent.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/at/oauth.gleam | 161 ++++++++++++++++++++++++--------------------- 1 file changed, 86 insertions(+), 75 deletions(-) diff --git a/src/at/oauth.gleam b/src/at/oauth.gleam index 489e408..130dbb7 100644 --- a/src/at/oauth.gleam +++ b/src/at/oauth.gleam @@ -3,6 +3,7 @@ import at/did.{type Did} import at/did_document.{type DidDocument} import at/internal/decoding +import at/internal/encoding import gleam/bit_array import gleam/bool import gleam/dynamic/decode.{type Decoder} @@ -252,6 +253,49 @@ fn unvalidated_authorisation_server_decoder() -> Decoder(AuthorisationServer) { )) } +fn authorisation_server_to_json(server: AuthorisationServer) -> Json { + let AuthorisationServer( + issuer:, + authorisation_endpoint:, + token_endpoint:, + response_types_supported:, + grant_types_supported:, + scopes_supported:, + pushed_authorization_request_endpoint:, + ) = server + + json.object([ + #("issuer", json.string(uri.to_string(issuer))), + #("authorization_endpoint", { + json.string(uri.to_string(authorisation_endpoint)) + }), + #("token_endpoint", json.string(uri.to_string(token_endpoint))), + #("response_types_supported", { + json.array(set.to_list(response_types_supported), json.string) + }), + #("grant_types_supported", { + json.array(set.to_list(grant_types_supported), json.string) + }), + #("token_endpoint_auth_methods_supported", { + json.array(["none", "private_key_jwt"], json.string) + }), + #("code_challenge_methods_supported", json.array(["S256"], json.string)), + #("token_endpoint_auth_signing_alg_values_supported", { + json.array(["ES256"], json.string) + }), + #("scopes_supported", { + json.array(set.to_list(scopes_supported), json.string) + }), + #("authorization_response_iss_parameter_supported", json.bool(True)), + #("require_pushed_authorization_requests", json.bool(True)), + #("pushed_authorization_request_endpoint", { + json.string(uri.to_string(pushed_authorization_request_endpoint)) + }), + #("dpop_signing_alg_values_supported", json.array(["ES256"], json.string)), + #("client_id_metadata_document_supported", json.bool(True)), + ]) +} + /// /// pub fn supported_grant_types(server: AuthorisationServer) -> Set(String) { @@ -405,7 +449,7 @@ fn client_to_id(client: Client) -> Uri { fn client_decoder() -> Decoder(Client) { use id <- decode.field("id", decoding.uri()) - use name <- decode.field("name", decode.optional(decode.string)) + use name <- decoding.optional_field("name", decode.string) use application_type <- decode.field("application_type", { decode.then(decode.string, fn(string) { case string { @@ -415,21 +459,21 @@ fn client_decoder() -> Decoder(Client) { } }) }) + use scopes <- decode.field("scopes", decoding.set(decode.string)) use response_types <- decode.field("response_types", { decoding.set(decode.string) }) use redirect_uri <- decode.field("redirect_uri", decoding.uri()) - use homepage <- decode.field("homepage", { decode.optional(decoding.uri()) }) - - use logo <- decode.field("logo", { decode.optional(decoding.uri()) }) - use terms_of_service <- decode.field("terms_of_service", { - decode.optional(decoding.uri()) + use homepage <- decoding.optional_field("homepage", decoding.uri()) + use logo <- decoding.optional_field("logo", decoding.uri()) + use terms_of_service <- decoding.optional_field("terms_of_service", { + decoding.uri() }) - use privacy_policy <- decode.field("privacy_policy", { - decode.optional(decoding.uri()) + use privacy_policy <- decoding.optional_field("privacy_policy", { + decoding.uri() }) decode.success(Client( @@ -446,6 +490,38 @@ fn client_decoder() -> Decoder(Client) { )) } +pub fn client_to_json(client: Client) -> Json { + encoding.optional_object( + [ + #("id", encoding.uri(client.id)), + #("application_type", { + json.string(case client.application_type { + Web -> "web" + Native -> "native" + }) + }), + #("scopes", encoding.set(client.scopes, json.string)), + #("response_types", encoding.set(client.response_types, json.string)), + #("redirect_uri", encoding.uri(client.redirect_uri)), + ], + [ + encoding.optional_field("name", client.name, json.string), + encoding.optional_field("homepage", client.homepage, encoding.uri), + encoding.optional_field("logo", client.logo, encoding.uri), + encoding.optional_field( + "terms_of_service", + client.terms_of_service, + encoding.uri, + ), + encoding.optional_field( + "privacy_policy", + client.privacy_policy, + encoding.uri, + ), + ], + ) +} + // PUSHED AUTHORISATION REQUESTS ----------------------------------------------- /// @@ -564,73 +640,8 @@ pub fn pending_authorisation_to_json( json.object([ #("subject", did_document.to_json(subject)), - #( - "client", - json.object([ - #("id", json.string(uri.to_string(client.id))), - #("name", case client.name { - Some(name) -> json.string(name) - None -> json.null() - }), - #("application_type", { - json.string(case client.application_type { - Web -> "web" - Native -> "native" - }) - }), - #("scopes", json.array(set.to_list(client.scopes), json.string)), - #( - "response_types", - json.array(set.to_list(client.response_types), json.string), - ), - #("redirect_uri", json.string(uri.to_string(client.redirect_uri))), - #("homepage", case client.homepage { - Some(homepage) -> json.string(uri.to_string(homepage)) - None -> json.null() - }), - #("logo", case client.logo { - Some(logo) -> json.string(uri.to_string(logo)) - None -> json.null() - }), - #("terms_of_service", case client.terms_of_service { - Some(terms_of_service) -> json.string(uri.to_string(terms_of_service)) - None -> json.null() - }), - #("privacy_policy", case client.privacy_policy { - Some(privacy_policy) -> json.string(uri.to_string(privacy_policy)) - None -> json.null() - }), - ]), - ), - #( - "server", - json.object([ - #("issuer", json.string(uri.to_string(server.issuer))), - #( - "authorisation_endpoint", - json.string(uri.to_string(server.authorisation_endpoint)), - ), - #("token_endpoint", json.string(uri.to_string(server.token_endpoint))), - #( - "response_types_supported", - json.array(set.to_list(server.response_types_supported), json.string), - ), - #( - "grant_types_supported", - json.array(set.to_list(server.grant_types_supported), json.string), - ), - #( - "scopes_supported", - json.array(set.to_list(server.scopes_supported), json.string), - ), - #( - "pushed_authorization_request_endpoint", - json.string(uri.to_string( - server.pushed_authorization_request_endpoint, - )), - ), - ]), - ), + #("client", client_to_json(client)), + #("server", authorisation_server_to_json(server)), #( "pkce_challenge", json.object([ -- 2.51.2