diff --git a/examples/slack-serverless/README.md b/examples/slack-serverless/README.md new file mode 100644 index 0000000..9f52c10 --- /dev/null +++ b/examples/slack-serverless/README.md @@ -0,0 +1 @@ +// ToDo update readme \ No newline at end of file diff --git a/examples/slack-serverless/deploy.sh b/examples/slack-serverless/deploy.sh new file mode 100755 index 0000000..93b343c --- /dev/null +++ b/examples/slack-serverless/deploy.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +export EVERYAUTH_TOKEN=$(everyauth token -e 10y) +sls deploy diff --git a/examples/slack-serverless/index.js b/examples/slack-serverless/index.js new file mode 100644 index 0000000..b9741cb --- /dev/null +++ b/examples/slack-serverless/index.js @@ -0,0 +1,91 @@ +const express = require('express'); +const serverless = require('serverless-http'); +const { v4: uuidv4 } = require('uuid'); +const cookieSession = require('cookie-session'); + +const { WebClient } = require('@slack/web-api'); + +const everyauth = require('@fusebit/everyauth-express'); + +const app = express(); +const port = 3000; + +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +// Get userId from the authorization redirect or via session if already authorized. +const validateSession = (req, res, next) => { + if (req.query.userId) { + req.session.userId = req.query.userId; + } + if (!req.session.userId) { + return res.redirect('/dev/'); + } + return next(); +}; + +app.use( + cookieSession({ + name: 'session', + secret: 'secret', + }) +); + +app.set('view engine', 'pug'); + +app.get('/', (req, res) => { + if (!req.session.userId) { + return res.redirect(`/dev/authorize/${uuidv4()}`); + } + res.redirect('/dev/finished'); +}); + +app.use( + '/authorize/:userId', + (req, res, next) => { + if (!req.params.userId) { + return res.redirect('/dev/'); + } + return next(); + }, + everyauth.authorize('slack', { + // The endpoint of your app where control will be returned afterwards + finishedUrl: '/dev/finished', + hostedBaseUrl: (req) => + `https://${req.requestContext.domainName}/${req.requestContext.stage}/authorize/${req.session.userId}`, + // The user ID of the authenticated user the credentials will be associated with + mapToUserId: (req) => req.params.userId, + }) +); + +app.get('/finished', validateSession, async (req, res) => { + const userCredentials = await everyauth.getIdentity('slack', req.session.userId); + // Call Slack API + const slackClient = new WebClient(userCredentials.accessToken); + const userResponse = await slackClient.users.info({ user: userCredentials.native.authed_user.id }); + res.render('index', { title: 'user profile', user: userResponse.user }); +}); + +app.post('/message', validateSession, async (req, res) => { + const userCredentials = await everyauth.getIdentity('slack', req.session.userId); + // Call Slack API + const slackClient = new WebClient(userCredentials.accessToken); + const userResponse = await slackClient.users.info({ user: userCredentials.native.authed_user.id }); + const message = req.body.message; + + if (message) { + await slackClient.chat.postMessage({ + text: message, + channel: userCredentials.native.authed_user.id, + }); + } + + res.render('index', { + title: 'user profile', + user: userResponse.user, + messageSent: !!message, + message: req.body.message || 'Please write a message', + }); +}); + +module.exports.handler = serverless(app); diff --git a/examples/slack-serverless/package.json b/examples/slack-serverless/package.json new file mode 100644 index 0000000..e76a70e --- /dev/null +++ b/examples/slack-serverless/package.json @@ -0,0 +1,22 @@ +{ + "name": "everyauth-slack", + "private": true, + "version": "1.0.0", + "description": "Use Slack API with EveryAuth", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "Fusebit, Inc.", + "license": "ISC", + "dependencies": { + "@fusebit/everyauth-express": "^2.0.4", + "@slack/web-api": "^6.7.1", + "cookie-session": "^2.0.0", + "express": "^4.17.3", + "pug": "^3.0.2", + "serverless-http": "^3.0.1", + "uuid": "^8.3.2" + } +} diff --git a/examples/slack-serverless/serverless.yml b/examples/slack-serverless/serverless.yml new file mode 100644 index 0000000..cfedfd6 --- /dev/null +++ b/examples/slack-serverless/serverless.yml @@ -0,0 +1,17 @@ +org: fusebitdemo +app: everyauth-slack-demo +service: everyauth-slack-demo + +provider: + name: aws + runtime: nodejs14.x + region: us-west-1 + +functions: + app: + handler: index.handler + events: + - http: ANY / + - http: "ANY /{proxy+}" + environment: + EVERYAUTH_TOKEN: ${env:EVERYAUTH_TOKEN} diff --git a/examples/slack-serverless/views/index.pug b/examples/slack-serverless/views/index.pug new file mode 100644 index 0000000..01c8959 --- /dev/null +++ b/examples/slack-serverless/views/index.pug @@ -0,0 +1,31 @@ +html + head + title=title + style + include ./style.css + script(src='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/js/all.min.js' defer integrity="sha512-6PM0qYu5KExuNcKt5bURAoT6KCThUmHRewN3zUFNaoI6Di7XJPTMoT6K0nsagZKk2OB4L7E3q1uQKHNHd4stIQ==" crossorigin="anonymous") + body + .profile + .pic-container + img.pic(src=user.profile.image_1024 alt='Slack Avatar') + span.hi-icon + i(class="fa-solid fa-shake fa-hand-peace") + h2=user.real_name + p=user.profile.title + p.send-me='You can send me a message from here, I will get it directly in my Slack!' + .form-container + unless !messageSent + p.success + i(class="fa-solid fa-bounce fa-wand-magic-sparkles") + span='Thanks!, I got your message:' + unless !message + p.message-text + span=message + a.button(href='/dev/') Go back + unless messageSent + form(action="/dev/message" method="post") + textarea(name="message", cols="30", rows="10") + .send-area + button.send-button(action='submit') + i(class="fa-solid fa-bounce fa-comment") + span='Send me a message' \ No newline at end of file diff --git a/examples/slack-serverless/views/style.css b/examples/slack-serverless/views/style.css new file mode 100644 index 0000000..19d4439 --- /dev/null +++ b/examples/slack-serverless/views/style.css @@ -0,0 +1,117 @@ +/* Global */ +* { + margin: 0; + padding: 0; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', + 'Segoe UI Emoji'; + background-color: #c3c9cf; +} + +body, +a { + color: #fff; +} + +h1 span { + font-size: 20px; + font-style: normal; + font-weight: 300; + line-height: 24px; +} + +/* GitHub profile section */ +.profile { + background-color: #30363d; + text-align: center; + box-shadow: 0px 1px 7px 3px #707a85; + width: 400px; + margin: 20px auto; + border-radius: 10px; +} + +.profile .pic { + height: 200px; + width: 200px; + border-radius: 50%; + margin: auto auto 10px auto; + display: block; + box-shadow: 0 0 0 1px rgba(240, 246, 252, 0.1); +} + +/* Public repository listing section */ +.message { + background-color: #30363ced; + width: 60%; + margin-left: 41%; +} + +.success { + background-color: #237d23; + padding: 10px; +} + +.form-container { + border-top: solid 1px #707a85; + background-color: gray; + border-radius: 0 0 10px 10px; + padding-top: 10px; + margin-top: 20px; + min-height: 100px; +} + +textarea { + width: 100%; + padding: 10px; +} +.send-button, +.button { + background-color: #373737; + color: #fff; + padding: 10px; + border: none; + margin: 5px; + border-radius: 50px; + box-shadow: 2px 3px 7px 0px #564d4d; +} + +.button { + display: block; + text-decoration: none; + margin-bottom: 10px; +} + +.send-button svg, +.button svg, +.success svg { + padding-right: 5px; +} + +.hi-icon { + color: yellow; + font-size: 50px; +} + +.send-me { + margin-top: 10px; + border-top: solid 1px #707a85; + padding-top: 10px; + font-size: 10px; +} + +.pic-container { + padding-top: 10px; +} + +a, +button { + cursor: pointer; +} + +.message-text { + background-color: #fff; + color: #707a85; + padding: 10px; +}