feat: add support for state param for OAuth2 Authorization Code flow (#2330)

* feat: add support for state param

* chore: revert package-lock.json

* test: update tests with state param

* chore: revert package-lock.json

* chore: add state to missing places

* Adding state to PKCE toggle

---------

Co-authored-by: Kadam Dhananjay <kadamdhananjay@johndeere.com>
This commit is contained in:
Dhananjay Kadam 2024-05-31 15:35:27 +05:30 committed by GitHub
parent 3ded960938
commit 32b1ba1c92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 37 additions and 5 deletions

View File

@ -22,7 +22,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, scope, pkce } = oAuth;
const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, scope, state, pkce } = oAuth;
const handleChange = (key, value) => {
dispatch(
@ -37,6 +37,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
clientId,
clientSecret,
scope,
state,
pkce,
[key]: value
}
@ -57,6 +58,7 @@ const OAuth2AuthorizationCode = ({ collection }) => {
clientId,
clientSecret,
scope,
state,
pkce: !Boolean(oAuth?.['pkce'])
}
})

View File

@ -22,6 +22,10 @@ const inputsConfig = [
{
key: 'scope',
label: 'Scope'
},
{
key: 'state',
label: 'State'
}
];

View File

@ -22,7 +22,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, scope, pkce } = oAuth;
const { callbackUrl, authorizationUrl, accessTokenUrl, clientId, clientSecret, scope, state, pkce } = oAuth;
const handleChange = (key, value) => {
dispatch(
@ -37,6 +37,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
accessTokenUrl,
clientId,
clientSecret,
state,
scope,
pkce,
[key]: value
@ -58,6 +59,7 @@ const OAuth2AuthorizationCode = ({ item, collection }) => {
accessTokenUrl,
clientId,
clientSecret,
state,
scope,
pkce: !Boolean(oAuth?.['pkce'])
}

View File

@ -22,6 +22,10 @@ const inputsConfig = [
{
key: 'scope',
label: 'Scope'
},
{
key: 'state',
label: 'State'
}
];

View File

@ -169,6 +169,7 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces
request.oauth2.clientId = _interpolate(request.oauth2.clientId) || '';
request.oauth2.clientSecret = _interpolate(request.oauth2.clientSecret) || '';
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':

View File

@ -23,14 +23,15 @@ 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 { clientId, clientSecret, callbackUrl, scope, state, pkce } = oAuth;
const data = {
grant_type: 'authorization_code',
code: authorizationCode,
redirect_uri: callbackUrl,
client_id: clientId,
client_secret: clientSecret,
scope: scope
scope: scope,
state: state
};
if (pkce) {
data['code_verifier'] = codeVerifier;
@ -46,7 +47,7 @@ const resolveOAuth2AuthorizationCodeAccessToken = async (request, collectionUid)
const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => {
return new Promise(async (resolve, reject) => {
const { oauth2 } = request;
const { callbackUrl, clientId, authorizationUrl, scope, pkce } = oauth2;
const { callbackUrl, clientId, authorizationUrl, scope, state, pkce } = oauth2;
let oauth2QueryParams =
(authorizationUrl.indexOf('?') > -1 ? '&' : '?') + `client_id=${clientId}&response_type=code`;
@ -59,6 +60,10 @@ const getOAuth2AuthorizationCode = (request, codeChallenge, collectionUid) => {
if (pkce) {
oauth2QueryParams += `&code_challenge=${codeChallenge}&code_challenge_method=S256`;
}
if (state) {
oauth2QueryParams += `&state=${state}`;
}
const authorizationUrlWithQueryParams = authorizationUrl + oauth2QueryParams;
try {
const oauth2Store = new Oauth2Store();

View File

@ -112,6 +112,7 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
clientId: get(request, 'auth.oauth2.clientId'),
clientSecret: get(request, 'auth.oauth2.clientSecret'),
scope: get(request, 'auth.oauth2.scope'),
state: get(request, 'auth.oauth2.state'),
pkce: get(request, 'auth.oauth2.pkce')
};
break;

View File

@ -444,6 +444,7 @@ const sem = grammar.createSemantics().addAttribute('ast', {
const clientIdKey = _.find(auth, { name: 'client_id' });
const clientSecretKey = _.find(auth, { name: 'client_secret' });
const scopeKey = _.find(auth, { name: 'scope' });
const stateKey = _.find(auth, { name: 'state' });
const pkceKey = _.find(auth, { name: 'pkce' });
return {
auth: {
@ -467,6 +468,7 @@ const sem = grammar.createSemantics().addAttribute('ast', {
clientId: clientIdKey ? clientIdKey.value : '',
clientSecret: clientSecretKey ? clientSecretKey.value : '',
scope: scopeKey ? scopeKey.value : '',
state: stateKey ? stateKey.value : '',
pkce: pkceKey ? JSON.parse(pkceKey?.value || false) : false
}
: grantTypeKey?.value && grantTypeKey?.value == 'client_credentials'

View File

@ -254,6 +254,7 @@ const sem = grammar.createSemantics().addAttribute('ast', {
const clientIdKey = _.find(auth, { name: 'client_id' });
const clientSecretKey = _.find(auth, { name: 'client_secret' });
const scopeKey = _.find(auth, { name: 'scope' });
const stateKey = _.find(auth, { name: 'state' });
const pkceKey = _.find(auth, { name: 'pkce' });
return {
auth: {
@ -277,6 +278,7 @@ const sem = grammar.createSemantics().addAttribute('ast', {
clientId: clientIdKey ? clientIdKey.value : '',
clientSecret: clientSecretKey ? clientSecretKey.value : '',
scope: scopeKey ? scopeKey.value : '',
state: stateKey ? stateKey.value : '',
pkce: pkceKey ? JSON.parse(pkceKey?.value || false) : false
}
: grantTypeKey?.value && grantTypeKey?.value == 'client_credentials'

View File

@ -180,6 +180,7 @@ ${indentString(`access_token_url: ${auth?.oauth2?.accessTokenUrl || ''}`)}
${indentString(`client_id: ${auth?.oauth2?.clientId || ''}`)}
${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)}
${indentString(`scope: ${auth?.oauth2?.scope || ''}`)}
${indentString(`state: ${auth?.oauth2?.state || ''}`)}
${indentString(`pkce: ${(auth?.oauth2?.pkce || false).toString()}`)}
}

View File

@ -138,6 +138,7 @@ ${indentString(`access_token_url: ${auth?.oauth2?.accessTokenUrl || ''}`)}
${indentString(`client_id: ${auth?.oauth2?.clientId || ''}`)}
${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)}
${indentString(`scope: ${auth?.oauth2?.scope || ''}`)}
${indentString(`state: ${auth?.oauth2?.state || ''}`)}
${indentString(`pkce: ${(auth?.oauth2?.pkce || false).toString()}`)}
}

View File

@ -53,6 +53,7 @@ auth:oauth2 {
client_id: client_id_1
client_secret: client_secret_1
scope: read write
state: 807061d5f0be
pkce: false
}

View File

@ -72,6 +72,7 @@
"callbackUrl": "http://localhost:8080/api/auth/oauth2/authorization_code/callback",
"accessTokenUrl": "http://localhost:8080/api/auth/oauth2/authorization_code/token",
"scope": "read write",
"state": "807061d5f0be",
"pkce": false
}
},

View File

@ -163,6 +163,11 @@ const oauth2Schema = Yup.object({
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
state: Yup.string().when('grantType', {
is: (val) => ['authorization_code'].includes(val),
then: Yup.string().nullable(),
otherwise: Yup.string().nullable().strip()
}),
pkce: Yup.boolean().when('grantType', {
is: (val) => ['authorization_code'].includes(val),
then: Yup.boolean().default(false),