Website: Update Vanta integration (#19349)

Closes: https://github.com/fleetdm/confidential/issues/6069

Changes:
- Added a new action to add support for the Vanta integration to be set
up from a partners website. This action sets the required cookies
provided via queryString and redirects users to the Vanta authorization
page.
- Updated the `create-vanta-authorization-request` action to redirect
users who provide a `redirectToExternalPageAfterAuthorization` value the
new endpoint instead of returning a vanta authorization URL.
- Updated `view-vanta-authorization` to redirect users to the URL
provided to the `create-vanta-authorization-request` endpoint (if one
was provided)
This commit is contained in:
Eric
2024-06-14 07:40:49 -04:00
committed by GitHub
parent 93ba31ebef
commit 9cd452e8d5
5 changed files with 88 additions and 11 deletions
@@ -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;
}
}
@@ -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);
}
};
+3 -1
View File
@@ -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
};
+1
View File
@@ -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,
};
+2 -1
View File
@@ -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},