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.
The Sign in With Slack (SIWS) link, built on the OpenID Connect (OIDC) protocol, 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.
There are two Sign in with Slack entry points:
Once you receive the user’s information you can decide whether to create an account, redirect them to the resource they initially clicked on, or implement another flow that makes sense for you.
See this guide on configuring Sign in with Slack links using Auth0.
identity.*
scopes.In order to properly utilize SIWS entry points you will need to provide Slack with the following:
app_id
you want to use to build on Sign in with Slack links.name
or id
where the app lives, to enable it for Domain Verification.Then complete the following steps for your app:
Add the following user scopes to your app setup in Slack.
Enable public distribution. This is required for using an OAuth flow. You do not need to actually publicly distribute your app.
Add a background color and icon under the Basic Information tab.
Add a privacy URL under the Basic Information tab.
Add a ToS URL under the Submit to Slack Marketplace tab: “Security & Compliance Section” > “General” > “Terms of Service URL”.
Follow the Domain Verification steps under Settings in your App dashboard. d The prompt will look something like this:
If the user clicks Decline, we will redirect to the link clicked. If a user declines three times, they will no longer see the prompt.
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:
The Header is used to determine how to verify the signature. It will always contain:
json "Header": { "alg": "RS256", "typ": "JWT" }
The Payload will contain all the info you need to understand who the user is that clicked on the link.
The Signature should be verified 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": "W123ABC456",
"https://slack.com/team_id": "T123ABC456",
"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": "W123ABC456",
"aud": "533193414769.1727340817111",
"exp": 1614211245,
"iat": 1614210945,
"auth_time": 1614210945,
"nonce": "",
"at_hash": "hkx6S31Hj18nDUFljRvr2A",
"email": "test@slack-corp.com",
"locale": "en-US",
"name": "Stewart Butterfield",
"given_name": "Stewart",
"family_name": "Butterfield",
"picture": "https://avatars.slack-edge.com/2019-09-27/123456..._512.png",
"https://slack.com/user_id": "W123ABC456",
"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": "T123ABC456",
"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"
}
}
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 pre-installation platform features of your app.
links.accountLinkedResult
After a user has gone through the OIDC flow, 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:
curl -X POST \
https://slack.com/api/links.accountLinkedResult \
-H 'Authorization: Bearer xoxp-....' \
-H 'Content-Type: application/json;charset=utf-8' \
-d '{
"team_id":"T123ABC456",
"user_id":"U123ABC456",
"result": "NEW_ACCOUNT_CREATED",
"source": "IDENTITY_LINKS",
}'
Parameter | Description | Format |
---|---|---|
user_id |
The Slack user_id returned during OIDC. |
U123ABC456 |
team_id |
The team_id returned during OIDC. |
T123ABC456 |
result |
Action taken by partner system every time user goes through OIDC. See below. | One of NEW_ACCOUNT_CREATED , UNLINKED_ACCOUNT_LINKED , LINKED_ACCOUNT_LOGGED_IN , IGNORED_DUE_TO_EXISTING_SESSION , ERROR_DETECTED |
source |
Initiation source for OIDC exchange. | One of SOCIAL_LOGIN , IDENTITY_LINKS . |
The result
parameter:
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. |
If you are interested in magic unfurls with Sign in with Slack Links, please reach out to partnersupport@slack-corp.com to be enabled.
In addition to prompting Sign in with Slack when a link gets clicked, you can trigger a SIWS link 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.
There are two additional app requirements to use magic unfurls:
link_shared
event.When a user pastes a link into their message, if Slack doesn'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.
After they click the prompt, they will see a 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.
A link_shared
event with "source": "composer"
will be dispatched to your service in the following cases:
The event structure will be:
{
"token": "XXYYZZ",
"team_id": "T123ABC456",
"api_app_id": "A123ABC456",
"event": {
"type": "link_shared",
"user": "U123ABC456",
"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": [
"U123ABC456",
"U222222222"
],
"event_id": "Ev123ABC456",
"event_time": 123456789
}
Unlike existing app unfurl link_shared
events, There won't be a message_ts
, and potentially not a channel_id
yet. It is possible to compose a message that does not (yet) contain this context.
To ensure backwards compatibility, the channel_id
will always be set to the value "COMPOSER"
. The unfurl_id
property serves as the unique identifier for an unfurl. The message_ts
property will be set to the same value as unfurl_id
.
The source
property indicates where this unfurl is coming from. Its value can be either composer
(for previews) or conversations_history
(for posted messages).
Your app then calls chat.unfurl
using the stored corresponding user token for the user who sent the link (or bot token if the app is installed) with the contents of the unfurl, including the unfurl_id
and source
(or alternatively the channel_id
& ts
):
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"
}
}
]
}
}
}
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"
}
}
]
}
}
}
The Slack client will update the message composition with a preview of the unfurl. The contents of the preview are pulled from the preview key if present, otherwise the preview will be generated from the existing blocks layout or legacy attachment. The app name and icon are retrieved from your Slack Marketplace listing.
See below for an example:
The user can click on this preview to show a full preview of what the unfurl will look like, pulled from the blocks key. Any included interactive elements will not be usable.
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, providing the channel
and message_ts
context:
{
"token": "XXYYZZ",
"team_id": "T123ABC456",
"api_app_id": "A123ABC456",
"event": {
"type": "link_shared",
"channel": "C123ABC456",
"is_bot_user_member": false,
"user": "U123ABC456",
"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": [
"U123ABC456",
"U222222222"
],
"event_id": "Ev123ABC456",
"event_time": 123456789
}
A "source": "conversations_history"
property and an unfurl_id
(different from the unfurl_id
received for the preview event) property will be included. Use the team_id
and user
information to look up the corresponding user token (or bot token if installed), then call chat.unfurl
using the unfurl_id
and source
properties.
This will result in the unfurl appearing in the Slack channel attached to the message:
When interactive elements are included in the pre-install unfurl there are some limitations:
trigger_id
will be allowed, you will not receive a response_url
.redirect_uri
will be https://slack.com/openid/connect/complete/interactivity/{unique_key}
), the user will have to click the element again for the app to successfully receive an event and clear the error.See this help desk article.
Utilize the apps.links.toggle
endpoint to enable or disable the SIWS links feature. 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. Can be found by going to the "Basic Information" with the app settings page. It'll be listed under "App Level Token". |
status |
Accepts enable or disable in order to determine if the feature is enabled. |
You can provide the token argument as the Bearer Token.
Error | Suggestion |
---|---|
invalid_app_icon |
Check your app setup above. |
invalid_app_icon_background |
Check your app setup above. |
invalid_app_privacy_link |
Check your app setup above. |
invalid_app_tos |
Check your app setup above. |
uri_not_handled_by_app |
Contact Slack. |
If the modal doesn't open and there are no errors, the user may have declined to share their identity. You can reset this.
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