From 6977f8011351e14cb188ca44c0148e959c605568 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Youn=C3=A8s=20Adem?= <63497031+admy7@users.noreply.github.com> Date: Wed, 15 May 2024 18:38:27 +0200 Subject: [PATCH] 225/refresh token error (#252) fix: refresh access token --- .../applications/[id]/attachments/route.ts | 3 +- src/app/api/applications/[id]/route.ts | 3 +- .../[id]/save-forms-and-duos/route.ts | 3 +- src/app/api/applications/[id]/submit/route.ts | 3 +- src/app/api/applications/route.ts | 3 +- src/app/api/auth/[...nextauth]/route.ts | 2 +- src/app/api/auth/auth.ts | 54 ++++++++ src/app/api/auth/auth.types.ts | 18 +++ src/app/api/auth/config.ts | 52 ++++++++ src/app/api/auth/logout/route.ts | 4 +- src/app/api/datasets/route.ts | 5 +- src/components/Header/Avatar.tsx | 2 +- src/components/Header/index.tsx | 2 +- .../backend/addAttachmentToApplication.ts | 2 +- .../daam/backend/createApplication.ts | 2 +- src/services/daam/backend/listApplications.ts | 2 +- .../daam/backend/retrieveApplication.ts | 2 +- src/services/daam/backend/saveFormAndDuos.ts | 2 +- .../daam/backend/submitApplication.ts | 2 +- .../discovery/__tests__/datasetGet.test.ts | 4 +- src/services/discovery/datasetGet.ts | 2 +- src/services/discovery/datasetList.ts | 4 +- src/services/discovery/utils.ts | 2 +- src/utils/auth.ts | 122 ------------------ src/utils/logout.ts | 11 ++ 25 files changed, 166 insertions(+), 145 deletions(-) create mode 100644 src/app/api/auth/auth.ts create mode 100644 src/app/api/auth/auth.types.ts create mode 100644 src/app/api/auth/config.ts delete mode 100644 src/utils/auth.ts create mode 100644 src/utils/logout.ts diff --git a/src/app/api/applications/[id]/attachments/route.ts b/src/app/api/applications/[id]/attachments/route.ts index 7230a125..9561ec20 100644 --- a/src/app/api/applications/[id]/attachments/route.ts +++ b/src/app/api/applications/[id]/attachments/route.ts @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; +import { authOptions } from '@/app/api/auth/config'; import { handleErrorResponse } from '@/app/api/errorHandling'; import { addAttachmentToApplication } from '@/services/daam/index.server'; -import { ExtendedSession, authOptions } from '@/utils/auth'; import { getServerSession } from 'next-auth'; import { NextResponse } from 'next/server'; diff --git a/src/app/api/applications/[id]/route.ts b/src/app/api/applications/[id]/route.ts index 2afc30d9..dc264e90 100644 --- a/src/app/api/applications/[id]/route.ts +++ b/src/app/api/applications/[id]/route.ts @@ -3,9 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 import { retrieveApplication } from '@/services/daam/index.server'; -import { ExtendedSession, authOptions } from '@/utils/auth'; import { getServerSession } from 'next-auth'; import { NextResponse } from 'next/server'; +import { ExtendedSession } from '../../auth/auth.types'; +import { authOptions } from '../../auth/config'; import { handleErrorResponse } from '../../errorHandling'; export async function GET(request: Request, params: { params: { id: string } }) { diff --git a/src/app/api/applications/[id]/save-forms-and-duos/route.ts b/src/app/api/applications/[id]/save-forms-and-duos/route.ts index 85d72ce8..4b228fc1 100644 --- a/src/app/api/applications/[id]/save-forms-and-duos/route.ts +++ b/src/app/api/applications/[id]/save-forms-and-duos/route.ts @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; +import { authOptions } from '@/app/api/auth/config'; import { handleErrorResponse } from '@/app/api/errorHandling'; import { saveFormAndDuos } from '@/services/daam/index.server'; -import { ExtendedSession, authOptions } from '@/utils/auth'; import { getServerSession } from 'next-auth'; import { NextResponse } from 'next/server'; diff --git a/src/app/api/applications/[id]/submit/route.ts b/src/app/api/applications/[id]/submit/route.ts index 2142dece..c2ebfad1 100644 --- a/src/app/api/applications/[id]/submit/route.ts +++ b/src/app/api/applications/[id]/submit/route.ts @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; +import { authOptions } from '@/app/api/auth/config'; import { handleErrorResponse } from '@/app/api/errorHandling'; import { submitApplication } from '@/services/daam/index.server'; -import { ExtendedSession, authOptions } from '@/utils/auth'; import { AxiosResponse } from 'axios'; import { getServerSession } from 'next-auth'; import { NextResponse } from 'next/server'; diff --git a/src/app/api/applications/route.ts b/src/app/api/applications/route.ts index 1f5cfaa1..d379d070 100644 --- a/src/app/api/applications/route.ts +++ b/src/app/api/applications/route.ts @@ -3,10 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 import { createApplication, listApplications } from '@/services/daam/index.server'; -import { ExtendedSession, authOptions } from '@/utils/auth'; import { getServerSession } from 'next-auth'; import { NextResponse } from 'next/server'; +import { ExtendedSession } from '../auth/auth.types'; import { handleErrorResponse } from '../errorHandling'; +import { authOptions } from '../auth/config'; export async function POST(request: Request) { const session: ExtendedSession | null = await getServerSession(authOptions); diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index 6a0339f0..293d0dde 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: Apache-2.0 -import { authOptions } from '@/utils/auth'; import NextAuth from 'next-auth/next'; +import { authOptions } from '../config'; const handler = NextAuth(authOptions); diff --git a/src/app/api/auth/auth.ts b/src/app/api/auth/auth.ts new file mode 100644 index 00000000..82fa2698 --- /dev/null +++ b/src/app/api/auth/auth.ts @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2024 PNED G.I.E. +// +// SPDX-License-Identifier: Apache-2.0 + +import { decrypt } from '@/utils/encryption'; +import { jwtDecode } from 'jwt-decode'; +import { Account, getServerSession } from 'next-auth'; +import type { JWT } from 'next-auth/jwt'; +import { ExtendedSession } from './auth.types'; +import { authOptions } from './config'; + +export async function getToken(tokenType: 'access_token' | 'id_token') { + const session = (await getServerSession(authOptions)) as ExtendedSession; + if (session) { + const tokenDecrypted = decrypt(session[tokenType]!); + return tokenDecrypted; + } + return null; +} + +export function completeTokenWithAccountInfo(token: JWT, account: Account): JWT { + return { + ...token, + decoded: jwtDecode(account.access_token!) as string, + access_token: account.access_token as string, + id_token: account.id_token as string, + refresh_token: account.refresh_token as string, + expires_at: account.expires_at as number, + }; +} + +export async function refreshAccessToken(token: JWT) { + const response = await fetch(`${process.env.REFRESH_TOKEN_URL}`, { + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ + client_id: `${process.env.KEYCLOAK_CLIENT_ID}`, + client_secret: `${process.env.KEYCLOAK_CLIENT_SECRET}`, + grant_type: 'refresh_token', + refresh_token: token.refresh_token as string, + }), + method: 'POST', + cache: 'no-cache', + }); + const refreshToken = await response.json(); + + return { + ...token, + access_token: refreshToken.access_token, + id_token: refreshToken.id_token, + decoded: jwtDecode(refreshToken.access_token), + expires_at: Math.floor(Date.now() / 1000) + refreshToken.expires_in, + refresh_token: refreshToken.refresh_token, + }; +} diff --git a/src/app/api/auth/auth.types.ts b/src/app/api/auth/auth.types.ts new file mode 100644 index 00000000..0403c680 --- /dev/null +++ b/src/app/api/auth/auth.types.ts @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 PNED G.I.E. +// +// SPDX-License-Identifier: Apache-2.0 + +import { Account, Session } from 'next-auth'; +import { JWT } from 'next-auth/jwt'; + +export type ExtendedSession = Session & { id_token: string; access_token: string; error?: string }; + +export type JWTCallbackEntry = { + token: JWT; + account: Account | null; +}; + +export type SessionCallbackEntry = { + token: JWT; + session: Session; +}; diff --git a/src/app/api/auth/config.ts b/src/app/api/auth/config.ts new file mode 100644 index 00000000..488bbc22 --- /dev/null +++ b/src/app/api/auth/config.ts @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2024 PNED G.I.E. +// +// SPDX-License-Identifier: Apache-2.0 + +import { encrypt } from '@/utils/encryption'; +import { keycloackSessionLogOut } from '@/utils/logout'; +import type { NextAuthOptions } from 'next-auth'; +import Keycloack from 'next-auth/providers/keycloak'; +import { signOut } from 'next-auth/react'; +import { completeTokenWithAccountInfo, refreshAccessToken } from './auth'; +import { JWTCallbackEntry, SessionCallbackEntry } from './auth.types'; + +export const authOptions: NextAuthOptions = { + providers: [ + Keycloack({ + clientId: `${process.env.KEYCLOAK_CLIENT_ID}`, + clientSecret: `${process.env.KEYCLOAK_CLIENT_SECRET}`, + issuer: process.env.KEYCLOAK_ISSUER_URL, + authorization: { params: { scope: 'openid profile email elixir_id' } }, + }), + ], + callbacks: { + async jwt({ token, account }: JWTCallbackEntry) { + const currTimestamp = Math.floor(Date.now() / 1000); + const isTokenExpired = (token?.expires_at as number) < currTimestamp; + + if (account) { + return completeTokenWithAccountInfo(token, account); + } else if (isTokenExpired) { + try { + const refreshedToken = await refreshAccessToken(token); + return refreshedToken; + } catch (error) { + keycloackSessionLogOut().then(() => signOut({ callbackUrl: '/' })); + throw new Error('Could not refresh the token. Logging out...'); + } + } else { + return token; + } + }, + + async session({ session, token }: SessionCallbackEntry) { + return { + ...session, + access_token: encrypt(token.access_token as string), + id_token: encrypt(token.id_token as string), + roles: (token.decoded as { realm_access?: { roles?: string[] } }).realm_access?.roles, + error: token.error, + }; + }, + }, +}; diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts index 315c2ee6..0f10e30a 100644 --- a/src/app/api/auth/logout/route.ts +++ b/src/app/api/auth/logout/route.ts @@ -2,8 +2,9 @@ // // SPDX-License-Identifier: Apache-2.0 -import { authOptions, getToken } from '@/utils/auth'; import { getServerSession } from 'next-auth'; +import { getToken } from '../auth'; +import { authOptions } from '../config'; export async function GET() { const session = await getServerSession(authOptions); @@ -15,6 +16,7 @@ export async function GET() { try { await fetch(url); } catch (err) { + console.error(`Could not log out from Keycloak`, err); return new Response(null, { status: 500 }); } } diff --git a/src/app/api/datasets/route.ts b/src/app/api/datasets/route.ts index 220ac42e..a5cfdf39 100644 --- a/src/app/api/datasets/route.ts +++ b/src/app/api/datasets/route.ts @@ -4,10 +4,11 @@ import { datasetList } from '@/services/discovery'; import { mapFacetGroups } from '@/services/discovery/utils'; -import { ExtendedSession, authOptions } from '@/utils/auth'; +import axios from 'axios'; import { getServerSession } from 'next-auth'; import { NextResponse } from 'next/server'; -import axios from 'axios'; +import { ExtendedSession } from '../auth/auth.types'; +import { authOptions } from '../auth/config'; export async function POST(request: Request) { const session: ExtendedSession | null = await getServerSession(authOptions); diff --git a/src/components/Header/Avatar.tsx b/src/components/Header/Avatar.tsx index ad07e584..441312ba 100644 --- a/src/components/Header/Avatar.tsx +++ b/src/components/Header/Avatar.tsx @@ -10,7 +10,7 @@ import { DropdownMenuTrigger, } from "@/components/shadcn/dropdown-menu"; import { User } from "@/types/user.types"; -import { keycloackSessionLogOut } from "@/utils/auth"; +import { keycloackSessionLogOut } from "@/utils/logout"; import { faSignOut } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { signOut } from "next-auth/react"; diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 9dc8535d..23ddd307 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -9,7 +9,7 @@ import { SCREEN_SIZE } from "@/hooks/useWindowSize"; import { useDatasetBasket } from "@/providers/DatasetBasketProvider"; import logo from "@/public/egdi-logo-horizontal-full-color-rgb.svg"; import { User } from "@/types/user.types"; -import { keycloackSessionLogOut } from "@/utils/auth"; +import { keycloackSessionLogOut } from "@/utils/logout"; import { faBars, faDatabase, diff --git a/src/services/daam/backend/addAttachmentToApplication.ts b/src/services/daam/backend/addAttachmentToApplication.ts index 97cf4f5e..1fcc855b 100644 --- a/src/services/daam/backend/addAttachmentToApplication.ts +++ b/src/services/daam/backend/addAttachmentToApplication.ts @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; import { AddedAttachment } from '@/types/application.types'; -import { ExtendedSession } from '@/utils/auth'; import { decrypt } from '@/utils/encryption'; import axios, { AxiosResponse } from 'axios'; diff --git a/src/services/daam/backend/createApplication.ts b/src/services/daam/backend/createApplication.ts index 6d5c47cb..a8e55c7e 100644 --- a/src/services/daam/backend/createApplication.ts +++ b/src/services/daam/backend/createApplication.ts @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; import { CreateApplicationResponse } from '@/types/application.types'; -import { ExtendedSession } from '@/utils/auth'; import { decrypt } from '@/utils/encryption'; import axios, { AxiosResponse } from 'axios'; diff --git a/src/services/daam/backend/listApplications.ts b/src/services/daam/backend/listApplications.ts index 66cc9913..4bc1989f 100644 --- a/src/services/daam/backend/listApplications.ts +++ b/src/services/daam/backend/listApplications.ts @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; import { ListedApplication } from '@/types/application.types'; -import { ExtendedSession } from '@/utils/auth'; import { decrypt } from '@/utils/encryption'; import axios, { AxiosResponse } from 'axios'; diff --git a/src/services/daam/backend/retrieveApplication.ts b/src/services/daam/backend/retrieveApplication.ts index c9ab317f..83ad2245 100644 --- a/src/services/daam/backend/retrieveApplication.ts +++ b/src/services/daam/backend/retrieveApplication.ts @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; import { RetrievedApplication } from '@/types/application.types'; -import { ExtendedSession } from '@/utils/auth'; import { decrypt } from '@/utils/encryption'; import axios, { AxiosResponse } from 'axios'; diff --git a/src/services/daam/backend/saveFormAndDuos.ts b/src/services/daam/backend/saveFormAndDuos.ts index c2e43881..3e1ad90c 100644 --- a/src/services/daam/backend/saveFormAndDuos.ts +++ b/src/services/daam/backend/saveFormAndDuos.ts @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; import { SaveDUOCode, SaveForm } from '@/types/application.types'; -import { ExtendedSession } from '@/utils/auth'; import { decrypt } from '@/utils/encryption'; import axios, { AxiosResponse } from 'axios'; diff --git a/src/services/daam/backend/submitApplication.ts b/src/services/daam/backend/submitApplication.ts index aba3a08a..7fa48ac4 100644 --- a/src/services/daam/backend/submitApplication.ts +++ b/src/services/daam/backend/submitApplication.ts @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; import { ErrorResponse, ValidationWarnings } from '@/types/api.types'; -import { ExtendedSession } from '@/utils/auth'; import { decrypt } from '@/utils/encryption'; import axios, { AxiosResponse } from 'axios'; diff --git a/src/services/discovery/__tests__/datasetGet.test.ts b/src/services/discovery/__tests__/datasetGet.test.ts index 161c6c8c..8a2479c4 100644 --- a/src/services/discovery/__tests__/datasetGet.test.ts +++ b/src/services/discovery/__tests__/datasetGet.test.ts @@ -2,12 +2,12 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; +import { encrypt } from '@/utils/encryption'; import { jest } from '@jest/globals'; import axios from 'axios'; import { makeDatasetGet } from '../datasetGet'; import { retrivedDatasetFixture } from '../fixtures/datasetFixtures'; -import { ExtendedSession } from '@/utils/auth'; -import { encrypt } from '@/utils/encryption'; jest.mock('axios'); const mockedAxios = axios as jest.Mocked; diff --git a/src/services/discovery/datasetGet.ts b/src/services/discovery/datasetGet.ts index 0cea88a7..d699fe6c 100644 --- a/src/services/discovery/datasetGet.ts +++ b/src/services/discovery/datasetGet.ts @@ -2,10 +2,10 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; import axios from 'axios'; import { RetrievedDataset } from './types/dataset.types'; import { createHeaders } from './utils'; -import { ExtendedSession } from '@/utils/auth'; export const makeDatasetGet = (discoveryUrl: string) => { return async (id: string, session?: ExtendedSession): Promise => { diff --git a/src/services/discovery/datasetList.ts b/src/services/discovery/datasetList.ts index d05fe382..0b434f57 100644 --- a/src/services/discovery/datasetList.ts +++ b/src/services/discovery/datasetList.ts @@ -2,10 +2,10 @@ // // SPDX-License-Identifier: Apache-2.0 +import { ExtendedSession } from '@/app/api/auth/auth.types'; import axios, { AxiosResponse } from 'axios'; -import { DatasetSearchQuery, DatasetSearchOptions, DatasetsSearchResponse } from './types/datasetSearch.types'; +import { DatasetSearchOptions, DatasetSearchQuery, DatasetsSearchResponse } from './types/datasetSearch.types'; import { createHeaders } from './utils'; -import { ExtendedSession } from '@/utils/auth'; export const makeDatasetList = (discoveryUrl: string) => { return async (options: DatasetSearchOptions, session?: ExtendedSession): Promise> => { diff --git a/src/services/discovery/utils.ts b/src/services/discovery/utils.ts index b3a000bb..5f59a9f5 100644 --- a/src/services/discovery/utils.ts +++ b/src/services/discovery/utils.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2024 PNED G.I.E. // // SPDX-License-Identifier: Apache-2.0 -import { ExtendedSession } from '@/utils/auth'; +import { ExtendedSession } from '@/app/api/auth/auth.types'; import { decrypt } from '@/utils/encryption'; import { DatasetSearchQuery, FacetGroup, facetToLabelMapping } from './types/datasetSearch.types'; diff --git a/src/utils/auth.ts b/src/utils/auth.ts deleted file mode 100644 index 0deb2a58..00000000 --- a/src/utils/auth.ts +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-FileCopyrightText: 2024 PNED G.I.E. -// -// SPDX-License-Identifier: Apache-2.0 - -import { encrypt } from '@/utils/encryption'; -import { jwtDecode } from 'jwt-decode'; -import type { NextAuthOptions, Session } from 'next-auth'; -import { Account, getServerSession } from 'next-auth'; -import type { JWT } from 'next-auth/jwt'; -import Keycloack from 'next-auth/providers/keycloak'; -import { signOut } from 'next-auth/react'; -import { decrypt } from './encryption'; - -export type ExtendedSession = Session & { id_token: string; access_token: string; error?: string }; - -type JWTCallbackEntry = { - token: JWT; - account: Account | null; -}; - -type SessionCallbackEntry = { - token: JWT; - session: Session; -}; - -export const authOptions: NextAuthOptions = { - providers: [ - Keycloack({ - clientId: `${process.env.KEYCLOAK_CLIENT_ID}`, - clientSecret: `${process.env.KEYCLOAK_CLIENT_SECRET}`, - issuer: process.env.KEYCLOAK_ISSUER_URL, - authorization: { params: { scope: 'openid profile email elixir_id' } }, - }), - ], - callbacks: { - async jwt({ token, account }: JWTCallbackEntry) { - const currTimestamp = Math.floor(Date.now() / 1000); - const isTokenExpired = token?.expires_at && (token?.expires_at as number) <= currTimestamp; - - if (account) { - return completeTokenWithAccountInfo(token, account); - } else if (!isTokenExpired) { - return token; - } else { - try { - const refreshedToken = await refreshAccessToken(token); - return refreshedToken; - } catch (error) { - return { ...token, error: 'RefreshAccessTokenError' }; - } - } - }, - - async session({ session, token }: SessionCallbackEntry) { - return { - ...session, - access_token: encrypt(token.access_token as string), - id_token: encrypt(token.id_token as string), - roles: (token.decoded as { realm_access?: { roles?: string[] } }).realm_access?.roles, - error: token.error, - }; - }, - }, -}; - -export async function getToken(tokenType: 'access_token' | 'id_token') { - const session = (await getServerSession(authOptions)) as ExtendedSession; - if (session) { - const tokenDecrypted = decrypt(session[tokenType]!); - return tokenDecrypted; - } - return null; -} - -export function completeTokenWithAccountInfo(token: JWT, account: Account): JWT { - return { - ...token, - decoded: jwtDecode(account.access_token!) as string, - access_token: account.access_token as string, - id_token: account.id_token as string, - refresh_token: account.refresh_token as string, - expires_at: account.expires_at as number, - }; -} - -export async function refreshAccessToken(token: JWT) { - const response = await fetch(`${process.env.REFRESH_TOKEN_URL}`, { - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: new URLSearchParams({ - client_id: `${process.env.KEYCLOAK_CLIENT_ID}`, - client_secret: `${process.env.KEYCLOAK_CLIENT_SECRET}`, - grant_type: 'refresh_token', - refresh_token: token.refresh_token as string, - }), - method: 'POST', - }); - const refreshToken = await response.json(); - if (!response.ok) throw refreshToken; - - return { - ...token, - access_token: refreshToken.access_token, - id_token: refreshToken.id_token, - decoded: jwtDecode(refreshToken.access_token), - expires_at: Math.floor(Date.now() / 1000) + refreshToken.expires_in, - refresh_token: refreshToken.refresh_token, - }; -} - -export async function keycloackSessionLogOut() { - try { - await fetch('/api/auth/logout'); - } catch (error) { - throw new Error(`Could not log out from Keycloak: ${error}`); - } -} - -export function logOutIfSessionError(session: ExtendedSession | null, status: string) { - if (session && status !== 'loading' && session?.error) { - signOut({ callbackUrl: '/' }); - } -} diff --git a/src/utils/logout.ts b/src/utils/logout.ts new file mode 100644 index 00000000..f818a60c --- /dev/null +++ b/src/utils/logout.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 PNED G.I.E. +// +// SPDX-License-Identifier: Apache-2.0 + +export async function keycloackSessionLogOut() { + try { + await fetch('/api/auth/logout'); + } catch (error) { + throw new Error(`Could not log out from Keycloak`); + } +}