Skip to content

Commit 34c76fb

Browse files
authored
✨ Source Okta: Authentication with private key (OAuth 2.0) support (#43382)
1 parent a7a9688 commit 34c76fb

File tree

10 files changed

+179
-35
lines changed

10 files changed

+179
-35
lines changed

airbyte-integrations/connectors/source-okta/metadata.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ data:
1919
connectorSubtype: api
2020
connectorType: source
2121
definitionId: 1d4fdb25-64fc-4569-92da-fcdca79a8372
22-
dockerImageTag: 0.2.11
22+
dockerImageTag: 0.3.0
2323
dockerRepository: airbyte/source-okta
2424
githubIssueLabel: source-okta
2525
icon: icon.svg

airbyte-integrations/connectors/source-okta/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",]
33
build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
6-
version = "0.2.11"
6+
version = "0.3.0"
77
name = "source-okta"
88
description = "Source implementation for okta."
99
authors = [ "Airbyte <[email protected]>",]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"domain": "myorg",
3+
"start_date": "2024-08-02T00:00:00Z",
4+
"credentials": {
5+
"auth_type": "oauth2.0_private_key",
6+
"client_id": "0odiji39hrrlrggPb7d7",
7+
"key_id": "IvLuOYbri0fMcIhdnaOhcjD5S_WXzkk7Cxdo0_M28r0",
8+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9b0BAQEFAASCBKcwggSjAgEAAoIBARC1Zzwf3mpAJBJ7\nbnSZr+wqgUlh10I/5hhLSYWlB0Ak+GlnhHFFJcSE8SByrAPlorggtBAuTdoVMGc8\nlDSodtQM+WiurwltCbbSA8VqmeftIA53Axi7ZwCqm9MVpiNIB6uUFzjFh1ewKRdM\nQYzIWmIL+onDl0P4vzQylsEO0dy6Z3SCoodpw1mgcViGRL3MH73hymauEn9n5n0s\n3CI6g9+tzk1f2gYHWB+o1XHBb78ZR7vkCBuOCY58P3Bps2hG0gTTDaBOrAtSd8Df\n4PVAoVAnh70WtUyzIGBu6pmSbIZ05G32xz+wANFgCZ+G4e5+k9GYz+2/2bwDh3Ra\nrL16CY4bAgMBAAECggEAHZt+YlJg5BwfGh9Cj6z7bkqQuBPin7xF3c/frLo8uWwD\n/2ITfLY313zlj2HM9wdyZwAMngod3JR8XRJRb+eJH577e3tdHftWZ/uuloINLRIs\n2jbarAeZP79UGfX2TzTVR8Psg6zd3oYuY8dVG4RI+WyIXLCNKwXGFrWtR+Zv0Mp4\nOnqc5aCq0qbSYmj/WEJFL3yW0VQznt/olpuV9Ek45FlahCTpojLxhcS9+LG+L+pm\nt0a5cAu98lkthrn0UIkkirl2Q5iDnn56bHydhBnjj5X4XCXmX4SMLOfnF0u3q3wz\nTkYWdfaVkMLoOfOYMTy+HOuo52KiPXrsknPR8TLneQKBgQDtHe2BzbY++KnUabDN\nvxGTrj89uUaDM53FRlsSWm8ATRCueNcXbFAfKXlnOZzPr0/GEZ2a6o6cRJh5iT4s\nDGLeWtCNSg59pHHfq1SNDqdrxESbZTVScxYpnTa2yVc6tAw3cYAUo4PSgks/ErYu\nkSjHXhb9RnuDSS+AWpwoYMnqcwKBgQDD2Xr2mQi5vic/WK03hV00gqXTezRVo+g7\nrOrCyj0HKjcewLtzBkkSFU5JLUeyC7fUFYOIBwaJMmt7UxwYePXX/AB//u/IRIkh\nbjPIZqSjgX0rq62Ou5uy12WZ1S+ZYyZV4rD4ehzR4z+5KkIHJ/7wGkAtZQYoKdsR\ntEJmz8cbuQKBgExdzid3DFH9nhy2KWYqOkv4249Sg34v+okVnrErhQJwz4WRj5yQ\nmsFehyYSrQlKagPdmofRMTrs8Lp71BU1rAX286H9jusyMiaaNHH1nUAdBweRMfoq\n7KFca8m00K4sXJ7ipCCBhSwgIIHg0eHviFWlXPwXXiIrSOwqwo5SldU3AoGAdUdt\nn/ACTqA1BnUGvUGqj8BQpvSXYVVWwy2II39RzlGUUmEdnwK7jQ2fJKjtzwu/WExN\nyI5UdqHvxRj+sRT2OxFYB03VrvqDl7ZTYgU9QABRwW3774Ye9ZiQ6e7EozjBgxrN\n2O3fBjzsMujAQ2LLAmLl3Ykqh7CQ0+g6/zAbTlkCgYEAjR/nNSCf7qClRKB88aWg\nFUmJsK4DW2we0fHufPyuUUG3dxbPT66SS7chvivLHhmZdOoZg6LX+v3vjfH08qv4\nARRKKgoyaEZR+Dna4fxYPUWzpv95egdea9q63Rw2xXc5pBvp7wDXkd3Sr4yhZYzk\nGg8CnyEMRaPgTor51VNN1gU=\n-----END PRIVATE KEY-----\n",
9+
"scope": "okta.users.read"
10+
}
11+
}

