feat: OAuth2 - Do not make axios request when executing collection level Get Access Token action

The actual the authorization request is now part of request preparation, and its response is returned for post-request script processing.
This commit is contained in:
Mateusz Pietryga
2024-04-13 23:59:36 +02:00
parent 4afcd44216
commit d982e35a17
4 changed files with 32 additions and 82 deletions

View File

@@ -12,7 +12,6 @@ const { ipcMain } = require('electron');
const { isUndefined, isNull, each, get, compact, cloneDeep, forOwn, extend } = require('lodash');
const { VarsRuntime, AssertRuntime, ScriptRuntime, TestRuntime } = require('@usebruno/js');
const prepareRequest = require('./prepare-request');
const prepareCollectionRequest = require('./prepare-collection-request');
const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request');
const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token');
const { uuid } = require('../../utils/common');
@@ -268,22 +267,23 @@ const configureRequest = async (
if (request.oauth2) {
let requestCopy = cloneDeep(request);
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
let credentials;
let credentials, response;
switch (request?.oauth2?.grantType) {
case 'authorization_code': {
({ credentials } = await oauth2AuthorizeWithAuthorizationCode(requestCopy, collectionUid));
({ credentials, response } = await oauth2AuthorizeWithAuthorizationCode(requestCopy, collectionUid));
break;
}
case 'client_credentials': {
({ credentials } = await oauth2AuthorizeWithClientCredentials(requestCopy, collectionUid));
({ credentials, response } = await oauth2AuthorizeWithClientCredentials(requestCopy, collectionUid));
break;
}
case 'password': {
({ credentials } = await oauth2AuthorizeWithPasswordCredentials(requestCopy, collectionUid));
({ credentials, response } = await oauth2AuthorizeWithPasswordCredentials(requestCopy, collectionUid));
break;
}
}
request.credentials = credentials;
request.authRequestResponse = response;
request.headers['Authorization'] = `Bearer ${credentials.access_token}`;
}
@@ -704,7 +704,11 @@ const registerNetworkIpc = (mainWindow) => {
const collectionRoot = get(collection, 'root', {});
const _request = collectionRoot?.request;
const request = prepareCollectionRequest(_request, collectionRoot, collectionPath);
const request = prepareRequest(_request, collectionRoot, collectionPath);
// Script from this collection-level pseudo-request should be erased as it duplicates the collection script
delete request.script;
const envVars = getEnvVars(environment);
const processEnvVars = getProcessEnvVars(collectionUid);
const brunoConfig = getBrunoConfig(collectionUid);
@@ -724,7 +728,7 @@ const registerNetworkIpc = (mainWindow) => {
);
interpolateVars(request, envVars, collection.runtimeVariables, processEnvVars);
const axiosInstance = await configureRequest(
await configureRequest(
collection.uid,
request,
envVars,
@@ -733,19 +737,13 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath
);
try {
response = await axiosInstance(request);
} catch (error) {
if (error?.response) {
response = error.response;
} else {
return Promise.reject(error);
}
const response = request.authRequestResponse;
// When credentials are loaded from cache, authRequestResponse has no data
if (response.data) {
const { data } = parseDataFromResponse(response, request.__brunoDisableParsingResponseJson);
response.data = data;
}
const { data } = parseDataFromResponse(response, request.__brunoDisableParsingResponseJson);
response.data = data;
await runPostResponse(
request,
response,
@@ -763,7 +761,8 @@ const registerNetworkIpc = (mainWindow) => {
status: response.status,
statusText: response.statusText,
headers: response.headers,
data: response.data
data: response.data,
credentials: request.credentials
};
} catch (error) {
return Promise.reject(error);

View File

@@ -35,7 +35,7 @@ const oauth2AuthorizeWithAuthorizationCode = async (request, collectionUid) => {
const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid);
if (cachedCredentials?.access_token) {
console.log('Reusing Stored access token');
return { credentials: cachedCredentials };
return { credentials: cachedCredentials, response: {} };
}
let codeVerifier = generateCodeVerifier();
@@ -65,7 +65,7 @@ const oauth2AuthorizeWithAuthorizationCode = async (request, collectionUid) => {
const response = await axiosInstance(request);
const credentials = JSON.parse(response.data);
persistOauth2Credentials(credentials, collectionUid);
return { credentials };
return { credentials, response };
};
const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => {
@@ -108,7 +108,7 @@ const oauth2AuthorizeWithClientCredentials = async (request, collectionUid) => {
const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid);
if (cachedCredentials?.access_token) {
console.log('Reusing Stored access token');
return { credentials: cachedCredentials };
return { credentials: cachedCredentials, response: {} };
}
let requestCopy = cloneDeep(request);
@@ -132,7 +132,7 @@ const oauth2AuthorizeWithClientCredentials = async (request, collectionUid) => {
let response = await axiosInstance(request);
let credentials = JSON.parse(response.data);
persistOauth2Credentials(credentials, collectionUid);
return { credentials };
return { credentials, response };
};
// PASSWORD CREDENTIALS
@@ -141,7 +141,7 @@ const oauth2AuthorizeWithPasswordCredentials = async (request, collectionUid) =>
const { cachedCredentials } = getPersistedOauth2Credentials(collectionUid);
if (cachedCredentials?.access_token) {
console.log('Reusing Stored access token');
return { credentials: cachedCredentials };
return { credentials: cachedCredentials, response: {} };
}
const oAuth = get(request, 'oauth2', {});
@@ -166,7 +166,7 @@ const oauth2AuthorizeWithPasswordCredentials = async (request, collectionUid) =>
let response = await axiosInstance(request);
let credentials = JSON.parse(response.data);
persistOauth2Credentials(credentials, collectionUid);
return { credentials };
return { credentials, response };
};
module.exports = {
oauth2AuthorizeWithAuthorizationCode,

View File

@@ -1,49 +0,0 @@
const { get, each } = require('lodash');
const { setAuthHeaders } = require('./prepare-request');
const prepareCollectionRequest = (request, collectionRoot) => {
const headers = {};
let contentTypeDefined = false;
let url = request.url;
// collection headers
each(get(collectionRoot, 'request.headers', []), (h) => {
if (h.enabled) {
headers[h.name] = h.value;
if (h.name.toLowerCase() === 'content-type') {
contentTypeDefined = true;
}
}
});
each(request.headers, (h) => {
if (h.enabled) {
headers[h.name] = h.value;
if (h.name.toLowerCase() === 'content-type') {
contentTypeDefined = true;
}
}
});
let axiosRequest = {
mode: request?.body?.mode,
method: request.method,
url,
headers,
responseType: 'arraybuffer'
};
axiosRequest = setAuthHeaders(axiosRequest, request, collectionRoot);
if (request.script) {
axiosRequest.script = request.script;
}
axiosRequest.vars = request.vars;
axiosRequest.method = 'POST';
return axiosRequest;
};
module.exports = prepareCollectionRequest;

View File

@@ -383,7 +383,7 @@ const prepareRequest = (item, collection) => {
});
let axiosRequest = {
mode: request.body.mode,
mode: request?.body?.mode,
method: request.method,
url,
headers,
@@ -393,7 +393,7 @@ const prepareRequest = (item, collection) => {
axiosRequest = setAuthHeaders(axiosRequest, request, collectionRoot);
if (request.body.mode === 'json') {
if (request.body?.mode === 'json') {
if (!contentTypeDefined) {
axiosRequest.headers['content-type'] = 'application/json';
}
@@ -404,28 +404,28 @@ const prepareRequest = (item, collection) => {
}
}
if (request.body.mode === 'text') {
if (request.body?.mode === 'text') {
if (!contentTypeDefined) {
axiosRequest.headers['content-type'] = 'text/plain';
}
axiosRequest.data = request.body.text;
}
if (request.body.mode === 'xml') {
if (request.body?.mode === 'xml') {
if (!contentTypeDefined) {
axiosRequest.headers['content-type'] = 'text/xml';
}
axiosRequest.data = request.body.xml;
}
if (request.body.mode === 'sparql') {
if (request.body?.mode === 'sparql') {
if (!contentTypeDefined) {
axiosRequest.headers['content-type'] = 'application/sparql-query';
}
axiosRequest.data = request.body.sparql;
}
if (request.body.mode === 'formUrlEncoded') {
if (request.body?.mode === 'formUrlEncoded') {
if (!contentTypeDefined) {
axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded';
}
@@ -433,7 +433,7 @@ const prepareRequest = (item, collection) => {
axiosRequest.data = buildFormUrlEncodedPayload(enabledParams);
}
if (request.body.mode === 'multipartForm') {
if (request.body?.mode === 'multipartForm') {
axiosRequest.headers['content-type'] = 'multipart/form-data';
const params = {};
const enabledParams = filter(request.body.multipartForm, (p) => p.enabled);
@@ -441,7 +441,7 @@ const prepareRequest = (item, collection) => {
axiosRequest.data = params;
}
if (request.body.mode === 'graphql') {
if (request.body?.mode === 'graphql') {
const graphqlQuery = {
query: get(request, 'body.graphql.query'),
// https://github.com/usebruno/bruno/issues/884 - we must only parse the variables after the variable interpolation