Skip to content

Commit

Permalink
Source Slack - remove OAuth2.0 refresh_token (#19970)
Browse files Browse the repository at this point in the history
Signed-off-by: Sergey Chvalyuk <grubberr@gmail.com>
  • Loading branch information
grubberr authored Dec 2, 2022
1 parent 3c6f979 commit 764496e
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1484,7 +1484,7 @@
- name: Slack
sourceDefinitionId: c2281cee-86f9-4a86-bb48-d23286b4c7bd
dockerRepository: airbyte/source-slack
dockerImageTag: 0.1.18
dockerImageTag: 0.1.19
documentationUrl: https://docs.airbyte.com/integrations/sources/slack
icon: slack.svg
sourceType: api
Expand Down
79 changes: 45 additions & 34 deletions airbyte-config/init/src/main/resources/seed/source_specs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13331,7 +13331,7 @@
supportsNormalization: false
supportsDBT: false
supported_destination_sync_modes: []
- dockerImage: "airbyte/source-slack:0.1.18"
- dockerImage: "airbyte/source-slack:0.1.19"
spec:
documentationUrl: "https://docs.airbyte.com/integrations/sources/slack"
connectionSpecification:
Expand Down Expand Up @@ -13386,52 +13386,37 @@
- type: "object"
title: "Sign in via Slack (OAuth)"
required:
- "access_token"
- "option_title"
- "client_id"
- "client_secret"
- "option_title"
- "access_token"
properties:
option_title:
type: "string"
const: "Default OAuth2.0 authorization"
client_id:
type: "string"
title: "Client ID"
description: "Slack client_id. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\"\
>docs</a> if you need help finding this id."
type: "string"
examples:
- "slack-client-id-example"
airbyte_secret: true
client_secret:
type: "string"
title: "Client Secret"
description: "Slack client_secret. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\"\
>docs</a> if you need help finding this secret."
type: "string"
examples:
- "slack-client-secret-example"
airbyte_secret: true
access_token:
type: "string"
title: "Access token"
description: "Slack access_token. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\"\
>docs</a> if you need help generating the token."
type: "string"
examples:
- "slack-access-token-example"
airbyte_secret: true
refresh_token:
title: "Refresh token"
description: "Slack refresh_token. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\"\
>docs</a> if you need help generating the token."
type: "string"
examples:
- "slack-refresh-token-example"
airbyte_secret: true
order: 0
- type: "object"
title: "API Token"
required:
- "api_token"
- "option_title"
- "api_token"
properties:
option_title:
type: "string"
Expand All @@ -13446,18 +13431,44 @@
supportsNormalization: false
supportsDBT: false
supported_destination_sync_modes: []
authSpecification:
auth_type: "oauth2.0"
oauth2Specification:
rootObject:
- "credentials"
- "0"
oauthFlowInitParameters:
- - "client_id"
- - "client_secret"
oauthFlowOutputParameters:
- - "access_token"
- - "refresh_token"
advanced_auth:
auth_flow_type: "oauth2.0"
predicate_key:
- "credentials"
- "option_title"
predicate_value: "Default OAuth2.0 authorization"
oauth_config_specification:
complete_oauth_output_specification:
type: "object"
additionalProperties: false
properties:
access_token:
type: "string"
path_in_connector_config:
- "credentials"
- "access_token"
complete_oauth_server_input_specification:
type: "object"
additionalProperties: false
properties:
client_id:
type: "string"
client_secret:
type: "string"
complete_oauth_server_output_specification:
type: "object"
additionalProperties: false
properties:
client_id:
type: "string"
path_in_connector_config:
- "credentials"
- "client_id"
client_secret:
type: "string"
path_in_connector_config:
- "credentials"
- "client_secret"
- dockerImage: "airbyte/source-smaily:0.1.0"
spec:
documentationUrl: "https://docs.airbyte.com/integrations/sources/smaily"
Expand Down
2 changes: 1 addition & 1 deletion airbyte-integrations/connectors/source-slack/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ COPY main.py ./
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]

