Skip to content

Commit 0828d04

Browse files
refactor(oauth): remove use_client_credentials_in_refresh option
The core fix (excluding credentials from body when Authorization header is present) is sufficient for Gong and similar OAuth providers. The additional use_client_credentials_in_refresh option was added during confusion about Gong's requirements and is no longer needed. This simplifies the implementation while maintaining the correct behavior: - When refresh_request_headers contains an Authorization header, client_id and client_secret are automatically excluded from the request body - This is required by OAuth providers like Gong that expect credentials ONLY in the Authorization header Co-Authored-By: aldo.gonzalez@airbyte.io <aldo.gonzalez@airbyte.io>
1 parent 499d3ee commit 0828d04

6 files changed

Lines changed: 6 additions & 90 deletions

File tree

airbyte_cdk/sources/declarative/auth/oauth.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ class DeclarativeOauth2Authenticator(AbstractOauth2Authenticator, DeclarativeAut
7878
refresh_token_error_status_codes: Tuple[int, ...] = ()
7979
refresh_token_error_key: str = ""
8080
refresh_token_error_values: Tuple[str, ...] = ()
81-
_use_client_credentials_in_refresh: bool = True
8281

8382
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
8483
super().__init__(
@@ -248,9 +247,6 @@ def get_refresh_request_body(self) -> Mapping[str, Any]:
248247
def get_refresh_request_headers(self) -> Mapping[str, Any]:
249248
return self._refresh_request_headers.eval(self.config)
250249

251-
def use_client_credentials_in_refresh(self) -> bool:
252-
return self._use_client_credentials_in_refresh
253-
254250
def get_token_expiry_date(self) -> AirbyteDateTime:
255251
if not self._has_access_token_been_initialized():
256252
return AirbyteDateTime.from_datetime(datetime.min)

airbyte_cdk/sources/declarative/declarative_component_schema.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,11 +1531,6 @@ definitions:
15311531
description: Enable using profile assertion as a flow for OAuth authorization.
15321532
type: boolean
15331533
default: false
1534-
use_client_credentials_in_refresh:
1535-
title: Use Client Credentials In Refresh
1536-
description: When enabled (default), client_id and client_secret are included in the refresh token request body. Set to false for OAuth implementations like Gong that require only the refresh_token in the request body without client credentials.
1537-
type: boolean
1538-
default: true
15391534
$parameters:
15401535
type: object
15411536
additionalProperties: true

airbyte_cdk/sources/declarative/models/declarative_component_schema.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,11 +1941,6 @@ class OAuthAuthenticator(BaseModel):
19411941
description="Enable using profile assertion as a flow for OAuth authorization.",
19421942
title="Use Profile Assertion",
19431943
)
1944-
use_client_credentials_in_refresh: Optional[bool] = Field(
1945-
True,
1946-
description="When enabled (default), client_id and client_secret are included in the refresh token request body. Set to false for OAuth implementations like Gong that require only the refresh_token in the request body without client credentials.",
1947-
title="Use Client Credentials In Refresh",
1948-
)
19491944
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
19501945

19511946

airbyte_cdk/sources/declarative/parsers/model_to_component_factory.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2833,9 +2833,6 @@ def create_oauth_authenticator(
28332833
refresh_token_error_status_codes=refresh_token_error_status_codes,
28342834
refresh_token_error_key=refresh_token_error_key,
28352835
refresh_token_error_values=refresh_token_error_values,
2836-
_use_client_credentials_in_refresh=model.use_client_credentials_in_refresh
2837-
if model.use_client_credentials_in_refresh is not None
2838-
else True,
28392836
)
28402837

28412838
@staticmethod

airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,17 @@ def build_refresh_request_body(self) -> Mapping[str, Any]:
104104
105105
Override to define additional parameters.
106106
107-
Client credentials (client_id and client_secret) are excluded from the body when:
108-
1. refresh_request_headers contains an Authorization header (e.g., Basic auth), OR
109-
2. use_client_credentials_in_refresh() returns False (for APIs like Gong that don't
110-
require client credentials in the refresh request at all)
107+
Client credentials (client_id and client_secret) are excluded from the body when
108+
refresh_request_headers contains an Authorization header (e.g., Basic auth).
109+
This is required by OAuth providers like Gong that expect credentials ONLY in the
110+
Authorization header and reject requests that include them in both places.
111111
"""
112112
# Check if credentials are being sent via Authorization header
113113
headers = self.get_refresh_request_headers()
114114
credentials_in_header = headers and "Authorization" in headers
115115

116-
# Check if client credentials should be included in refresh request
117-
include_client_credentials = (
118-
self.use_client_credentials_in_refresh() and not credentials_in_header
119-
)
116+
# Only include client credentials in body if not already in header
117+
include_client_credentials = not credentials_in_header
120118

121119
payload: MutableMapping[str, Any] = {
122120
self.get_grant_type_name(): self.get_grant_type(),
@@ -518,14 +516,6 @@ def get_grant_type(self) -> str:
518516
def get_grant_type_name(self) -> str:
519517
"""Returns grant_type specified name for requesting access_token"""
520518

521-
def use_client_credentials_in_refresh(self) -> bool:
522-
"""Returns whether to include client credentials in the refresh token request body.
523-
524-
Override to return False for OAuth implementations (like Gong) that don't require
525-
client_id and client_secret in the refresh request.
526-
"""
527-
return True
528-
529519
@property
530520
@abstractmethod
531521
def access_token(self) -> str:

unit_tests/sources/declarative/auth/test_oauth.py

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -169,63 +169,6 @@ def test_refresh_request_body_includes_credentials_when_no_authorization_header(
169169
}
170170
assert body == expected
171171

172-
def test_refresh_request_body_excludes_credentials_when_use_client_credentials_in_refresh_is_false(
173-
self,
174-
):
175-
"""
176-
When use_client_credentials_in_refresh is set to False, client_id and client_secret
177-
should be excluded from the request body entirely.
178-
179-
This is required by OAuth providers like Gong that don't require client credentials
180-
in the refresh token request at all - only the refresh_token is needed.
181-
"""
182-
oauth = DeclarativeOauth2Authenticator(
183-
token_refresh_endpoint="{{ config['refresh_endpoint'] }}",
184-
client_id="{{ config['client_id'] }}",
185-
client_secret="{{ config['client_secret'] }}",
186-
refresh_token="{{ parameters['refresh_token'] }}",
187-
config=config,
188-
token_expiry_date="{{ config['token_expiry_date'] }}",
189-
parameters=parameters,
190-
grant_type="{{ config['grant_type'] }}",
191-
_use_client_credentials_in_refresh=False,
192-
)
193-
body = oauth.build_refresh_request_body()
194-
expected = {
195-
"grant_type": "some_grant_type",
196-
"refresh_token": "some_refresh_token",
197-
}
198-
assert body == expected
199-
assert "client_id" not in body
200-
assert "client_secret" not in body
201-
202-
def test_refresh_request_body_includes_credentials_when_use_client_credentials_in_refresh_is_true(
203-
self,
204-
):
205-
"""
206-
When use_client_credentials_in_refresh is True (default), client_id and client_secret
207-
should be included in the request body.
208-
"""
209-
oauth = DeclarativeOauth2Authenticator(
210-
token_refresh_endpoint="{{ config['refresh_endpoint'] }}",
211-
client_id="{{ config['client_id'] }}",
212-
client_secret="{{ config['client_secret'] }}",
213-
refresh_token="{{ parameters['refresh_token'] }}",
214-
config=config,
215-
token_expiry_date="{{ config['token_expiry_date'] }}",
216-
parameters=parameters,
217-
grant_type="{{ config['grant_type'] }}",
218-
_use_client_credentials_in_refresh=True,
219-
)
220-
body = oauth.build_refresh_request_body()
221-
expected = {
222-
"grant_type": "some_grant_type",
223-
"client_id": "some_client_id",
224-
"client_secret": "some_client_secret",
225-
"refresh_token": "some_refresh_token",
226-
}
227-
assert body == expected
228-
229172
def test_refresh_with_encode_config_params(self):
230173
oauth = DeclarativeOauth2Authenticator(
231174
token_refresh_endpoint="{{ config['refresh_endpoint'] }}",

0 commit comments

Comments
 (0)