Intermediate

Get Started with Sign in with Slack links

Sign in With Slack (SIWS) Links allows a user to share their Slack information with you when they click on a link from your service. This can happen without your app being installed in a workspace.

Once you receive the user’s information you can decide whether to create an account, redirect right to the resource they initially clicked on, or another flow that makes sense for you.

We power this by moving to our SIWS workflow to build on the OpenID Connect protocol, allowing a more familiar and flexible sign in flow.

Sign in with Slack links is a paid feature. Interested parties should fill out the interest form or reach out to partnersupport@slack-corp.com for more information.

Check out this guide on configuring Sign in with Slack links using Auth0.

Implement or Migrate to OIDC Sign in with Slack

Before proceeding check out this guide to get started on SIWS or migrate from an existing legacy implementation using identity.* scopes.

Sign in with Slack Links entry points

  1. A user clicks on a link in Slack and is forwarded to you with identity info in the URL. Giving you the option of dealing with the user without having to ask them to approve access.
  2. A user pastes a link from your domain in Slack's composer and wants to activate the Unfurl Preview.

Creating a test app

In order to properly utilize SIWS entry points there are some team and app requirements.

You will need to provide Slack with the following:

  1. A domain to enable, this must be a different domain than your production domain for testing and building.
  2. Your dev app_id you want to use to build on Sign in with Slack links.
  3. Team name or id where the app lives, to enable it for Domain Verification.
  4. Any team names or ids you want to use to test the feature.
  5. The dev URL where you want us to send users after they click on an SIWS link.

In additon to the requirements sent to Slack, your app will need to meet the following requirements.

  1. Add the following user scopes to your app setup in Slack.

  2. Enable public distribution. This will allow the OAuth process, you do not need to submit to the app directory or make your app public outside of your team.

  3. A background color and icon under the Basic Information tab.

  4. A privacy URL under the Basic Information tab.

  5. A ToS URL under the Submit to App Directory tab: “Security & Compliance Section” > “General” > “Terms of Service URL”.

  6. Follow Domain Verification steps under Settings in your App dashboard.

Sign in with Slack Modal

After clicking on a SIWS link, the modal below will appear if the user hasn't clicked Accept and if they haven’t declined access 3 times. We will show the modal up to 3 times then stop forever.

SIWS Modal

Identity transfer

When a user clicks on an Sign in with Slack link in Slack, they will be redirected to the URL you provided along with the parameter, login_hint; a JSON Web Token. This will contain a base64 encoded value with 3 parts:

  1. Header. This is used to determine how to verify the signature. From Slack this will always contain:
"Header": {
  "alg": "RS256",
  "typ": "JWT"
}
  1. Payload. This will contain all the info you need to understand who the user is that clicked on the link.
  2. Signature. It is important that you verify this using the implementation or service of your choice.

When you receive the payload from an Sign in with Slack link it will look like:

{
    "iss": "https://slack.com",
    "sub": "test@slack-corp.com",
    "aud": "533193414769.1727340817111",
    "exp": 1614211080,
    "iat": 1614210780,
    "auth_time": 1614210780,
    "https://slack.com/user_id": "WKUP65Y72",
    "https://slack.com/team_id": "TFP5PC6NM",
    "https://slack.com/target_uri": "https://grask.me/testing"
}

The https://slack.com/target_uri is the URL that the user initially clicked on in Slack (or the URL to return to Slack if coming from Composer Auth). Use this URL to redirect the user.

When you receive the payload from calling the openid.connect.token endpoint there will be different data:

