feat: OAuth 2.0 Client Credentials as Basic Auth - request logic

This commit is contained in:
Mateusz Pietryga 2024-04-25 09:04:56 +02:00
parent df35862ee7
commit 0e4a4307ad
No known key found for this signature in database
GPG Key ID: 549A107FB8327670
4 changed files with 104 additions and 79 deletions

View File

@ -268,34 +268,36 @@ const configureRequest = async (
if (request.oauth2) {
let requestCopy = cloneDeep(request);
switch (request?.oauth2?.grantType) {
case 'authorization_code':
case 'authorization_code': {
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
const { data: authorizationCodeData, url: authorizationCodeAccessTokenUrl } =
const { accessTokenRequestHeaders, accessTokenRequestData, accessTokenRequestUrl } =
await resolveOAuth2AuthorizationCodeAccessToken(requestCopy, collectionUid);
request.method = 'POST';
request.headers['content-type'] = 'application/x-www-form-urlencoded';
request.data = authorizationCodeData;
request.url = authorizationCodeAccessTokenUrl;
request.headers = accessTokenRequestHeaders;
request.data = accessTokenRequestData;
request.url = accessTokenRequestUrl;
break;
case 'client_credentials':
}
case 'client_credentials': {
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
const { data: clientCredentialsData, url: clientCredentialsAccessTokenUrl } =
const { accessTokenRequestHeaders, accessTokenRequestData, accessTokenRequestUrl } =
await transformClientCredentialsRequest(requestCopy);
request.method = 'POST';
request.headers['content-type'] = 'application/x-www-form-urlencoded';
request.data = clientCredentialsData;
request.url = clientCredentialsAccessTokenUrl;
request.headers = accessTokenRequestHeaders;
request.data = accessTokenRequestData;
request.url = accessTokenRequestUrl;
break;
case 'password':
}
case 'password': {
interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars);
const { data: passwordData, url: passwordAccessTokenUrl } = await transformPasswordCredentialsRequest(
requestCopy
);
const { accessTokenRequestHeaders, accessTokenRequestData, accessTokenRequestUrl } =
await transformPasswordCredentialsRequest(requestCopy);
request.method = 'POST';
request.headers['content-type'] = 'application/x-www-form-urlencoded';
request.data = passwordData;
request.url = passwordAccessTokenUrl;
request.headers = accessTokenRequestHeaders;
request.data = accessTokenRequestData;
request.url = accessTokenRequestUrl;
break;
}
}
}

View File

@ -146,28 +146,15 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
}
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) || '';
@ -175,24 +162,17 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
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.state = _interpolate(request.oauth2.state) || '';
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;

View File

@ -3,6 +3,10 @@ const crypto = require('crypto');
const { authorizeUserInWindow } = require('./authorize-user-in-window');
const Oauth2Store = require('../../store/oauth2');
const encodeClientCredentials = (clientId, clientSecret) => {
return 'Basic ' + Buffer.from(clientId + ':' + clientSecret).toString('base64');
};
const generateCodeVerifier = () => {
return crypto.randomBytes(22).toString('hex');
};
@ -23,22 +27,34 @@ const resolveOAuth2AuthorizationCodeAccessToken = async (request, collectionUid)
let requestCopy = cloneDeep(request);
const { authorizationCode } = await getOAuth2AuthorizationCode(requestCopy, codeChallenge, collectionUid);
const oAuth = get(requestCopy, 'oauth2', {});
const { clientId, clientSecret, callbackUrl, scope, pkce } = oAuth;
const data = {
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: callbackUrl,
client_id: clientId,
client_secret: clientSecret
};
if (pkce) {
data['code_verifier'] = codeVerifier;
const { clientId, clientSecret, clientSecretMethod, callbackUrl, pkce } = oAuth;
const accessTokenRequestHeaders = request.headers;
accessTokenRequestHeaders['content-type'] = 'application/x-www-form-urlencoded';
if (clientSecretMethod === 'client_credentials_basic') {
accessTokenRequestHeaders['authorization'] = encodeClientCredentials(clientId, clientSecret);
}
const url = requestCopy?.oauth2?.accessTokenUrl;
const accessTokenRequestData = {
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: callbackUrl
};
if (clientSecretMethod === 'client_credentials_post') {
accessTokenRequestData['client_id'] = clientId;
accessTokenRequestData['client_secret'] = clientSecret;
}
if (pkce) {
accessTokenRequestData['code_verifier'] = codeVerifier;
}
const accessTokenRequestUrl = requestCopy?.oauth2?.accessTokenUrl;
return {
data,
url
accessTokenRequestHeaders,
accessTokenRequestData,
accessTokenRequestUrl
};
};
@ -82,19 +98,30 @@ const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => {
const transformClientCredentialsRequest = async (request) => {
let requestCopy = cloneDeep(request);
const oAuth = get(requestCopy, 'oauth2', {});
const { clientId, clientSecret, scope } = oAuth;
const data = {
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret
const { clientId, clientSecret, clientSecretMethod, scope } = oAuth;
const accessTokenRequestHeaders = request.headers;
accessTokenRequestHeaders['content-type'] = 'application/x-www-form-urlencoded';
if (clientSecretMethod === 'client_credentials_basic') {
accessTokenRequestHeaders['authorization'] = encodeClientCredentials(clientId, clientSecret);
}
const accessTokenRequestData = {
grant_type: 'client_credentials'
};
if (scope) {
data.scope = scope;
accessTokenRequestData.scope = scope;
}
const url = requestCopy?.oauth2?.accessTokenUrl;
if (clientSecretMethod === 'client_credentials_post') {
accessTokenRequestData['client_id'] = clientId;
accessTokenRequestData['client_secret'] = clientSecret;
}
const accessTokenRequestUrl = requestCopy?.oauth2?.accessTokenUrl;
return {
data,
url
accessTokenRequestHeaders,
accessTokenRequestData,
accessTokenRequestUrl
};
};
@ -103,21 +130,34 @@ const transformClientCredentialsRequest = async (request) => {
const transformPasswordCredentialsRequest = async (request) => {
let requestCopy = cloneDeep(request);
const oAuth = get(requestCopy, 'oauth2', {});
const { username, password, clientId, clientSecret, scope } = oAuth;
const data = {
const { username, password, clientId, clientSecret, clientSecretMethod, scope } = oAuth;
const accessTokenRequestHeaders = request.headers;
accessTokenRequestHeaders['content-type'] = 'application/x-www-form-urlencoded';
if (clientSecretMethod === 'client_credentials_basic') {
accessTokenRequestHeaders['authorization'] = encodeClientCredentials(clientId, clientSecret);
}
const accessTokenRequestData = {
grant_type: 'password',
username,
password,
client_id: clientId,
client_secret: clientSecret
password
};
if (scope) {
data.scope = scope;
accessTokenRequestData.scope = scope;
}
const url = requestCopy?.oauth2?.accessTokenUrl;
if (clientSecretMethod === 'client_credentials_post') {
accessTokenRequestData['client_id'] = clientId;
if(clientSecret) {
accessTokenRequestData['client_secret'] = clientSecret;
}
}
const accessTokenRequestUrl = requestCopy?.oauth2?.accessTokenUrl;
return {
data,
url
accessTokenRequestHeaders,
accessTokenRequestData,
accessTokenRequestUrl
};
};

View File

@ -286,6 +286,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;
@ -297,6 +298,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'),
state: get(request, 'auth.oauth2.state'),
pkce: get(request, 'auth.oauth2.pkce')
@ -308,6 +310,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;