feat: ability to refresh tokens for oauth flows (#4302)
Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user