@@ -114,18 +114,76 @@ def detect_customer_agent_credentials(
114114 return None
115115
116116
117- def load_customer_credentials (path : str ) -> CustomerCredentials :
118- """Load and parse customer credentials from file.
117+ def load_customer_credentials (
118+ path : str | None ,
119+ tls_mode : TlsMode = TlsMode .STANDARD ,
120+ ) -> CustomerCredentials :
121+ """Load customer credentials from a file (standard mode) or environment variables (transparent mode).
122+
123+ In transparent mode (``tls_mode=TlsMode.TRANSPARENT``) the OpenShell Gateway
124+ injects the mTLS client certificate at the TLS layer. Credentials are read
125+ from environment variables and ``certificate``/``private_key`` are set to
126+ ``None``.
127+
128+ In standard mode ``path`` must point to a valid JSON credentials file.
119129
120130 Args:
121- path: Path to the credentials JSON file.
131+ path: Path to the credentials JSON file. Required in standard mode,
132+ ignored in transparent mode.
133+ tls_mode: TLS handling mode. When TRANSPARENT, loads from env vars.
122134
123135 Returns:
124136 Parsed CustomerCredentials.
125137
126138 Raises:
127- AgentGatewaySDKError: If file cannot be read or is missing required fields.
139+ AgentGatewaySDKError: If credentials cannot be loaded or required fields
140+ are missing.
128141 """
142+ if tls_mode == TlsMode .TRANSPARENT :
143+ logger .debug ("TLS_MODE=transparent: loading credentials from environment variables" )
144+ required = [_ENV_CLIENT_ID , _ENV_TOKEN_SERVICE_URL , _ENV_GATEWAY_URL ]
145+ missing = [v for v in required if not os .environ .get (v )]
146+ if missing :
147+ raise AgentGatewaySDKError (
148+ f"TLS_MODE=transparent requires environment variables: { missing } "
149+ )
150+
151+ raw_deps = os .environ .get (_ENV_INTEGRATION_DEPENDENCIES , "[]" )
152+ try :
153+ deps_data = json .loads (raw_deps )
154+ integration_dependencies = [
155+ IntegrationDependency (
156+ ord_id = dep [_CredentialFields .ORD_ID ],
157+ global_tenant_id = dep [_CredentialFields .GLOBAL_TENANT_ID ],
158+ )
159+ for dep in deps_data
160+ ]
161+ except (json .JSONDecodeError , KeyError , TypeError ) as e :
162+ raise AgentGatewaySDKError (
163+ f"Failed to parse INTEGRATION_DEPENDENCIES: { e } . "
164+ 'Expected format: [{"ordId": "...", "globalTenantId": "..."}]'
165+ )
166+
167+ logger .debug (
168+ "Loaded %d integration dependencies from environment" ,
169+ len (integration_dependencies ),
170+ )
171+
172+ return CustomerCredentials (
173+ token_service_url = os .environ [_ENV_TOKEN_SERVICE_URL ],
174+ client_id = os .environ [_ENV_CLIENT_ID ],
175+ certificate = None ,
176+ private_key = None ,
177+ gateway_url = os .environ [_ENV_GATEWAY_URL ].rstrip ("/" ),
178+ integration_dependencies = integration_dependencies ,
179+ )
180+
181+ # Standard mode: file-based credentials
182+ if not path :
183+ raise AgentGatewaySDKError (
184+ "Credentials file path is required in standard TLS mode."
185+ )
186+
129187 logger .debug ("Loading customer credentials from: %s" , path )
130188
131189 try :
@@ -134,8 +192,6 @@ def load_customer_credentials(path: str) -> CustomerCredentials:
134192 except (OSError , json .JSONDecodeError ) as e :
135193 raise AgentGatewaySDKError (f"Failed to load credentials from '{ path } ': { e } " )
136194
137- # Map credential file keys to dataclass fields
138- # Credential file uses camelCase, we use snake_case
139195 required_fields = {
140196 _CredentialFields .TOKEN_SERVICE_URL : "token_service_url" ,
141197 _CredentialFields .CLIENT_ID : "client_id" ,
@@ -144,13 +200,12 @@ def load_customer_credentials(path: str) -> CustomerCredentials:
144200 _CredentialFields .GATEWAY_URL : "gateway_url" ,
145201 }
146202
147- missing = [k for k in required_fields if k not in data ]
148- if missing :
203+ missing_fields = [k for k in required_fields if k not in data ]
204+ if missing_fields :
149205 raise AgentGatewaySDKError (
150- f"Credentials file missing required fields: { missing } "
206+ f"Credentials file missing required fields: { missing_fields } "
151207 )
152208
153- # Parse integrationDependencies (required)
154209 if _CredentialFields .INTEGRATION_DEPENDENCIES not in data :
155210 raise AgentGatewaySDKError (
156211 "Credentials file missing required field: integrationDependencies. "
@@ -187,64 +242,6 @@ def load_customer_credentials(path: str) -> CustomerCredentials:
187242 )
188243
189244
190- def load_customer_credentials_from_env () -> CustomerCredentials :
191- """Load customer credentials from environment variables (transparent mode).
192-
193- Used when TlsMode.TRANSPARENT is active and the OpenShell Gateway handles
194- mTLS. Certificate and private key are not required — they are injected at
195- the TLS layer by the gateway.
196-
197- Environment variables:
198- CLIENT_ID: IAS client ID (may be a gateway-resolved placeholder at runtime)
199- TOKEN_SERVICE_URL: IAS token service endpoint
200- GATEWAY_URL: Agent Gateway base URL
201- INTEGRATION_DEPENDENCIES: JSON array of {ordId, globalTenantId} objects
202-
203- Returns:
204- CustomerCredentials with certificate and private_key set to None.
205-
206- Raises:
207- AgentGatewaySDKError: If required environment variables are missing or
208- INTEGRATION_DEPENDENCIES cannot be parsed.
209- """
210- required = [_ENV_CLIENT_ID , _ENV_TOKEN_SERVICE_URL , _ENV_GATEWAY_URL ]
211- missing = [v for v in required if not os .environ .get (v )]
212- if missing :
213- raise AgentGatewaySDKError (
214- f"TLS_MODE=transparent requires environment variables: { missing } "
215- )
216-
217- raw_deps = os .environ .get (_ENV_INTEGRATION_DEPENDENCIES , "[]" )
218- try :
219- deps_data = json .loads (raw_deps )
220- integration_dependencies = [
221- IntegrationDependency (
222- ord_id = dep [_CredentialFields .ORD_ID ],
223- global_tenant_id = dep [_CredentialFields .GLOBAL_TENANT_ID ],
224- )
225- for dep in deps_data
226- ]
227- except (json .JSONDecodeError , KeyError , TypeError ) as e :
228- raise AgentGatewaySDKError (
229- f"Failed to parse INTEGRATION_DEPENDENCIES: { e } . "
230- 'Expected format: [{"ordId": "...", "globalTenantId": "..."}]'
231- )
232-
233- logger .debug (
234- "Loaded %d integration dependencies from environment" ,
235- len (integration_dependencies ),
236- )
237-
238- return CustomerCredentials (
239- token_service_url = os .environ [_ENV_TOKEN_SERVICE_URL ],
240- client_id = os .environ [_ENV_CLIENT_ID ],
241- certificate = None ,
242- private_key = None ,
243- gateway_url = os .environ [_ENV_GATEWAY_URL ].rstrip ("/" ),
244- integration_dependencies = integration_dependencies ,
245- )
246-
247-
248245def _create_ssl_context (certificate : str , private_key : str ) -> ssl .SSLContext :
249246 """Create SSL context for mTLS from in-memory certificate and key.
250247
0 commit comments