Skip to content

Commit d4e91a8

Browse files
committed
feat: OAuth 2.0 Client Credentials as Basic Auth - request logic
#2106
1 parent 8e15b6a commit d4e91a8

File tree

4 files changed

+79
-55
lines changed

4 files changed

+79
-55
lines changed

packages/bruno-electron/src/ipc/network/index.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -205,34 +205,48 @@ const configureRequest = async (
205205
if (request.oauth2) {
206206
let requestCopy = cloneDeep(request);
207207
switch (request?.oauth2?.grantType) {
208-
case 'authorization_code':
208+
case 'authorization_code': {
209209
interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars);
210-
const { data: authorizationCodeData, url: authorizationCodeAccessTokenUrl } =
211-
await resolveOAuth2AuthorizationCodeAccessToken(requestCopy, collectionUid);
210+
const {
211+
headers: optionalBasicAuthHeader,
212+
data: authorizationCodeData,
213+
url: authorizationCodeAccessTokenUrl
214+
} = await resolveOAuth2AuthorizationCodeAccessToken(requestCopy, collectionUid);
212215
request.method = 'POST';
213216
request.headers['content-type'] = 'application/x-www-form-urlencoded';
217+
request.headers = { ...request.headers, ...optionalBasicAuthHeader };
214218
request.data = authorizationCodeData;
215219
request.url = authorizationCodeAccessTokenUrl;
216220
break;
217-
case 'client_credentials':
221+
}
222+
case 'client_credentials': {
218223
interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars);
219-
const { data: clientCredentialsData, url: clientCredentialsAccessTokenUrl } =
220-
await transformClientCredentialsRequest(requestCopy);
224+
const {
225+
headers: optionalBasicAuthHeader,
226+
data: clientCredentialsData,
227+
url: clientCredentialsAccessTokenUrl
228+
} = await transformClientCredentialsRequest(requestCopy);
221229
request.method = 'POST';
222230
request.headers['content-type'] = 'application/x-www-form-urlencoded';
231+
request.headers = { ...request.headers, ...optionalBasicAuthHeader };
223232
request.data = clientCredentialsData;
224233
request.url = clientCredentialsAccessTokenUrl;
225234
break;
226-
case 'password':
235+
}
236+
case 'password': {
227237
interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars);
228-
const { data: passwordData, url: passwordAccessTokenUrl } = await transformPasswordCredentialsRequest(
229-
requestCopy
230-
);
238+
const {
239+
headers: optionalBasicAuthHeader,
240+
data: passwordData,
241+
url: passwordAccessTokenUrl
242+
} = await transformPasswordCredentialsRequest(requestCopy);
231243
request.method = 'POST';
232244
request.headers['content-type'] = 'application/x-www-form-urlencoded';
245+
request.headers = { ...request.headers, ...optionalBasicAuthHeader };
233246
request.data = passwordData;
234247
request.url = passwordAccessTokenUrl;
235248
break;
249+
}
236250
}
237251
}
238252

packages/bruno-electron/src/ipc/network/interpolate-vars.js

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -139,53 +139,33 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces
139139
}
140140

