From 32b1ba1c92d74ef867791a1324cf2aed069c76c7 Mon Sep 17 00:00:00 2001 From: Dhananjay Kadam Date: Fri, 31 May 2024 15:35:27 +0530 Subject: [PATCH] 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 --- .../Auth/OAuth2/AuthorizationCode/index.js | 4 +++- .../Auth/OAuth2/AuthorizationCode/inputsConfig.js | 4 ++++ .../Auth/OAuth2/AuthorizationCode/index.js | 4 +++- .../Auth/OAuth2/AuthorizationCode/inputsConfig.js | 4 ++++ .../src/ipc/network/interpolate-vars.js | 1 + .../bruno-electron/src/ipc/network/oauth2-helper.js | 11 ++++++++--- .../bruno-electron/src/ipc/network/prepare-request.js | 1 + packages/bruno-lang/v2/src/bruToJson.js | 2 ++ packages/bruno-lang/v2/src/collectionBruToJson.js | 2 ++ packages/bruno-lang/v2/src/jsonToBru.js | 1 + packages/bruno-lang/v2/src/jsonToCollectionBru.js | 1 + packages/bruno-lang/v2/tests/fixtures/request.bru | 1 + packages/bruno-lang/v2/tests/fixtures/request.json | 1 + packages/bruno-schema/src/collections/index.js | 5 +++++ 14 files changed, 37 insertions(+), 5 deletions(-) diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/AuthorizationCode/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/AuthorizationCode/index.js index 674db53a8..8ec71a69a 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/AuthorizationCode/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/AuthorizationCode/index.js @@ -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']) } }) diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/AuthorizationCode/inputsConfig.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/AuthorizationCode/inputsConfig.js index f7cc7801a..67bc277aa 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/AuthorizationCode/inputsConfig.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/AuthorizationCode/inputsConfig.js @@ -22,6 +22,10 @@ const inputsConfig = [ { key: 'scope', label: 'Scope' + }, + { + key: 'state', + label: 'State' } ]; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js index 08a77555c..3c813b14b 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/index.js @@ -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']) } diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/inputsConfig.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/inputsConfig.js index f7cc7801a..67bc277aa 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/inputsConfig.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/AuthorizationCode/inputsConfig.js @@ -22,6 +22,10 @@ const inputsConfig = [ { key: 'scope', label: 'Scope' + }, + { + key: 'state', + label: 'State' } ]; diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index b544ed7d1..f8c02c4b7 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -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': diff --git a/packages/bruno-electron/src/ipc/network/oauth2-helper.js b/packages/bruno-electron/src/ipc/network/oauth2-helper.js index e254e8c74..a7921e85a 100644 --- a/packages/bruno-electron/src/ipc/network/oauth2-helper.js +++ b/packages/bruno-electron/src/ipc/network/oauth2-helper.js @@ -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(); diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index 94fe834b1..ed13eeb54 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -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; diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index e78b1b8ad..08e4332c5 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -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' diff --git a/packages/bruno-lang/v2/src/collectionBruToJson.js b/packages/bruno-lang/v2/src/collectionBruToJson.js index 355f2f966..3c02a6225 100644 --- a/packages/bruno-lang/v2/src/collectionBruToJson.js +++ b/packages/bruno-lang/v2/src/collectionBruToJson.js @@ -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' diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js index 45d7bba0f..e93c4915c 100644 --- a/packages/bruno-lang/v2/src/jsonToBru.js +++ b/packages/bruno-lang/v2/src/jsonToBru.js @@ -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()}`)} } diff --git a/packages/bruno-lang/v2/src/jsonToCollectionBru.js b/packages/bruno-lang/v2/src/jsonToCollectionBru.js index e4d6ab5fd..11df88da4 100644 --- a/packages/bruno-lang/v2/src/jsonToCollectionBru.js +++ b/packages/bruno-lang/v2/src/jsonToCollectionBru.js @@ -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()}`)} } diff --git a/packages/bruno-lang/v2/tests/fixtures/request.bru b/packages/bruno-lang/v2/tests/fixtures/request.bru index fcfe7b818..e333df0ec 100644 --- a/packages/bruno-lang/v2/tests/fixtures/request.bru +++ b/packages/bruno-lang/v2/tests/fixtures/request.bru @@ -53,6 +53,7 @@ auth:oauth2 { client_id: client_id_1 client_secret: client_secret_1 scope: read write + state: 807061d5f0be pkce: false } diff --git a/packages/bruno-lang/v2/tests/fixtures/request.json b/packages/bruno-lang/v2/tests/fixtures/request.json index afb7ca3f9..a69ff1813 100644 --- a/packages/bruno-lang/v2/tests/fixtures/request.json +++ b/packages/bruno-lang/v2/tests/fixtures/request.json @@ -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 } }, diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js index 3dc1352c8..cc93ed671 100644 --- a/packages/bruno-schema/src/collections/index.js +++ b/packages/bruno-schema/src/collections/index.js @@ -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),