Add integration auth revocation

This commit is contained in:
Tuan Dang
2022-12-15 15:27:01 -05:00
parent a49fcf49f1
commit 35fd1520e2
5 changed files with 86 additions and 75 deletions
@@ -5,7 +5,7 @@ import { readFileSync } from 'fs';
import { IntegrationAuth, Integration } from '../models'; import { IntegrationAuth, Integration } from '../models';
import { INTEGRATION_SET, ENV_DEV } from '../variables'; import { INTEGRATION_SET, ENV_DEV } from '../variables';
import { IntegrationService } from '../services'; import { IntegrationService } from '../services';
import { getApps } from '../integrations'; import { getApps, revokeAccess } from '../integrations';
/** /**
* Perform OAuth2 code-token exchange as part of integration [integration] for workspace with id [workspaceId] * Perform OAuth2 code-token exchange as part of integration [integration] for workspace with id [workspaceId]
@@ -74,46 +74,22 @@ export const getIntegrationAuthApps = async (req: Request, res: Response) => {
* @returns * @returns
*/ */
export const deleteIntegrationAuth = async (req: Request, res: Response) => { export const deleteIntegrationAuth = async (req: Request, res: Response) => {
// TODO: unfinished - disable application via Heroku API and make compatible with other integration types
try { try {
const { integrationAuthId } = req.params; const { integrationAuthId } = req.params;
// TODO: disable application via Heroku API; figure out what authorization id is await revokeAccess({
integrationAuth: req.integrationAuth,
const integrations = JSON.parse( accessToken: req.accessToken
readFileSync('./src/json/integrations.json').toString()
);
let authorizationId;
switch (req.integrationAuth.integration) {
case 'heroku':
authorizationId = integrations.heroku.clientId;
}
// not sure what authorizationId is?
// // revoke authorization
// const res2 = await axios.delete(
// `https://api.heroku.com/oauth/authorizations/${authorizationId}`,
// {
// headers: {
// 'Accept': 'application/vnd.heroku+json; version=3',
// 'Authorization': 'Bearer ' + req.accessToken
// }
// }
// );
const deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
_id: integrationAuthId
}); });
if (deletedIntegrationAuth) {
await Integration.deleteMany({
integrationAuth: deletedIntegrationAuth._id
});
}
} catch (err) { } catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({ return res.status(400).send({
message: 'Failed to delete integration authorization' message: 'Failed to delete integration authorization'
}); });
} }
};
return res.status(200).send({
message: 'Successfully deleted integration authorization'
});
}
+3 -1
View File
@@ -2,10 +2,12 @@ import { exchangeCode } from './exchange';
import { exchangeRefresh } from './refresh'; import { exchangeRefresh } from './refresh';
import { getApps } from './apps'; import { getApps } from './apps';
import { syncSecrets } from './sync'; import { syncSecrets } from './sync';
import { revokeAccess } from './revoke';
export { export {
exchangeCode, exchangeCode,
exchangeRefresh, exchangeRefresh,
getApps, getApps,
syncSecrets syncSecrets,
revokeAccess
} }
+50
View File
@@ -0,0 +1,50 @@
import axios from 'axios';
import * as Sentry from '@sentry/node';
import {
IIntegrationAuth,
IntegrationAuth,
Integration
} from '../models';
import {
INTEGRATION_HEROKU,
INTEGRATION_VERCEL,
INTEGRATION_NETLIFY
} from '../variables';
const revokeAccess = async ({
integrationAuth,
accessToken
}: {
integrationAuth: IIntegrationAuth,
accessToken: String
}) => {
try {
// add any integration-specific revocation logic
switch (integrationAuth.integration) {
case INTEGRATION_HEROKU:
break;
case INTEGRATION_VERCEL:
break;
case INTEGRATION_NETLIFY:
break;
}
const deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
_id: integrationAuth._id
});
if (deletedIntegrationAuth) {
await Integration.deleteMany({
integrationAuth: deletedIntegrationAuth._id
});
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to delete integration authorization');
}
}
export {
revokeAccess
}
@@ -1,5 +1,6 @@
import React from "react"; import React from "react";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import {
faCheck, faCheck,
@@ -33,6 +34,7 @@ const CloudIntegration = ({
integrationOptionPress, integrationOptionPress,
integrationAuths integrationAuths
}: Props) => { }: Props) => {
const router = useRouter();
return integrationAuths ? ( return integrationAuths ? (
<div <div
className={`relative ${ className={`relative ${
@@ -74,7 +76,8 @@ const CloudIntegration = ({
.includes(cloudIntegrationOption.name.toLowerCase()) && ( .includes(cloudIntegrationOption.name.toLowerCase()) && (
<div className="absolute group z-50 top-0 right-0 flex flex-row"> <div className="absolute group z-50 top-0 right-0 flex flex-row">
<div <div
onClick={() => { onClick={(event) => {
event.stopPropagation();
deleteIntegrationAuth({ deleteIntegrationAuth({
integrationAuthId: integrationAuths integrationAuthId: integrationAuths
.filter( .filter(
+4 -24
View File
@@ -115,45 +115,25 @@ export default function Integrations() {
* @returns * @returns
*/ */
const handleIntegrationOption = async ({ integrationOption }) => { const handleIntegrationOption = async ({ integrationOption }) => {
// TODO: modularize and handle switch by slug try {
console.log('handle', integrationOption);
// generate CSRF token for OAuth2 code-token exchange integrations // generate CSRF token for OAuth2 code-token exchange integrations
const state = crypto.randomBytes(16).toString("hex"); const state = crypto.randomBytes(16).toString("hex");
localStorage.setItem('latestCSRFToken', state); localStorage.setItem('latestCSRFToken', state);
switch (integrationOption.name) { switch (integrationOption.name) {
case 'Heroku': case 'Heroku':
// console.log('Heroku integration ', integrationOption);
window.location = `https://id.heroku.com/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${state}`; window.location = `https://id.heroku.com/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${state}`;
break; break;
case 'Vercel': case 'Vercel':
window.location = `https://vercel.com/integrations/infisical/new?state=${state}`; window.location = `https://vercel.com/integrations/infisical/new?state=${state}`;
break; break;
case 'Netlify': case 'Netlify':
// window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=token&redirect_uri=${integrationOption.redirectURL}&state=${state}`;
window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${integrationOption.redirectURL}&state=${state}`; window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${integrationOption.redirectURL}&state=${state}`;
// const res = await axios.post('https://api.netlify.com/api/v1/oauth/tickets' + '?client_id=' + integrationOption.clientId);
// window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=ticket&redirect_uri=${integrationOption.redirectURL}&state=${state}&ticket=${res.data.id}`;
// `https://app.netlify.com/authorize?response_type=ticket&ticket=${ticket.id}`
try {
// const res = await axios.post('https://api.netlify.com/api/v1/oauth/tickets' + '?client_id=' + integrationOption.clientId);
// console.log('res response', res);
// const res2 = await axios.get('https://api.netlify.com/api/v1/oauth/tickets/' + res.data.id);
// console.log('res2 response', res2);
// console.log('ticket_id', res.data.id);
// // exchange ticket:
// const res3 = await axios.get(`https://api.netlify.com/api/v1/oauth/tickets/${res.data.id}/exchange`);
// console.log('res3 response', res3);
} catch (err) {
console.error('Netlify ', err);
}
break; break;
} }
} catch (err) {
console.log(err);
}
} }
/** /**