141141
if (request?.oauth2?.grantType) {
142-
let username, password, scope, clientId, clientSecret;
143142
switch (request.oauth2.grantType) {
144143
case 'password':
145-
username = _interpolate(request.oauth2.username) || '';
146-
password = _interpolate(request.oauth2.password) || '';
147-
clientId = _interpolate(request.oauth2.clientId) || '';
148-
clientSecret = _interpolate(request.oauth2.clientSecret) || '';
149-
scope = _interpolate(request.oauth2.scope) || '';
150144
request.oauth2.accessTokenUrl = _interpolate(request.oauth2.accessTokenUrl) || '';
151-
request.oauth2.username = username;
152-
request.oauth2.password = password;
153-
request.oauth2.clientId = clientId;
154-
request.oauth2.clientSecret = clientSecret;
155-
request.oauth2.scope = scope;
156-
request.data = {
157-
grant_type: 'password',
158-
username,
159-
password,
160-
client_id: clientId,
161-
client_secret: clientSecret,
162-
scope
163-
};
145+
request.oauth2.username = _interpolate(request.oauth2.username) || '';
146+
request.oauth2.password = _interpolate(request.oauth2.password) || '';
147+
request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
148+
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
149+
request.oauth2.clientSecretMethod = _interpolate(request.oauth2.clientSecretMethod) || '';
150+
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
164151
break;
165152
case 'authorization_code':
166153
request.oauth2.callbackUrl = _interpolate(request.oauth2.callbackUrl) || '';
167154
request.oauth2.authorizationUrl = _interpolate(request.oauth2.authorizationUrl) || '';
168155
request.oauth2.accessTokenUrl = _interpolate(request.oauth2.accessTokenUrl) || '';
169156
request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
170157
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
158+
request.oauth2.clientSecretMethod = _interpolate(request.oauth2.clientSecretMethod) || '';
171159
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
172160
request.oauth2.state = _interpolate(request.oauth2.state) || '';
173161
request.oauth2.pkce = _interpolate(request.oauth2.pkce) || false;
174162
break;
175163
case 'client_credentials':
176-
clientId = _interpolate(request.oauth2.clientId) || '';
177-
clientSecret = _interpolate(request.oauth2.clientSecret) || '';
178-
scope = _interpolate(request.oauth2.scope) || '';
179164
request.oauth2.accessTokenUrl = _interpolate(request.oauth2.accessTokenUrl) || '';
180-
request.oauth2.clientId = clientId;
181-
request.oauth2.clientSecret = clientSecret;
182-
request.oauth2.scope = scope;
183-
request.data = {
184-
grant_type: 'client_credentials',
185-
client_id: clientId,
186-
client_secret: clientSecret,
187-
scope
188-
};
165+
request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
166+
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
167+
request.oauth2.clientSecretMethod = _interpolate(request.oauth2.clientSecretMethod) || '';
168+
request.oauth2.scope = _interpolate(request.oauth2.scope) || '';
189169
break;
190170
default:
191171
break;

packages/bruno-electron/src/ipc/network/oauth2-helper.js

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,28 @@ const crypto = require('crypto');
33
const { authorizeUserInWindow } = require('./authorize-user-in-window');
44
const Oauth2Store = require('../../store/oauth2');
55

6+
const clientCredentials = (clientId, clientSecret, clientSecretMethod) => {
7+
let credentialsInBody;
8+
let credentialsInHeader;
9+
if (clientSecret) {
10+
switch (clientSecretMethod) {
11+
case 'client_credentials_post': {
12+
credentialsInBody = {
13+
client_secret: clientSecret,
14+
client_id: clientId
15+
};
16+
break;
17+
}
18+
case 'client_credentials_basic': {
19+
const credentials = 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64');
20+
credentialsInHeader = { Authorization: credentials };
21+
break;
22+
}
23+
}
24+
}
25+
return { credentialsInHeader, credentialsInBody };
26+
};
27+
628
const generateCodeVerifier = () => {
729
return crypto.randomBytes(22).toString('hex');
830
};
@@ -23,22 +45,23 @@ const resolveOAuth2AuthorizationCodeAccessToken = async (request, collectionUid)
2345
let requestCopy = cloneDeep(request);
2446
const { authorizationCode } = await getOAuth2AuthorizationCode(requestCopy, codeChallenge, collectionUid);
2547
const oAuth = get(requestCopy, 'oauth2', {});
26-
const { clientId, clientSecret, callbackUrl, scope, state, pkce } = oAuth;
48+
const { clientId, clientSecret, clientSecretMethod, callbackUrl, scope, state, pkce } = oAuth;
49+
const { optionalCredentialsInBody, optionalCredentialsInHeader } = clientCredentials(clientId, clientSecret, clientSecretMethod);
2750
const data = {
2851
grant_type: 'authorization_code',
2952
code: authorizationCode,
3053
redirect_uri: callbackUrl,
31-
client_id: clientId,
32-
client_secret: clientSecret,
3354
scope: scope,
34-
state: state
55+
state: state,
56+
...optionalCredentialsInBody
3557
};
3658
if (pkce) {
3759
data['code_verifier'] = codeVerifier;
3860
}
3961

4062
const url = requestCopy?.oauth2?.accessTokenUrl;
4163
return {
64+
headers: optionalCredentialsInHeader,
4265
data,
4366
url
4467
};
@@ -84,15 +107,17 @@ const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => {
84107
const transformClientCredentialsRequest = async (request) => {
85108
let requestCopy = cloneDeep(request);
86109
const oAuth = get(requestCopy, 'oauth2', {});
87-
const { clientId, clientSecret, scope } = oAuth;
88-
const data = {
110+
const { clientId, clientSecret, clientSecretMethod, scope } = oAuth;
111+
const { optionalCredentialsInBody, optionalCredentialsInHeader } = clientCredentials(clientId, clientSecret, clientSecretMethod);
112+
let data = {
89113
grant_type: 'client_credentials',
90-
client_id: clientId,
91-
client_secret: clientSecret,
92-
scope
114+
scope,
115+
...optionalCredentialsInBody
93116
};
117+
94118
const url = requestCopy?.oauth2?.accessTokenUrl;
95119
return {
120+
headers: optionalCredentialsInHeader,
96121
data,
97122
url
98123
};
@@ -103,17 +128,19 @@ const transformClientCredentialsRequest = async (request) => {
103128
const transformPasswordCredentialsRequest = async (request) => {
104129
let requestCopy = cloneDeep(request);
105130
const oAuth = get(requestCopy, 'oauth2', {});
106-
const { username, password, clientId, clientSecret, scope } = oAuth;
107-
const data = {
131+
const { username, password, clientId, clientSecret, clientSecretMethod, scope } = oAuth;
132+
const { optionalCredentialsInBody, optionalCredentialsInHeader } = clientCredentials(clientId, clientSecret, clientSecretMethod);
133+
let data = {
108134
grant_type: 'password',
109135
username,
110136
password,
111-
client_id: clientId,
112-
client_secret: clientSecret,
113-
scope
137+
scope,
138+
...optionalCredentialsInBody
114139
};
140+
115141
const url = requestCopy?.oauth2?.accessTokenUrl;
116142
return {
143+
headers: optionalCredentialsInHeader,
117144
data,
118145
url
119146
};

packages/bruno-electron/src/ipc/network/prepare-request.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
100100
password: get(request, 'auth.oauth2.password'),
101101
clientId: get(request, 'auth.oauth2.clientId'),
102102
clientSecret: get(request, 'auth.oauth2.clientSecret'),
103+
clientSecretMethod: get(request, 'auth.oauth2.clientSecretMethod'),
103104
scope: get(request, 'auth.oauth2.scope')
104105
};
105106
break;
@@ -111,6 +112,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
111112
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
112113
clientId: get(request, 'auth.oauth2.clientId'),
113114
clientSecret: get(request, 'auth.oauth2.clientSecret'),
115+
clientSecretMethod: get(request, 'auth.oauth2.clientSecretMethod'),
114116
scope: get(request, 'auth.oauth2.scope'),
115117
state: get(request, 'auth.oauth2.state'),
116118
pkce: get(request, 'auth.oauth2.pkce')
@@ -122,6 +124,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
122124
accessTokenUrl: get(request, 'auth.oauth2.accessTokenUrl'),
123125
clientId: get(request, 'auth.oauth2.clientId'),
124126
clientSecret: get(request, 'auth.oauth2.clientSecret'),
127+
clientSecretMethod: get(request, 'auth.oauth2.clientSecretMethod'),
125128
scope: get(request, 'auth.oauth2.scope')
126129
};
127130
break;

0 commit comments

Comments
 (0)