When you're ready to integrate your app with external services, use the CLI to encrypt and store OAuth2 credentials, then configure your app to utilize an OAuth2 authorization flow.
The following steps will take you through integrating your Slack app with an external service, using Google as an example.
The first thing to do is tell your app about your OAuth2 provider. To do that, head over to the external service that you're integrating your app with and create a new OAuth2 credential.
If you're asked for a redirect URL, use the following:
https://oauth2.slack.com/external/auth/callback
When you're done creating your credential, locate the credential's client ID and client secret, then head to your manifest file (manifest.ts
).
The next step is to define an OAuth2 provider for your app.
Inside your app's manifest, import DefineOAuth2Provider
, then create a
new provider instance.
Following is an example provider instance for Google. You can define it right in your manifest, or put it in its own file and import it into the manifest later.
// manifest.ts
import {
DefineFunction,
DefineOAuth2Provider, // <-- import this
DefineWorkflow,
Manifest,
Schema,
} from "deno-slack-sdk/mod.ts";
// ...
// Define a new OAuth2 provider:
// Note: replace <your_client_id> with your actual client ID
// if you're following along and creating an OAuth2 provider for
// Google.
const GoogleProvider = DefineOAuth2Provider({
provider_key: "google",
provider_type: Schema.providers.oauth2.CUSTOM,
options: {
"provider_name": "Google",
"authorization_url": "https://accounts.google.com/o/oauth2/auth",
"token_url": "https://oauth2.googleapis.com/token",
"client_id":
"<your_client_id>.apps.googleusercontent.com",
"scope": [
"https://www.googleapis.com/auth/spreadsheets.readonly",
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
],
"authorization_url_extras": {
"prompt": "consent",
"access_type": "offline",
},
"identity_config": {
"url": "https://www.googleapis.com/oauth2/v1/userinfo",
"account_identifier": "$.email",
},
},
});
// ...
Field | Type | Description |
---|---|---|
provider_key |
string |
The unique string identifier for a provider. An app cannot have two providers with the same unique identifier. Changing unique identifier will be treated like deletion of a provider. Providers with active tokens cannot be deleted. |
provider_type |
Schema.providers.oauth2 |
The only supported provider type value at this time is Schema.providers.oauth2.CUSTOM . |
provider_name |
string |
The name of your provider. |
client_id |
string |
The client ID from your external authentication provider. |
authorization_url |
string |
An OAuth2 requirement to complete the OAuth2 flow and to direct the user to the provider consent screen. |
token_url |
string |
An OAuth2 requirement to complete the OAuth2 flow and to exchange the code with an access token. |
scope |
array |
An OAuth2 requirement to complete the OAuth2 flow and to grant only the scopes provided to the access token. |
identity_config |
object |
Used to obtain user identity by finding the account associated with the access token. Must include url (the endpoint the provider exposes to fetch user identity) and account_identifier (the field in the response that represents the identity). May also include headers (object ) in case your OAuth2 provider requires additional headers to fetch user identity. |
authorization_url_extras |
object |
(Optional) This is for providers who require additional query parameters in their authorize_url . |
With a new OAuth2 provider defined, the next step is to tell your app about it.
Open your manifest file and insert your newly defined provider into the externalAuthProviders
property in your app's manifest (if that property doesn't exist yet, go ahead and create it):
export default Manifest({
//...
// Tell your app about your OAuth2 providers here:
externalAuthProviders: [GoogleProvider],
//...
});
With your OAuth2 provider defined and your manifest configured to use it, it's time to encrypt and store your client secret so that your app's users can utilize the OAuth2 authorization flow.
Your app needs to be deployed to Slack once in order to create a safe place to store your encrypted client secret. Do that with slack deploy
:
slack deploy
The slack deploy
command will bring up a list of currently authorized workspaces. Select the workspace where your app will live, and wait for the CLI to finish deploying your app.
Once your app is finished deploying, stay in your terminal window to add your client secret for the newly defined provider, making sure to wrap the secret string in double quotes:
slack external-auth add-secret --provider google --secret "GOCSPX-abc123..."
Running the above add-secret
command will bring up a list of workspaces available to you. Find and select the workspace you recently deployed your app to; you'll know it's the
workspace you recently installed the app in by locating the item in the list with your app's name and ID (e.g., myapp A01BC...
) rather than "App is not installed to this workspace."
If you get a provider_not_found
error, go back to your manifest file and check that you included your OAuth2 provider in the externalAuthProviders
properties of your manifest definition.
If everything was successful, the CLI will let you know:
✨ successfully added external auth client secret for google
Great! With your app configured to interact with your defined OAuth2 provider, we can now initialize the OAuth2 sign-in flow, connecting your external provider to your Slack app.
Once your provider's client secret has been added or updated, it's time to
create a token for your app to interact with your OAuth2 provider with external-auth add
.
First, run the following command:
slack external-auth add
This will provide a list of workspaces your app is deployed to. Select the one that you're currently working in.
Upon selection, you'll be provided a list of all providers that have been defined for this app, along with whether there's a secret and token.
$ slack external-auth add
? Select a provider [Use arrows to move, type to filter]
> Provider Key: google
Provider Name: Google
Client ID: <your_id>.apps.googleusercontent.com
Client Secret Exists? Yes
Token Exists? No
If you have just created a provider, you'll notice that it reports no tokens existing. Let's go ahead and create a token by initializing the OAuth2 sign-in flow.
Select the provider you're working on, which will open a browser window for you to complete the OAuth2 sign-in flow according to your provider's requirements.
You'll know you're successful when your browser sends you to a oauth2.slack.com
page that
says "Your account was successfully connected. You can safely close this window and go back to your terminal."
Verify that a valid token has been created by re-running the external-auth add
command:
slack external-auth add
? Select a provider [Use arrows to move, type to filter]
> Provider Key: google
Provider Name: Google
Client ID: <your_id>.apps.googleusercontent.com
Client Secret Exists? Yes
Token Exists? Yes
If you see Token Exists? Yes
, then a valid auth token has been created, meaning you're ready to rely on OAuth2 in your app! Exit out of this command flow by entering Ctrl+C
in
your terminal -- otherwise you'll be shuttled through the OAuth2 sign-in flow again.
Your custom functions can leverage your provider's token
by configuring it to receive an oauth2 type as an input. To do this, add an
input parameter of type Schema.slack.types.oauth2
to your function's definition.
Here's how that might look if we were to use the sample function from the Starter App template:
// functions/sample_function.ts
import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";
import { SlackAPI } from "deno-slack-api/mod.ts";
export const SampleFunctionDefinition = DefineFunction({
callback_id: "sample_function",
title: "Sample function",
description: "A sample function",
source_file: "functions/sample_function.ts",
input_parameters: {
properties: {
message: {
type: Schema.types.string,
description: "Message to be posted",
},
// Define token here
googleAccessTokenId: {
type: Schema.slack.types.oauth2,
oauth2_provider_key: "google",
},
user: {
type: Schema.slack.types.user_id,
description: "The user invoking the workflow",
},
},
required: ["user"],
},
output_parameters: {
properties: {
updatedMsg: {
type: Schema.types.string,
description: "Updated message to be posted",
},
},
required: ["updatedMsg"],
},
});
export default SlackFunction(
SampleFunctionDefinition, // Define custom function
async ({ inputs, client }) => {
// Get the token:
const res = await client.apiCall("apps.auth.external.get", {
external_token_id: inputs.googleAccessTokenId,
});
let updatedMsg = `:newspaper: Message for <@${inputs.user}>!\n\n>`;
// If the token was retrieved successfully, use it:
if (res.ok) {
const externalToken = res.external_token;
// Make external API call with externalToken
const myApiResponse = await fetch("https://somewhere.tld/myendpoint", {
headers: new Headers({
"Authorization": `Bearer ${externalToken}`,
"Content-Type": "application/x-www-form-urlencoded",
}),
})
.then((response) => response.json())
.then((some_data) => {
return some_data;
});
updatedMsg += myApiResponse;
} else {
// handle error response
}
return { outputs: { updatedMsg } };
},
);
To invoke your function that uses the oauth2 token, pass an empty object to the function's oauth2 input parameter while adding the function as a workflow step:
// Somewhere in your workflow's implementation:
const sampleFunctionStep = SampleWorkflow.addStep(SampleFunctionDefinition, {
user: SampleWorkflow.inputs.user,
googleAccessTokenId: {},
});
Slack will automatically inject your app's oauth2 token into the oauth2 input property; you do not have to provide a token ID here.
If you run into issues, please don't hesitate to connect with us over at the Slack Community forums.
If you'd like to delete your tokens and remove authentication from your Slack app, the following commands will help you do so:
Command | Description |
---|---|
$ slack external-auth remove |
Choose a provider to delete tokens for from the list displayed |
$ slack external-auth remove --all |
Delete all tokens for the app by specifying the --all flag |
$ slack external-auth remove --provider provider_name --<app_name> |
Delete all tokens for a provider by specifying the --provider flag |
Note that this will not revoke the token from the provider's system. It will just delete the reference to the token from Slack and prevent it from being used within the External Authentication system.
You can view logs for external auth via slack activity
. These logs will contain information about errors encountered by users during the OAuth2 exchange and workflow execution. Below are some common errors:
Error | Description |
---|---|
access_token_exchange_failed |
An error was returned from the configured token_url |
external_user_identity_not_found |
The configured account_identifier was not found in user identity response |
internal_error |
An internal system error happened. Please reach out to Slack if this occurs consistently |
invalid_identity_config_response |
url in the configured identity_config returned an invalid response |
invalid_token_response |
token_url returned an invalid response |
missing_client_secret |
No client secret was found for this provider |
no_refresh_token |
Token to refresh the expired access token does not exist |
oauth2_callback_error |
The OAuth2 provider returned an error |
oauth2_exchange_error |
There was an error while obtaining the OAuth2 token from the configured provider |
scope_mismatch_error |
Slack was not able to find an OAuth2 token that matched the scope configured on your provider |
token_not_found |
Slack was not able to find an OAuth2 token for this user and provider |
Have 2 minutes to provide some feedback?
We'd love to hear about your experience with the new Slack platform. Please complete our short survey so we can use your feedback to improve.