diff --git a/apps/dashboard/src/routes/+layout.svelte b/apps/dashboard/src/routes/+layout.svelte index fc18e553c..568a47a4e 100644 --- a/apps/dashboard/src/routes/+layout.svelte +++ b/apps/dashboard/src/routes/+layout.svelte @@ -88,7 +88,10 @@ dAppDefinitionAddress: CURRENT_NETWORK.dashboardDappAddress, networkId: CURRENT_NETWORK?.id, logger: Logger(1), - onDisconnect: () => updateAccounts([]) + onDisconnect: () => { + updateAccounts([]) + authApi.logout() + } }) rdt.walletApi.setRequestData( @@ -110,6 +113,11 @@ rdt.walletApi.walletData$.subscribe(({ accounts }) => { updateAccounts(accounts) + if (accounts.length > 0) { + authApi.renewAuthToken().mapErr((err) => { + rdt.disconnect() + }) + } }) resolveRDT(rdt) diff --git a/apps/dashboard/src/routes/api/auth/logout/+server.ts b/apps/dashboard/src/routes/api/auth/logout/+server.ts new file mode 100644 index 000000000..42c18dd32 --- /dev/null +++ b/apps/dashboard/src/routes/api/auth/logout/+server.ts @@ -0,0 +1,14 @@ +import { authController } from '@dashboard/server/auth/controller' +import type { RequestHandler } from './$types' +import { error, json } from '@sveltejs/kit' + +export const POST: RequestHandler = async ({ cookies }) => { + await authController.logout(cookies) + + return json( + {}, + { + status: 200 + } + ) +} diff --git a/apps/dashboard/src/server/auth/auth-api.ts b/apps/dashboard/src/server/auth/auth-api.ts index 0f321fced..5513522e6 100644 --- a/apps/dashboard/src/server/auth/auth-api.ts +++ b/apps/dashboard/src/server/auth/auth-api.ts @@ -16,6 +16,15 @@ export const authApi = { fetchWrapper<{ challenge: string }>( (() => serverFetch ?? fetch)()('/api/auth/challenge') ).map(({ data }) => data.challenge), + logout: (serverFetch?: typeof fetch) => + fetchWrapper<{ authToken: string }>( + (() => serverFetch ?? fetch)()('/api/auth/logout', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }) + ), renewAuthToken: (serverFetch?: typeof fetch) => fetchWrapper<{ authToken: string }>( (() => serverFetch ?? fetch)()('/api/auth/renew', { diff --git a/apps/dashboard/src/server/auth/controller.ts b/apps/dashboard/src/server/auth/controller.ts index bd865bdac..62b419950 100644 --- a/apps/dashboard/src/server/auth/controller.ts +++ b/apps/dashboard/src/server/auth/controller.ts @@ -6,7 +6,7 @@ import { Rola } from '@common/rola' import { SignedChallenge } from '@common/rdt' import type { GatewayApiClient } from '@common/gateway-sdk' import { CURRENT_NETWORK } from '@networks' -import { err, errAsync } from 'neverthrow' +import { err, errAsync, okAsync } from 'neverthrow' import { OAuth2 } from './oauth2' import { UserModel } from '../user/model' import type { Cookies } from '@sveltejs/kit' @@ -91,9 +91,14 @@ export const AuthController = ({ : err({ reason: 'invalidToken' }) } + const logout = (cookies: Cookies) => { + return oAuth2.logout(cookies) + } + return { createChallenge, login, + logout, renewAuthToken, isValid: verifyAuthToken } diff --git a/apps/dashboard/src/server/auth/oauth2.ts b/apps/dashboard/src/server/auth/oauth2.ts index 0664947f6..fc36bf1f8 100644 --- a/apps/dashboard/src/server/auth/oauth2.ts +++ b/apps/dashboard/src/server/auth/oauth2.ts @@ -14,8 +14,8 @@ export type OAuth2 = ReturnType export const OAuth2 = (input?: Partial) => { const { secret, refreshToken, authToken }: OAuth2Input = { secret: JWT_SECRET, - refreshToken: { expiresIn: '30d', key: 'jwt' }, - authToken: { expiresIn: '10m' }, + refreshToken: { expiresIn: '90d', key: 'jwt' }, + authToken: { expiresIn: '90d' }, ...(input || {}) } @@ -84,16 +84,21 @@ export const OAuth2 = (input?: Partial) => { const ONE_DAY = 1000 * 60 * 60 * 24 return { httpOnly: true, - expires: new Date(Date.now() + ONE_DAY), + expires: new Date(Date.now() + ONE_DAY * 90), sameSite: 'lax', path: '/' } } + const logout = (cookies: Cookies) => { + cookies.delete(refreshToken.key, createRefreshTokenOptions()) + } + return { createTokens, rotateRefreshToken, renewAuthToken, + logout, createRefreshTokenCookie, verifyToken }