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

@@ -46,6 +46,13 @@ export type AuthCodeOauthFlowParams = z.infer<
typeof AuthCodeOauthFlowParamsSchema
>
export type AuthCodeOauthRefreshParams = {
tokenEndpoint: string
clientID: string
clientSecret?: string
refreshToken: string
}
export const getDefaultAuthCodeOauthFlowParams =
(): AuthCodeOauthFlowParams => ({
authEndpoint: "",
@@ -233,6 +240,7 @@ const handleRedirectForAuthCodeOauthFlow = async (localConfig: string) => {
const withAccessTokenSchema = z.object({
access_token: z.string(),
refresh_token: z.string().optional(),
})
const parsedTokenResponse = withAccessTokenSchema.safeParse(
@@ -284,9 +292,60 @@ const encodeArrayBufferAsUrlEncodedBase64 = (buffer: ArrayBuffer) => {
return hashBase64URL
}
const refreshToken = async ({
tokenEndpoint,
clientID,
refreshToken,
clientSecret,
}: AuthCodeOauthRefreshParams) => {
const formData = new URLSearchParams()
formData.append("grant_type", "refresh_token")
formData.append("refresh_token", refreshToken)
formData.append("client_id", clientID)
if (clientSecret) {
formData.append("client_secret", clientSecret)
}
const { response } = interceptorService.runRequest({
url: tokenEndpoint,
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
data: formData.toString(),
})
const res = await response
if (E.isLeft(res)) {
return E.left("AUTH_TOKEN_REQUEST_FAILED" as const)
}
const responsePayload = decodeResponseAsJSON(res.right)
if (E.isLeft(responsePayload)) {
return E.left("AUTH_TOKEN_REQUEST_FAILED" as const)
}
const withAccessTokenAndRefreshTokenSchema = z.object({
access_token: z.string(),
refresh_token: z.string().optional(),
})
const parsedTokenResponse = withAccessTokenAndRefreshTokenSchema.safeParse(
responsePayload.right
)
return parsedTokenResponse.success
? E.right(parsedTokenResponse.data)
: E.left("AUTH_TOKEN_REQUEST_INVALID_RESPONSE" as const)
}
export default createFlowConfig(
"AUTHORIZATION_CODE" as const,
AuthCodeOauthFlowParamsSchema,
initAuthCodeOauthFlow,
handleRedirectForAuthCodeOauthFlow
handleRedirectForAuthCodeOauthFlow,
refreshToken
)

View File

@@ -22,6 +22,7 @@ export type PersistedOAuthConfig = {
state: string
}
token?: string
refresh_token?: string
}
const persistenceService = getService(PersistenceService)
@@ -66,6 +67,7 @@ export function createFlowConfig<
Flow extends string,
AuthParams extends Record<string, unknown>,
InitFuncReturnObject extends Record<string, unknown>,
RefreshTokenParams extends Record<string, unknown>,
>(
flow: Flow,
params: ZodType<AuthParams>,
@@ -81,8 +83,14 @@ export function createFlowConfig<
string,
{
access_token: string
refresh_token?: string
}
>
>,
refreshToken?: (
params: RefreshTokenParams
) => Promise<
E.Either<string, { access_token: string; refresh_token?: string }>
>
) {
return {
@@ -90,6 +98,7 @@ export function createFlowConfig<
params,
init,
onRedirectReceived,
refreshToken,
}
}