{
    "ok": "true",
    "access_token": "xoxp-...",
    "token_type": "Bearer",
    "id_token": {
   "iss": "https://slack.com",
   "sub": "WKUP65Y72",
   "aud": "533193414769.1727340817111",
   "exp": 1614211245,
   "iat": 1614210945,
   "auth_time": 1614210945,
   "nonce": "",
   "at_hash": "hkx6S31Hj18nDUFljRvr2A",
   "email": "test@slack-corp.com",
   "locale": "en-US",
   "name": "Gregg R",
   "given_name": "Gregg",
   "family_name": "R",
   "picture": "https://avatars.slack-edge.com/2019-09-27/775889580725_d5462030f819915fdfdc_512.png",
   "https://slack.com/user_id": "WKUP65Y72",
   "https://slack.com/user_image_24": "https://avatars.slack-edge.com/...",
   "https://slack.com/user_image_32": "https://avatars.slack-edge.com/...",
   "https://slack.com/user_image_48": "https://avatars.slack-edge.com/...",
   "https://slack.com/user_image_72": "https://avatars.slack-edge.com/...",
   "https://slack.com/user_image_192": "https://avatars.slack-edge.com/...",
   "https://slack.com/user_image_512": "https://avatars.slack-edge.com/...",
   "https://slack.com/user_image_1024": "https://avatars.slack-edge.com/...",
   "https://slack.com/team_id": "TFP5PC6NM",
   "https://slack.com/team_name": "Finance",
   "https://slack.com/team_domain": "finance-greggdemo",
   "https://slack.com/team_image_34": "https://avatars.slack-edge.com/...",
   "https://slack.com/team_image_44": "https://avatars.slack-edge.com/...",
   "https://slack.com/team_image_68": "https://avatars.slack-edge.com/...",
   "https://slack.com/team_image_88": "https://avatars.slack-edge.com/...",
   "https://slack.com/team_image_102": "https://avatars.slack-edge.com/...",
   "https://slack.com/team_image_132": "https://avatars.slack-edge.com/...",
   "https://slack.com/team_image_230": "https://avatars.slack-edge.com/...",
   "https://slack.com/target_uri": "https://grask.me/testing"
    }
}

Storing user data

You can choose which fields of this payload to store as you associate the Slack user identity to a user of your service. We recommend at a minimum storing email, sub or user_id, name, and access_token. The access_token will serve to unlock some platform features pre-installation of your app.


Metrics Endpoint

After a user has gone through the OIDC flow, we ask that you send the results to a Slack hosted endpoint. This endpoint should be called on every OIDC identity exchange (every time the user clicks on an identity link or uses the Sign in with Slack social login button).

You can call the endpoint with the token you receive during the OIDC flow.

Here is an example call to the endpoint:

curl -X POST \
      https://slack.com/api/links.accountLinkedResult \
      -H 'Authorization: Bearer xoxp-....' \
      -H 'Content-Type: application/json;charset=utf-8' \
      -d '{
            "team_id":"TXXXXX",
            "user_id":"UXXXXXX",
            "result": "NEW_ACCOUNT_CREATED",
            "source": "IDENTITY_LINKS",
     }'

links.accountLinkedResult

Parameter Description Required
user_id The Slack user_id returned during OIDC. Y
team_id The team_id returned during OIDC. Y
result Action taken by partner system every time user goes through OIDC. Enum: [NEW_ACCOUNT_CREATED, UNLINKED_ACCOUNT_LINKED, LINKED_ACCOUNT_LOGGED_IN, IGNORED_DUE_TO_EXISTING_SESSION, ERROR_DETECTED] Y
source Initiation source for OIDC exchange. Enum: [SOCIAL_LOGIN, IDENTITY_LINKS] Y

Possible values for result argument:

Value Description
NEW_ACCOUNT_CREATED A new account was created and linked to a Slack identity, and the user was logged into that account.
UNLINKED_ACCOUNT_LINKED The user was logged into an existing unlinked account which was linked for the first time.
LINKED_ACCOUNT_LOGGED_IN The user was logged into an account that was previously linked.
IGNORED_DUE_TO_EXISTING_SESSION An existing session was found, and no action was taken as a result.
ERROR_DETECTED Any detectable error in the flow, either because of Slack or your service, that prevents one of the other results from being reached.

Magic Unfurl

In addition to prompting Sign in with Slack when a link gets clicked, you can trigger a SIWS links flow when a user shares a link in the message composer. Once the user agrees to transfer their identity, your app will get permission to unfurl rich previews for links shared by the user in Slack, even if the app is not installed in the workspace yet.

Once the link unfurl is posted, any user can click it and trigger SIWS. With a virtuous cycle of users sharing and clicking your Sign in with Slack links, you’ll be able to drive more users to connect with your service.

If you are interested in Magic Unfurls with Sign in with Slack Links, please reach out to partnersupport@slack-corp.com to be enabled.

There a couple more additional app requirements to get magic unfurls to work.

  1. link_shared event subscription
  2. App unfurl domain setup in the Event Subscription of your app config, which needs to be the exact same as verified domain for SIWS links.

Composer Auth

After a user pastes a link into their message, if we don't detect that they've already shared their identity with your app through SIWS links, they will see a prompt under the message composition prompting them to activate the link preview.

Composer Auth prompt