airbyte-integrations/connectors/source-okta/source_okta/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55

66
from .source import SourceOkta
77

8-
__all__ = ["SourceOkta", "custom_authenticators"]
8+
__all__ = ["SourceOkta", "components"]

airbyte-integrations/connectors/source-okta/source_okta/custom_authenticators.py airbyte-integrations/connectors/source-okta/source_okta/components.py

+55
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
33
#
44

5+
import time
6+
import uuid
57
from dataclasses import InitVar, dataclass
68
from typing import Any, Mapping, Tuple
79

10+
import jwt
811
import requests
912
from airbyte_cdk.sources.declarative.auth import DeclarativeOauth2Authenticator
1013
from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator
@@ -61,3 +64,55 @@ def refresh_access_token(self) -> Tuple[str, int]:
6164
return response_json["access_token"], response_json["expires_in"]
6265
except Exception as e:
6366
raise Exception(f"Error while refreshing access token: {e}") from e
67+
68+
69+
@dataclass
70+
class CustomOauth2PrivateKeyAuthenticator(DeclarativeAuthenticator):
71+
"""
72+
Custom authenticator that uses a signed JWT with a private key to authenticate against Okta.
73+
"""
74+
75+
config: Config
76+
77+
@property
78+
def auth_header(self) -> str:
79+
return "Authorization"
80+
81+
@property
82+
def token(self) -> str:
83+
domain = self.config["domain"]
84+
client_id = self.config["credentials"]["client_id"]
85+
key_id = self.config["credentials"]["key_id"]
86+
private_key = self.config["credentials"]["private_key"]
87+
scope = self.config["credentials"]["scope"]
88+
now = int(time.time())
89+
90+
jwt_payload = {
91+
"iss": client_id,
92+
"sub": client_id,
93+
"aud": f"https://{domain}.okta.com/oauth2/v1/token",
94+
"iat": now,
95+
"exp": now + 3600,
96+
"jti": str(uuid.uuid4()),
97+
}
98+
jwt_headers = {"kid": key_id, "alg": "RS256"}
99+
100+
client_assertion = jwt.encode(jwt_payload, private_key, algorithm="RS256", headers=jwt_headers)
101+
token_url = f"https://{domain}.okta.com/oauth2/v1/token"
102+
token_response = requests.post(
103+
token_url,
104+
data={
105+
"grant_type": "client_credentials",
106+
"client_id": client_id,
107+
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
108+
"client_assertion": client_assertion,
109+
"scope": scope,
110+
},
111+
headers={"Content-Type": "application/x-www-form-urlencoded"},
112+
)
113+
114+
try:
115+
response = token_response.json()
116+
return f"Bearer {response['access_token']}"
117+
except Exception as e:
118+
raise Exception(f"Error while getting access token: {e}") from e

airbyte-integrations/connectors/source-okta/source_okta/manifest.yaml

+43-2
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,28 @@ type: DeclarativeSource
44
definitions:
55
custom_oauth_authenticator:
66
type: CustomAuthenticator
7-
class_name: source_okta.custom_authenticators.CustomOauth2Authenticator
7+
class_name: source_okta.components.CustomOauth2Authenticator
88
client_id: "{{ config['credentials']['client_id'] }}"
99
client_secret: "{{ config['credentials']['client_secret'] }}"
1010
refresh_token: "{{ config['credentials']['refresh_token'] }}"
1111
refresh_request_body: {}
1212
token_refresh_endpoint: "https://{{ config['domain'] }}.okta.com/oauth2/v1/token"
1313
grant_type: refresh_token
1414

