Skip to content

Commit

Permalink
Add refresh token example
Browse files Browse the repository at this point in the history
  • Loading branch information
bnchrch committed Jan 20, 2025
1 parent 81c886f commit 8ea142b
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 34 deletions.
122 changes: 100 additions & 22 deletions docs/connector-development/config-based/declarative-oauth.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,11 @@ index 58a4db9d73..9f20a44f70 100644

```

### Advanced Case: access token is returned in a different or nested field
Now imagine that the OAuth flow is updated so that the access token returned by `/oauth/token` is in a non standard / nested field. Specifically, the response is now:
### Advanced Case: access token is returned in a different field
Now imagine that the OAuth flow is updated so that the access token returned by `/oauth/token` is in a non standard field. Specifically, the response is now:
```json
{
"data": {
"super_duperaccess_token": "YOUR_ACCESS_TOKEN_123"
}
"super_duperaccess_token": "YOUR_ACCESS_TOKEN_123"
}
```

Expand All @@ -315,34 +313,114 @@ Now imagine that the OAuth flow is updated so that the access token returned by

```

### Query Parameters
TODO: Demonstrate adding custom query parameters and using oauth_user_input_from_connector_config_specification
### Advanced Case: access token is returned in a nested field
Now imagine that the OAuth flow is updated so that the access token returned by `/oauth/token` is in a nested non standard field. Specifically, the response is now:
```json
{
"data": {
"super_duperaccess_token": "YOUR_ACCESS_TOKEN_123"
}
}
```

### Authorization Headers
TODO: Show how to add custom auth headers with access_token_headers
#### Example Declarative OAuth Change
```diff

### Complete Example with Refresh Flow
TODO: Show a complete production example incorporating all features including refresh token support
```

## Best Practices
### Advanced Case: refresh token support
Imagine that the OAuth flow is updated so that the OAuth flow now supports refresh tokens.

TODO: Document recommended patterns, common pitfalls to avoid, and security considerations.
Meaning that the OAuth flow now has an additional endpoint:
`/oauth/refresh` - This is the refresh token URL that the connector will use to exchange the refresh token for an access token

## Troubleshooting
Example URL: https://yourconnectorservice.com/oauth/refresh?client_id={{client_id_value}}&client_secret={{client_secret_value}}&&refresh_token={{refresh_token}}

TODO: Add common issues and their solutions, debugging tips, and how to validate configurations.
and the response of `/oauth/token` now includes a refresh token field.
```json
{
"access_token": "YOUR_ACCESS_TOKEN_123",
"refresh_token": "YOUR_REFRESH_TOKEN_123"
}
```