After they click the prompt, they will see the SIWS links interstitial modal, asking if they would like to grant permission to transfer their Slack identity and activate link previews.

If the user clicks Accept, it will initiate an identity transfer using OpenID Connect to your site. This is similar to what happens when a user clicks on a Sign in with Slack link, but instead of redirecting to the link clicked at the end of the flow, your site will redirect back to a Slack URL which will take the user back into Slack.

After you store the association between the Slack user (including their access_token) and the existing user of your service, your app should redirect to the URI defined in the key https://slack.com/target_uri which should bring the user back into Slack. You'll also receive the key https://slack.com/resource_uri, which includes the link the user pasted in the composer.

Unfurl Previews

A link_shared event with "source": "composer" will be dispatched to your service in the following cases:

  1. Shortly after your app redirects the user to Slack after Composer Auth account linking
  2. After a user that has already accepted the SIWS interstitial pastes a link from your domain into the composer
  3. After a user that has your full app installed in their workspace with app unfurls enabled pastes a link from your domain into the composer

The event structure will be:

{
    "token": "XXYYZZ",
    "team_id": "TXXXXXXXX",
    "api_app_id": "AXXXXXXXXX",
    "event": {
        "type": "link_shared",
        "user": "Uxxxxxxx",
        "channel_id": "COMPOSER",
        "message_ts": "gryl3kb80b3wm49ihzoo35fyqoq08n2y",
        "unfurl_id": "gryl3kb80b3wm49ihzoo35fyqoq08n2y",
        "source": "composer",
        "links": [
            {
                "domain": "example.com",
                "url": "https://example.com/12345"
            },
            {
                "domain": "example.com",
                "url": "https://example.com/67890"
            },
            {
                "domain": "another-example.com",
                "url": "https://yet.another-example.com/v/abcde"
            }
        ]
    },
    "type": "event_callback",
    "authed_users": [
        "UXXXXXXX1",
        "UXXXXXXX2"
    ],
    "event_id": "Ev08MFMKH6",
    "event_time": 123456789
}

Unlike existing app unfurls link_shared events, we won’t have a message_ts and potentially don’t have a channel_id yet. It is possible to compose a message that does not (yet) contain this context. To ensure backwards compatibility to our best effort, the channel_id will always be set to the value "COMPOSER". We are adding a new property unfurl_id which is a unique identifier for this unfurl. Again, to ensure backwards compatibility we will set message_ts to the same value as unfurl_id. We will also add a new source property to indicate where this unfurl is coming from. This value can be either composer (for previews) or conversations_history for posted messages. Your app will then call chat.unfurl using the stored corresponding user token for the user who sent the link (or bot token if app is installed) with the contents of the unfurl, including the unfurl_id and source (or alternatively the channel_id & ts):

Sample body of request with unfurl_id and source:

{
    "token": "xoxp-xxxxxxx-xxxxxx",
    "unfurl_id": "gryl3kb80b3wm49ihzoo35fyqoq08n2y",
    "source": "composer",
    "unfurls": {
        "https://gentle-buttons.com/carafe": {
            "preview": {
                "type": "preview",
                "elements": [
                    {
                        "type": "image",
                        "image_url": "https://gentle-buttons.com/img/carafe-filled-with-red-wine.png",
                        "alt_text": "Secret Project Walkthrough thumbnail"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "*Secret Project Walkthrough*"
                    }
                ]
            },
            "blocks": [
                {
                    "type": "section",
                    "text": {
                        "type": "mrkdwn",
                        "text": "Take a look at this carafe, just another cousin of glass"
                    },
                    "accessory": {
                        "type": "image",
                        "image_url": "https://gentle-buttons.com/img/carafe-filled-with-red-wine.png",
                        "alt_text": "Stein's wine carafe"
                    }
                }
            ]
        }
    }
}

Sample body of request with channel_id and ts:

{
    "token": "xoxp-xxxxxxx-xxxxxx",
    "channel_id": "COMPOSER",
    "ts": "gryl3kb80b3wm49ihzoo35fyqoq08n2y",
    "unfurls": {
        "https://gentle-buttons.com/carafe": {
            "preview": {
                "type": "preview",
                "elements": [
                    {
                        "type": "image",
                        "image_url": "https://gentle-buttons.com/img/carafe-filled-with-red-wine.png",
                        "alt_text": "Secret Project Walkthrough thumbnail"
                    },
                    {
                        "type": "mrkdwn",
                        "text": "*Secret Project Walkthrough*"
                    }
                ]
            },
            "blocks": [
                {
                    "type": "section",
                    "text": {
                        "type": "mrkdwn",
                        "text": "Take a look at this carafe, just another cousin of glass"
                    },
                    "accessory": {
                        "type": "image",
                        "image_url": "https://gentle-buttons.com/img/carafe-filled-with-red-wine.png",
                        "alt_text": "Stein's wine carafe"
                    }
                }
            ]
        }
    }
}

