The new Slack platform and the features described below are in beta and under active development.

External authentication

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.

1. Gather your OAuth2 credentials

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

2. Define your OAuth2 provider

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",
    },
  },
});
// ...

OAuth2 provider properties

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.

3. Add your OAuth2 provider to your manifest

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.

4. Encrypt and store your client secret

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.

5. Initialize the OAuth2 flow

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.

6. Add OAuth2 to your function

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 } };
  },
);

7. Include OAuth2 input in workflow step

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.

Delete external auth tokens

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.

Debugging issues

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.