Skip to content

Commit 7ab013d

Browse files
brianjlaimaxi297
andauthored
feat(jwt authenticator): Allow configuring the signed JWT token to be injected as a request_option for JwtAuthenticator (#776)
Co-authored-by: maxime.c <maxime@airbyte.io>
1 parent 8a01e33 commit 7ab013d

File tree

8 files changed

+427
-233
lines changed

8 files changed

+427
-233
lines changed

airbyte_cdk/sources/declarative/auth/jwt.py

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,23 @@
66
import json
77
from dataclasses import InitVar, dataclass
88
from datetime import datetime
9-
from typing import Any, Mapping, Optional, Union, cast
9+
from typing import Any, Mapping, MutableMapping, Optional, Union, cast
1010

1111
import jwt
1212
from cryptography.hazmat.primitives import serialization
1313
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
1414
from cryptography.hazmat.primitives.asymmetric.ed448 import Ed448PrivateKey
1515
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
1616
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
17-
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
1817

1918
from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator
2019
from airbyte_cdk.sources.declarative.interpolation.interpolated_boolean import InterpolatedBoolean
2120
from airbyte_cdk.sources.declarative.interpolation.interpolated_mapping import InterpolatedMapping
2221
from airbyte_cdk.sources.declarative.interpolation.interpolated_string import InterpolatedString
22+
from airbyte_cdk.sources.declarative.requesters.request_option import (
23+
RequestOption,
24+
RequestOptionType,
25+
)
2326

2427
# Type alias for keys that JWT library accepts
2528
JwtKeyTypes = Union[
@@ -86,6 +89,7 @@ class JwtAuthenticator(DeclarativeAuthenticator):
8689
additional_jwt_headers: Optional[Mapping[str, Any]] = None
8790
additional_jwt_payload: Optional[Mapping[str, Any]] = None
8891
passphrase: Optional[Union[InterpolatedString, str]] = None
92+
request_option: Optional[RequestOption] = None
8993

9094
def __post_init__(self, parameters: Mapping[str, Any]) -> None:
9195
self._secret_key = InterpolatedString.create(self.secret_key, parameters=parameters)
@@ -121,6 +125,13 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None:
121125
else None
122126
)
123127

128+
# When we first implemented the JWT authenticator, we assumed that the signed token was always supposed
129+
# to be loaded into the request headers under the `Authorization` key. This is not always the case, but
130+
# this default option allows for backwards compatibility to be retained for existing connectors
131+
self._request_option = self.request_option or RequestOption(
132+
inject_into=RequestOptionType.header, field_name="Authorization", parameters=parameters
133+
)
134+
124135
def _get_jwt_headers(self) -> dict[str, Any]:
125136
"""
126137
Builds and returns the headers used when signing the JWT.
@@ -213,7 +224,8 @@ def _get_header_prefix(self) -> Union[str, None]:
213224

214225
@property
215226
def auth_header(self) -> str:
216-
return "Authorization"
227+
options = self._get_request_options(RequestOptionType.header)
228+
return next(iter(options.keys()), "")
217229

218230
@property
219231
def token(self) -> str:
@@ -222,3 +234,18 @@ def token(self) -> str:
222234
if self._get_header_prefix()
223235
else self._get_signed_token()
224236
)
237+
238+
def get_request_params(self) -> Mapping[str, Any]:
239+
return self._get_request_options(RequestOptionType.request_parameter)
240+
241+
def get_request_body_data(self) -> Union[Mapping[str, Any], str]:
242+
return self._get_request_options(RequestOptionType.body_data)
243+
244+
def get_request_body_json(self) -> Mapping[str, Any]:
245+
return self._get_request_options(RequestOptionType.body_json)
246+
247+
def _get_request_options(self, option_type: RequestOptionType) -> Mapping[str, Any]:
248+
options: MutableMapping[str, Any] = {}
249+
if self._request_option.inject_into == option_type:
250+
self._request_option.inject_into_request(options, self.token, self.config)
251+
return options

airbyte_cdk/sources/declarative/declarative_component_schema.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,10 @@ definitions:
12761276
type: string
12771277
examples:
12781278
- "{{ config['passphrase'] }}"
1279+
request_option:
1280+
title: Request Option
1281+
description: A request option describing where the signed JWT token that is generated should be injected into the outbound API request.
1282+
"$ref": "#/definitions/RequestOption"
12791283
$parameters:
12801284
type: object
12811285
additionalProperties: true

0 commit comments

Comments
 (0)