diff --git a/website/api/controllers/create-vanta-authorization-request.js b/website/api/controllers/create-vanta-authorization-request.js index 30fb4dbf15..8bb2c89c63 100644 --- a/website/api/controllers/create-vanta-authorization-request.js +++ b/website/api/controllers/create-vanta-authorization-request.js @@ -19,6 +19,10 @@ module.exports = { fleetApiKey: { type: 'string', required: true, + }, + redirectToExternalPageAfterAuthorization: { + type: 'string', + description: 'If provided, the user will be sent to this URL after they complete the setup of this integration' } }, @@ -59,7 +63,6 @@ module.exports = { }, fn: async function (inputs) { - let url = require('url'); // Look for any existing VantaConnection records that use this fleet instance URL. @@ -139,17 +142,26 @@ module.exports = { fleetApiKey: inputs.fleetApiKey, }); } - + let callbackUrl = `/vanta-authorization`; + if(inputs.redirectToExternalPageAfterAuthorization){ + callbackUrl += `?redirectAfterSetup=${inputs.redirectToExternalPageAfterAuthorization}`; + } // Build the authorization URL for this request. - let vantaAuthorizationRequestURL = `https://app.vanta.com/oauth/authorize?client_id=${encodeURIComponent(sails.config.custom.vantaAuthorizationClientId)}&scope=connectors.self:write-resource connectors.self:read-resource&state=${encodeURIComponent(generatedStateForThisRequest)}&source_id=${encodeURIComponent(sourceIDForThisRequest)}&redirect_uri=${encodeURIComponent(url.resolve(sails.config.custom.baseUrl, '/vanta-authorization'))}&response_type=code`; + let vantaAuthorizationRequestURL = `https://app.vanta.com/oauth/authorize?client_id=${encodeURIComponent(sails.config.custom.vantaAuthorizationClientId)}&scope=connectors.self:write-resource connectors.self:read-resource&state=${encodeURIComponent(generatedStateForThisRequest)}&source_id=${encodeURIComponent(sourceIDForThisRequest)}&redirect_uri=${encodeURIComponent(url.resolve(sails.config.custom.baseUrl, callbackUrl))}&response_type=code`; - // Set a `state` cookie on the user's browser. This value will be checked against a query parameter when the user returns to fleetdm.com. - this.res.cookie('state', generatedStateForThisRequest, {signed: true}); + if(inputs.redirectToExternalPageAfterAuthorization){ + let internalRedirectUrl = `${sails.config.custom.baseUrl}/redirect-vanta-authorization-request?vantaSourceId=${encodeURIComponent(sourceIDForThisRequest)}&state=${encodeURIComponent(generatedStateForThisRequest)}&vantaAuthorizationRequestURL=${encodeURIComponent(vantaAuthorizationRequestURL)}&redirectAfterSetup=${encodeURIComponent(inputs.redirectToExternalPageAfterAuthorization)}`; - // Set the sourceId to a cookie, we'll use this value to find the database record we created for this request when the user returns to fleetdm.com. - this.res.cookie('vantaSourceId', sourceIDForThisRequest, {signed: true}); - - return vantaAuthorizationRequestURL; + return internalRedirectUrl; + // If the useInternalRedirect input was provided, we'll return the URL of an internal endpoiint that will set the required cookies for this request. + } else { + // Otherwise, if this request came from a user on the connect-vanta page, we'll set the cookies are redirect them directly to Vanta. + // Set a `state` cookie on the user's browser. This value will be checked against a query parameter when the user returns to fleetdm.com. + this.res.cookie('state', generatedStateForThisRequest, {signed: true}); + // Set the sourceId to a cookie, we'll use this value to find the database record we created for this request when the user returns to fleetdm.com. + this.res.cookie('vantaSourceId', sourceIDForThisRequest, {signed: true}); + return vantaAuthorizationRequestURL; + } } diff --git a/website/api/controllers/redirect-vanta-authorization-request.js b/website/api/controllers/redirect-vanta-authorization-request.js new file mode 100644 index 0000000000..82e380342f --- /dev/null +++ b/website/api/controllers/redirect-vanta-authorization-request.js @@ -0,0 +1,61 @@ +module.exports = { + + + friendlyName: 'Redirect vanta authorization request', + + + description: 'Sets provided inputs in the user`s browser as cookies and redirects them to Vanta.', + + + inputs: { + vantaSourceId: { + type: 'string', + description: 'The generated vanta Source ID for this request.', + required: true, + }, + state: { + type: 'string', + description: 'The state provided to Vanta when an authorization request was created', + required: true, + }, + vantaAuthorizationRequestURL: { + type: 'string', + description: 'The Vanta authorization url that the user will be directed to after they are sent to this page.', + required: true, + }, + redirectAfterSetup: { + type: 'string', + description: 'The URL that the user will be redirected to after they complete setup.', + required: true, + } + }, + + + exits: { + noMatchingVantaConnection: { + description: 'No Vanta connection could be found using the provided vantaSourceId', + responseType: 'badRequest' + }, + }, + + + fn: async function ({vantaSourceId, state, vantaAuthorizationRequestURL, redirectAfterSetup}) { + + // Find the VantaConnection record that we created when the user created this request. + let recordOfThisAuthorization = await VantaConnection.findOne({vantaSourceId: vantaSourceId}); + + // If no record of this authorization could be found, return a noMatchingVantaConnection response. + if(!recordOfThisAuthorization){ + throw 'noMatchingVantaConnection'; + } + + // Set a 'state' and 'vantaSourceId' cookie on the users browser. + this.res.cookie('redirectAfterSetup', redirectAfterSetup, {signed: true}); + this.res.cookie('state', state, {signed: true}); + this.res.cookie('vantaSourceId', vantaSourceId, {signed: true}); + // now that the user has the required cookies to complete the vanta integration setup, redirect them to the provided VantaAuthorizationUrl. + return this.res.redirect(vantaAuthorizationRequestURL); + } + + +}; diff --git a/website/api/controllers/view-vanta-authorization.js b/website/api/controllers/view-vanta-authorization.js index 3fd465bb0b..cf6bb03afd 100644 --- a/website/api/controllers/view-vanta-authorization.js +++ b/website/api/controllers/view-vanta-authorization.js @@ -89,7 +89,9 @@ module.exports = { if(!updatedRecord){ throw new Error(`When trying to update a VantaConnection record (id: ${recordOfThisAuthorization.id}) with an authorization token from Vanta, the database record associated with this request has gone missing.`); } - + if(this.req.signedCookies.redirectAfterSetup){ + return this.res.redirect(this.req.signedCookies.redirectAfterSetup); + } return { showSuccessMessage: true }; diff --git a/website/config/policies.js b/website/config/policies.js index 2c46a7f477..5d4552459d 100644 --- a/website/config/policies.js +++ b/website/config/policies.js @@ -55,4 +55,5 @@ module.exports.policies = { 'deliver-talk-to-us-form-submission': true, 'get-human-interpretation-from-osquery-sql': true, 'customers/view-new-license': true, + 'redirect-vanta-authorization-request': true, }; diff --git a/website/config/routes.js b/website/config/routes.js index 7814ed055c..7a07552257 100644 --- a/website/config/routes.js +++ b/website/config/routes.js @@ -589,7 +589,8 @@ module.exports.routes = { 'POST /api/v1/create-or-update-one-newsletter-subscription': { action: 'create-or-update-one-newsletter-subscription' }, '/api/v1/unsubscribe-from-all-newsletters': { action: 'unsubscribe-from-all-newsletters' }, 'POST /api/v1/admin/build-license-key': { action: 'admin/build-license-key' }, - 'POST /api/v1/create-vanta-authorization-request': { action: 'create-vanta-authorization-request' }, + 'POST /api/v1/create-vanta-authorization-request': { action: 'create-vanta-authorization-request', csrf: false }, + 'GET /redirect-vanta-authorization-request': { action: 'redirect-vanta-authorization-request' }, 'POST /api/v1/deliver-mdm-beta-signup': { action: 'deliver-mdm-beta-signup' }, 'POST /api/v1/get-human-interpretation-from-osquery-sql': { action: 'get-human-interpretation-from-osquery-sql', csrf: false }, 'POST /api/v1/deliver-apple-csr ': { action: 'deliver-apple-csr', csrf: false},