Skip to content

Commit

Permalink
feat: OAuth 2.0 Client Credentials as Basic Auth - request logic
Browse files Browse the repository at this point in the history
  • Loading branch information
pietrygamat committed Apr 25, 2024
1 parent 99bc295 commit 90c916a
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 52 deletions.
36 changes: 26 additions & 10 deletions packages/bruno-electron/src/ipc/network/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,34 +204,50 @@ const configureRequest = async (
if (request.oauth2) {
let requestCopy = cloneDeep(request);
switch (request?.oauth2?.grantType) {
case 'authorization_code':
case 'authorization_code': {
interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars);
const { data: authorizationCodeData, url: authorizationCodeAccessTokenUrl } =
await resolveOAuth2AuthorizationCodeAccessToken(requestCopy, collectionUid);
const {
basicAuth,
data: authorizationCodeData,
url: authorizationCodeAccessTokenUrl
} = await resolveOAuth2AuthorizationCodeAccessToken(requestCopy, collectionUid);
request.method = 'POST';
request.headers['content-type'] = 'application/x-www-form-urlencoded';
request.headers = { ...request.headers, ...basicAuth };
request.data = authorizationCodeData;
request.url = authorizationCodeAccessTokenUrl;
break;
case 'client_credentials':
}
case 'client_credentials': {
interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars);
const { data: clientCredentialsData, url: clientCredentialsAccessTokenUrl } =
await transformClientCredentialsRequest(requestCopy);
const {
basicAuth,
data: clientCredentialsData,
url: clientCredentialsAccessTokenUrl
} = await transformClientCredentialsRequest(requestCopy);
request.method = 'POST';
request.headers['content-type'] = 'application/x-www-form-urlencoded';
request.headers = { ...request.headers, ...basicAuth };
request.data = clientCredentialsData;
request.url = clientCredentialsAccessTokenUrl;
console.log(request);
break;
case 'password':
}
case 'password': {
interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars);
const { data: passwordData, url: passwordAccessTokenUrl } = await transformPasswordCredentialsRequest(
requestCopy
);
const {
basicAuth,
data: passwordData,
url: passwordAccessTokenUrl
} = await transformPasswordCredentialsRequest(requestCopy);
request.method = 'POST';
request.headers['content-type'] = 'application/x-www-form-urlencoded';
request.headers = { ...request.headers, ...basicAuth };
request.data = passwordData;
request.url = passwordAccessTokenUrl;
console.log(request);
break;
}
}
}

Expand Down
42 changes: 11 additions & 31 deletions packages/bruno-electron/src/ipc/network/interpolate-vars.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,52 +109,32 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces
}