15+
custom_oauth_private_key_authenticator:
16+
type: CustomAuthenticator
17+
class_name: source_okta.components.CustomOauth2PrivateKeyAuthenticator
18+
1519
custom_bearer_authenticator:
1620
type: CustomAuthenticator
17-
class_name: source_okta.custom_authenticators.CustomBearerAuthenticator
21+
class_name: source_okta.components.CustomBearerAuthenticator
1822

1923
selective_authenticator:
2024
type: SelectiveAuthenticator
2125
authenticator_selection_path: ["credentials", "auth_type"]
2226
authenticators:
2327
oauth2.0: "#/definitions/custom_oauth_authenticator"
28+
oauth2.0_private_key: "#/definitions/custom_oauth_private_key_authenticator"
2429
api_token: "#/definitions/custom_bearer_authenticator"
2530

2631
paginator:
@@ -1850,6 +1855,42 @@ spec:
18501855
title: Refresh Token
18511856
description: Refresh Token to obtain new Access Token, when it's expired.
18521857
airbyte_secret: true
1858+
- type: object
1859+
title: OAuth 2.0 with private key
1860+
required:
1861+
- auth_type
1862+
- client_id
1863+
- key_id
1864+
- private_key
1865+
- scope
1866+
properties:
1867+
auth_type:
1868+
type: string
1869+
const: oauth2.0_private_key
1870+
order: 0
1871+
client_id:
1872+
type: string
1873+
title: Client ID
1874+
description: The Client ID of your OAuth application.
1875+
airbyte_secret: true
1876+
order: 1
1877+
key_id:
1878+
type: string
1879+
title: Key ID
1880+
description: The key ID (kid).
1881+
airbyte_secret: true
1882+
order: 2
1883+
private_key:
1884+
type: string
1885+
title: Private key
1886+
description: The private key in PEM format
1887+
airbyte_secret: true
1888+
order: 3
1889+
scope:
1890+
type: string
1891+
title: Scope
1892+
description: The OAuth scope.
1893+
order: 4
18531894
- type: object
18541895
title: API Token
18551896
required:

airbyte-integrations/connectors/source-okta/source_okta/spec.yaml

+36
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,42 @@ connectionSpecification:
3535
title: Refresh Token
3636
description: Refresh Token to obtain new Access Token, when it's expired.
3737
airbyte_secret: true
38+
- type: object
39+
title: OAuth 2.0 with private key
40+
required:
41+
- auth_type
42+
- client_id
43+
- key_id
44+
- private_key
45+
- scope
46+
properties:
47+
auth_type:
48+
type: string
49+
const: oauth2.0_private_key
50+
order: 0
51+
client_id:
52+
type: string
53+
title: Client ID
54+
description: The Client ID of your OAuth application.
55+
airbyte_secret: true
56+
order: 1
57+
key_id:
58+
type: string
59+
title: Key ID
60+
description: The key ID (kid).
61+
airbyte_secret: true
62+
order: 2
63+
private_key:
64+
type: string
65+
title: Private key
66+
description: The private key in PEM format
67+
airbyte_secret: true
68+
order: 3
69+
scope:
70+
type: string
71+
title: Scope
72+
description: The OAuth scope.
73+
order: 4
3874
- type: object
3975
title: API Token
4076
required:

airbyte-integrations/connectors/source-okta/unit_tests/test_source.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
33
#
44

5-
from source_okta.custom_authenticators import CustomBearerAuthenticator, CustomOauth2Authenticator
5+
from source_okta.components import CustomBearerAuthenticator, CustomOauth2Authenticator
66
from source_okta.source import SourceOkta
77

88

airbyte-integrations/connectors/source-okta/unit_tests/test_streams.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import pytest
1111
import requests
1212
from airbyte_cdk.sources.streams import Stream
13-
from source_okta.custom_authenticators import CustomBearerAuthenticator, CustomOauth2Authenticator
13+
from source_okta.components import CustomBearerAuthenticator, CustomOauth2Authenticator
1414
from source_okta.source import SourceOkta
1515

1616

docs/integrations/sources/okta.md

+29-28
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Okta is the complete identity solution for all your apps and people that’s uni
44

55
## Prerequisites
66

