Class: OpenAI::Auth::X509Transport

Inherits:
Object
  • Object
show all
Defined in:
lib/openai/auth/x509_transport.rb,
sig/openai/auth/x509_transport.rbs

Overview

A caller-attested, application-owned transport for X.509 authentication.

The caller attests that its native client consistently selects one static certificate identity and the declared proxy configuration. This attestation cannot cryptographically bind a bearer token to a client certificate. Certificate rotation requires a new native client and transport capability.

Constant Summary collapse

ISSUER_ORIGIN =

Returns:

  • (String)
"https://mtls.auth.openai.com"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(http_client:, certificate_identity:, proxy: :direct, api_origin: "https://mtls.api.openai.com") ⇒ X509Transport

Returns a new instance of X509Transport.

Parameters:

  • http_client (OpenAI::NetHTTPClient)

    caller-owned configured native transport

  • certificate_identity (Symbol)

    must explicitly be :static

  • proxy (Symbol) (defaults to: :direct)

    either :direct or :http_connect

  • api_origin (String) (defaults to: "https://mtls.api.openai.com")

    approved OpenAI mTLS API origin



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/openai/auth/x509_transport.rb', line 62

def initialize(
  http_client:,
  certificate_identity:,
  proxy: :direct,
  api_origin: "https://mtls.api.openai.com"
)
  unless X509Transport.exact_instance?(http_client, OpenAI::NetHTTPClient)
    raise ArgumentError, "X.509 transport requires a caller-owned OpenAI::NetHTTPClient"
  end

  unless certificate_identity == :static
    raise ArgumentError, "X.509 transport requires an explicitly attested static certificate identity"
  end

  unless PROXY_MODES.include?(proxy)
    raise ArgumentError, "X.509 transport supports only direct connections and HTTP CONNECT proxies"
  end

  @api_origin = normalize_api_origin(api_origin).freeze
  @proxy_mode = proxy
  @http_client = http_client
  freeze
end

Instance Attribute Details

#api_originString (readonly)

The approved OpenAI API origin for this immutable capability.

Returns:

  • (String)


43
44
45
# File 'lib/openai/auth/x509_transport.rb', line 43

def api_origin
  @api_origin
end

#proxy_modeSymbol (readonly)

The proxy behavior explicitly attested by the application.

Returns:

  • (Symbol)


48
49
50
# File 'lib/openai/auth/x509_transport.rb', line 48

def proxy_mode
  @proxy_mode
end

Class Method Details

.exact_instance?(candidate, expected_class) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Checks an exact security-boundary class without dispatching through a potentially overridden predicate on the untrusted candidate.

Returns:



54
55
56
# File 'lib/openai/auth/x509_transport.rb', line 54

def self.exact_instance?(candidate, expected_class)
  Object.instance_method(:instance_of?).bind_call(candidate, expected_class)
end

Instance Method Details

#execute(request) ⇒ OpenAI::HTTPClient::Response

Sends one request through the caller's attested native transport.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/openai/auth/x509_transport.rb', line 90

def execute(request)
  unless request.is_a?(OpenAI::HTTPClient::Request)
    raise ArgumentError, "X.509 transport requires an OpenAI::HTTPClient::Request"
  end

  url = normalize_destination(request.url)
  exchange = exchange_request?(url, request.method)
  headers = validated_headers(request.headers, url: url, exchange: exchange)
  safe_request = OpenAI::HTTPClient::Request.new(
    method: request.method,
    url: url.freeze,
    headers: headers,
    body: request.body,
    timeout: request.timeout
  )
  response = begin
    native_response = @http_client.execute(safe_request) do |connection|
      validate_tls_policy!(connection)
      validate_proxy_policy!(connection)
    end

    unless native_response.is_a?(OpenAI::HTTPClient::Response)
      raise TypeError, "`http_client#execute` must return an OpenAI::HTTPClient::Response"
    end

    reject_redirect!(native_response, url: url) if (300..399).cover?(native_response.status)
    native_response

  rescue OpenAI::Errors::APIConnectionError => error
    raise error.class.new(url: sanitized_url(url)), cause: nil
  end

  stream = Enumerator.new do |chunks|
    response.body.each { |chunk| chunks << chunk }

  rescue OpenAI::Errors::APIConnectionError => error
    raise error.class.new(url: sanitized_url(url)), cause: nil
  end

  body = OpenAI::Internal::Util.fused_enum(stream) do
    OpenAI::Internal::Util.close_fused!(response.body)

  rescue OpenAI::Errors::APIConnectionError => error
    raise error.class.new(url: sanitized_url(url)), cause: nil
  end

  OpenAI::HTTPClient::Response.new(status: response.status, headers: response.headers, body: body)
end

#supports_data_residency?(residency) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • residency (Symbol, String)

Returns:



154
155
156
# File 'lib/openai/auth/x509_transport.rb', line 154

def supports_data_residency?(residency)
  REGIONAL_API_ORIGINS[residency.to_s] == @api_origin
end

#validate_api_request!(url:, headers:) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Validates the effective API destination before a credential is acquired.



142
143
144
145
146
147
148
149
# File 'lib/openai/auth/x509_transport.rb', line 142

def validate_api_request!(url:, headers:)
  destination = normalize_destination(url)
  unless "https://#{destination.host.downcase}" == @api_origin
    raise ArgumentError, "X.509 API request must use its attested API origin"
  end

  validated_headers(headers, url: destination, exchange: false)
end