feat: authenticationProviders API endpoints (#1962)
This commit is contained in:
@@ -1,100 +0,0 @@
|
||||
// @flow
|
||||
import passport from "@outlinewiki/koa-passport";
|
||||
import Router from "koa-router";
|
||||
import { capitalize } from "lodash";
|
||||
import { Strategy as GoogleStrategy } from "passport-google-oauth2";
|
||||
import accountProvisioner from "../commands/accountProvisioner";
|
||||
import env from "../env";
|
||||
import {
|
||||
GoogleWorkspaceRequiredError,
|
||||
GoogleWorkspaceInvalidError,
|
||||
} from "../errors";
|
||||
import auth from "../middlewares/authentication";
|
||||
import passportMiddleware from "../middlewares/passport";
|
||||
import { getAllowedDomains } from "../utils/authentication";
|
||||
import { StateStore } from "../utils/passport";
|
||||
|
||||
const router = new Router();
|
||||
const providerName = "google";
|
||||
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
|
||||
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
|
||||
const allowedDomains = getAllowedDomains();
|
||||
|
||||
const scopes = [
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
];
|
||||
|
||||
export const config = {
|
||||
name: "Google",
|
||||
enabled: !!GOOGLE_CLIENT_ID,
|
||||
};
|
||||
|
||||
if (GOOGLE_CLIENT_ID) {
|
||||
passport.use(
|
||||
new GoogleStrategy(
|
||||
{
|
||||
clientID: GOOGLE_CLIENT_ID,
|
||||
clientSecret: GOOGLE_CLIENT_SECRET,
|
||||
callbackURL: `${env.URL}/auth/google.callback`,
|
||||
prompt: "select_account consent",
|
||||
passReqToCallback: true,
|
||||
store: new StateStore(),
|
||||
scope: scopes,
|
||||
},
|
||||
async function (req, accessToken, refreshToken, profile, done) {
|
||||
try {
|
||||
const domain = profile._json.hd;
|
||||
|
||||
if (!domain) {
|
||||
throw new GoogleWorkspaceRequiredError();
|
||||
}
|
||||
|
||||
if (allowedDomains.length && !allowedDomains.includes(domain)) {
|
||||
throw new GoogleWorkspaceInvalidError();
|
||||
}
|
||||
|
||||
const subdomain = domain.split(".")[0];
|
||||
const teamName = capitalize(subdomain);
|
||||
|
||||
const result = await accountProvisioner({
|
||||
ip: req.ip,
|
||||
team: {
|
||||
name: teamName,
|
||||
domain,
|
||||
subdomain,
|
||||
},
|
||||
user: {
|
||||
name: profile.displayName,
|
||||
email: profile.email,
|
||||
avatarUrl: profile.picture,
|
||||
},
|
||||
authenticationProvider: {
|
||||
name: providerName,
|
||||
providerId: domain,
|
||||
},
|
||||
authentication: {
|
||||
providerId: profile.id,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
scopes,
|
||||
},
|
||||
});
|
||||
return done(null, result.user, result);
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
router.get("google", passport.authenticate(providerName));
|
||||
|
||||
router.get(
|
||||
"google.callback",
|
||||
auth({ required: false }),
|
||||
passportMiddleware(providerName)
|
||||
);
|
||||
}
|
||||
|
||||
export default router;
|
||||
@@ -9,7 +9,7 @@ import { AuthenticationError } from "../errors";
|
||||
import auth from "../middlewares/authentication";
|
||||
import validation from "../middlewares/validation";
|
||||
import { Team } from "../models";
|
||||
import { requireDirectory } from "../utils/fs";
|
||||
import providers from "./providers";
|
||||
|
||||
const log = debug("server");
|
||||
const app = new Koa();
|
||||
@@ -17,15 +17,11 @@ const router = new Router();
|
||||
|
||||
router.use(passport.initialize());
|
||||
|
||||
// dynamically load available authentication providers
|
||||
requireDirectory(__dirname).forEach(([{ default: provider, config }]) => {
|
||||
if (provider && provider.routes) {
|
||||
if (!config) {
|
||||
throw new Error("Auth providers must export a 'config' object");
|
||||
}
|
||||
|
||||
router.use("/", provider.routes());
|
||||
log(`loaded ${config.name} auth provider`);
|
||||
// dynamically load available authentication provider routes
|
||||
providers.forEach((provider) => {
|
||||
if (provider.enabled) {
|
||||
router.use("/", provider.router.routes());
|
||||
log(`loaded ${provider.name} auth provider`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -8,5 +8,6 @@ Auth providers generally use [Passport](http://www.passportjs.org/) strategies,
|
||||
although they can use any custom logic if needed. See the `google` auth provider for the cleanest example of what is required – some rules:
|
||||
|
||||
- The strategy name _must_ be lowercase
|
||||
- The stragegy _must_ call the `accountProvisioner` command in the verify callback
|
||||
- The strategy _must_ call the `accountProvisioner` command in the verify callback
|
||||
- The auth file _must_ export a `config` object with `name` and `enabled` keys
|
||||
- The auth file _must_ have a default export with a koa-router
|
||||
@@ -2,13 +2,13 @@
|
||||
import subMinutes from "date-fns/sub_minutes";
|
||||
import Router from "koa-router";
|
||||
import { find } from "lodash";
|
||||
import { AuthorizationError } from "../errors";
|
||||
import mailer from "../mailer";
|
||||
import auth from "../middlewares/authentication";
|
||||
import methodOverride from "../middlewares/methodOverride";
|
||||
import validation from "../middlewares/validation";
|
||||
import { User, Team } from "../models";
|
||||
import { getUserForEmailSigninToken } from "../utils/jwt";
|
||||
import { AuthorizationError } from "../../errors";
|
||||
import mailer from "../../mailer";
|
||||
import auth from "../../middlewares/authentication";
|
||||
import methodOverride from "../../middlewares/methodOverride";
|
||||
import validation from "../../middlewares/validation";
|
||||
import { User, Team } from "../../models";
|
||||
import { getUserForEmailSigninToken } from "../../utils/jwt";
|
||||
|
||||
const router = new Router();
|
||||
|
||||
98
server/auth/providers/google.js
Normal file
98
server/auth/providers/google.js
Normal file
@@ -0,0 +1,98 @@
|
||||
// @flow
|
||||
import passport from "@outlinewiki/koa-passport";
|
||||
import Router from "koa-router";
|
||||
import { capitalize } from "lodash";
|
||||
import { Strategy as GoogleStrategy } from "passport-google-oauth2";
|
||||
import accountProvisioner from "../../commands/accountProvisioner";
|
||||
import env from "../../env";
|
||||
import {
|
||||
GoogleWorkspaceRequiredError,
|
||||
GoogleWorkspaceInvalidError,
|
||||
} from "../../errors";
|
||||
import auth from "../../middlewares/authentication";
|
||||
import passportMiddleware from "../../middlewares/passport";
|
||||
import { getAllowedDomains } from "../../utils/authentication";
|
||||
import { StateStore } from "../../utils/passport";
|
||||
|
||||
const router = new Router();
|
||||
const providerName = "google";
|
||||
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
|
||||
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
|
||||
const allowedDomains = getAllowedDomains();
|
||||
|
||||
const scopes = [
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
];
|
||||
|
||||
export const config = {
|
||||
name: "Google",
|
||||
enabled: !!GOOGLE_CLIENT_ID,
|
||||
};
|
||||
|
||||
passport.use(
|
||||
new GoogleStrategy(
|
||||
{
|
||||
clientID: GOOGLE_CLIENT_ID,
|
||||
clientSecret: GOOGLE_CLIENT_SECRET,
|
||||
callbackURL: `${env.URL}/auth/google.callback`,
|
||||
prompt: "select_account consent",
|
||||
passReqToCallback: true,
|
||||
store: new StateStore(),
|
||||
scope: scopes,
|
||||
},
|
||||
async function (req, accessToken, refreshToken, profile, done) {
|
||||
try {
|
||||
const domain = profile._json.hd;
|
||||
|
||||
if (!domain) {
|
||||
throw new GoogleWorkspaceRequiredError();
|
||||
}
|
||||
|
||||
if (allowedDomains.length && !allowedDomains.includes(domain)) {
|
||||
throw new GoogleWorkspaceInvalidError();
|
||||
}
|
||||
|
||||
const subdomain = domain.split(".")[0];
|
||||
const teamName = capitalize(subdomain);
|
||||
|
||||
const result = await accountProvisioner({
|
||||
ip: req.ip,
|
||||
team: {
|
||||
name: teamName,
|
||||
domain,
|
||||
subdomain,
|
||||
},
|
||||
user: {
|
||||
name: profile.displayName,
|
||||
email: profile.email,
|
||||
avatarUrl: profile.picture,
|
||||
},
|
||||
authenticationProvider: {
|
||||
name: providerName,
|
||||
providerId: domain,
|
||||
},
|
||||
authentication: {
|
||||
providerId: profile.id,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
scopes,
|
||||
},
|
||||
});
|
||||
return done(null, result.user, result);
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
router.get("google", passport.authenticate(providerName));
|
||||
|
||||
router.get(
|
||||
"google.callback",
|
||||
auth({ required: false }),
|
||||
passportMiddleware(providerName)
|
||||
);
|
||||
|
||||
export default router;
|
||||
37
server/auth/providers/index.js
Normal file
37
server/auth/providers/index.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// @flow
|
||||
import { signin } from "../../../shared/utils/routeHelpers";
|
||||
import { requireDirectory } from "../../utils/fs";
|
||||
|
||||
let providers = [];
|
||||
|
||||
requireDirectory(__dirname).forEach(([module, id]) => {
|
||||
const { config, default: router } = module;
|
||||
|
||||
if (id === "index") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
throw new Error(
|
||||
`Auth providers must export a 'config' object, missing in ${id}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!router || !router.routes) {
|
||||
throw new Error(
|
||||
`Default export of an auth provider must be a koa-router, missing in ${id}`
|
||||
);
|
||||
}
|
||||
|
||||
if (config && config.enabled) {
|
||||
providers.push({
|
||||
id,
|
||||
name: config.name,
|
||||
enabled: config.enabled,
|
||||
authUrl: signin(id),
|
||||
router: router,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default providers;
|
||||
196
server/auth/providers/slack.js
Normal file
196
server/auth/providers/slack.js
Normal file
@@ -0,0 +1,196 @@
|
||||
// @flow
|
||||
import passport from "@outlinewiki/koa-passport";
|
||||
import Router from "koa-router";
|
||||
import { Strategy as SlackStrategy } from "passport-slack-oauth2";
|
||||
import accountProvisioner from "../../commands/accountProvisioner";
|
||||
import env from "../../env";
|
||||
import auth from "../../middlewares/authentication";
|
||||
import passportMiddleware from "../../middlewares/passport";
|
||||
import { Authentication, Collection, Integration, Team } from "../../models";
|
||||
import * as Slack from "../../slack";
|
||||
import { StateStore } from "../../utils/passport";
|
||||
|
||||
const router = new Router();
|
||||
const providerName = "slack";
|
||||
const SLACK_CLIENT_ID = process.env.SLACK_KEY;
|
||||
const SLACK_CLIENT_SECRET = process.env.SLACK_SECRET;
|
||||
|
||||
const scopes = [
|
||||
"identity.email",
|
||||
"identity.basic",
|
||||
"identity.avatar",
|
||||
"identity.team",
|
||||
];
|
||||
|
||||
export const config = {
|
||||
name: "Slack",
|
||||
enabled: !!SLACK_CLIENT_ID,
|
||||
};
|
||||
|
||||
const strategy = new SlackStrategy(
|
||||
{
|
||||
clientID: SLACK_CLIENT_ID,
|
||||
clientSecret: SLACK_CLIENT_SECRET,
|
||||
callbackURL: `${env.URL}/auth/slack.callback`,
|
||||
passReqToCallback: true,
|
||||
store: new StateStore(),
|
||||
scope: scopes,
|
||||
},
|
||||
async function (req, accessToken, refreshToken, profile, done) {
|
||||
try {
|
||||
const result = await accountProvisioner({
|
||||
ip: req.ip,
|
||||
team: {
|
||||
name: profile.team.name,
|
||||
subdomain: profile.team.domain,
|
||||
avatarUrl: profile.team.image_230,
|
||||
},
|
||||
user: {
|
||||
name: profile.user.name,
|
||||
email: profile.user.email,
|
||||
avatarUrl: profile.user.image_192,
|
||||
},
|
||||
authenticationProvider: {
|
||||
name: providerName,
|
||||
providerId: profile.team.id,
|
||||
},
|
||||
authentication: {
|
||||
providerId: profile.user.id,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
scopes,
|
||||
},
|
||||
});
|
||||
return done(null, result.user, result);
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// For some reason the author made the strategy name capatilised, I don't know
|
||||
// why but we need everything lowercase so we just monkey-patch it here.
|
||||
strategy.name = providerName;
|
||||
passport.use(strategy);
|
||||
|
||||
router.get("slack", passport.authenticate(providerName));
|
||||
|
||||
router.get(
|
||||
"slack.callback",
|
||||
auth({ required: false }),
|
||||
passportMiddleware(providerName)
|
||||
);
|
||||
|
||||
router.get("slack.commands", auth({ required: false }), async (ctx) => {
|
||||
const { code, state, error } = ctx.request.query;
|
||||
const user = ctx.state.user;
|
||||
ctx.assertPresent(code || error, "code is required");
|
||||
|
||||
if (error) {
|
||||
ctx.redirect(`/settings/integrations/slack?error=${error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// this code block accounts for the root domain being unable to
|
||||
// access authentication for subdomains. We must forward to the appropriate
|
||||
// subdomain to complete the oauth flow
|
||||
if (!user) {
|
||||
if (state) {
|
||||
try {
|
||||
const team = await Team.findByPk(state);
|
||||
return ctx.redirect(
|
||||
`${team.url}/auth${ctx.request.path}?${ctx.request.querystring}`
|
||||
);
|
||||
} catch (err) {
|
||||
return ctx.redirect(
|
||||
`/settings/integrations/slack?error=unauthenticated`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return ctx.redirect(`/settings/integrations/slack?error=unauthenticated`);
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `${process.env.URL || ""}/auth/slack.commands`;
|
||||
const data = await Slack.oauthAccess(code, endpoint);
|
||||
|
||||
const authentication = await Authentication.create({
|
||||
service: "slack",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: data.access_token,
|
||||
scopes: data.scope.split(","),
|
||||
});
|
||||
|
||||
await Integration.create({
|
||||
service: "slack",
|
||||
type: "command",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
authenticationId: authentication.id,
|
||||
settings: {
|
||||
serviceTeamId: data.team_id,
|
||||
},
|
||||
});
|
||||
|
||||
ctx.redirect("/settings/integrations/slack");
|
||||
});
|
||||
|
||||
router.get("slack.post", auth({ required: false }), async (ctx) => {
|
||||
const { code, error, state } = ctx.request.query;
|
||||
const user = ctx.state.user;
|
||||
ctx.assertPresent(code || error, "code is required");
|
||||
|
||||
const collectionId = state;
|
||||
ctx.assertUuid(collectionId, "collectionId must be an uuid");
|
||||
|
||||
if (error) {
|
||||
ctx.redirect(`/settings/integrations/slack?error=${error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// this code block accounts for the root domain being unable to
|
||||
// access authentcation for subdomains. We must forward to the
|
||||
// appropriate subdomain to complete the oauth flow
|
||||
if (!user) {
|
||||
try {
|
||||
const collection = await Collection.findByPk(state);
|
||||
const team = await Team.findByPk(collection.teamId);
|
||||
return ctx.redirect(
|
||||
`${team.url}/auth${ctx.request.path}?${ctx.request.querystring}`
|
||||
);
|
||||
} catch (err) {
|
||||
return ctx.redirect(`/settings/integrations/slack?error=unauthenticated`);
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `${process.env.URL || ""}/auth/slack.post`;
|
||||
const data = await Slack.oauthAccess(code, endpoint);
|
||||
|
||||
const authentication = await Authentication.create({
|
||||
service: "slack",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: data.access_token,
|
||||
scopes: data.scope.split(","),
|
||||
});
|
||||
|
||||
await Integration.create({
|
||||
service: "slack",
|
||||
type: "post",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
authenticationId: authentication.id,
|
||||
collectionId,
|
||||
events: [],
|
||||
settings: {
|
||||
url: data.incoming_webhook.url,
|
||||
channel: data.incoming_webhook.channel,
|
||||
channelId: data.incoming_webhook.channel_id,
|
||||
},
|
||||
});
|
||||
|
||||
ctx.redirect("/settings/integrations/slack");
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -1,202 +0,0 @@
|
||||
// @flow
|
||||
import passport from "@outlinewiki/koa-passport";
|
||||
import Router from "koa-router";
|
||||
import { Strategy as SlackStrategy } from "passport-slack-oauth2";
|
||||
import accountProvisioner from "../commands/accountProvisioner";
|
||||
import env from "../env";
|
||||
import auth from "../middlewares/authentication";
|
||||
import passportMiddleware from "../middlewares/passport";
|
||||
import { Authentication, Collection, Integration, Team } from "../models";
|
||||
import * as Slack from "../slack";
|
||||
import { StateStore } from "../utils/passport";
|
||||
|
||||
const router = new Router();
|
||||
const providerName = "slack";
|
||||
const SLACK_CLIENT_ID = process.env.SLACK_KEY;
|
||||
const SLACK_CLIENT_SECRET = process.env.SLACK_SECRET;
|
||||
|
||||
const scopes = [
|
||||
"identity.email",
|
||||
"identity.basic",
|
||||
"identity.avatar",
|
||||
"identity.team",
|
||||
];
|
||||
|
||||
export const config = {
|
||||
name: "Slack",
|
||||
enabled: !!SLACK_CLIENT_ID,
|
||||
};
|
||||
|
||||
if (SLACK_CLIENT_ID) {
|
||||
const strategy = new SlackStrategy(
|
||||
{
|
||||
clientID: SLACK_CLIENT_ID,
|
||||
clientSecret: SLACK_CLIENT_SECRET,
|
||||
callbackURL: `${env.URL}/auth/slack.callback`,
|
||||
passReqToCallback: true,
|
||||
store: new StateStore(),
|
||||
scope: scopes,
|
||||
},
|
||||
async function (req, accessToken, refreshToken, profile, done) {
|
||||
try {
|
||||
const result = await accountProvisioner({
|
||||
ip: req.ip,
|
||||
team: {
|
||||
name: profile.team.name,
|
||||
subdomain: profile.team.domain,
|
||||
avatarUrl: profile.team.image_230,
|
||||
},
|
||||
user: {
|
||||
name: profile.user.name,
|
||||
email: profile.user.email,
|
||||
avatarUrl: profile.user.image_192,
|
||||
},
|
||||
authenticationProvider: {
|
||||
name: providerName,
|
||||
providerId: profile.team.id,
|
||||
},
|
||||
authentication: {
|
||||
providerId: profile.user.id,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
scopes,
|
||||
},
|
||||
});
|
||||
return done(null, result.user, result);
|
||||
} catch (err) {
|
||||
return done(err, null);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// For some reason the author made the strategy name capatilised, I don't know
|
||||
// why but we need everything lowercase so we just monkey-patch it here.
|
||||
strategy.name = providerName;
|
||||
passport.use(strategy);
|
||||
|
||||
router.get("slack", passport.authenticate(providerName));
|
||||
|
||||
router.get(
|
||||
"slack.callback",
|
||||
auth({ required: false }),
|
||||
passportMiddleware(providerName)
|
||||
);
|
||||
|
||||
router.get("slack.commands", auth({ required: false }), async (ctx) => {
|
||||
const { code, state, error } = ctx.request.query;
|
||||
const user = ctx.state.user;
|
||||
ctx.assertPresent(code || error, "code is required");
|
||||
|
||||
if (error) {
|
||||
ctx.redirect(`/settings/integrations/slack?error=${error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// this code block accounts for the root domain being unable to
|
||||
// access authentication for subdomains. We must forward to the appropriate
|
||||
// subdomain to complete the oauth flow
|
||||
if (!user) {
|
||||
if (state) {
|
||||
try {
|
||||
const team = await Team.findByPk(state);
|
||||
return ctx.redirect(
|
||||
`${team.url}/auth${ctx.request.path}?${ctx.request.querystring}`
|
||||
);
|
||||
} catch (err) {
|
||||
return ctx.redirect(
|
||||
`/settings/integrations/slack?error=unauthenticated`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return ctx.redirect(
|
||||
`/settings/integrations/slack?error=unauthenticated`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `${process.env.URL || ""}/auth/slack.commands`;
|
||||
const data = await Slack.oauthAccess(code, endpoint);
|
||||
|
||||
const authentication = await Authentication.create({
|
||||
service: "slack",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: data.access_token,
|
||||
scopes: data.scope.split(","),
|
||||
});
|
||||
|
||||
await Integration.create({
|
||||
service: "slack",
|
||||
type: "command",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
authenticationId: authentication.id,
|
||||
settings: {
|
||||
serviceTeamId: data.team_id,
|
||||
},
|
||||
});
|
||||
|
||||
ctx.redirect("/settings/integrations/slack");
|
||||
});
|
||||
|
||||
router.get("slack.post", auth({ required: false }), async (ctx) => {
|
||||
const { code, error, state } = ctx.request.query;
|
||||
const user = ctx.state.user;
|
||||
ctx.assertPresent(code || error, "code is required");
|
||||
|
||||
const collectionId = state;
|
||||
ctx.assertUuid(collectionId, "collectionId must be an uuid");
|
||||
|
||||
if (error) {
|
||||
ctx.redirect(`/settings/integrations/slack?error=${error}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// this code block accounts for the root domain being unable to
|
||||
// access authentcation for subdomains. We must forward to the
|
||||
// appropriate subdomain to complete the oauth flow
|
||||
if (!user) {
|
||||
try {
|
||||
const collection = await Collection.findByPk(state);
|
||||
const team = await Team.findByPk(collection.teamId);
|
||||
return ctx.redirect(
|
||||
`${team.url}/auth${ctx.request.path}?${ctx.request.querystring}`
|
||||
);
|
||||
} catch (err) {
|
||||
return ctx.redirect(
|
||||
`/settings/integrations/slack?error=unauthenticated`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const endpoint = `${process.env.URL || ""}/auth/slack.post`;
|
||||
const data = await Slack.oauthAccess(code, endpoint);
|
||||
|
||||
const authentication = await Authentication.create({
|
||||
service: "slack",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
token: data.access_token,
|
||||
scopes: data.scope.split(","),
|
||||
});
|
||||
|
||||
await Integration.create({
|
||||
service: "slack",
|
||||
type: "post",
|
||||
userId: user.id,
|
||||
teamId: user.teamId,
|
||||
authenticationId: authentication.id,
|
||||
collectionId,
|
||||
events: [],
|
||||
settings: {
|
||||
url: data.incoming_webhook.url,
|
||||
channel: data.incoming_webhook.channel,
|
||||
channelId: data.incoming_webhook.channel_id,
|
||||
},
|
||||
});
|
||||
|
||||
ctx.redirect("/settings/integrations/slack");
|
||||
});
|
||||
}
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user