Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com> Co-authored-by: amk-dev <akash.k.mohan98@gmail.com>
995 lines
26 KiB
Vue
995 lines
26 KiB
Vue
<template>
|
|
<div class="flex items-center px-4 border-b border-dividerLight">
|
|
<label class="truncate font-semibold text-secondaryLight">
|
|
{{ t("authorization.oauth.grant_type") }}
|
|
</label>
|
|
|
|
<tippy
|
|
placement="bottom"
|
|
interactive
|
|
trigger="click"
|
|
theme="popover"
|
|
:on-shown="() => grantTypeTippyActions?.focus()"
|
|
class="!flex-initial"
|
|
>
|
|
<HoppSmartSelectWrapper>
|
|
<HoppButtonSecondary
|
|
class="ml-2 rounded-none pr-8"
|
|
:label="selectedGrantType?.label"
|
|
/>
|
|
</HoppSmartSelectWrapper>
|
|
<template #content="{ hide }">
|
|
<div
|
|
ref="grantTypeTippyActions"
|
|
class="flex flex-col focus:outline-none"
|
|
tabindex="0"
|
|
@keyup.escape="hide()"
|
|
>
|
|
<HoppSmartItem
|
|
v-for="grantType in supportedGrantTypes"
|
|
:key="grantType.id"
|
|
:label="grantType.label"
|
|
:icon="
|
|
selectedGrantTypeID === grantType.id ? IconCircleDot : IconCircle
|
|
"
|
|
:active="selectedGrantTypeID === grantType.id"
|
|
@click="
|
|
() => {
|
|
changeSelectedGrantType(grantType.id)
|
|
|
|
hide()
|
|
}
|
|
"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</tippy>
|
|
</div>
|
|
|
|
<div class="flex flex-col">
|
|
<div
|
|
v-for="element in currentOAuthGrantTypeFormElements"
|
|
:key="element.id"
|
|
class="flex flex-1 border-b border-dividerLight"
|
|
>
|
|
<SmartEnvInput
|
|
v-if="element.type === 'text'"
|
|
v-model="element.ref.value"
|
|
:placeholder="element.label"
|
|
:envs="envs"
|
|
:auto-complete-env="true"
|
|
/>
|
|
<div
|
|
v-else-if="element.type === 'checkbox'"
|
|
class="px-4 py-2 flex items-center"
|
|
>
|
|
<span class="text-secondaryLight font-semibold mr-6">{{
|
|
element.label
|
|
}}</span>
|
|
<HoppSmartCheckbox
|
|
class="text-secondaryLight flex"
|
|
:on="element.ref.value"
|
|
@change="element.ref.value = !element.ref.value"
|
|
></HoppSmartCheckbox>
|
|
</div>
|
|
<div
|
|
v-else-if="element.type === 'dropdown'"
|
|
class="flex items-center px-4"
|
|
>
|
|
<label class="truncate text-secondaryLight">
|
|
{{ element.label }}
|
|
</label>
|
|
<tippy
|
|
v-if="element.ref.value"
|
|
interactive
|
|
trigger="click"
|
|
theme="popover"
|
|
class="!flex-initial"
|
|
>
|
|
<HoppSmartSelectWrapper>
|
|
<HoppButtonSecondary
|
|
class="ml-2 rounded-none pr-8"
|
|
:label="element.ref.value.label"
|
|
/>
|
|
</HoppSmartSelectWrapper>
|
|
<template #content="{ hide }">
|
|
<div
|
|
:ref="element.tippyRefName"
|
|
class="flex flex-col focus:outline-none"
|
|
tabindex="0"
|
|
@keyup.escape="hide()"
|
|
>
|
|
<HoppSmartItem
|
|
v-for="item in element.options"
|
|
:key="item.id"
|
|
:label="item.label"
|
|
:icon="
|
|
element.ref.value.id === item.id ? IconCircleDot : IconCircle
|
|
"
|
|
:active="element.ref.value.id === item.id"
|
|
@click="
|
|
() => {
|
|
element.ref.value = item
|
|
hide()
|
|
}
|
|
"
|
|
></HoppSmartItem>
|
|
</div>
|
|
</template>
|
|
</tippy>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center border-b border-dividerLight">
|
|
<span class="flex items-center">
|
|
<label class="ml-4 text-secondaryLight">
|
|
{{ t("authorization.pass_key_by") }}
|
|
</label>
|
|
<tippy
|
|
interactive
|
|
trigger="click"
|
|
theme="popover"
|
|
:on-shown="() => authTippyActions?.focus()"
|
|
>
|
|
<HoppSmartSelectWrapper>
|
|
<HoppButtonSecondary
|
|
:label="passBy"
|
|
class="ml-2 rounded-none pr-8"
|
|
/>
|
|
</HoppSmartSelectWrapper>
|
|
<template #content="{ hide }">
|
|
<div
|
|
ref="authTippyActions"
|
|
class="flex flex-col focus:outline-none"
|
|
tabindex="0"
|
|
@keyup.escape="hide()"
|
|
>
|
|
<HoppSmartItem
|
|
v-for="addToTarget in addToTargets"
|
|
:key="addToTarget.id"
|
|
:label="addToTarget.label"
|
|
:icon="
|
|
auth.addTo === addToTarget.id ? IconCircleDot : IconCircle
|
|
"
|
|
:active="auth.addTo === addToTarget.id"
|
|
@click="
|
|
() => {
|
|
auth.addTo = addToTarget.id
|
|
hide()
|
|
}
|
|
"
|
|
/>
|
|
</div>
|
|
</template>
|
|
</tippy>
|
|
</span>
|
|
</div>
|
|
|
|
<div class="p-2">
|
|
<HoppButtonSecondary
|
|
filled
|
|
:label="`${t('authorization.generate_token')}`"
|
|
@click="generateOAuthToken()"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {
|
|
HoppGQLAuthOAuth2,
|
|
HoppRESTAuthOAuth2,
|
|
parseTemplateStringE,
|
|
} from "@hoppscotch/data"
|
|
import { useService } from "dioc/vue"
|
|
import * as E from "fp-ts/Either"
|
|
import { Ref, computed, ref } from "vue"
|
|
import { z } from "zod"
|
|
import { useI18n } from "~/composables/i18n"
|
|
import { refWithCallbackOnChange } from "~/composables/ref"
|
|
import { useToast } from "~/composables/toast"
|
|
import { getCombinedEnvVariables } from "~/helpers/preRequest"
|
|
import { AggregateEnvironment } from "~/newstore/environments"
|
|
import authCode, {
|
|
AuthCodeOauthFlowParams,
|
|
getDefaultAuthCodeOauthFlowParams,
|
|
} from "~/services/oauth/flows/authCode"
|
|
import clientCredentials, {
|
|
ClientCredentialsFlowParams,
|
|
getDefaultClientCredentialsFlowParams,
|
|
} from "~/services/oauth/flows/clientCredentials"
|
|
import implicit, {
|
|
ImplicitOauthFlowParams,
|
|
getDefaultImplicitOauthFlowParams,
|
|
} from "~/services/oauth/flows/implicit"
|
|
import passwordFlow, {
|
|
PasswordFlowParams,
|
|
getDefaultPasswordFlowParams,
|
|
} from "~/services/oauth/flows/password"
|
|
import {
|
|
PersistedOAuthConfig,
|
|
grantTypesInvolvingRedirect,
|
|
} from "~/services/oauth/oauth.service"
|
|
import { PersistenceService } from "~/services/persistence"
|
|
import { GQLTabService } from "~/services/tab/graphql"
|
|
import { RESTTabService } from "~/services/tab/rest"
|
|
import IconCircle from "~icons/lucide/circle"
|
|
import IconCircleDot from "~icons/lucide/circle-dot"
|
|
|
|
const t = useI18n()
|
|
const toast = useToast()
|
|
|
|
const gqlTabsService = useService(GQLTabService)
|
|
const persistenceService = useService(PersistenceService)
|
|
const restTabsService = useService(RESTTabService)
|
|
|
|
const props = defineProps<{
|
|
modelValue: HoppRESTAuthOAuth2 | HoppGQLAuthOAuth2
|
|
isCollectionProperty?: boolean
|
|
envs?: AggregateEnvironment[]
|
|
source: "REST" | "GraphQL"
|
|
}>()
|
|
|
|
const auth = ref(props.modelValue)
|
|
|
|
const addToTargets = [
|
|
{
|
|
id: "HEADERS" as const,
|
|
label: "Headers",
|
|
},
|
|
{
|
|
id: "QUERY_PARAMS" as const,
|
|
label: "Query Params",
|
|
},
|
|
]
|
|
|
|
const passBy = computed(() => {
|
|
return (
|
|
addToTargets.find((target) => target.id === auth.value.addTo)?.label ||
|
|
t("state.none")
|
|
)
|
|
})
|
|
|
|
const supportedGrantTypes = [
|
|
{
|
|
// used for both authCode and authCodePKCE
|
|
id: "authCode" as const,
|
|
label: t("authorization.oauth.label_auth_code"),
|
|
formElements: computed(() => {
|
|
if (!(auth.value.grantTypeInfo.grantType === "AUTHORIZATION_CODE")) {
|
|
return
|
|
}
|
|
|
|
const grantType = auth.value.grantTypeInfo
|
|
|
|
const authEndpoint = refWithCallbackOnChange(
|
|
grantType?.authEndpoint,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
authEndpoint: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const tokenEndpoint = refWithCallbackOnChange(
|
|
grantType?.tokenEndpoint,
|
|
(value) => {
|
|
if (!("tokenEndpoint" in auth.value.grantTypeInfo)) {
|
|
return
|
|
}
|
|
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
tokenEndpoint: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const clientID = refWithCallbackOnChange(grantType?.clientID, (value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
clientID: value,
|
|
}
|
|
})
|
|
|
|
const clientSecret = refWithCallbackOnChange(
|
|
grantType?.clientSecret,
|
|
(value) => {
|
|
if (!("clientSecret" in auth.value.grantTypeInfo)) {
|
|
return
|
|
}
|
|
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
clientSecret: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const scopes = refWithCallbackOnChange(
|
|
grantType?.scopes ? grantType.scopes : undefined,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
scopes: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const isPKCE = refWithCallbackOnChange(
|
|
auth.value.grantTypeInfo.isPKCE,
|
|
(value) => {
|
|
if (!("isPKCE" in auth.value.grantTypeInfo)) {
|
|
return
|
|
}
|
|
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
isPKCE: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const codeChallenge: Ref<{
|
|
id: "plain" | "S256"
|
|
label: string
|
|
} | null> = refWithCallbackOnChange(
|
|
auth.value.grantTypeInfo.codeVerifierMethod
|
|
? {
|
|
id: auth.value.grantTypeInfo.codeVerifierMethod,
|
|
label:
|
|
auth.value.grantTypeInfo.codeVerifierMethod === "plain"
|
|
? "Plain"
|
|
: "SHA-256",
|
|
}
|
|
: null,
|
|
(value) => {
|
|
if (!("codeVerifierMethod" in auth.value.grantTypeInfo) || !value) {
|
|
return
|
|
}
|
|
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
codeVerifierMethod: value.id,
|
|
}
|
|
}
|
|
)
|
|
|
|
const runAction = () => {
|
|
const params: AuthCodeOauthFlowParams = {
|
|
authEndpoint: authEndpoint.value,
|
|
tokenEndpoint: tokenEndpoint.value,
|
|
clientID: clientID.value,
|
|
clientSecret: clientSecret.value,
|
|
scopes: scopes.value,
|
|
isPKCE: isPKCE.value,
|
|
codeVerifierMethod: codeChallenge.value?.id,
|
|
}
|
|
|
|
const unwrappedParams = replaceTemplateStringsInObjectValues(params)
|
|
|
|
const parsedArgs = authCode.params.safeParse(unwrappedParams)
|
|
|
|
if (!parsedArgs.success) {
|
|
return E.left("VALIDATION_FAILED" as const)
|
|
}
|
|
|
|
authCode.init(parsedArgs.data)
|
|
|
|
return E.right(undefined)
|
|
}
|
|
|
|
const pkceElements = computed(() => {
|
|
const checkbox = {
|
|
id: "isPKCE",
|
|
label: t("authorization.oauth.label_use_pkce"),
|
|
type: "checkbox" as const,
|
|
ref: isPKCE,
|
|
onChange: (e: Event) => {
|
|
const target = e.target as HTMLInputElement
|
|
|
|
isPKCE.value = target.checked
|
|
},
|
|
}
|
|
|
|
return isPKCE.value
|
|
? [
|
|
checkbox,
|
|
{
|
|
id: "codeChallenge",
|
|
label: t("authorization.oauth.label_code_challenge"),
|
|
type: "dropdown" as const,
|
|
ref: codeChallenge,
|
|
tippyRefName: "pkceTippyActions",
|
|
tippyRef: pkceTippyActions,
|
|
options: [
|
|
{
|
|
id: "plain" as const,
|
|
label: "Plain",
|
|
},
|
|
{
|
|
id: "S256" as const,
|
|
label: "SHA-256",
|
|
},
|
|
],
|
|
},
|
|
]
|
|
: [checkbox]
|
|
})
|
|
|
|
const elements = computed(() => {
|
|
return [
|
|
...pkceElements.value,
|
|
{
|
|
id: "authEndpoint",
|
|
label: t("authorization.oauth.label_authorization_endpoint"),
|
|
type: "text" as const,
|
|
ref: authEndpoint,
|
|
},
|
|
{
|
|
id: "tokenEndpoint",
|
|
label: t("authorization.oauth.label_token_endpoint"),
|
|
type: "text" as const,
|
|
ref: tokenEndpoint,
|
|
},
|
|
{
|
|
id: "clientId",
|
|
label: t("authorization.oauth.label_client_id"),
|
|
type: "text" as const,
|
|
ref: clientID,
|
|
},
|
|
{
|
|
id: "clientSecret",
|
|
label: t("authorization.oauth.label_client_secret"),
|
|
type: "text" as const,
|
|
ref: clientSecret,
|
|
},
|
|
{
|
|
id: "scopes",
|
|
label: t("authorization.oauth.label_scopes"),
|
|
type: "text" as const,
|
|
ref: scopes,
|
|
},
|
|
]
|
|
})
|
|
|
|
return {
|
|
runAction,
|
|
elements,
|
|
}
|
|
}),
|
|
},
|
|
{
|
|
id: "clientCredentials" as const,
|
|
label: t("authorization.oauth.label_client_credentials"),
|
|
formElements: computed(() => {
|
|
if (!(auth.value.grantTypeInfo.grantType === "CLIENT_CREDENTIALS")) {
|
|
return
|
|
}
|
|
|
|
const grantTypeInfo = auth.value.grantTypeInfo
|
|
|
|
const authEndpoint = refWithCallbackOnChange(
|
|
grantTypeInfo?.authEndpoint,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
authEndpoint: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const clientID = refWithCallbackOnChange(
|
|
grantTypeInfo?.clientID,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
clientID: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const clientSecret = refWithCallbackOnChange(
|
|
grantTypeInfo?.clientSecret,
|
|
(value) => {
|
|
if (!("clientSecret" in auth.value.grantTypeInfo)) {
|
|
return
|
|
}
|
|
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
clientSecret: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const scopes = refWithCallbackOnChange(
|
|
grantTypeInfo?.scopes ? grantTypeInfo.scopes : undefined,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
scopes: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const runAction = async () => {
|
|
const values: ClientCredentialsFlowParams =
|
|
replaceTemplateStringsInObjectValues({
|
|
authEndpoint: authEndpoint.value,
|
|
clientID: clientID.value,
|
|
clientSecret: clientSecret.value,
|
|
scopes: scopes.value,
|
|
})
|
|
|
|
const parsedArgs = clientCredentials.params.safeParse(values)
|
|
|
|
if (!parsedArgs.success) {
|
|
return E.left("VALIDATION_FAILED" as const)
|
|
}
|
|
|
|
const res = await clientCredentials.init(parsedArgs.data)
|
|
|
|
if (E.isLeft(res)) {
|
|
return E.left("OAUTH_TOKEN_FETCH_FAILED" as const)
|
|
}
|
|
|
|
setAccessTokenInActiveContext(res.right?.access_token)
|
|
|
|
toast.success(t("authorization.oauth.token_fetched_successfully"))
|
|
|
|
return E.right(undefined)
|
|
}
|
|
|
|
const elements = computed(() => {
|
|
return [
|
|
{
|
|
id: "authEndpoint",
|
|
label: "Authorization Endpoint",
|
|
type: "text" as const,
|
|
ref: authEndpoint,
|
|
},
|
|
{
|
|
id: "clientId",
|
|
label: "Client ID",
|
|
type: "text" as const,
|
|
ref: clientID,
|
|
},
|
|
{
|
|
id: "clientSecret",
|
|
label: "Client Secret",
|
|
type: "text" as const,
|
|
ref: clientSecret,
|
|
},
|
|
{
|
|
id: "scopes",
|
|
label: "Scopes",
|
|
type: "text" as const,
|
|
ref: scopes,
|
|
},
|
|
]
|
|
})
|
|
|
|
return {
|
|
runAction,
|
|
elements,
|
|
}
|
|
}),
|
|
},
|
|
{
|
|
id: "password" as const,
|
|
label: "Password",
|
|
formElements: computed(() => {
|
|
if (!(auth.value.grantTypeInfo.grantType === "PASSWORD")) {
|
|
return
|
|
}
|
|
|
|
const grantTypeInfo = auth.value.grantTypeInfo
|
|
|
|
const authEndpoint = refWithCallbackOnChange(
|
|
grantTypeInfo?.authEndpoint,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
authEndpoint: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const clientID = refWithCallbackOnChange(
|
|
grantTypeInfo?.clientID,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
clientID: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const clientSecret = refWithCallbackOnChange(
|
|
grantTypeInfo?.clientSecret,
|
|
(value) => {
|
|
if (!("clientSecret" in auth.value.grantTypeInfo)) {
|
|
return
|
|
}
|
|
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
clientSecret: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const scopes = refWithCallbackOnChange(
|
|
grantTypeInfo?.scopes ? grantTypeInfo.scopes : undefined,
|
|
(value) => {
|
|
auth.value.grantTypeInfo.scopes = value
|
|
}
|
|
)
|
|
|
|
const username = refWithCallbackOnChange(
|
|
grantTypeInfo?.username,
|
|
(value) => {
|
|
if (!("username" in auth.value.grantTypeInfo)) {
|
|
return
|
|
}
|
|
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
username: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const password = refWithCallbackOnChange(
|
|
grantTypeInfo?.password,
|
|
(value) => {
|
|
if (!("password" in auth.value.grantTypeInfo)) {
|
|
return
|
|
}
|
|
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
password: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const runAction = async () => {
|
|
const values: PasswordFlowParams = replaceTemplateStringsInObjectValues(
|
|
{
|
|
authEndpoint: authEndpoint.value,
|
|
clientID: clientID.value,
|
|
clientSecret: clientSecret.value,
|
|
scopes: scopes.value,
|
|
username: username.value,
|
|
password: password.value,
|
|
}
|
|
)
|
|
|
|
const parsedArgs = passwordFlow.params.safeParse(values)
|
|
|
|
if (!parsedArgs.success) {
|
|
return E.left("VALIDATION_FAILED" as const)
|
|
}
|
|
|
|
const res = await passwordFlow.init(parsedArgs.data)
|
|
|
|
if (E.isLeft(res)) {
|
|
return E.left("OAUTH_TOKEN_FETCH_FAILED" as const)
|
|
}
|
|
|
|
setAccessTokenInActiveContext(res.right?.access_token)
|
|
|
|
toast.success(t("authorization.oauth.token_fetched_successfully"))
|
|
|
|
return E.right(undefined)
|
|
}
|
|
|
|
const elements = computed(() => {
|
|
return [
|
|
{
|
|
id: "authEndpoint",
|
|
label: t("authorization.oauth.label_authorization_endpoint"),
|
|
type: "text" as const,
|
|
ref: authEndpoint,
|
|
},
|
|
{
|
|
id: "clientId",
|
|
label: t("authorization.oauth.label_client_id"),
|
|
type: "text" as const,
|
|
ref: clientID,
|
|
},
|
|
{
|
|
id: "clientSecret",
|
|
label: t("authorization.oauth.label_client_secret"),
|
|
type: "text" as const,
|
|
ref: clientSecret,
|
|
},
|
|
{
|
|
id: "username",
|
|
label: t("authorization.oauth.label_username"),
|
|
type: "text" as const,
|
|
ref: username,
|
|
},
|
|
{
|
|
id: "password",
|
|
label: t("authorization.oauth.label_password"),
|
|
type: "text" as const,
|
|
ref: password,
|
|
},
|
|
{
|
|
id: "scopes",
|
|
label: t("authorization.oauth.label_scopes"),
|
|
type: "text" as const,
|
|
ref: scopes,
|
|
},
|
|
]
|
|
})
|
|
|
|
return {
|
|
runAction,
|
|
elements,
|
|
}
|
|
}),
|
|
},
|
|
{
|
|
id: "implicit" as const,
|
|
label: t("authorization.oauth.label_implicit"),
|
|
formElements: computed(() => {
|
|
const grantTypeInfo = auth.value.grantTypeInfo
|
|
|
|
const authEndpoint = refWithCallbackOnChange(
|
|
grantTypeInfo?.authEndpoint,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
authEndpoint: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const clientID = refWithCallbackOnChange(
|
|
grantTypeInfo?.clientID,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
clientID: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const scopes = refWithCallbackOnChange(
|
|
grantTypeInfo?.scopes ? grantTypeInfo.scopes : undefined,
|
|
(value) => {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
scopes: value,
|
|
}
|
|
}
|
|
)
|
|
|
|
const runAction = () => {
|
|
const values: ImplicitOauthFlowParams =
|
|
replaceTemplateStringsInObjectValues({
|
|
authEndpoint: authEndpoint.value,
|
|
clientID: clientID.value,
|
|
scopes: scopes.value,
|
|
})
|
|
|
|
const unwrappedValues = replaceTemplateStringsInObjectValues(values)
|
|
|
|
const parsedArgs = implicit.params.safeParse(unwrappedValues)
|
|
|
|
if (!parsedArgs.success) {
|
|
return E.left("VALIDATION_FAILED" as const)
|
|
}
|
|
|
|
implicit.init(parsedArgs.data)
|
|
|
|
return E.right(undefined)
|
|
}
|
|
|
|
const elements = computed(() => {
|
|
return [
|
|
{
|
|
id: "authEndpoint",
|
|
label: t("authorization.oauth.label_authorization_endpoint"),
|
|
type: "text" as const,
|
|
ref: authEndpoint,
|
|
},
|
|
{
|
|
id: "clientId",
|
|
label: t("authorization.oauth.label_client_id"),
|
|
type: "text" as const,
|
|
ref: clientID,
|
|
},
|
|
{
|
|
id: "scopes",
|
|
label: t("authorization.oauth.label_scopes"),
|
|
type: "text" as const,
|
|
ref: scopes,
|
|
},
|
|
]
|
|
})
|
|
|
|
return {
|
|
runAction,
|
|
elements,
|
|
}
|
|
}),
|
|
},
|
|
]
|
|
|
|
type GrantTypes = z.infer<
|
|
typeof HoppRESTAuthOAuth2
|
|
>["grantTypeInfo"]["grantType"]
|
|
|
|
const grantTypeMap: Record<
|
|
GrantTypes,
|
|
(typeof supportedGrantTypes)[number]["id"]
|
|
> = {
|
|
AUTHORIZATION_CODE: "authCode",
|
|
CLIENT_CREDENTIALS: "clientCredentials",
|
|
IMPLICIT: "implicit",
|
|
PASSWORD: "password",
|
|
} as const
|
|
|
|
const grantTypeDefaultPayload = {
|
|
AUTHORIZATION_CODE: getDefaultAuthCodeOauthFlowParams,
|
|
CLIENT_CREDENTIALS: getDefaultClientCredentialsFlowParams,
|
|
IMPLICIT: getDefaultImplicitOauthFlowParams,
|
|
PASSWORD: getDefaultPasswordFlowParams,
|
|
} as const
|
|
|
|
const selectedGrantTypeID = computed(() => {
|
|
const currentGrantType = auth.value.grantTypeInfo.grantType
|
|
return grantTypeMap[currentGrantType]
|
|
})
|
|
|
|
const selectedGrantType = computed(() => {
|
|
return supportedGrantTypes.find(
|
|
(grantType) => grantType.id === selectedGrantTypeID.value
|
|
)
|
|
})
|
|
|
|
const setAccessTokenInActiveContext = (accessToken?: string) => {
|
|
if (props.isCollectionProperty && accessToken) {
|
|
auth.value.grantTypeInfo = {
|
|
...auth.value.grantTypeInfo,
|
|
token: accessToken,
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
const tabService = props.source === "REST" ? restTabsService : gqlTabsService
|
|
|
|
if (
|
|
tabService.currentActiveTab.value.document.request.auth.authType ===
|
|
"oauth-2" &&
|
|
accessToken
|
|
) {
|
|
tabService.currentActiveTab.value.document.request.auth.grantTypeInfo.token =
|
|
accessToken
|
|
}
|
|
}
|
|
|
|
const changeSelectedGrantType = (
|
|
grantType: (typeof supportedGrantTypes)[number]["id"]
|
|
) => {
|
|
const keys = Object.keys(grantTypeMap) as GrantTypes[]
|
|
|
|
const grantTypeToSet = keys.find((key) => grantTypeMap[key] === grantType)
|
|
|
|
if (grantTypeToSet) {
|
|
auth.value.grantTypeInfo.grantType = grantTypeToSet
|
|
|
|
const getDefaultPayload = grantTypeDefaultPayload[grantTypeToSet]
|
|
|
|
// set the default payload for the grant type
|
|
// for eg: if the grant type was auth code, and then the user selected the password grant type,
|
|
// there wont be a password key in the payload
|
|
// so we set the default payload, and it'll add all the keys that are missing
|
|
if (getDefaultPayload) {
|
|
auth.value.grantTypeInfo = {
|
|
...getDefaultPayload(),
|
|
...auth.value.grantTypeInfo,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const runAction = computed(() => {
|
|
return selectedGrantType.value?.formElements.value?.runAction
|
|
})
|
|
|
|
const currentOAuthGrantTypeFormElements = computed(() => {
|
|
return selectedGrantType.value?.formElements.value?.elements.value
|
|
})
|
|
|
|
const generateOAuthToken = async () => {
|
|
if (
|
|
grantTypesInvolvingRedirect.includes(auth.value.grantTypeInfo.grantType)
|
|
) {
|
|
const authConfig: PersistedOAuthConfig = {
|
|
source: props.source,
|
|
context: props.isCollectionProperty
|
|
? { type: "collection-properties", metadata: {} }
|
|
: { type: "request-tab", metadata: {} },
|
|
grant_type: auth.value.grantTypeInfo.grantType,
|
|
}
|
|
persistenceService.setLocalConfig(
|
|
"oauth_temp_config",
|
|
JSON.stringify(authConfig)
|
|
)
|
|
}
|
|
|
|
const res = await runAction.value?.()
|
|
|
|
if (res && E.isLeft(res)) {
|
|
const errorMessages = {
|
|
VALIDATION_FAILED: t("authorization.oauth.validation_failed"),
|
|
OAUTH_TOKEN_FETCH_FAILED: t("authorization.oauth.token_fetch_failed"),
|
|
}
|
|
|
|
toast.error(errorMessages[res.left])
|
|
return
|
|
}
|
|
}
|
|
|
|
const replaceTemplateStringsInObjectValues = <
|
|
T extends Record<string, unknown>,
|
|
>(
|
|
obj: T
|
|
) => {
|
|
const envs = getCombinedEnvVariables()
|
|
|
|
const requestVariables =
|
|
props.source === "REST"
|
|
? restTabsService.currentActiveTab.value.document.request.requestVariables.map(
|
|
({ key, value }) => ({
|
|
key,
|
|
value,
|
|
secret: false,
|
|
})
|
|
)
|
|
: []
|
|
|
|
// Ensure request variables are prioritized by removing any selected/global environment variables with the same key
|
|
const selectedEnvVars = envs.selected.filter(
|
|
({ key }) =>
|
|
!requestVariables.some(({ key: reqVarKey }) => reqVarKey === key)
|
|
)
|
|
const globalEnvVars = envs.global.filter(
|
|
({ key }) =>
|
|
!requestVariables.some(({ key: reqVarKey }) => reqVarKey === key)
|
|
)
|
|
|
|
const envVars = [...selectedEnvVars, ...globalEnvVars, ...requestVariables]
|
|
|
|
const newObj: Partial<T> = {}
|
|
|
|
for (const key in obj) {
|
|
const val = obj[key]
|
|
|
|
if (typeof val === "string") {
|
|
const parseResult = parseTemplateStringE(val, envVars)
|
|
|
|
newObj[key] = E.isRight(parseResult)
|
|
? (parseResult.right as T[typeof key])
|
|
: (val as T[typeof key])
|
|
} else {
|
|
newObj[key] = val
|
|
}
|
|
}
|
|
|
|
return newObj as T
|
|
}
|
|
|
|
const grantTypeTippyActions = ref<HTMLElement | null>(null)
|
|
const pkceTippyActions = ref<HTMLElement | null>(null)
|
|
const authTippyActions = ref<HTMLElement | null>(null)
|
|
</script>
|