LABEL io.airbyte.version=0.1.18
LABEL io.airbyte.version=0.1.19
LABEL io.airbyte.name=airbyte/source-slack
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from airbyte_cdk.sources import AbstractSource
from airbyte_cdk.sources.streams import Stream
from airbyte_cdk.sources.streams.http import HttpStream, HttpSubStream
from airbyte_cdk.sources.streams.http.requests_native_auth import Oauth2Authenticator, TokenAuthenticator
from airbyte_cdk.sources.streams.http.requests_native_auth import TokenAuthenticator
from pendulum import DateTime, Period


Expand Down Expand Up @@ -342,16 +342,6 @@ def _get_authenticator(self, config: Mapping[str, Any]):
credentials = config.get("credentials", {})
credentials_title = credentials.get("option_title")
if credentials_title == "Default OAuth2.0 authorization":
# We can get `refresh_token` only if the token rotation function is enabled for the Slack Oauth Application.
# If it is disabled, then we use the generated `access_token`, which acts without expiration.
# https://api.slack.com/authentication/rotation
if credentials.get("refresh_token", "").strip():
return Oauth2Authenticator(
token_refresh_endpoint="https://slack.com/api/oauth.v2.access",
client_id=credentials["client_id"],
client_secret=credentials["client_secret"],
refresh_token=credentials["refresh_token"],
)
return TokenAuthenticator(credentials["access_token"])
elif credentials_title == "API Token Credentials":
return TokenAuthenticator(credentials["api_token"])
Expand Down
72 changes: 48 additions & 24 deletions airbyte-integrations/connectors/source-slack/source_slack/spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,42 +46,31 @@
"type": "object",
"title": "Sign in via Slack (OAuth)",
"required": [
"access_token",
"option_title",
"client_id",
"client_secret",
"option_title"
"access_token"
],
"properties": {
"option_title": {
"type": "string",
"const": "Default OAuth2.0 authorization"
},
"client_id": {
"title": "Client ID",
"description": "Slack client_id. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\">docs</a> if you need help finding this id.",
"type": "string",
"examples": ["slack-client-id-example"],
"airbyte_secret": true
"title": "Client ID",
"description": "Slack client_id. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\">docs</a> if you need help finding this id."
},
"client_secret": {
"type": "string",
"title": "Client Secret",
"description": "Slack client_secret. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\">docs</a> if you need help finding this secret.",
"type": "string",
"examples": ["slack-client-secret-example"],
"airbyte_secret": true
},
"access_token": {
"type": "string",
"title": "Access token",
"description": "Slack access_token. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\">docs</a> if you need help generating the token.",
"type": "string",
"examples": ["slack-access-token-example"],
"airbyte_secret": true
},
"refresh_token": {
"title": "Refresh token",
"description": "Slack refresh_token. See our <a href=\"https://docs.airbyte.com/integrations/sources/slack\">docs</a> if you need help generating the token.",
"type": "string",
"examples": ["slack-refresh-token-example"],
"airbyte_secret": true
}
},
Expand All @@ -90,7 +79,7 @@
{
"type": "object",
"title": "API Token",
"required": ["api_token", "option_title"],
"required": ["option_title", "api_token"],
"properties": {
"option_title": {
"type": "string",
Expand All @@ -109,12 +98,47 @@
}
}
},
"authSpecification": {
"auth_type": "oauth2.0",
"oauth2Specification": {
"rootObject": ["credentials", 0],
"oauthFlowInitParameters": [["client_id"], ["client_secret"]],
"oauthFlowOutputParameters": [["access_token"], ["refresh_token"]]
"advanced_auth": {
"auth_flow_type": "oauth2.0",
"predicate_key": ["credentials", "option_title"],
"predicate_value": "Default OAuth2.0 authorization",
"oauth_config_specification": {
"complete_oauth_output_specification": {
"type": "object",
"additionalProperties": false,
"properties": {
"access_token": {
"type": "string",
"path_in_connector_config": ["credentials", "access_token"]
}
}
},
"complete_oauth_server_input_specification": {
"type": "object",
"additionalProperties": false,
"properties": {
"client_id": {
"type": "string"
},
"client_secret": {
"type": "string"
}
}
},
"complete_oauth_server_output_specification": {
"type": "object",
"additionalProperties": false,
"properties": {
"client_id": {
"type": "string",
"path_in_connector_config": ["credentials", "client_id"]
},
"client_secret": {
"type": "string",
"path_in_connector_config": ["credentials", "client_secret"]
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import org.apache.http.client.utils.URIBuilder;

public class SlackOAuthFlow extends BaseOAuth2Flow {

final String SLACK_CONSENT_URL_BASE = "https://slack.com/oauth/authorize";
final String SLACK_TOKEN_URL = "https://slack.com/api/oauth.access";
private static final String AUTHORIZE_URL = "https://slack.com/oauth/authorize";
private static final String ACCESS_TOKEN_URL = "https://slack.com/api/oauth.access";

public SlackOAuthFlow(final ConfigRepository configRepository, final HttpClient httpClient) {
super(configRepository, httpClient);
Expand All @@ -41,7 +42,7 @@ protected String formatConsentUrl(final UUID definitionId,
final JsonNode inputOAuthConfiguration)
throws IOException {
try {
return new URIBuilder(SLACK_CONSENT_URL_BASE)
return new URIBuilder(AUTHORIZE_URL)
.addParameter("client_id", clientId)
.addParameter("redirect_uri", redirectUrl)
.addParameter("state", getState())
Expand All @@ -57,7 +58,16 @@ protected String formatConsentUrl(final UUID definitionId,
*/
@Override
protected String getAccessTokenUrl(final JsonNode inputOAuthConfiguration) {
return SLACK_TOKEN_URL;
return ACCESS_TOKEN_URL;
}

@Override
protected Map<String, Object> extractOAuthOutput(final JsonNode data, final String accessTokenUrl) throws IOException {
if (data.has("access_token")) {
return Map.of("access_token", data.get("access_token").asText());
} else {
throw new IOException(String.format("Missing 'access_token' in query params from %s", ACCESS_TOKEN_URL));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

package io.airbyte.oauth.flows;

import com.fasterxml.jackson.databind.JsonNode;
import io.airbyte.oauth.BaseOAuthFlow;
import io.airbyte.oauth.MoreOAuthParameters;
import java.util.Map;

public class SlackOAuthFlowTest extends BaseOAuthFlowTest {

Expand All @@ -18,4 +21,24 @@ protected String getExpectedConsentUrl() {
return "https://slack.com/oauth/authorize?client_id=test_client_id&redirect_uri=https%3A%2F%2Fairbyte.io&state=state&scope=read";
}

@Override
protected Map<String, String> getExpectedOutput() {
return Map.of(
"access_token", "access_token_response",
"client_id", MoreOAuthParameters.SECRET_MASK,
"client_secret", MoreOAuthParameters.SECRET_MASK);
}

@Override
protected JsonNode getCompleteOAuthOutputSpecification() {
return getJsonSchema(Map.of("access_token", Map.of("type", "string")));
}

@Override
protected Map<String, String> getExpectedFilteredOutput() {
return Map.of(
"access_token", "access_token_response",
"client_id", MoreOAuthParameters.SECRET_MASK);
}

}
1 change: 1 addition & 0 deletions docs/integrations/sources/slack.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ It is recommended to sync required channels only, this can be done by specifying

| Version | Date | Pull Request | Subject |
|:--------|:-----------|:---------------------------------------------------------|:----------------------------------------------------|
| 0.1.19 | 2022-12-01 | [19970](https://github.com/airbytehq/airbyte/pull/19970) | Remove OAuth2.0 broken `refresh_token` support |
| 0.1.18 | 2022-09-28 | [17315](https://github.com/airbytehq/airbyte/pull/17315) | Always install latest version of Airbyte CDK |
| 0.1.17 | 2022-08-28 | [16085](https://github.com/airbytehq/airbyte/pull/16085) | Increase unit test coverage |
| 0.1.16 | 2022-08-28 | [16050](https://github.com/airbytehq/airbyte/pull/16050) | Fix SATs |
Expand Down

0 comments on commit 764496e

Please sign in to comment.