Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wfo-ui",
"version": "1.0.0",
"version": "1.19.1",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
108 changes: 98 additions & 10 deletions pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,69 @@ const token_endpoint_auth_method = OAUTH2_CLIENT_SECRET
? 'client_secret_basic'
: 'none';

const getCurrentDateInSeconds = () => Math.floor(Date.now() / 1000);

const calculateExpirationDate = (expiresIn?: number) => {
if (!expiresIn) {
return undefined;
}

return getCurrentDateInSeconds() + expiresIn;
};
const getWellKnownData = async () => {
const wellKnownUrl = new URL(OIDC_CONF_FULL_WELL_KNOWN_URL);
const wellKnown = await fetch(wellKnownUrl);
return await wellKnown.json();
};
async function refreshAccessToken(token: JWT): Promise<JWT> {
const { token_endpoint } = await getWellKnownData();

try {
const raw = {
client_id: OAUTH2_CLIENT_ID,
client_secret: OAUTH2_CLIENT_SECRET,
grant_type: 'refresh_token',
refresh_token: token.refreshToken as string,
};

const response = await fetch(token_endpoint, {
method: 'POST',
body: new URLSearchParams(raw),
});

if (response.ok) {
const data: {
access_token: string;
expires_in: number;
refresh_token: string;
refresh_expires_in: number;
} = await response.json();

return {
...token,
accessToken: data.access_token,
accessTokenExpiresAt: calculateExpirationDate(data.expires_in),
refreshToken: data.refresh_token,
refreshTokenExpiresAt: calculateExpirationDate(
data.refresh_expires_in,
),
};
} else {
console.error(
'An error occurred while refreshing the access token: ',
response.statusText,
);
return token;
}
} catch (error) {
console.error(
'An error occurred while refreshing the access token: ',
error,
);
return token;
}
}

const wfoProvider: OAuthConfig<WfoUserProfile> = {
id: NEXTAUTH_PROVIDER_ID,
name: NEXTAUTH_PROVIDER_NAME,
Expand Down Expand Up @@ -74,21 +137,46 @@ export const authOptions: AuthOptions = {
providers: isOauth2Enabled ? [wfoProvider] : [],
callbacks: {
async jwt({ token, account, profile }) {
// First time after signing in
// The "account" is only available right after signing in -- adding useful data to the token
if (account) {
token.accessToken = account.access_token;
token.profile = profile;
return {
...token,
accessToken: account.access_token,
refreshToken: account.refresh_token,
accessTokenExpiresAt: account.expires_at as number,
refreshTokenExpiresAt: calculateExpirationDate(
account.refresh_expires_in as number,
),
profile,
};
}
return token;

const now = getCurrentDateInSeconds();
if (
typeof token.accessTokenExpiresAt === 'number' &&
now < token.accessTokenExpiresAt
) {
return token;
}

return await refreshAccessToken(token);
},
async session({ session, token }: { session: WfoSession; token: JWT }) {
async session({
session,
token,
}: {
session: WfoSession;
token: JWT;
}): Promise<WfoSession> {
// Assign data to the session to be available in the client through the useSession hook
session.profile = token.profile as WfoUserProfile | undefined;
session.accessToken = token.accessToken
? String(token.accessToken)
: '';

return session;
return {
...session,
profile: token.profile as WfoUserProfile | undefined,
accessToken: token.accessToken ? String(token.accessToken) : '',
accessTokenExpiresAt: token.accessTokenExpiresAt as number,
refreshTokenExpiresAt: token.refreshTokenExpiresAt as number,
};
},
},
};
Expand Down