-
Notifications
You must be signed in to change notification settings - Fork 664
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
29 changed files
with
524 additions
and
173 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
node_modules | ||
/package-lock.json | ||
scripts/package-lock.json | ||
examples/*/package-lock.json | ||
|
||
npm-debug.log | ||
lerna-debug.log | ||
tmp/ | ||
.env* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# OpenID Connect Example | ||
|
||
This repo contains a sample app for implementing Sign in with Slack (OpenID Connect compatible). Checkout `app.js`. The code includes a few different options which have been commented out. As you play around with the app, you can uncomment some of these options to get a deeper understanding of how to use this library. | ||
|
||
Local development requires a public URL where Slack can send requests. In this guide, we'll be using [`ngrok`](https://ngrok.com/download). Checkout [this guide](https://api.slack.com/tutorials/tunneling-with-ngrok) for setting it up. | ||
|
||
Before we get started, make sure you have a development workspace where you have permissions to install apps. If you don’t have one setup, go ahead and [create one](https://slack.com/create). You also need to [create a new app](https://api.slack.com/apps?new_app=1) with the following App Manifest if you haven’t already. | ||
|
||
```yaml | ||
_metadata: | ||
major_version: 1 | ||
minor_version: 1 | ||
display_information: | ||
name: my-openid-connect-app | ||
oauth_config: | ||
redirect_urls: | ||
# TODO: Replace the URL with our own one | ||
- https://your-own-domain/slack/oauth_redirect | ||
scopes: | ||
user: | ||
- openid | ||
- profile | ||
``` | ||
## Install Dependencies | ||
``` | ||
npm install | ||
``` | ||
|
||
## Setup Environment Variables | ||
|
||
This app requires you setup a few environment variables. You can get these values by navigating to your app's [**BASIC INFORMATION** Page](https://api.slack.com/apps). | ||
|
||
``` | ||
export SLACK_CLIENT_ID=YOUR_SLACK_CLIENT_ID | ||
export SLACK_CLIENT_SECRET=YOUR_SLACK_CLIENT_SECRET | ||
export SLACK_REDIRECT_URI=https://{your-public-domain}/slack/oauth_redirect | ||
``` | ||
|
||
## Run the App | ||
|
||
Start the app with the following command: | ||
|
||
``` | ||
npm start | ||
``` | ||
|
||
This will start the app on port `3000`. | ||
|
||
Now lets start `ngrok` so we can access the app on an external network and create a `redirect url` for OAuth. | ||
|
||
``` | ||
ngrok http 3000 | ||
``` | ||
|
||
This should output a forwarding address for `http` and `https`. Take note of the `https` one. It should look something like the following: | ||
|
||
``` | ||
Forwarding https://3cb89939.ngrok.io -> http://localhost:3000 | ||
``` | ||
|
||
Go to your app on https://api.slack.com/apps and navigate to your apps **OAuth & Permissions** page. Under **Redirect URLs**, add your `ngrok` forwarding address with the `/slack/oauth_redirect` path appended. ex: | ||
|
||
``` | ||
https://3cb89939.ngrok.io/slack/oauth_redirect | ||
``` | ||
|
||
Everything is now setup. In your browser, navigate to http://localhost:3000/slack/install to initiate the oAuth flow. Once you install the app, it should receive the OpenID Connect claims and an OAuth access token for the connected Slack account. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
const Koa = require("koa"); | ||
const Router = require("@koa/router"); | ||
const { WebClient } = require("@slack/web-api"); // requires v6.4 or higher | ||
const jwt = require("jsonwebtoken"); | ||
const uuid = require("uuid"); | ||
|
||
const app = new Koa(); | ||
const router = new Router(); | ||
|
||
router.get("/", async (ctx) => { | ||
ctx.redirect("/slack/install"); | ||
}); | ||
|
||
const clientId = process.env.SLACK_CLIENT_ID; | ||
const clientSecret = process.env.SLACK_CLIENT_SECRET; | ||
const oidcScopes = "openid,email,profile"; // openid is required at least | ||
const redirectUri = process.env.SLACK_REDIRECT_URI; | ||
|
||
class MyStateStore { | ||
constructor() { | ||
this.activeStates = {}; | ||
} | ||
async generate() { | ||
const newValue = uuid.v4(); | ||
this.activeStates[newValue] = Date.now() + 10 * 60 * 1000; // 10 minutes | ||
return newValue; | ||
} | ||
async validate(state) { | ||
const expiresAt = this.activeStates[state]; | ||
if (expiresAt && Date.now() <= expiresAt) { | ||
delete this.activeStates[state]; | ||
return true; | ||
} | ||
return false; | ||
} | ||
} | ||
const myStateStore = new MyStateStore(); | ||
|
||
router.get("/slack/install", async (ctx) => { | ||
const state = await myStateStore.generate(); | ||
// (optional) you can pass nonce parameter as well | ||
// refer to https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest for details | ||
const nonce = "your-own-nonce-value"; | ||
const url = `https://slack.com/openid/connect/authorize?response_type=code&state=${state}&client_id=${clientId}&scope=${oidcScopes}&redirect_uri=${redirectUri}&nonce=${nonce}`; | ||
|
||
ctx.headers["content-type"] = "text/html; charset=utf-8"; | ||
ctx.body = `<html> | ||
<head><style>body {padding: 10px 15px;font-family: verdana;text-align: center;}</style></head> | ||
<body> | ||
<h2>Slack OpenID Connect</h2> | ||
<p><a href="${url}"><img alt="Sign in with Slack" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x" /></a></p> | ||
</body> | ||
</html>`; | ||
}); | ||
|
||
const client = new WebClient(); | ||
|
||
router.get("/slack/oauth_redirect", async (ctx) => { | ||
if (!(await myStateStore.validate(ctx.query.state))) { | ||
ctx.status = 400; | ||
ctx.headers["content-type"] = "text/html; charset=utf-8"; | ||
ctx.body = `<html> | ||
<head><style>body {padding: 10px 15px;font-family: verdana;text-align: center;}</style></head> | ||
<body> | ||
<h2>Invalid request</h2> | ||
<p>Try again from <a href="./install">here</a></p> | ||
</body> | ||
</html>`; | ||
return; | ||
} | ||
|
||
const token = await client.openid.connect.token({ | ||
client_id: clientId, | ||
client_secret: clientSecret, | ||
grant_type: "authorization_code", | ||
code: ctx.query.code, | ||
}); | ||
console.log( | ||
`openid.connect.token response: ${JSON.stringify(token, null, 2)}` | ||
); | ||
|
||
let userAccessToken = token.access_token; | ||
|
||
if (token.refresh_token) { | ||
// token.refresh_token can exist if the token rotation is enabled. | ||
// The following lines of code demonstrate how to refresh the token. | ||
// If you don't enable token rotation, you can safely remove this part. | ||
const refreshedToken = await client.openid.connect.token({ | ||
client_id: clientId, | ||
client_secret: clientSecret, | ||
grant_type: "refresh_token", | ||
refresh_token: token.refresh_token, | ||
}); | ||
console.log( | ||
`openid.connect.token (refresh) response: ${JSON.stringify( | ||
refreshedToken, | ||
null, | ||
2 | ||
)}` | ||
); | ||
userAccessToken = refreshedToken.access_token; | ||
} | ||
|
||
// You can save the id_token (JWT string) as-is but you can decode the value this way: | ||
const claims = jwt.decode(token.id_token); | ||
|
||
// TODO: you can do something meaningful here | ||
// (e.g., storing the data in database + navigating the user to your service top paeg) | ||
|
||
// The is a quick example demonstrating how to perform openid.connect.userInfo with the given access token. | ||
// You don't need to do this here. | ||
const tokenWiredClient = new WebClient(userAccessToken); | ||
const userInfo = await tokenWiredClient.openid.connect.userInfo(); | ||
console.log( | ||
`openid.connect.userInfo response: ${JSON.stringify(userInfo, null, 2)}` | ||
); | ||
|
||
ctx.headers["content-type"] = "text/html; charset=utf-8"; | ||
ctx.body = `<html> | ||
<head><style>body h2 {padding: 10px 15px;font-family: verdana;text-align: center;}</style></head> | ||
<body> | ||
<h2>OpenID Connect Claims</h2> | ||
<pre>${JSON.stringify(claims, null, 2)}</pre> | ||
<h2>openid.connect.userInfo response</h2> | ||
<pre>${JSON.stringify(userInfo, null, 2)}</pre> | ||
</body> | ||
</html>`; | ||
}); | ||
|
||
// Enable the routes | ||
app.use(router.routes()).use(router.allowedMethods()); | ||
// Start the web app, which is available at http://localhost:3000/slack/* | ||
app.listen(3000); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
_metadata: | ||
major_version: 1 | ||
minor_version: 1 | ||
display_information: | ||
name: my-openid-connect-app | ||
oauth_config: | ||
redirect_urls: | ||
# TODO: Replace the URL with our own one | ||
- https://your-own-domain/slack/oauth_redirect | ||
scopes: | ||
user: | ||
- openid | ||
- profile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#!/bin/bash | ||
|
||
current_dir=`dirname $0` | ||
cd ${current_dir} | ||
npm unlink @slack/web-api \ | ||
&& npm i \ | ||
&& cd ../../packages/web-api \ | ||
&& npm link \ | ||
&& cd - \ | ||
&& npm i \ | ||
&& npm link @slack/web-api |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"name": "openid-connect-example", | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "app.js", | ||
"scripts": { | ||
"start": "node app.js" | ||
}, | ||
"author": "Slack Technologies, Inc.", | ||
"license": "MIT", | ||
"repository": "slackapi/node-slack-sdk", | ||
"dependencies": { | ||
"@koa/router": "^10.1.0", | ||
"jsonwebtoken": "^8.5.1", | ||
"koa": "^2.13.1", | ||
"uuid": "^8.3.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
/* tslint:disable */ | ||
import { WebAPICallResult } from '../WebClient'; | ||
export type DndSetSnoozeResponse = WebAPICallResult & { | ||
ok?: boolean; | ||
error?: string; | ||
snooze_enabled?: boolean; | ||
snooze_endtime?: number; | ||
snooze_remaining?: number; | ||
needed?: string; | ||
provided?: string; | ||
ok?: boolean; | ||
error?: string; | ||
snooze_enabled?: boolean; | ||
snooze_endtime?: number; | ||
snooze_remaining?: number; | ||
snooze_is_indefinite?: boolean; | ||
needed?: string; | ||
provided?: string; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.