Begin Netlify integration

This commit is contained in:
Tuan Dang
2022-12-13 21:12:40 -05:00
parent d86c335671
commit fe17d8459b
7 changed files with 114 additions and 17 deletions
+6 -2
View File
@@ -11,8 +11,10 @@ const JWT_SIGNUP_SECRET = process.env.JWT_SIGNUP_SECRET!;
const MONGO_URL = process.env.MONGO_URL!; const MONGO_URL = process.env.MONGO_URL!;
const NODE_ENV = process.env.NODE_ENV! || 'production'; const NODE_ENV = process.env.NODE_ENV! || 'production';
const OAUTH_CLIENT_SECRET_HEROKU = process.env.OAUTH_CLIENT_SECRET_HEROKU!; const OAUTH_CLIENT_SECRET_HEROKU = process.env.OAUTH_CLIENT_SECRET_HEROKU!;
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!; const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!;
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!;
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com'; const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
const POSTHOG_PROJECT_API_KEY = const POSTHOG_PROJECT_API_KEY =
process.env.POSTHOG_PROJECT_API_KEY! || process.env.POSTHOG_PROJECT_API_KEY! ||
@@ -48,8 +50,10 @@ export {
MONGO_URL, MONGO_URL,
NODE_ENV, NODE_ENV,
OAUTH_CLIENT_SECRET_HEROKU, OAUTH_CLIENT_SECRET_HEROKU,
CLIENT_SECRET_VERCEL,
CLIENT_ID_VERCEL, CLIENT_ID_VERCEL,
CLIENT_ID_NETLIFY,
CLIENT_SECRET_VERCEL,
CLIENT_SECRET_NETLIFY,
POSTHOG_HOST, POSTHOG_HOST,
POSTHOG_PROJECT_API_KEY, POSTHOG_PROJECT_API_KEY,
PRIVATE_KEY, PRIVATE_KEY,
+2 -2
View File
@@ -55,8 +55,8 @@ const handleOAuthExchangeHelper = async ({
integration, integration,
code code
}); });
// TODO: continue ironing out Vercel integration return;
let update: Update = { let update: Update = {
workspace: workspaceId, workspace: workspaceId,
+65 -1
View File
@@ -3,15 +3,19 @@ import * as Sentry from '@sentry/node';
import { import {
INTEGRATION_HEROKU, INTEGRATION_HEROKU,
INTEGRATION_VERCEL, INTEGRATION_VERCEL,
INTEGRATION_NETLIFY,
INTEGRATION_HEROKU_TOKEN_URL, INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_VERCEL_TOKEN_URL, INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
ACTION_PUSH_TO_HEROKU ACTION_PUSH_TO_HEROKU
} from '../variables'; } from '../variables';
import { import {
SITE_URL, SITE_URL,
OAUTH_CLIENT_SECRET_HEROKU, OAUTH_CLIENT_SECRET_HEROKU,
CLIENT_ID_VERCEL, CLIENT_ID_VERCEL,
CLIENT_SECRET_VERCEL CLIENT_ID_NETLIFY,
CLIENT_SECRET_VERCEL,
CLIENT_SECRET_NETLIFY
} from '../config'; } from '../config';
interface ExchangeCodeHerokuResponse { interface ExchangeCodeHerokuResponse {
@@ -63,6 +67,12 @@ const exchangeCode = async ({
obj = await exchangeCodeVercel({ obj = await exchangeCodeVercel({
code code
}); });
break;
case INTEGRATION_NETLIFY:
obj = await exchangeCodeNetlify({
code
});
break;
} }
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
@@ -157,6 +167,60 @@ const exchangeCodeVercel = async ({
}); });
} }
/**
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel
* code-token exchange
* @param {Object} obj1
* @param {Object} obj1.code - code for code-token exchange
* @returns {Object} obj2
* @returns {String} obj2.accessToken - access token for Heroku API
* @returns {String} obj2.refreshToken - refresh token for Heroku API
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
*/
const exchangeCodeNetlify = async ({
code
}: {
code: string;
}) => {
console.log('exchangeCodeNetlify');
let res: ExchangeCodeVercelResponse;
try {
res = (await axios.post(
INTEGRATION_VERCEL_TOKEN_URL,
new URLSearchParams({
code: code,
client_id: CLIENT_ID_VERCEL,
client_secret: CLIENT_SECRET_VERCEL,
redirect_uri: `${SITE_URL}/vercel`
} as any)
)).data;
res = (await axios.post(
INTEGRATION_NETLIFY_TOKEN_URL,
`${"https://api.netlify.com/oauth/token"}?code=${code}&client_id=${CLIENT_ID_NETLIFY}&client_secret=${CLIENT_SECRET_NETLIFY}&grant_type=authorization_code&redirect_uri=${SITE_URL}/netlify`
// INTEGRATION_NETLIFY_TOKEN_URL,
// new URLSearchParams({
// code: code,
// client_id: CLIENT_ID_NETLIFY,
// client_secret: CLIENT_SECRET_NETLIFY,
// redirect_uri: `${SITE_URL}/netlify`
// } as any)
));
console.log('resss', res);
} catch (err) {
console.error('netlify err', err);
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Netlify');
}
return ({
});
}
export { export {
exchangeCode exchangeCode
} }
+1 -1
View File
@@ -14,7 +14,7 @@ const INTEGRATION_OAUTH2 = 'oauth2';
// integration oauth endpoints // integration oauth endpoints
const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token'; const INTEGRATION_HEROKU_TOKEN_URL = 'https://id.heroku.com/oauth/token';
const INTEGRATION_VERCEL_TOKEN_URL = 'https://api.vercel.com/v2/oauth/access_token'; const INTEGRATION_VERCEL_TOKEN_URL = 'https://api.vercel.com/v2/oauth/access_token';
const INTEGRATION_NETLIFY_TOKEN_URL = 'https://app.netlify.com/authorize'; const INTEGRATION_NETLIFY_TOKEN_URL = 'https://api.netlify.com/oauth/token';
// integration apps endpoints // integration apps endpoints
const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com'; const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com';
+28 -5
View File
@@ -21,6 +21,8 @@ const {
} = require('../../components/utilities/cryptography/crypto'); } = require('../../components/utilities/cryptography/crypto');
const crypto = require("crypto"); const crypto = require("crypto");
import axios from "axios";
export default function Integrations() { export default function Integrations() {
const [integrationAuths, setIntegrationAuths] = useState([]); const [integrationAuths, setIntegrationAuths] = useState([]);
const [integrations, setIntegrations] = useState([]); const [integrations, setIntegrations] = useState([]);
@@ -115,20 +117,41 @@ export default function Integrations() {
const handleIntegrationOption = async ({ integrationOption }) => { const handleIntegrationOption = async ({ integrationOption }) => {
// TODO: modularize and handle switch by slug // TODO: modularize and handle switch by slug
console.log('handle', integrationOption);
// generate CSRF token for OAuth2 code-token exchange integrations // generate CSRF token for OAuth2 code-token exchange integrations
const csrfToken = crypto.randomBytes(16).toString("hex"); const state = crypto.randomBytes(16).toString("hex");
localStorage.setItem('latestCSRFToken', csrfToken); localStorage.setItem('latestCSRFToken', state);
switch (integrationOption.name) { switch (integrationOption.name) {
case 'Heroku': case 'Heroku':
// console.log('Heroku integration ', integrationOption); // console.log('Heroku integration ', integrationOption);
window.location = `https://id.heroku.com/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${csrfToken}`; 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=${csrfToken}`; window.location = `https://vercel.com/integrations/infisical/new?state=${state}`;
break; break;
case 'Netlify': case 'Netlify':
console.log('netlifyyy'); // 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}`;
// 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;
} }
} }
+10 -5
View File
@@ -7,6 +7,8 @@ import AuthorizeIntegration from "./api/integrations/authorizeIntegration";
export default function Netlify() { export default function Netlify() {
const router = useRouter(); const router = useRouter();
const parsedUrl = queryString.parse(router.asPath.split("?")[1]); const parsedUrl = queryString.parse(router.asPath.split("?")[1]);
const code = parsedUrl.code;
const state = parsedUrl.state;
// modify comment here // modify comment here
/** /**
@@ -14,16 +16,19 @@ export default function Netlify() {
*/ */
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(async () => { useEffect(async () => {
console.log('AA');
if (state === localStorage.getItem('latestCSRFToken')) { if (state === localStorage.getItem('latestCSRFToken')) {
localStorage.removeItem('latestCSRFToken'); localStorage.removeItem('latestCSRFToken');
console.log('Netlify', parsedUrl); console.log('Netlify', parsedUrl);
// http://localhost:8080/netlify?code=qnG0g_krhklWDpdUqfhU-t1sLeZzYI3gF2d6QVnL-Gc&state=3a78cd3154a9a99ddd4eb5d99dbb3289
// http://localhost:8080/netlify#access_token=d-78qUAnnSzvlgfG9Y_oUV6_4TQBxLbofImiBbKAjzE&token_type=Bearer&state=5da34fa49e301e9fa1a6e40925694b77
// await AuthorizeIntegration({ await AuthorizeIntegration({
// workspaceId: localStorage.getItem('projectData.id'), workspaceId: localStorage.getItem('projectData.id'),
// code, code,
// integration: "vercel" integration: "netlify"
// }); });
// router.push("/integrations/" + localStorage.getItem("projectData.id")); // router.push("/integrations/" + localStorage.getItem("projectData.id"));
} }
+2 -1
View File
@@ -21,7 +21,8 @@
"image": "Netlify", "image": "Netlify",
"isAvailable": true, "isAvailable": true,
"type": "oauth2", "type": "oauth2",
"clientId": "" "clientId": "frGheMeEzVEUgM5yHLcPeTj9kYJhYvqkR5IkSexOS50",
"redirectURL": "http://localhost:8080/netlify"
}, },
{ {
"name": "Google Cloud Platform", "name": "Google Cloud Platform",