# Declarative OAuth (experimental)
This is an experimental feature that allows you to configure OAuth authentication via a connectors spec output.
#### Example Declarative OAuth Change
```diff
diff --git a/docs/connector-development/config-based/understanding-the-yaml-file/declarative_oauth_examples/base_oauth.yml b/docs/connector-development/config-based/understanding-the-yaml-file/declarative_oauth_examples/refresh_token.yml
index 58a4db9d73..23c39503e3 100644
--- a/docs/connector-development/config-based/understanding-the-yaml-file/declarative_oauth_examples/base_oauth.yml
+++ b/docs/connector-development/config-based/understanding-the-yaml-file/declarative_oauth_examples/refresh_token.yml
@@ -41,9 +41,18 @@ definitions:
authenticator:
type: OAuthAuthenticator
refresh_request_body: {}
+ grant_type: refresh_token
client_id: "{{ config[\"client_id\"] }}"
client_secret: "{{ config[\"client_secret\"] }}"
+ refresh_token: "{{ config[\"client_refresh_token\"] }}"
access_token_value: "{{ config[\"client_access_token\"] }}"
+ access_token_name: access_token
+ refresh_token_updater:
+ refresh_token_name: refresh_token
+ refresh_token_config_path:
+ - client_refresh_token
+ token_refresh_endpoint: >-
+ https://yourconnectorservice.com/oauth/refresh?client_id={{client_id_value}}&client_secret={{client_secret_value}}&&refresh_token={{refresh_token}}

This feature is available for all connectors and has support in both our CDK and Connector Builder.
streams:
- $ref: "#/definitions/streams/moves"
@@ -56,7 +65,7 @@ spec:
required:
- client_id
- client_secret
- - client_access_token
+ - client_refresh_token
properties:
client_id:
type: string
@@ -68,9 +77,9 @@ spec:
- client_access_token:
+ client_refresh_token:
type: string
- title: Access token
+ title: Refresh token
@@ -86,16 +95,22 @@ spec:
https://yourconnectorservice.com/oauth/token?client_id={{client_id_value}}&client_secret={{client_secret_value}}&code={{auth_code_value}}
extract_output:
- access_token
+ - refresh_token
access_token_headers: {}
access_token_params: {}
complete_oauth_output_specification:
required:
- access_token
+ - refresh_token
properties:
access_token:
type: string
path_in_connector_config:
- access_token
+ refresh_token:
+ type: string
+ path_in_connector_config:
+ - refresh_token
complete_oauth_server_input_specification:
required:
- client_id

## Overview
This feature is primarily concerned with the `OAuthConfigSpecification` type as defined in the [protocol](https://github.com/airbytehq/airbyte-protocol/blob/d14cffb8123a8debe2d1c9c32c466f127ac1fb19/protocol-models/src/main/resources/airbyte_protocol/airbyte_protocol.yaml#L658C7-L658C42) and made available for use in the connector `spec` at `advanced_auth.oauth_config`.
```

### Advanced Case: The Connector is already using Legacy OAuth and the credentials are stored in a strange place
Imagine that the connector is already using Legacy OAuth and the credentials are stored in a strange place.

Specifically:
1. `client_id` is located in a users config file at `airbyte.super_secret_credentials.pokemon_client_id`
2. `client_secret` is located in a users config file at `airbyte.super_secret_credentials.pokemon_client_secret`
3. `access_token` is located in a users config file at `airbyte.super_secret_credentials.pokemon_access_token`

## Progressive Example
and we need to make sure that updating the spec to use Declarative OAuth doesn't break existing syncs.

### Simple OAuth with no overrides
#### Example Declarative OAuth Change
```diff

```
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ definitions:
authenticator:
type: OAuthAuthenticator
refresh_request_body: {}
client_id: "{{ config[\"client_id\"] }}"
client_secret: "{{ config[\"client_secret\"] }}"
access_token_value: "{{ config[\"client_access_token\"] }}"
client_id: '{{ config["client_id"] }}'
client_secret: '{{ config["client_secret"] }}'
access_token_value: '{{ config["client_access_token"] }}'

streams:
- $ref: "#/definitions/streams/moves"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,3 @@ schemas:
type:
- string
- "null"

Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ definitions:
type: OAuthAuthenticator
refresh_request_body: {}
grant_type: refresh_token
client_id: "{{ config[\"client_id\"] }}"
client_secret: "{{ config[\"client_secret\"] }}"
refresh_token: "{{ config[\"client_refresh_token\"] }}"
access_token_value: "{{ config[\"client_access_token\"] }}"
client_id: '{{ config["client_id"] }}'
client_secret: '{{ config["client_secret"] }}'
refresh_token: '{{ config["client_refresh_token"] }}'
access_token_value: '{{ config["client_access_token"] }}'

streams:
- $ref: "#/definitions/streams/moves"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
version: 6.13.0

type: DeclarativeSource

check:
type: CheckStream
stream_names:
- moves

