feat: ability to refresh tokens for oauth flows (#4302)

Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Akash K
2024-08-29 13:27:31 +05:30
committed by GitHub
parent 2ed7221182
commit 181ad098e0
20 changed files with 488 additions and 68 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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)