This will cause the Slack client to update the message composition with a preview of the unfurl. The contents of the preview are pulled from the preview key if present, otherwise we will generate the preview from the existing legacy attachment or blocks layout. The app name and icon are retrieved from your App Directory listing:

Unfurl Preview

The user can click on this preview to show a full preview of what the unfurl will look like. This is pulled from the blocks key. Any interactive elements that are included will not be usable.

Unfurl Preview Full Message

Pre-install Unfurls

The user has an option to remove the unfurl while composing the message, but if they leave it in and hit send, a second link_shared event will be dispatched to your app, this time with the channel and message_ts context:

{
    "token": "XXYYZZ",
    "team_id": "TXXXXXXXX",
    "api_app_id": "AXXXXXXXXX",
    "event": {
        "type": "link_shared",
        "channel": "Cxxxxxx",
        "is_bot_user_member": false,
        "user": "Uxxxxxxx",
        "message_ts": "123452389.9875",
        "unfurl_id": "gryl3kb80b3wm49ihzoo35fyqoq08n2y",
        "source": "conversations_history",
        "links": [
            {
                "domain": "example.com",
                "url": "https://example.com/12345"
            },
            {
                "domain": "example.com",
                "url": "https://example.com/67890"
            },
            {
                "domain": "another-example.com",
                "url": "https://yet.another-example.com/v/abcde"
            }
        ]
    },
    "type": "event_callback",
    "authed_users": [
        "UXXXXXXX1",
        "UXXXXXXX2"
    ],
    "event_id": "Ev08MFMKH6",
    "event_time": 123456789
}

Additionally we will add the "source": "conversations_history" property and an unfurl_id (this will be a different unfurl_id than the one you received for the preview event). Your app will use the team_id and user information to look up the corresponding user token (or bot token if installed) and permission to view the entity and then call chat.unfurl with unfurl_id and source. This will result in the unfurl appearing in the Slack channel attached to the message:

Unfurl Pre-Install

Interactive Block Kit elements in Pre-install unfurls

When interactive elements are included in the pre-install unfurl there are some limitations:

  1. Only actions based on trigger_id will be allowed, you will not receive a response_url
  2. If unauthenticated users (have not accepted the SIWS interstitial) click on an interactive element, they will see an error and immediately trigger the interstitial. If they accept and successfully complete the SIWS Links flow (redirect_uri will be https://slack.com/openid/connect/complete/interactivity/{unique_key} ), the user will have to click the element again in order for the app to successfully receive an event and the error to be cleared.
Unfurl Pre-Install Interactivity Unauthenticated User Error

How do I reset a user’s selection for testing?

Please refer to this help desk article.

Sign in with Slack Kill Switch

If you are using Sign in with Slack links you are able to utilize the apps.links.toggle endpoint to enable or disable the feature at will. This POST request requires two arguments:

Field Description
token An app level token to verify the app and the required scope permissions. Requires the app_configurations:write scope.
status Accepts enable or disable in order to determine if the feature is enabled.

This is a CLI based endpoint and can be used in a programatic way with POST request to the apps.links.toggle endpoint. You can provide the token argument as the Bearer Token.

To retrieve the app level token required, you must go to the App Config Page → Settings → Basic Information and go to the App Level Token section.

Troubleshooting

  • If you see uri_not_handled_by_app as an error in dev tools, contact Slack.
  • If you see invalid_app_tos, invalid_app_privacy_link, invalid_app_icon or invalid_app_icon_background then check your app setup above.
  • Can’t tell what URLs contain because windows don’t open with dev tools already enabled? Try this trick for using Chrome (on a Mac) to capture the network hops and login hint. First quit Chrome, then from terminal run: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --auto-open-devtools-for-tabs
  • Great tool for manually inspecting JWTs: https://jwt.io/
  • If the modal does not open and there are no errors, it could be that the user has declined to share their identity. You can reset this if you like.

Was this page helpful?