Skip to content

Commit

Permalink
✨ Source Okta: Authentication with private key (OAuth 2.0) support (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
evyros authored Aug 14, 2024
1 parent a7a9688 commit 34c76fb
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 35 deletions.
2 changes: 1 addition & 1 deletion airbyte-integrations/connectors/source-okta/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ data:
connectorSubtype: api
connectorType: source
definitionId: 1d4fdb25-64fc-4569-92da-fcdca79a8372
dockerImageTag: 0.2.11
dockerImageTag: 0.3.0
dockerRepository: airbyte/source-okta
githubIssueLabel: source-okta
icon: icon.svg
Expand Down
2 changes: 1 addition & 1 deletion airbyte-integrations/connectors/source-okta/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ requires = [ "poetry-core>=1.0.0",]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
version = "0.2.11"
version = "0.3.0"
name = "source-okta"
description = "Source implementation for okta."
authors = [ "Airbyte <[email protected]>",]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"domain": "myorg",
"start_date": "2024-08-02T00:00:00Z",
"credentials": {
"auth_type": "oauth2.0_private_key",
"client_id": "0odiji39hrrlrggPb7d7",
"key_id": "IvLuOYbri0fMcIhdnaOhcjD5S_WXzkk7Cxdo0_M28r0",
"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",
"scope": "okta.users.read"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

from .source import SourceOkta

__all__ = ["SourceOkta", "custom_authenticators"]
__all__ = ["SourceOkta", "components"]
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
#

import time
import uuid
from dataclasses import InitVar, dataclass
from typing import Any, Mapping, Tuple

import jwt
import requests
from airbyte_cdk.sources.declarative.auth import DeclarativeOauth2Authenticator
from airbyte_cdk.sources.declarative.auth.declarative_authenticator import DeclarativeAuthenticator
Expand Down Expand Up @@ -61,3 +64,55 @@ def refresh_access_token(self) -> Tuple[str, int]:
return response_json["access_token"], response_json["expires_in"]
except Exception as e:
raise Exception(f"Error while refreshing access token: {e}") from e


@dataclass
class CustomOauth2PrivateKeyAuthenticator(DeclarativeAuthenticator):
"""
Custom authenticator that uses a signed JWT with a private key to authenticate against Okta.
"""

config: Config

@property
def auth_header(self) -> str:
return "Authorization"

@property
def token(self) -> str:
domain = self.config["domain"]
client_id = self.config["credentials"]["client_id"]
key_id = self.config["credentials"]["key_id"]
private_key = self.config["credentials"]["private_key"]
scope = self.config["credentials"]["scope"]
now = int(time.time())

jwt_payload = {
"iss": client_id,
"sub": client_id,
"aud": f"https://{domain}.okta.com/oauth2/v1/token",
"iat": now,
"exp": now + 3600,
"jti": str(uuid.uuid4()),
}
jwt_headers = {"kid": key_id, "alg": "RS256"}

client_assertion = jwt.encode(jwt_payload, private_key, algorithm="RS256", headers=jwt_headers)
token_url = f"https://{domain}.okta.com/oauth2/v1/token"
token_response = requests.post(
token_url,
data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": client_assertion,
"scope": scope,
},
headers={"Content-Type": "application/x-www-form-urlencoded"},
)

try:
response = token_response.json()
return f"Bearer {response['access_token']}"
except Exception as e:
raise Exception(f"Error while getting access token: {e}") from e
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,28 @@ type: DeclarativeSource
definitions:
custom_oauth_authenticator:
type: CustomAuthenticator
class_name: source_okta.custom_authenticators.CustomOauth2Authenticator
class_name: source_okta.components.CustomOauth2Authenticator
client_id: "{{ config['credentials']['client_id'] }}"
client_secret: "{{ config['credentials']['client_secret'] }}"
refresh_token: "{{ config['credentials']['refresh_token'] }}"
refresh_request_body: {}
token_refresh_endpoint: "https://{{ config['domain'] }}.okta.com/oauth2/v1/token"
grant_type: refresh_token

custom_oauth_private_key_authenticator:
type: CustomAuthenticator
class_name: source_okta.components.CustomOauth2PrivateKeyAuthenticator

custom_bearer_authenticator:
type: CustomAuthenticator
class_name: source_okta.custom_authenticators.CustomBearerAuthenticator
class_name: source_okta.components.CustomBearerAuthenticator

selective_authenticator:
type: SelectiveAuthenticator
authenticator_selection_path: ["credentials", "auth_type"]
authenticators:
oauth2.0: "#/definitions/custom_oauth_authenticator"
oauth2.0_private_key: "#/definitions/custom_oauth_private_key_authenticator"
api_token: "#/definitions/custom_bearer_authenticator"

paginator:
Expand Down Expand Up @@ -1850,6 +1855,42 @@ spec:
title: Refresh Token
description: Refresh Token to obtain new Access Token, when it's expired.
airbyte_secret: true
- type: object
title: OAuth 2.0 with private key
required:
- auth_type
- client_id
- key_id
- private_key
- scope
properties:
auth_type:
type: string
const: oauth2.0_private_key
order: 0
client_id:
type: string
title: Client ID
description: The Client ID of your OAuth application.
airbyte_secret: true
order: 1
key_id:
type: string
title: Key ID
description: The key ID (kid).
airbyte_secret: true
order: 2
private_key:
type: string
title: Private key
description: The private key in PEM format
airbyte_secret: true
order: 3
scope:
type: string
title: Scope
description: The OAuth scope.
order: 4
- type: object
title: API Token
required:
Expand Down
36 changes: 36 additions & 0 deletions airbyte-integrations/connectors/source-okta/source_okta/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,42 @@ connectionSpecification:
title: Refresh Token
description: Refresh Token to obtain new Access Token, when it's expired.
airbyte_secret: true
- type: object
title: OAuth 2.0 with private key
required:
- auth_type
- client_id
- key_id
- private_key
- scope
properties:
auth_type:
type: string
const: oauth2.0_private_key
order: 0
client_id:
type: string
title: Client ID
description: The Client ID of your OAuth application.
airbyte_secret: true
order: 1
key_id:
type: string
title: Key ID
description: The key ID (kid).
airbyte_secret: true
order: 2
private_key:
type: string
title: Private key
description: The private key in PEM format
airbyte_secret: true
order: 3
scope:
type: string
title: Scope
description: The OAuth scope.
order: 4
- type: object
title: API Token
required:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
#

from source_okta.custom_authenticators import CustomBearerAuthenticator, CustomOauth2Authenticator
from source_okta.components import CustomBearerAuthenticator, CustomOauth2Authenticator
from source_okta.source import SourceOkta


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import pytest
import requests
from airbyte_cdk.sources.streams import Stream
from source_okta.custom_authenticators import CustomBearerAuthenticator, CustomOauth2Authenticator
from source_okta.components import CustomBearerAuthenticator, CustomOauth2Authenticator
from source_okta.source import SourceOkta


Expand Down
57 changes: 29 additions & 28 deletions docs/integrations/sources/okta.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Okta is the complete identity solution for all your apps and people that’s uni

## Prerequisites

- 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)
- 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)

