From ec20c6604b4eaba2f6abad3ad7fa6a5074d4eeed Mon Sep 17 00:00:00 2001 From: Mateusz Pietryga Date: Tue, 26 Mar 2024 16:23:13 +0100 Subject: [PATCH] OAuth2: Implement OAuth 2.0 Implicit Grant #2056 --- .../Auth/OAuth2/GrantTypeSelector/index.js | 9 ++ .../Auth/OAuth2/Implicit/StyledWrapper.js | 16 +++ .../Auth/OAuth2/Implicit/index.js | 86 +++++++++++++ .../Auth/OAuth2/Implicit/inputsConfig.js | 20 +++ .../CollectionSettings/Auth/OAuth2/index.js | 4 + .../Auth/OAuth2/GrantTypeSelector/index.js | 9 ++ .../Auth/OAuth2/Implicit/StyledWrapper.js | 16 +++ .../RequestPane/Auth/OAuth2/Implicit/index.js | 87 +++++++++++++ .../Auth/OAuth2/Implicit/inputsConfig.js | 20 +++ .../RequestPane/Auth/OAuth2/index.js | 4 + .../bruno-app/src/utils/collections/index.js | 4 + .../ipc/network/authorize-user-in-window.js | 119 ++++++++++++++---- .../bruno-electron/src/ipc/network/index.js | 8 +- .../src/ipc/network/interpolate-vars.js | 6 + .../src/ipc/network/oauth2-helper.js | 33 ++++- .../src/ipc/network/prepare-request.js | 9 ++ packages/bruno-lang/v2/src/bruToJson.js | 8 ++ .../bruno-lang/v2/src/collectionBruToJson.js | 8 ++ packages/bruno-lang/v2/src/jsonToBru.js | 11 ++ .../bruno-lang/v2/src/jsonToCollectionBru.js | 11 ++ .../bruno-schema/src/collections/index.js | 10 +- 21 files changed, 466 insertions(+), 32 deletions(-) create mode 100644 packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/index.js create mode 100644 packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/inputsConfig.js create mode 100644 packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/StyledWrapper.js create mode 100644 packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/index.js create mode 100644 packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/inputsConfig.js diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/GrantTypeSelector/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/GrantTypeSelector/index.js index 5d92893820..4cd8519b00 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/GrantTypeSelector/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/GrantTypeSelector/index.js @@ -90,6 +90,15 @@ const GrantTypeSelector = ({ collection }) => { > Client Credentials +
{ + dropdownTippyRef.current.hide(); + onGrantTypeChange('implicit'); + }} + > + Implicit +
diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/StyledWrapper.js new file mode 100644 index 0000000000..856f35b9b9 --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/StyledWrapper.js @@ -0,0 +1,16 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + label { + font-size: 0.8125rem; + } + .single-line-editor-wrapper { + max-width: 400px; + padding: 0.15rem 0.4rem; + border-radius: 3px; + border: solid 1px ${(props) => props.theme.input.border}; + background-color: ${(props) => props.theme.input.bg}; + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/index.js new file mode 100644 index 0000000000..1dee00f16a --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/index.js @@ -0,0 +1,86 @@ +import React from 'react'; +import get from 'lodash/get'; +import { useTheme } from 'providers/Theme'; +import { useDispatch } from 'react-redux'; +import SingleLineEditor from 'components/SingleLineEditor'; +import { saveCollectionRoot, sendCollectionOauth2Request } from 'providers/ReduxStore/slices/collections/actions'; +import StyledWrapper from './StyledWrapper'; +import { inputsConfig } from './inputsConfig'; +import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections/index'; +import { clearOauth2Cache } from 'utils/network'; +import toast from 'react-hot-toast'; + +const OAuth2Implicit = ({ item, collection }) => { + const dispatch = useDispatch(); + const { storedTheme } = useTheme(); + + const oAuth = get(collection, 'root.request.auth.oauth2', {}); + + const handleRun = async () => { + dispatch(sendCollectionOauth2Request(collection.uid)); + }; + + const handleSave = () => dispatch(saveCollectionRoot(collection.uid)); + + const { callbackUrl, authorizationUrl, clientId, scope } = oAuth; + + const handleChange = (key, value) => { + dispatch( + updateCollectionAuth({ + mode: 'oauth2', + collectionUid: collection.uid, + content: { + grantType: 'implicit', + callbackUrl, + authorizationUrl, + clientId, + scope, + [key]: value + } + }) + ); + }; + + const handleClearCache = (e) => { + clearOauth2Cache(collection?.uid) + .then(() => { + toast.success('cleared cache successfully'); + }) + .catch((err) => { + toast.error(err.message); + }); + }; + + return ( + + {inputsConfig.map((input) => { + const { key, label } = input; + return ( +
+ +
+ handleChange(key, val)} + onRun={handleRun} + collection={collection} + /> +
+
+ ); + })} +
+ + +
+
+ ); +}; + +export default OAuth2Implicit; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/inputsConfig.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/inputsConfig.js new file mode 100644 index 0000000000..87e1fd42d5 --- /dev/null +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/Implicit/inputsConfig.js @@ -0,0 +1,20 @@ +const inputsConfig = [ + { + key: 'callbackUrl', + label: 'Callback URL' + }, + { + key: 'authorizationUrl', + label: 'Authorization URL' + }, + { + key: 'clientId', + label: 'Client ID' + }, + { + key: 'scope', + label: 'Scope' + } +]; + +export { inputsConfig }; diff --git a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js index 1aa674ab95..2075aa8ca9 100644 --- a/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js +++ b/packages/bruno-app/src/components/CollectionSettings/Auth/OAuth2/index.js @@ -5,6 +5,7 @@ import GrantTypeSelector from './GrantTypeSelector/index'; import OAuth2PasswordCredentials from './PasswordCredentials/index'; import OAuth2AuthorizationCode from './AuthorizationCode/index'; import OAuth2ClientCredentials from './ClientCredentials/index'; +import OAuth2Implicit from './Implicit/index'; const grantTypeComponentMap = (grantType, collection) => { switch (grantType) { @@ -17,6 +18,9 @@ const grantTypeComponentMap = (grantType, collection) => { case 'client_credentials': return ; break; + case 'implicit': + return ; + break; default: return
TBD
; break; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js index 3fa12b9474..52425fc2ce 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/GrantTypeSelector/index.js @@ -84,6 +84,15 @@ const GrantTypeSelector = ({ item, collection }) => { > Client Credentials +
{ + dropdownTippyRef.current.hide(); + onGrantTypeChange('implicit'); + }} + > + Implicit +
diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/StyledWrapper.js new file mode 100644 index 0000000000..856f35b9b9 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/StyledWrapper.js @@ -0,0 +1,16 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + label { + font-size: 0.8125rem; + } + .single-line-editor-wrapper { + max-width: 400px; + padding: 0.15rem 0.4rem; + border-radius: 3px; + border: solid 1px ${(props) => props.theme.input.border}; + background-color: ${(props) => props.theme.input.bg}; + } +`; + +export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/index.js new file mode 100644 index 0000000000..690b821977 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/index.js @@ -0,0 +1,87 @@ +import React from 'react'; +import get from 'lodash/get'; +import { useTheme } from 'providers/Theme'; +import { useDispatch } from 'react-redux'; +import SingleLineEditor from 'components/SingleLineEditor'; +import { updateAuth } from 'providers/ReduxStore/slices/collections'; +import { saveRequest, sendRequest } from 'providers/ReduxStore/slices/collections/actions'; +import StyledWrapper from './StyledWrapper'; +import { inputsConfig } from './inputsConfig'; +import { clearOauth2Cache } from 'utils/network/index'; +import toast from 'react-hot-toast'; + +const OAuth2Implicit = ({ item, collection }) => { + const dispatch = useDispatch(); + const { storedTheme } = useTheme(); + + const oAuth = item.draft ? get(item, 'draft.request.auth.oauth2', {}) : get(item, 'request.auth.oauth2', {}); + + const handleRun = async () => { + dispatch(sendRequest(item, collection.uid)); + }; + + const handleSave = () => dispatch(saveRequest(item.uid, collection.uid)); + + const { callbackUrl, authorizationUrl, clientId, scope } = oAuth; + + const handleChange = (key, value) => { + dispatch( + updateAuth({ + mode: 'oauth2', + collectionUid: collection.uid, + itemUid: item.uid, + content: { + grantType: 'implicit', + callbackUrl, + authorizationUrl, + clientId, + scope, + [key]: value + } + }) + ); + }; + + const handleClearCache = (e) => { + clearOauth2Cache(collection?.uid) + .then(() => { + toast.success('cleared cache successfully'); + }) + .catch((err) => { + toast.error(err.message); + }); + }; + + return ( + + {inputsConfig.map((input) => { + const { key, label } = input; + return ( +
+ +
+ handleChange(key, val)} + onRun={handleRun} + collection={collection} + /> +
+
+ ); + })} +
+ + +
+
+ ); +}; + +export default OAuth2Implicit; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/inputsConfig.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/inputsConfig.js new file mode 100644 index 0000000000..87e1fd42d5 --- /dev/null +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/Implicit/inputsConfig.js @@ -0,0 +1,20 @@ +const inputsConfig = [ + { + key: 'callbackUrl', + label: 'Callback URL' + }, + { + key: 'authorizationUrl', + label: 'Authorization URL' + }, + { + key: 'clientId', + label: 'Client ID' + }, + { + key: 'scope', + label: 'Scope' + } +]; + +export { inputsConfig }; diff --git a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js index 3965c8d3e4..05a3fe79c1 100644 --- a/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js +++ b/packages/bruno-app/src/components/RequestPane/Auth/OAuth2/index.js @@ -5,6 +5,7 @@ import GrantTypeSelector from './GrantTypeSelector/index'; import OAuth2PasswordCredentials from './PasswordCredentials/index'; import OAuth2AuthorizationCode from './AuthorizationCode/index'; import OAuth2ClientCredentials from './ClientCredentials/index'; +import OAuth2Implicit from './Implicit/index'; const grantTypeComponentMap = (grantType, item, collection) => { switch (grantType) { @@ -17,6 +18,9 @@ const grantTypeComponentMap = (grantType, item, collection) => { case 'client_credentials': return ; break; + case 'implicit': + return ; + break; default: return
TBD
; break; diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index 26a1209916..d87bd5d81f 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -533,6 +533,10 @@ export const humanizeGrantType = (mode) => { label = 'Client Credentials'; break; } + case 'implicit': { + label = 'Implicit'; + break; + } } return label; diff --git a/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js b/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js index 3ed05d45c6..b27110c6bd 100644 --- a/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js +++ b/packages/bruno-electron/src/ipc/network/authorize-user-in-window.js @@ -1,33 +1,40 @@ const { BrowserWindow } = require('electron'); const { preferencesUtil } = require('../../store/preferences'); -const authorizeUserInWindow = ({ authorizeUrl, callbackUrl, session }) => { - return new Promise(async (resolve, reject) => { - let finalUrl = null; +const openNewWindow = (session) => { + let allOpenWindows = BrowserWindow.getAllWindows(); - let allOpenWindows = BrowserWindow.getAllWindows(); + // main window id is '1' + // get all other windows + let windowsExcludingMain = allOpenWindows.filter((w) => w.id != 1); + windowsExcludingMain.forEach((w) => { + w.close(); + }); - // main window id is '1' - // get all other windows - let windowsExcludingMain = allOpenWindows.filter((w) => w.id != 1); - windowsExcludingMain.forEach((w) => { - w.close(); - }); + const window = new BrowserWindow({ + webPreferences: { + nodeIntegration: false, + partition: session + }, + show: false + }); - const window = new BrowserWindow({ - webPreferences: { - nodeIntegration: false, - partition: session - }, - show: false - }); - window.on('ready-to-show', window.show.bind(window)); + window.on('ready-to-show', window.show.bind(window)); - // We want browser window to comply with "SSL/TLS Certificate Verification" toggle in Preferences - window.webContents.on('certificate-error', (event, url, error, certificate, callback) => { - event.preventDefault(); - callback(!preferencesUtil.shouldVerifyTls()); - }); + // We want browser window to comply with "SSL/TLS Certificate Verification" toggle in Preferences + window.webContents.on('certificate-error', (event, url, error, certificate, callback) => { + event.preventDefault(); + callback(!preferencesUtil.shouldVerifyTls()); + }); + + return window; +}; + +const authorizeUserInWindow = ({ authorizeUrl, callbackUrl, session }) => { + return new Promise(async (resolve, reject) => { + let finalUrl = null; + + let window = openNewWindow(session); function onWindowRedirect(url) { // check if the url contains an authorization code @@ -93,4 +100,68 @@ const authorizeUserInWindow = ({ authorizeUrl, callbackUrl, session }) => { }); }; -module.exports = { authorizeUserInWindow }; +const authorizeUserInWindowImplicit = ({ authorizeUrl, session }) => { + return new Promise(async (resolve, reject) => { + let finalUrl = null; + + let window = openNewWindow(session); + + function onWindowRedirect(url) { + // check if the url contains an access token + if (new URLSearchParams(new URL(url).hash.slice(1)).has('access_token')) { + finalUrl = url; + window.close(); + } + if (url.match(/(error=).*/) || url.match(/(error_description=).*/) || url.match(/(error_uri=).*/)) { + const _url = new URL(url); + const error = _url.searchParams.get('error'); + const errorDescription = _url.searchParams.get('error_description'); + const errorUri = _url.searchParams.get('error_uri'); + let errorData = { + message: 'Authorization Failed!', + error, + errorDescription, + errorUri + }; + reject(new Error(JSON.stringify(errorData))); + window.close(); + } + } + + window.on('close', () => { + if (finalUrl) { + try { + const uriFragmentWithToken = new URLSearchParams(new URL(finalUrl).hash.slice(1)); + const accessToken = uriFragmentWithToken.get('access_token'); + return resolve({ accessToken }); + } catch (error) { + return reject(error); + } + } else { + return reject(new Error('Authorization window closed')); + } + }); + + // wait for the window to navigate to the callback url + const didNavigateListener = (_, url) => { + onWindowRedirect(url); + }; + window.webContents.on('did-navigate', didNavigateListener); + const willRedirectListener = (_, authorizeUrl) => { + onWindowRedirect(authorizeUrl); + }; + window.webContents.on('will-redirect', willRedirectListener); + + try { + await window.loadURL(authorizeUrl); + } catch (error) { + reject(error); + window.close(); + } + }); +}; + +module.exports = { + authorizeUserInWindow, + authorizeUserInWindowImplicit +}; diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 73d8a59232..fd69220f20 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -34,7 +34,8 @@ const { getCookieStringForUrl, addCookieToJar, getDomainsWithCookies } = require const { resolveOAuth2AuthorizationCodeAccessToken, transformClientCredentialsRequest, - transformPasswordCredentialsRequest + transformPasswordCredentialsRequest, + getOAuth2ImplicitToken } = require('./oauth2-helper'); const Oauth2Store = require('../../store/oauth2'); @@ -232,6 +233,11 @@ const configureRequest = async ( request.data = passwordData; request.url = passwordAccessTokenUrl; break; + case 'implicit': + interpolateVars(requestCopy, envVars, collectionVariables, processEnvVars); + const { accessToken } = await getOAuth2ImplicitToken(requestCopy, collectionUid); + request.headers['Authorization'] = `Bearer ${accessToken}`; + break; } } diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index 2139194a2a..c25af722c5 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -156,6 +156,12 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces scope }; break; + case 'implicit': + request.oauth2.callbackUrl = _interpolate(request.oauth2.callbackUrl) || ''; + request.oauth2.authorizationUrl = _interpolate(request.oauth2.authorizationUrl) || ''; + request.oauth2.clientId = _interpolate(request.oauth2.clientId) || ''; + request.oauth2.scope = _interpolate(request.oauth2.scope) || ''; + break; default: break; } diff --git a/packages/bruno-electron/src/ipc/network/oauth2-helper.js b/packages/bruno-electron/src/ipc/network/oauth2-helper.js index e254e8c746..4ab0ee1836 100644 --- a/packages/bruno-electron/src/ipc/network/oauth2-helper.js +++ b/packages/bruno-electron/src/ipc/network/oauth2-helper.js @@ -1,6 +1,6 @@ const { get, cloneDeep } = require('lodash'); const crypto = require('crypto'); -const { authorizeUserInWindow } = require('./authorize-user-in-window'); +const { authorizeUserInWindow, authorizeUserInWindowImplicit } = require('./authorize-user-in-window'); const Oauth2Store = require('../../store/oauth2'); const generateCodeVerifier = () => { @@ -114,9 +114,38 @@ const transformPasswordCredentialsRequest = async (request) => { }; }; +// IMPLICIT + +const getOAuth2ImplicitToken = async (request, collectionUid) => { + return new Promise(async (resolve, reject) => { + const { oauth2 } = request; + const { callbackUrl, authorizationUrl, clientId, scope } = oauth2; + let oauth2QueryParams = + (authorizationUrl.indexOf('?') > -1 ? '&' : '?') + `client_id=${clientId}&response_type=token`; + if (callbackUrl) { + oauth2QueryParams += `&redirect_uri=${callbackUrl}`; + } + if (scope) { + oauth2QueryParams += `&scope=${scope}`; + } + const authorizationUrlWithQueryParams = authorizationUrl + oauth2QueryParams; + try { + const oauth2Store = new Oauth2Store(); + const { accessToken } = await authorizeUserInWindowImplicit({ + authorizeUrl: authorizationUrlWithQueryParams, + session: oauth2Store.getSessionIdOfCollection(collectionUid) + }); + resolve({ accessToken }); + } catch (err) { + reject(err); + } + }); +}; + module.exports = { resolveOAuth2AuthorizationCodeAccessToken, getOAuth2AuthorizationCode, transformClientCredentialsRequest, - transformPasswordCredentialsRequest + transformPasswordCredentialsRequest, + getOAuth2ImplicitToken }; diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index 37196589ae..bc83cc3a6e 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -135,6 +135,15 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => { scope: get(request, 'auth.oauth2.scope') }; break; + case 'implicit': + axiosRequest.oauth2 = { + grantType: grantType, + callbackUrl: get(request, 'auth.oauth2.callbackUrl'), + authorizationUrl: get(request, 'auth.oauth2.authorizationUrl'), + clientId: get(request, 'auth.oauth2.clientId'), + scope: get(request, 'auth.oauth2.scope') + }; + break; } break; } diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index afb0a43e02..872e956f62 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -425,6 +425,14 @@ const sem = grammar.createSemantics().addAttribute('ast', { clientSecret: clientSecretKey ? clientSecretKey.value : '', scope: scopeKey ? scopeKey.value : '' } + : grantTypeKey?.value && grantTypeKey?.value == 'implicit' + ? { + grantType: grantTypeKey ? grantTypeKey.value : '', + callbackUrl: callbackUrlKey ? callbackUrlKey.value : '', + authorizationUrl: authorizationUrlKey ? authorizationUrlKey.value : '', + clientId: clientIdKey ? clientIdKey.value : '', + scope: scopeKey ? scopeKey.value : '' + } : {} } }; diff --git a/packages/bruno-lang/v2/src/collectionBruToJson.js b/packages/bruno-lang/v2/src/collectionBruToJson.js index 355f2f9664..ae1a13ee3a 100644 --- a/packages/bruno-lang/v2/src/collectionBruToJson.js +++ b/packages/bruno-lang/v2/src/collectionBruToJson.js @@ -287,6 +287,14 @@ const sem = grammar.createSemantics().addAttribute('ast', { clientSecret: clientSecretKey ? clientSecretKey.value : '', scope: scopeKey ? scopeKey.value : '' } + : grantTypeKey?.value && grantTypeKey?.value == 'implicit' + ? { + grantType: grantTypeKey ? grantTypeKey.value : '', + callbackUrl: callbackUrlKey ? callbackUrlKey.value : '', + authorizationUrl: authorizationUrlKey ? authorizationUrlKey.value : '', + clientId: clientIdKey ? clientIdKey.value : '', + scope: scopeKey ? scopeKey.value : '' + } : {} } }; diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js index a59d7cd7c5..790dd3e9b9 100644 --- a/packages/bruno-lang/v2/src/jsonToBru.js +++ b/packages/bruno-lang/v2/src/jsonToBru.js @@ -164,6 +164,17 @@ ${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)} ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} } +`; + break; + case 'implicit': + bru += `auth:oauth2 { +${indentString(`grant_type: implicit`)} +${indentString(`callback_url: ${auth?.oauth2?.callbackUrl || ''}`)} +${indentString(`authorization_url: ${auth?.oauth2?.authorizationUrl || ''}`)} +${indentString(`client_id: ${auth?.oauth2?.clientId || ''}`)} +${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} +} + `; break; } diff --git a/packages/bruno-lang/v2/src/jsonToCollectionBru.js b/packages/bruno-lang/v2/src/jsonToCollectionBru.js index e4d6ab5fdb..b093323291 100644 --- a/packages/bruno-lang/v2/src/jsonToCollectionBru.js +++ b/packages/bruno-lang/v2/src/jsonToCollectionBru.js @@ -152,6 +152,17 @@ ${indentString(`client_secret: ${auth?.oauth2?.clientSecret || ''}`)} ${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} } +`; + break; + case 'implicit': + bru += `auth:oauth2 { +${indentString(`grant_type: implicit`)} +${indentString(`callback_url: ${auth?.oauth2?.callbackUrl || ''}`)} +${indentString(`authorization_url: ${auth?.oauth2?.authorizationUrl || ''}`)} +${indentString(`client_id: ${auth?.oauth2?.clientId || ''}`)} +${indentString(`scope: ${auth?.oauth2?.scope || ''}`)} +} + `; break; } diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js index 033e68277f..f9e120a2c2 100644 --- a/packages/bruno-schema/src/collections/index.js +++ b/packages/bruno-schema/src/collections/index.js @@ -121,7 +121,7 @@ const authDigestSchema = Yup.object({ const oauth2Schema = Yup.object({ grantType: Yup.string() - .oneOf(['client_credentials', 'password', 'authorization_code']) + .oneOf(['client_credentials', 'password', 'authorization_code', 'implicit']) .required('grantType is required'), username: Yup.string().when('grantType', { is: (val) => ['client_credentials', 'password'].includes(val), @@ -134,12 +134,12 @@ const oauth2Schema = Yup.object({ otherwise: Yup.string().nullable().strip() }), callbackUrl: Yup.string().when('grantType', { - is: (val) => ['authorization_code'].includes(val), + is: (val) => ['authorization_code', 'implicit'].includes(val), then: Yup.string().nullable(), otherwise: Yup.string().nullable().strip() }), authorizationUrl: Yup.string().when('grantType', { - is: (val) => ['authorization_code'].includes(val), + is: (val) => ['authorization_code', 'implicit'].includes(val), then: Yup.string().nullable(), otherwise: Yup.string().nullable().strip() }), @@ -149,7 +149,7 @@ const oauth2Schema = Yup.object({ otherwise: Yup.string().nullable().strip() }), clientId: Yup.string().when('grantType', { - is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val), + is: (val) => ['client_credentials', 'password', 'authorization_code', 'implicit'].includes(val), then: Yup.string().nullable(), otherwise: Yup.string().nullable().strip() }), @@ -159,7 +159,7 @@ const oauth2Schema = Yup.object({ otherwise: Yup.string().nullable().strip() }), scope: Yup.string().when('grantType', { - is: (val) => ['client_credentials', 'password', 'authorization_code'].includes(val), + is: (val) => ['client_credentials', 'password', 'authorization_code', 'implicit'].includes(val), then: Yup.string().nullable(), otherwise: Yup.string().nullable().strip() }),