definitions:
streams:
moves:
type: DeclarativeStream
name: moves
retriever:
type: SimpleRetriever
requester:
$ref: "#/definitions/base_requester"
path: /api/v2/move/
http_method: GET
record_selector:
type: RecordSelector
extractor:
type: DpathExtractor
field_path: []
paginator:
type: DefaultPaginator
page_token_option:
type: RequestPath
pagination_strategy:
type: CursorPagination
cursor_value: "{{ response.get('next') }}"
stop_condition: "{{ response.get('next') is none }}"
schema_loader:
type: InlineSchemaLoader
schema:
$ref: "#/schemas/moves"
base_requester:
type: HttpRequester
url_base: https://pokeapi.co
authenticator:
type: OAuthAuthenticator
refresh_request_body: {}
grant_type: refresh_token
client_id: '{{ config["client_id"] }}'
client_secret: '{{ config["client_secret"] }}'
refresh_token: '{{ config["client_refresh_token"] }}'
access_token_value: '{{ config["client_access_token"] }}'
access_token_name: access_token
refresh_token_updater:
refresh_token_name: refresh_token
refresh_token_config_path:
- client_refresh_token
token_refresh_endpoint: >-
https://yourconnectorservice.com/oauth/refresh?client_id={{client_id_value}}&client_secret={{client_secret_value}}&&refresh_token={{refresh_token}}
streams:
- $ref: "#/definitions/streams/moves"

spec:
type: Spec
connection_specification:
type: object
$schema: http://json-schema.org/draft-07/schema#
required:
- client_id
- client_secret
- client_refresh_token
properties:
client_id:
type: string
title: Client ID
airbyte_secret: true
order: 0
client_secret:
type: string
title: Client secret
airbyte_secret: true
order: 1
client_refresh_token:
type: string
title: Refresh token
airbyte_secret: true
airbyte_hidden: false
order: 2
additionalProperties: true
advanced_auth:
auth_flow_type: oauth2.0
oauth_config_specification:
oauth_connector_input_specification:
consent_url: >-
https://yourconnectorservice.com/oauth/consent?client_id={{client_id_value}}&redirect_uri={{
redirect_uri }}&state={{ state }}
access_token_url: >-
https://yourconnectorservice.com/oauth/token?client_id={{client_id_value}}&client_secret={{client_secret_value}}&code={{auth_code_value}}
extract_output:
- access_token
- refresh_token
access_token_headers: {}
access_token_params: {}
complete_oauth_output_specification:
required:
- access_token
- refresh_token
properties:
access_token:
type: string
path_in_connector_config:
- access_token
refresh_token:
type: string
path_in_connector_config:
- refresh_token
complete_oauth_server_input_specification:
required:
- client_id
- client_secret
properties:
client_id:
type: string
client_secret:
type: string
complete_oauth_server_output_specification:
required:
- client_id
- client_secret
properties:
client_id:
type: string
path_in_connector_config:
- client_id
client_secret:
type: string
path_in_connector_config:
- client_secret

metadata:
autoImportSchema:
moves: true
testedStreams:
moves:
streamHash: b552388689db1277a90c14c5972124e995a273ab
hasResponse: true
responsesAreSuccessful: true
hasRecords: true
primaryKeysArePresent: true
primaryKeysAreUnique: true
assist:
docsUrl: https://pokeapi.co/docs/v2

schemas:
moves:
type: object
$schema: http://json-schema.org/schema#
additionalProperties: true
properties:
count:
type:
- number
- "null"
next:
type:
- string
- "null"
previous:
type:
- string
- "null"
results:
type:
- array
- "null"
items:
type:
- object
- "null"
properties:
name:
type:
- string
- "null"
url:
type:
- string
- "null"
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ definitions:
type: OAuthAuthenticator
refresh_request_body: {}
grant_type: refresh_token
client_id: "{{ config[\"client_id\"] }}"
client_secret: "{{ config[\"client_secret\"] }}"
refresh_token: "{{ config[\"client_refresh_token\"] }}"
access_token_value: "{{ config[\"client_access_token\"] }}"
client_id: '{{ config["client_id"] }}'
client_secret: '{{ config["client_secret"] }}'
refresh_token: '{{ config["client_refresh_token"] }}'
access_token_value: '{{ config["client_access_token"] }}'

streams:
- $ref: "#/definitions/streams/moves"
Expand Down

0 comments on commit 8ea142b

Please sign in to comment.