7-
- Created Okta account with added application on [Add Application Page](https://okta-domain.okta.com/enduser/catalog) page. (change okta-domain to you'r domain received after complete registration)
7+
- Created Okta account with added application on [Add Application Page](https://okta-domain.okta.com/enduser/catalog) page. (change okta-domain to your domain received after complete registration)
88

99
## Airbyte Open Source
1010

@@ -86,33 +86,34 @@ The connector is restricted by normal Okta [requests limitation](https://develop
8686

8787
| Version | Date | Pull Request | Subject |
8888
|:--------|:-----------|:---------------------------------------------------------|:-------------------------------------------------------------------------------|
89-
| 0.2.11 | 2024-08-12 | [43820](https://github.com/airbytehq/airbyte/pull/43820) | Update dependencies |
90-
| 0.2.10 | 2024-08-10 | [43672](https://github.com/airbytehq/airbyte/pull/43672) | Update dependencies |
91-
| 0.2.9 | 2024-08-03 | [43279](https://github.com/airbytehq/airbyte/pull/43279) | Update dependencies |
92-
| 0.2.8 | 2024-07-27 | [42739](https://github.com/airbytehq/airbyte/pull/42739) | Update dependencies |
93-
| 0.2.7 | 2024-07-20 | [42284](https://github.com/airbytehq/airbyte/pull/42284) | Update dependencies |
94-
| 0.2.6 | 2024-07-13 | [41756](https://github.com/airbytehq/airbyte/pull/41756) | Update dependencies |
95-
| 0.2.5 | 2024-07-10 | [41269](https://github.com/airbytehq/airbyte/pull/41269) | Update dependencies |
96-
| 0.2.4 | 2024-07-06 | [40904](https://github.com/airbytehq/airbyte/pull/40904) | Update dependencies |
97-
| 0.2.3 | 2024-06-25 | [40316](https://github.com/airbytehq/airbyte/pull/40316) | Update dependencies |
98-
| 0.2.2 | 2024-06-22 | [40002](https://github.com/airbytehq/airbyte/pull/40002) | Update dependencies |
99-
| 0.2.1 | 2024-06-04 | [39016](https://github.com/airbytehq/airbyte/pull/39016) | [autopull] Upgrade base image to v1.2.1 |
100-
| 0.2.0 | 2024-05-16 | [36509](https://github.com/airbytehq/airbyte/pull/36509) | Migrate to Low Code |
101-
| 0.1.16 | 2023-07-07 | [20833](https://github.com/airbytehq/airbyte/pull/20833) | Fix infinite loop for GroupMembers stream |
102-
| 0.1.15 | 2023-06-20 | [27533](https://github.com/airbytehq/airbyte/pull/27533) | Fixed group member stream and resource sets stream pagination |
103-
| 0.1.14 | 2022-12-24 | [20877](https://github.com/airbytehq/airbyte/pull/20877) | Disabled OAuth2.0 authorization method |
104-
| 0.1.13 | 2022-08-12 | [14700](https://github.com/airbytehq/airbyte/pull/14700) | Add resource sets |
105-
| 0.1.12 | 2022-08-05 | [15050](https://github.com/airbytehq/airbyte/pull/15050) | Add parameter `start_date` for Logs stream |
106-
| 0.1.11 | 2022-08-03 | [14739](https://github.com/airbytehq/airbyte/pull/14739) | Add permissions for custom roles |
107-
| 0.1.10 | 2022-08-01 | [15179](https://github.com/airbytehq/airbyte/pull/15179) | Fix broken schemas for all streams |
108-
| 0.1.9 | 2022-07-25 | [15001](https://github.com/airbytehq/airbyte/pull/15001) | Return deprovisioned users |
109-
| 0.1.8 | 2022-07-19 | [14710](https://github.com/airbytehq/airbyte/pull/14710) | Implement OAuth2.0 authorization method |
110-
| 0.1.7 | 2022-07-13 | [14556](https://github.com/airbytehq/airbyte/pull/14556) | Add User_Role_Assignments and Group_Role_Assignments streams (full fetch only) |
111-
| 0.1.6 | 2022-07-11 | [14610](https://github.com/airbytehq/airbyte/pull/14610) | Add custom roles stream |
112-
| 0.1.5 | 2022-07-04 | [14380](https://github.com/airbytehq/airbyte/pull/14380) | Add Group_Members stream to okta source |
113-
| 0.1.4 | 2021-11-02 | [7584](https://github.com/airbytehq/airbyte/pull/7584) | Fix incremental params for log stream |
114-
| 0.1.3 | 2021-09-08 | [5905](https://github.com/airbytehq/airbyte/pull/5905) | Fix incremental stream defect |
115-
| 0.1.2 | 2021-07-01 | [4456](https://github.com/airbytehq/airbyte/pull/4456) | Fix infinite pagination in logs stream |
89+
| 0.3.0 | 2024-08-13 | [43382](https://github.com/airbytehq/airbyte/pull/43382) | Support OAuth 2.0 with private key |
90+
| 0.2.11 | 2024-08-12 | [43820](https://github.com/airbytehq/airbyte/pull/43820) | Update dependencies |
91+
| 0.2.10 | 2024-08-10 | [43672](https://github.com/airbytehq/airbyte/pull/43672) | Update dependencies |
92+
| 0.2.9 | 2024-08-03 | [43279](https://github.com/airbytehq/airbyte/pull/43279) | Update dependencies |
93+
| 0.2.8 | 2024-07-27 | [42739](https://github.com/airbytehq/airbyte/pull/42739) | Update dependencies |
94+
| 0.2.7 | 2024-07-20 | [42284](https://github.com/airbytehq/airbyte/pull/42284) | Update dependencies |
95+
| 0.2.6 | 2024-07-13 | [41756](https://github.com/airbytehq/airbyte/pull/41756) | Update dependencies |
96+
| 0.2.5 | 2024-07-10 | [41269](https://github.com/airbytehq/airbyte/pull/41269) | Update dependencies |
97+
| 0.2.4 | 2024-07-06 | [40904](https://github.com/airbytehq/airbyte/pull/40904) | Update dependencies |
98+
| 0.2.3 | 2024-06-25 | [40316](https://github.com/airbytehq/airbyte/pull/40316) | Update dependencies |
99+
| 0.2.2 | 2024-06-22 | [40002](https://github.com/airbytehq/airbyte/pull/40002) | Update dependencies |
100+
| 0.2.1 | 2024-06-04 | [39016](https://github.com/airbytehq/airbyte/pull/39016) | [autopull] Upgrade base image to v1.2.1 |
101+
| 0.2.0 | 2024-05-16 | [36509](https://github.com/airbytehq/airbyte/pull/36509) | Migrate to Low Code |
102+
| 0.1.16 | 2023-07-07 | [20833](https://github.com/airbytehq/airbyte/pull/20833) | Fix infinite loop for GroupMembers stream |
103+
| 0.1.15 | 2023-06-20 | [27533](https://github.com/airbytehq/airbyte/pull/27533) | Fixed group member stream and resource sets stream pagination |
104+
| 0.1.14 | 2022-12-24 | [20877](https://github.com/airbytehq/airbyte/pull/20877) | Disabled OAuth2.0 authorization method |
105+
| 0.1.13 | 2022-08-12 | [14700](https://github.com/airbytehq/airbyte/pull/14700) | Add resource sets |
106+
| 0.1.12 | 2022-08-05 | [15050](https://github.com/airbytehq/airbyte/pull/15050) | Add parameter `start_date` for Logs stream |
107+
| 0.1.11 | 2022-08-03 | [14739](https://github.com/airbytehq/airbyte/pull/14739) | Add permissions for custom roles |
108+
| 0.1.10 | 2022-08-01 | [15179](https://github.com/airbytehq/airbyte/pull/15179) | Fix broken schemas for all streams |
109+
| 0.1.9 | 2022-07-25 | [15001](https://github.com/airbytehq/airbyte/pull/15001) | Return deprovisioned users |
110+
| 0.1.8 | 2022-07-19 | [14710](https://github.com/airbytehq/airbyte/pull/14710) | Implement OAuth2.0 authorization method |
111+
| 0.1.7 | 2022-07-13 | [14556](https://github.com/airbytehq/airbyte/pull/14556) | Add User_Role_Assignments and Group_Role_Assignments streams (full fetch only) |
112+
| 0.1.6 | 2022-07-11 | [14610](https://github.com/airbytehq/airbyte/pull/14610) | Add custom roles stream |
113+
| 0.1.5 | 2022-07-04 | [14380](https://github.com/airbytehq/airbyte/pull/14380) | Add Group_Members stream to okta source |
114+
| 0.1.4 | 2021-11-02 | [7584](https://github.com/airbytehq/airbyte/pull/7584) | Fix incremental params for log stream |
115+
| 0.1.3 | 2021-09-08 | [5905](https://github.com/airbytehq/airbyte/pull/5905) | Fix incremental stream defect |
116+
| 0.1.2 | 2021-07-01 | [4456](https://github.com/airbytehq/airbyte/pull/4456) | Fix infinite pagination in logs stream |
116117
| 0.1.1 | 2021-06-09 | [3937](https://github.com/airbytehq/airbyte/pull/3973) | Add `AIRBYTE_ENTRYPOINT` env variable for kubernetes support |
117118
| 0.1.0 | 2021-05-30 | [3563](https://github.com/airbytehq/airbyte/pull/3563) | Initial Release |
118119

0 commit comments

Comments
 (0)