diff --git a/src/internal/oauth.zig b/src/internal/oauth.zig index 0f162fc..7ee14bc 100644 --- a/src/internal/oauth.zig +++ b/src/internal/oauth.zig @@ -32,7 +32,9 @@ pub const parseTokenResponse = client.parseTokenResponse; pub const clientMetadataJson = client.clientMetadataJson; pub const authorizationUrl = client.authorizationUrl; pub const discoverAuthorizationServer = client.discoverAuthorizationServer; +pub const discoverAuthorizationServerResolved = client.discoverAuthorizationServerResolved; pub const fetchAuthorizationServerMetadata = client.fetchAuthorizationServerMetadata; +pub const fetchAuthorizationServerMetadataResolved = client.fetchAuthorizationServerMetadataResolved; pub const parseAuthorizationServerMetadata = client.parseAuthorizationServerMetadata; pub const sendParRequest = client.sendParRequest; pub const exchangeCodeForToken = client.exchangeCodeForToken; diff --git a/src/internal/oauth/client.zig b/src/internal/oauth/client.zig index 3204ccf..657cb0a 100644 --- a/src/internal/oauth/client.zig +++ b/src/internal/oauth/client.zig @@ -82,6 +82,7 @@ pub const ParParams = struct { login_hint: ?[]const u8 = null, client_keypair: *const Keypair, dpop_keypair: *const Keypair, + resolved_connection: ?HttpTransport.ResolvedConnection = null, }; pub const ParResult = struct { @@ -105,6 +106,7 @@ pub const CodeTokenParams = struct { client_keypair: *const Keypair, dpop_keypair: *const Keypair, dpop_nonce: ?[]const u8 = null, + resolved_connection: ?HttpTransport.ResolvedConnection = null, }; pub const RefreshTokenParams = struct { @@ -115,6 +117,7 @@ pub const RefreshTokenParams = struct { client_keypair: *const Keypair, dpop_keypair: *const Keypair, dpop_nonce: ?[]const u8 = null, + resolved_connection: ?HttpTransport.ResolvedConnection = null, }; pub const TokenResult = struct { @@ -144,6 +147,7 @@ pub const DpopRequest = struct { content_type: ?[]const u8 = "application/json", accept: ?[]const u8 = "application/json", max_response_size: ?usize = null, + resolved_connection: ?HttpTransport.ResolvedConnection = null, }; pub const DpopResponse = struct { @@ -255,6 +259,15 @@ pub fn discoverAuthorizationServer( allocator: Allocator, transport: *HttpTransport, pds_url: []const u8, +) ![]const u8 { + return discoverAuthorizationServerResolved(allocator, transport, pds_url, null); +} + +pub fn discoverAuthorizationServerResolved( + allocator: Allocator, + transport: *HttpTransport, + pds_url: []const u8, + resolved_connection: ?HttpTransport.ResolvedConnection, ) ![]const u8 { const url = try joinUrl(allocator, pds_url, "/.well-known/oauth-protected-resource"); defer allocator.free(url); @@ -265,6 +278,7 @@ pub fn discoverAuthorizationServer( .max_response_size = 256 * 1024, .redirect_behavior = .unhandled, .capture_response_headers = true, + .resolved_connection = resolved_connection, }); defer result.deinit(allocator); if (result.status != .ok) return error.HttpStatus; @@ -281,6 +295,15 @@ pub fn fetchAuthorizationServerMetadata( allocator: Allocator, transport: *HttpTransport, issuer: []const u8, +) !AuthorizationServerMetadata { + return fetchAuthorizationServerMetadataResolved(allocator, transport, issuer, null); +} + +pub fn fetchAuthorizationServerMetadataResolved( + allocator: Allocator, + transport: *HttpTransport, + issuer: []const u8, + resolved_connection: ?HttpTransport.ResolvedConnection, ) !AuthorizationServerMetadata { const url = try joinUrl(allocator, issuer, "/.well-known/oauth-authorization-server"); defer allocator.free(url); @@ -291,6 +314,7 @@ pub fn fetchAuthorizationServerMetadata( .max_response_size = 256 * 1024, .redirect_behavior = .unhandled, .capture_response_headers = true, + .resolved_connection = resolved_connection, }); defer result.deinit(allocator); if (result.status != .ok) return error.HttpStatus; @@ -370,6 +394,7 @@ pub fn sendParRequest( .payload = body, .content_type = "application/x-www-form-urlencoded", .dpop_keypair = params.dpop_keypair, + .resolved_connection = params.resolved_connection, }); defer result.deinit(allocator); if (result.status != .ok and result.status != .created) return error.ParFailed; @@ -399,7 +424,7 @@ pub fn exchangeCodeForToken( .{ "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" }, .{ "client_assertion", client_assertion }, }; - return tokenRequest(allocator, io, transport, params.token_url, params.dpop_keypair, params.dpop_nonce, &form_params); + return tokenRequest(allocator, io, transport, params.token_url, params.dpop_keypair, params.dpop_nonce, params.resolved_connection, &form_params); } pub fn refreshAccessToken( @@ -417,7 +442,7 @@ pub fn refreshAccessToken( .{ "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" }, .{ "client_assertion", client_assertion }, }; - return tokenRequest(allocator, io, transport, params.token_url, params.dpop_keypair, params.dpop_nonce, &form_params); + return tokenRequest(allocator, io, transport, params.token_url, params.dpop_keypair, params.dpop_nonce, params.resolved_connection, &form_params); } pub fn dpopRequest( @@ -439,6 +464,7 @@ pub fn dpopRequest( .access_token = params.access_token, .access_token_hash = ath, .max_response_size = params.max_response_size, + .resolved_connection = params.resolved_connection, }); } @@ -463,6 +489,7 @@ const DpopFetchParams = struct { access_token: ?[]const u8 = null, access_token_hash: ?[]const u8 = null, max_response_size: ?usize = 256 * 1024, + resolved_connection: ?HttpTransport.ResolvedConnection = null, }; fn tokenRequest( @@ -472,6 +499,7 @@ fn tokenRequest( token_url: []const u8, dpop_keypair: *const Keypair, dpop_nonce: ?[]const u8, + resolved_connection: ?HttpTransport.ResolvedConnection, form_params: []const [2][]const u8, ) !TokenResult { const body = try primitives.formEncode(allocator, form_params); @@ -483,6 +511,7 @@ fn tokenRequest( .content_type = "application/x-www-form-urlencoded", .dpop_keypair = dpop_keypair, .dpop_nonce = dpop_nonce, + .resolved_connection = resolved_connection, }); defer result.deinit(allocator); if (result.status != .ok) return error.TokenRequestFailed; @@ -532,6 +561,7 @@ fn fetchWithDpopNonceRetry( .extra_headers = &extra, .max_response_size = params.max_response_size, .capture_response_headers = true, + .resolved_connection = params.resolved_connection, }); const new_nonce = if (fetch_result.oauth.dpop_nonce) |value| try allocator.dupe(u8, value) else null;