## Airbyte Open Source

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

| Version | Date | Pull Request | Subject |
|:--------|:-----------|:---------------------------------------------------------|:-------------------------------------------------------------------------------|
| 0.2.11 | 2024-08-12 | [43820](https://github.com/airbytehq/airbyte/pull/43820) | Update dependencies |
| 0.2.10 | 2024-08-10 | [43672](https://github.com/airbytehq/airbyte/pull/43672) | Update dependencies |
| 0.2.9 | 2024-08-03 | [43279](https://github.com/airbytehq/airbyte/pull/43279) | Update dependencies |
| 0.2.8 | 2024-07-27 | [42739](https://github.com/airbytehq/airbyte/pull/42739) | Update dependencies |
| 0.2.7 | 2024-07-20 | [42284](https://github.com/airbytehq/airbyte/pull/42284) | Update dependencies |
| 0.2.6 | 2024-07-13 | [41756](https://github.com/airbytehq/airbyte/pull/41756) | Update dependencies |
| 0.2.5 | 2024-07-10 | [41269](https://github.com/airbytehq/airbyte/pull/41269) | Update dependencies |
| 0.2.4 | 2024-07-06 | [40904](https://github.com/airbytehq/airbyte/pull/40904) | Update dependencies |
| 0.2.3 | 2024-06-25 | [40316](https://github.com/airbytehq/airbyte/pull/40316) | Update dependencies |
| 0.2.2 | 2024-06-22 | [40002](https://github.com/airbytehq/airbyte/pull/40002) | Update dependencies |
| 0.2.1 | 2024-06-04 | [39016](https://github.com/airbytehq/airbyte/pull/39016) | [autopull] Upgrade base image to v1.2.1 |
| 0.2.0 | 2024-05-16 | [36509](https://github.com/airbytehq/airbyte/pull/36509) | Migrate to Low Code |
| 0.1.16 | 2023-07-07 | [20833](https://github.com/airbytehq/airbyte/pull/20833) | Fix infinite loop for GroupMembers stream |
| 0.1.15 | 2023-06-20 | [27533](https://github.com/airbytehq/airbyte/pull/27533) | Fixed group member stream and resource sets stream pagination |
| 0.1.14 | 2022-12-24 | [20877](https://github.com/airbytehq/airbyte/pull/20877) | Disabled OAuth2.0 authorization method |
| 0.1.13 | 2022-08-12 | [14700](https://github.com/airbytehq/airbyte/pull/14700) | Add resource sets |
| 0.1.12 | 2022-08-05 | [15050](https://github.com/airbytehq/airbyte/pull/15050) | Add parameter `start_date` for Logs stream |
| 0.1.11 | 2022-08-03 | [14739](https://github.com/airbytehq/airbyte/pull/14739) | Add permissions for custom roles |
| 0.1.10 | 2022-08-01 | [15179](https://github.com/airbytehq/airbyte/pull/15179) | Fix broken schemas for all streams |
| 0.1.9 | 2022-07-25 | [15001](https://github.com/airbytehq/airbyte/pull/15001) | Return deprovisioned users |
| 0.1.8 | 2022-07-19 | [14710](https://github.com/airbytehq/airbyte/pull/14710) | Implement OAuth2.0 authorization method |
| 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) |
| 0.1.6 | 2022-07-11 | [14610](https://github.com/airbytehq/airbyte/pull/14610) | Add custom roles stream |
| 0.1.5 | 2022-07-04 | [14380](https://github.com/airbytehq/airbyte/pull/14380) | Add Group_Members stream to okta source |
| 0.1.4 | 2021-11-02 | [7584](https://github.com/airbytehq/airbyte/pull/7584) | Fix incremental params for log stream |
| 0.1.3 | 2021-09-08 | [5905](https://github.com/airbytehq/airbyte/pull/5905) | Fix incremental stream defect |
| 0.1.2 | 2021-07-01 | [4456](https://github.com/airbytehq/airbyte/pull/4456) | Fix infinite pagination in logs stream |
| 0.3.0 | 2024-08-13 | [43382](https://github.com/airbytehq/airbyte/pull/43382) | Support OAuth 2.0 with private key |
| 0.2.11 | 2024-08-12 | [43820](https://github.com/airbytehq/airbyte/pull/43820) | Update dependencies |
| 0.2.10 | 2024-08-10 | [43672](https://github.com/airbytehq/airbyte/pull/43672) | Update dependencies |
| 0.2.9 | 2024-08-03 | [43279](https://github.com/airbytehq/airbyte/pull/43279) | Update dependencies |
| 0.2.8 | 2024-07-27 | [42739](https://github.com/airbytehq/airbyte/pull/42739) | Update dependencies |
| 0.2.7 | 2024-07-20 | [42284](https://github.com/airbytehq/airbyte/pull/42284) | Update dependencies |
| 0.2.6 | 2024-07-13 | [41756](https://github.com/airbytehq/airbyte/pull/41756) | Update dependencies |
| 0.2.5 | 2024-07-10 | [41269](https://github.com/airbytehq/airbyte/pull/41269) | Update dependencies |
| 0.2.4 | 2024-07-06 | [40904](https://github.com/airbytehq/airbyte/pull/40904) | Update dependencies |
| 0.2.3 | 2024-06-25 | [40316](https://github.com/airbytehq/airbyte/pull/40316) | Update dependencies |
| 0.2.2 | 2024-06-22 | [40002](https://github.com/airbytehq/airbyte/pull/40002) | Update dependencies |
| 0.2.1 | 2024-06-04 | [39016](https://github.com/airbytehq/airbyte/pull/39016) | [autopull] Upgrade base image to v1.2.1 |
| 0.2.0 | 2024-05-16 | [36509](https://github.com/airbytehq/airbyte/pull/36509) | Migrate to Low Code |
| 0.1.16 | 2023-07-07 | [20833](https://github.com/airbytehq/airbyte/pull/20833) | Fix infinite loop for GroupMembers stream |
| 0.1.15 | 2023-06-20 | [27533](https://github.com/airbytehq/airbyte/pull/27533) | Fixed group member stream and resource sets stream pagination |
| 0.1.14 | 2022-12-24 | [20877](https://github.com/airbytehq/airbyte/pull/20877) | Disabled OAuth2.0 authorization method |
| 0.1.13 | 2022-08-12 | [14700](https://github.com/airbytehq/airbyte/pull/14700) | Add resource sets |
| 0.1.12 | 2022-08-05 | [15050](https://github.com/airbytehq/airbyte/pull/15050) | Add parameter `start_date` for Logs stream |
| 0.1.11 | 2022-08-03 | [14739](https://github.com/airbytehq/airbyte/pull/14739) | Add permissions for custom roles |
| 0.1.10 | 2022-08-01 | [15179](https://github.com/airbytehq/airbyte/pull/15179) | Fix broken schemas for all streams |
| 0.1.9 | 2022-07-25 | [15001](https://github.com/airbytehq/airbyte/pull/15001) | Return deprovisioned users |
| 0.1.8 | 2022-07-19 | [14710](https://github.com/airbytehq/airbyte/pull/14710) | Implement OAuth2.0 authorization method |
| 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) |
| 0.1.6 | 2022-07-11 | [14610](https://github.com/airbytehq/airbyte/pull/14610) | Add custom roles stream |
| 0.1.5 | 2022-07-04 | [14380](https://github.com/airbytehq/airbyte/pull/14380) | Add Group_Members stream to okta source |
| 0.1.4 | 2021-11-02 | [7584](https://github.com/airbytehq/airbyte/pull/7584) | Fix incremental params for log stream |
| 0.1.3 | 2021-09-08 | [5905](https://github.com/airbytehq/airbyte/pull/5905) | Fix incremental stream defect |
| 0.1.2 | 2021-07-01 | [4456](https://github.com/airbytehq/airbyte/pull/4456) | Fix infinite pagination in logs stream |
| 0.1.1 | 2021-06-09 | [3937](https://github.com/airbytehq/airbyte/pull/3973) | Add `AIRBYTE_ENTRYPOINT` env variable for kubernetes support |
| 0.1.0 | 2021-05-30 | [3563](https://github.com/airbytehq/airbyte/pull/3563) | Initial Release |

Expand Down

0 comments on commit 34c76fb

Please sign in to comment.