if (request?.oauth2?.grantType) {
let username, password, scope, clientId, clientSecret;
switch (request.oauth2.grantType) {
case 'password':
username = _interpolate(request.oauth2.username) || '';
password = _interpolate(request.oauth2.password) || '';
clientId = _interpolate(request.oauth2.clientId) || '';
clientSecret = _interpolate(request.oauth2.clientSecret) || '';
scope = _interpolate(request.oauth2.scope) || '';
request.oauth2.accessTokenUrl = _interpolate(request.oauth2.accessTokenUrl) || '';
request.oauth2.username = username;
request.oauth2.password = password;
request.oauth2.clientId = clientId;
request.oauth2.clientSecret = clientSecret;
request.oauth2.scope = scope;
request.data = {
grant_type: 'password',
username,
password,
client_id: clientId,
client_secret: clientSecret,
scope
};
request.oauth2.username = _interpolate(request.oauth2.username) || '';
request.oauth2.password = _interpolate(request.oauth2.password) || '';
request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
request.oauth2.clientSecretMethod = _interpolate(request.oauth2.clientSecretMethod) || '';
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
break;
case 'authorization_code':
request.oauth2.callbackUrl = _interpolate(request.oauth2.callbackUrl) || '';
request.oauth2.authorizationUrl = _interpolate(request.oauth2.authorizationUrl) || '';
request.oauth2.accessTokenUrl = _interpolate(request.oauth2.accessTokenUrl) || '';
request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
request.oauth2.clientSecretMethod = _interpolate(request.oauth2.clientSecretMethod) || '';
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
request.oauth2.pkce = _interpolate(request.oauth2.pkce) || false;
break;
case 'client_credentials':
clientId = _interpolate(request.oauth2.clientId) || '';
clientSecret = _interpolate(request.oauth2.clientSecret) || '';
scope = _interpolate(request.oauth2.scope) || '';
request.oauth2.accessTokenUrl = _interpolate(request.oauth2.accessTokenUrl) || '';
request.oauth2.clientId = clientId;
request.oauth2.clientSecret = clientSecret;
request.oauth2.scope = scope;
request.data = {
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
scope
};
request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
request.oauth2.clientSecretMethod = _interpolate(request.oauth2.clientSecretMethod) || '';
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
break;
default:
break;
Expand Down
49 changes: 38 additions & 11 deletions packages/bruno-electron/src/ipc/network/oauth2-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@ const crypto = require('crypto');
const { authorizeUserInWindow } = require('./authorize-user-in-window');
const Oauth2Store = require('../../store/oauth2');

const clientCredentials = (clientId, clientSecret, clientSecretMethod = 'client_credentials_basic') => {
let credentialsInBody;
let credentialsInHeader;
if (clientSecret) {
switch (clientSecretMethod) {
case 'client_credentials_post': {
credentialsInBody = {
client_secret: clientSecret,
client_id: clientId
};
break;
}
case 'client_credentials_basic': {
const credentials = 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64');
credentialsInHeader = { Authorization: credentials };
break;
}
}
}
return { credentialsInHeader, credentialsInBody };
};

const generateCodeVerifier = () => {
return crypto.randomBytes(22).toString('hex');
};
Expand All @@ -24,20 +46,21 @@ const resolveOAuth2AuthorizationCodeAccessToken = async (request, collectionUid)
const { authorizationCode } = await getOAuth2AuthorizationCode(requestCopy, codeChallenge, collectionUid);
const oAuth = get(requestCopy, 'oauth2', {});
const { clientId, clientSecret, callbackUrl, scope, pkce } = oAuth;
const { credentialsInBody, credentialsInHeader } = clientCredentials(clientId, clientSecret);
const data = {
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: callbackUrl,
client_id: clientId,
client_secret: clientSecret,
scope: scope
scope: scope,
...credentialsInBody
};
if (pkce) {
data['code_verifier'] = codeVerifier;
}

const url = requestCopy?.oauth2?.accessTokenUrl;
return {
basicAuth: credentialsInHeader,
data,
url
};
Expand Down Expand Up @@ -80,14 +103,16 @@ const transformClientCredentialsRequest = async (request) => {
let requestCopy = cloneDeep(request);
const oAuth = get(requestCopy, 'oauth2', {});
const { clientId, clientSecret, scope } = oAuth;
const data = {
const { credentialsInBody, credentialsInHeader } = clientCredentials(clientId, clientSecret);
let data = {
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
scope
scope,
...credentialsInBody
};

const url = requestCopy?.oauth2?.accessTokenUrl;
return {
basicAuth: credentialsInHeader,
data,
url
};
Expand All @@ -99,16 +124,18 @@ const transformPasswordCredentialsRequest = async (request) => {
let requestCopy = cloneDeep(request);
const oAuth = get(requestCopy, 'oauth2', {});
const { username, password, clientId, clientSecret, scope } = oAuth;
const data = {
const { credentialsInBody, credentialsInHeader } = clientCredentials(clientId, clientSecret);
let data = {
grant_type: 'password',
username,
password,
client_id: clientId,
client_secret: clientSecret,
scope
scope,
...credentialsInBody
};

const url = requestCopy?.oauth2?.accessTokenUrl;
return {
basicAuth: credentialsInHeader,
data,
url
};
Expand Down
3 changes: 3 additions & 0 deletions packages/bruno-electron/src/ipc/network/prepare-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
password: get(request, 'auth.oauth2.password'),
clientId: get(request, 'auth.oauth2.clientId'),
clientSecret: get(request, 'auth.oauth2.clientSecret'),
clientSecretMethod: get(request, 'auth.oauth2.clientSecretMethod'),
scope: get(request, 'auth.oauth2.scope')
};
break;
Expand All @@ -122,6 +123,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
clientId: get(request, 'auth.oauth2.clientId'),
clientSecret: get(request, 'auth.oauth2.clientSecret'),
clientSecretMethod: get(request, 'auth.oauth2.clientSecretMethod'),
scope: get(request, 'auth.oauth2.scope'),
pkce: get(request, 'auth.oauth2.pkce')
};
Expand All @@ -132,6 +134,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
clientId: get(request, 'auth.oauth2.clientId'),
clientSecret: get(request, 'auth.oauth2.clientSecret'),
clientSecretMethod: get(request, 'auth.oauth2.clientSecretMethod'),
scope: get(request, 'auth.oauth2.scope')
};
break;
Expand Down

0 comments on commit 90c916a

Please sign in to comment.