feat: ability to refresh tokens for oauth flows (#4302)
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
@@ -255,7 +255,7 @@ onMounted(() => {
|
||||
return
|
||||
}
|
||||
|
||||
const { context, source, token }: PersistedOAuthConfig =
|
||||
const { context, source, token, refresh_token }: PersistedOAuthConfig =
|
||||
JSON.parse(localOAuthTempConfig)
|
||||
|
||||
if (source === "REST") {
|
||||
@@ -279,6 +279,10 @@ onMounted(() => {
|
||||
const grantTypeInfo = auth.grantTypeInfo
|
||||
|
||||
grantTypeInfo && (grantTypeInfo.token = token ?? "")
|
||||
|
||||
if (refresh_token && grantTypeInfo.grantType === "AUTHORIZATION_CODE") {
|
||||
grantTypeInfo.refreshToken = refresh_token
|
||||
}
|
||||
}
|
||||
|
||||
editingProperties.value = unsavedCollectionProperties
|
||||
|
||||
@@ -428,7 +428,7 @@ onMounted(() => {
|
||||
return
|
||||
}
|
||||
|
||||
const { context, source, token }: PersistedOAuthConfig =
|
||||
const { context, source, token, refresh_token }: PersistedOAuthConfig =
|
||||
JSON.parse(localOAuthTempConfig)
|
||||
|
||||
if (source === "GraphQL") {
|
||||
@@ -452,6 +452,10 @@ onMounted(() => {
|
||||
const grantTypeInfo = auth.grantTypeInfo
|
||||
|
||||
grantTypeInfo && (grantTypeInfo.token = token ?? "")
|
||||
|
||||
if (refresh_token && grantTypeInfo.grantType === "AUTHORIZATION_CODE") {
|
||||
grantTypeInfo.refreshToken = refresh_token
|
||||
}
|
||||
}
|
||||
|
||||
editingProperties.value = unsavedCollectionProperties
|
||||
|
||||
@@ -165,12 +165,18 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="p-2">
|
||||
<div class="p-2 gap-1 flex">
|
||||
<HoppButtonSecondary
|
||||
filled
|
||||
:label="`${t('authorization.generate_token')}`"
|
||||
@click="generateOAuthToken()"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-if="runTokenRefresh"
|
||||
filled
|
||||
:label="`${t('authorization.refresh_token')}`"
|
||||
@click="refreshOauthToken()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -192,6 +198,7 @@ import { getCombinedEnvVariables } from "~/helpers/preRequest"
|
||||
import { AggregateEnvironment } from "~/newstore/environments"
|
||||
import authCode, {
|
||||
AuthCodeOauthFlowParams,
|
||||
AuthCodeOauthRefreshParams,
|
||||
getDefaultAuthCodeOauthFlowParams,
|
||||
} from "~/services/oauth/flows/authCode"
|
||||
import clientCredentials, {
|
||||
@@ -356,6 +363,48 @@ const supportedGrantTypes = [
|
||||
}
|
||||
)
|
||||
|
||||
const refreshToken = async () => {
|
||||
const grantTypeInfo = auth.value.grantTypeInfo
|
||||
|
||||
if (!("refreshToken" in grantTypeInfo)) {
|
||||
return E.left("NO_REFRESH_TOKEN_PRESENT" as const)
|
||||
}
|
||||
|
||||
const refreshToken = grantTypeInfo.refreshToken
|
||||
|
||||
if (!refreshToken) {
|
||||
return E.left("NO_REFRESH_TOKEN_PRESENT" as const)
|
||||
}
|
||||
|
||||
const params: AuthCodeOauthRefreshParams = {
|
||||
clientID: clientID.value,
|
||||
clientSecret: clientSecret.value,
|
||||
tokenEndpoint: tokenEndpoint.value,
|
||||
refreshToken,
|
||||
}
|
||||
|
||||
const unwrappedParams = replaceTemplateStringsInObjectValues(params)
|
||||
|
||||
const refreshTokenFunc = authCode.refreshToken
|
||||
|
||||
if (!refreshTokenFunc) {
|
||||
return E.left("REFRESH_TOKEN_FUNCTION_NOT_DEFINED" as const)
|
||||
}
|
||||
|
||||
const res = await refreshTokenFunc(unwrappedParams)
|
||||
|
||||
if (E.isLeft(res)) {
|
||||
return E.left("OAUTH_REFRESH_TOKEN_FAILED" as const)
|
||||
}
|
||||
|
||||
setAccessTokenInActiveContext(
|
||||
res.right.access_token,
|
||||
res.right.refresh_token
|
||||
)
|
||||
|
||||
return E.right(undefined)
|
||||
}
|
||||
|
||||
const runAction = () => {
|
||||
const params: AuthCodeOauthFlowParams = {
|
||||
authEndpoint: authEndpoint.value,
|
||||
@@ -456,6 +505,7 @@ const supportedGrantTypes = [
|
||||
|
||||
return {
|
||||
runAction,
|
||||
refreshToken,
|
||||
elements,
|
||||
}
|
||||
}),
|
||||
@@ -854,13 +904,28 @@ const selectedGrantType = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
const setAccessTokenInActiveContext = (accessToken?: string) => {
|
||||
const setAccessTokenInActiveContext = (
|
||||
accessToken?: string,
|
||||
refreshToken?: string
|
||||
) => {
|
||||
if (props.isCollectionProperty && accessToken) {
|
||||
auth.value.grantTypeInfo = {
|
||||
...auth.value.grantTypeInfo,
|
||||
token: accessToken,
|
||||
}
|
||||
|
||||
// set the refresh token if provided
|
||||
// we also make sure the grantTypes supporting refreshTokens are
|
||||
if (
|
||||
refreshToken &&
|
||||
auth.value.grantTypeInfo.grantType === "AUTHORIZATION_CODE"
|
||||
) {
|
||||
auth.value.grantTypeInfo = {
|
||||
...auth.value.grantTypeInfo,
|
||||
refreshToken,
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -874,6 +939,16 @@ const setAccessTokenInActiveContext = (accessToken?: string) => {
|
||||
tabService.currentActiveTab.value.document.request.auth.grantTypeInfo.token =
|
||||
accessToken
|
||||
}
|
||||
|
||||
if (
|
||||
refreshToken &&
|
||||
tabService.currentActiveTab.value.document.request.auth.authType ===
|
||||
"oauth-2"
|
||||
) {
|
||||
// @ts-expect-error - todo: narrow the grantType to only supporting refresh tokens
|
||||
tabService.currentActiveTab.value.document.request.auth.grantTypeInfo.refreshToken =
|
||||
refreshToken
|
||||
}
|
||||
}
|
||||
|
||||
const changeSelectedGrantType = (
|
||||
@@ -905,10 +980,53 @@ const runAction = computed(() => {
|
||||
return selectedGrantType.value?.formElements.value?.runAction
|
||||
})
|
||||
|
||||
const runTokenRefresh = computed(() => {
|
||||
// the only grant type that supports refresh tokens is the authCode grant type
|
||||
if (selectedGrantType.value?.id === "authCode") {
|
||||
return selectedGrantType.value?.formElements.value?.refreshToken
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
const currentOAuthGrantTypeFormElements = computed(() => {
|
||||
return selectedGrantType.value?.formElements.value?.elements.value
|
||||
})
|
||||
|
||||
const refreshOauthToken = async () => {
|
||||
if (!runTokenRefresh.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const res = await runTokenRefresh.value()
|
||||
|
||||
if (E.isLeft(res)) {
|
||||
const errorMessages = {
|
||||
NO_REFRESH_TOKEN_PRESENT: t(
|
||||
"authorization.oauth.no_refresh_token_present"
|
||||
),
|
||||
REFRESH_TOKEN_FUNCTION_NOT_DEFINED: t(
|
||||
"authorization.oauth.refresh_token_request_failed"
|
||||
),
|
||||
OAUTH_REFRESH_TOKEN_FAILED: t(
|
||||
"authorization.oauth.refresh_token_request_failed"
|
||||
),
|
||||
}
|
||||
|
||||
const isKnownError = res.left in errorMessages
|
||||
|
||||
if (!isKnownError) {
|
||||
toast.error(t("authorization.oauth.refresh_token_failed"))
|
||||
return
|
||||
}
|
||||
|
||||
toast.error(errorMessages[res.left])
|
||||
return
|
||||
}
|
||||
|
||||
toast.success(t("authorization.oauth.token_refreshed_successfully"))
|
||||
}
|
||||
|
||||
const generateOAuthToken = async () => {
|
||||
if (
|
||||
grantTypesInvolvingRedirect.includes(auth.value.grantTypeInfo.grantType)
|
||||
|
||||
Reference in New Issue
Block a user