Custom Refresh on Client Side (#2376)

This commit is contained in:
pablodanswer
2024-09-13 00:04:03 -07:00
committed by GitHub
parent 6dd91414be
commit 31ca6857fb
9 changed files with 122 additions and 69 deletions

View File

@@ -58,6 +58,9 @@ ENV NEXT_PUBLIC_DO_NOT_USE_TOGGLE_OFF_DANSWER_POWERED=${NEXT_PUBLIC_DO_NOT_USE_T
ARG NEXT_PUBLIC_DISABLE_LOGOUT
ENV NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT}
ARG NEXT_PUBLIC_CUSTOM_REFRESH_URL
ENV NEXT_PUBLIC_CUSTOM_REFRESH_URL=${NEXT_PUBLIC_CUSTOM_REFRESH_URL}
RUN npx next build
@@ -116,6 +119,9 @@ ENV NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN=${NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN}
ARG NEXT_PUBLIC_DISABLE_LOGOUT
ENV NEXT_PUBLIC_DISABLE_LOGOUT=${NEXT_PUBLIC_DISABLE_LOGOUT}
ARG NEXT_PUBLIC_CUSTOM_REFRESH_URL
ENV NEXT_PUBLIC_CUSTOM_REFRESH_URL=${NEXT_PUBLIC_CUSTOM_REFRESH_URL}
# Note: Don't expose ports here, Compose will handle that for us if necessary.
# If you want to run this without compose, specify the ports to
# expose via cli

View File

@@ -6,6 +6,8 @@ import { Modal } from "../Modal";
import { useEffect, useState } from "react";
import { getSecondsUntilExpiration } from "@/lib/time";
import { User } from "@/lib/types";
import { mockedRefreshToken, refreshToken } from "./refreshUtils";
import { CUSTOM_REFRESH_URL } from "@/lib/constants";
export const HealthCheckBanner = () => {
const { error } = useSWR("/api/health", errorHandlingFetcher);
@@ -33,27 +35,32 @@ export const HealthCheckBanner = () => {
}, [user]);
useEffect(() => {
if (true) {
if (CUSTOM_REFRESH_URL) {
const refreshUrl = CUSTOM_REFRESH_URL;
let refreshTimeoutId: NodeJS.Timeout;
let expireTimeoutId: NodeJS.Timeout;
const refreshToken = async () => {
const attemptTokenRefresh = async () => {
try {
// NOTE: This is a mocked refresh token for testing purposes.
// const refreshTokenData = mockedRefreshToken();
const refreshTokenData = await refreshToken(refreshUrl);
const response = await fetch(
"/api/enterprise-settings/refresh-token",
{
method: "GET",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(refreshTokenData),
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
console.debug("Token refresh successful");
// Force revalidation of user data
await new Promise((resolve) => setTimeout(resolve, 4000));
await mutateUser(undefined, { revalidate: true });
updateExpirationTime();
@@ -65,7 +72,7 @@ export const HealthCheckBanner = () => {
const scheduleRefreshAndExpire = () => {
if (secondsUntilExpiration !== null) {
const timeUntilRefresh = (secondsUntilExpiration + 0.5) * 1000;
refreshTimeoutId = setTimeout(refreshToken, timeUntilRefresh);
refreshTimeoutId = setTimeout(attemptTokenRefresh, timeUntilRefresh);
const timeUntilExpire = (secondsUntilExpiration + 10) * 1000;
expireTimeoutId = setTimeout(() => {

View File

@@ -0,0 +1,59 @@
import { User } from "@/lib/types";
export interface CustomRefreshTokenResponse {
access_token: string;
refresh_token: string;
session: {
exp: number;
};
userinfo: {
sub: string;
familyName: string;
givenName: string;
fullName: string;
userId: string;
email: string;
};
}
export function mockedRefreshToken(): CustomRefreshTokenResponse {
/**
* This function mocks the response from a token refresh endpoint.
* It generates a mock access token, refresh token, and user information
* with an expiration time set to 1 hour from now.
* This is useful for testing or development when the actual refresh endpoint is not available.
*/
const mockExp = Date.now() + 3600000; // 1 hour from now in milliseconds
const data: CustomRefreshTokenResponse = {
access_token: "asdf Mock access token",
refresh_token: "asdf Mock refresh token",
session: { exp: mockExp },
userinfo: {
sub: "Mock email",
familyName: "Mock name",
givenName: "Mock name",
fullName: "Mock name",
userId: "Mock User ID",
email: "email@danswer.ai",
},
};
return data;
}
export async function refreshToken(
customRefreshUrl: string
): Promise<CustomRefreshTokenResponse> {
try {
console.debug("Sending request to custom refresh URL");
const url = new URL(customRefreshUrl);
url.searchParams.append("info", "json");
url.searchParams.append("access_token_refresh_interval", "3600");
const response = await fetch(url.toString());
return await response.json();
} catch (error) {
console.error("Error refreshing token:", error);
throw error;
}
}

View File

@@ -36,6 +36,7 @@ export const NEXT_PUBLIC_DEFAULT_SIDEBAR_OPEN =
export const TOGGLED_CONNECTORS_COOKIE_NAME = "toggled_connectors";
/* Enterprise-only settings */
export const CUSTOM_REFRESH_URL = process.env.NEXT_PUBLIC_CUSTOM_REFRESH_URL;
// NOTE: this should ONLY be used on the server-side. If used client side,
// it will not be accurate (will always be false).