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
5 changes: 5 additions & 0 deletions .zed/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"format_on_save": "off",
"remove_trailing_whitespace_on_save": false,
"ensure_final_newline_on_save": false
}
112 changes: 55 additions & 57 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,16 @@ const redisClient = redisService.getClient();
function initHealthChecks() {
healthCheckRegistry.register({
serviceName: 'OBP API',
url: `${PUBLIC_OBP_BASE_URL}/obp/v5.1.0/root`,
url: `${PUBLIC_OBP_BASE_URL}/obp/v5.1.0/root`
});

healthCheckRegistry.register({
serviceName: 'Opey II',
url: `${env.OPEY_BASE_URL}/status`,
url: `${env.OPEY_BASE_URL}/status`
});

const redisHealthCheck = new RedisHealthCheckService();
healthCheckRegistry.register(redisHealthCheck)
healthCheckRegistry.register(redisHealthCheck);

const oauthHealthChecks = oauth2ProviderManager.getHealthCheckEntries();
for (const check of oauthHealthChecks) {
Expand All @@ -79,7 +79,6 @@ await oauth2ProviderManager.start();

initHealthChecks();


async function initWebUIProps() {
try {
const webuiProps = await obp_requests.get('/obp/v5.1.0/webui-props');
Expand All @@ -91,58 +90,58 @@ async function initWebUIProps() {
}
}


function needsAuthorization(routeId: string): boolean {
// protected routes are put in the /(protected)/ route group
return routeId.startsWith('/(protected)/');
}

const checkSessionValidity: Handle = async ({ event, resolve }) => {
const session = event.locals.session;
if (session.data.user) {
// Here you can add additional checks if needed
// For example, check if the session has expired based on your own logic
// or if certain required data is present in the session
const sessionOAuth = SessionOAuthHelper.getSessionOAuth(session);
if (!sessionOAuth) {
logger.warn('No valid OAuth data found in session. Destroying session.');
await session.destroy();

// Redirect to trigger a fresh load instead of just resolving
throw redirect(302, event.url.pathname);
}

const sessionExpired = await sessionOAuth.client.checkAccessTokenExpiration(sessionOAuth.accessToken)
// Check if the access token is expired,
// if it is, attempt to refresh it
if (sessionExpired) {
// will return true if the token is expired
try {
await SessionOAuthHelper.refreshAccessToken(session);
return await resolve(event);
} catch (error) {
logger.info(
'Token refresh failed - redirecting user to login (normal OAuth behavior):',
error
);
// If the refresh fails, redirect to login
// Destroy the session
logger.info('Destroying expired session.');
await session.destroy();
// Redirect to trigger a fresh load and clear client-side cache
throw redirect(302, event.url.pathname);
}
}

// If we reach here, the session is valid (either not expired or successfully refreshed)
logger.debug('Session is valid for user:', session.data.user?.username);
return await resolve(event);

}

// Always return a response, even when there's no session
return await resolve(event);
}
const session = event.locals.session;
if (session.data.user) {
// Here you can add additional checks if needed
// For example, check if the session has expired based on your own logic
// or if certain required data is present in the session
const sessionOAuth = SessionOAuthHelper.getSessionOAuth(session);
if (!sessionOAuth) {
logger.warn('No valid OAuth data found in session. Destroying session.');
await session.destroy();

// Redirect to trigger a fresh load instead of just resolving
throw redirect(302, event.url.pathname);
}

const sessionExpired = await sessionOAuth.client.checkAccessTokenExpiration(
sessionOAuth.accessToken
);
// Check if the access token is expired,
// if it is, attempt to refresh it
if (sessionExpired) {
// will return true if the token is expired
try {
await SessionOAuthHelper.refreshAccessToken(session);
return await resolve(event);
} catch (error) {
logger.info(
'Token refresh failed - redirecting user to login (normal OAuth behavior):',
error
);
// If the refresh fails, redirect to login
// Destroy the session
logger.info('Destroying expired session.');
await session.destroy();
// Redirect to trigger a fresh load and clear client-side cache
throw redirect(302, event.url.pathname);
}
}

// If we reach here, the session is valid (either not expired or successfully refreshed)
logger.debug('Session is valid for user:', session.data.user?.username);
return await resolve(event);
}

// Always return a response, even when there's no session
return await resolve(event);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a formatting change

};

// Middleware to check user authorization
const checkAuthorization: Handle = async ({ event, resolve }) => {
Expand All @@ -152,10 +151,9 @@ const checkAuthorization: Handle = async ({ event, resolve }) => {
if (!!routeId && needsAuthorization(routeId)) {
logger.debug('Checking authorization for user route:', event.url.pathname);
if (!oauth2ProviderManager.isReady()) {
logger.warn('OAuth2 providers not ready:', oauth2ProviderManager.getStatus().error);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep the bubble up of the error from the provider manager here

logger.warn('OAuth2 providers not ready');
throw error(503, 'Service Unavailable. Please try again later.');
}


if (!session || !session.data.user) {
// Redirect to login page if not authenticated
Expand All @@ -177,7 +175,7 @@ const checkAuthorization: Handle = async ({ event, resolve }) => {

// Init SvelteKitSessions
export const handle: Handle = sequence(
sveltekitSessionHandle({
sveltekitSessionHandle({
secret: 'secret',
store: new RedisStore({ client: redisClient })
}),
Expand All @@ -193,16 +191,16 @@ declare module 'svelte-kit-sessions' {
user_id: string;
email: string;
username: string;
entitlements: {
entitlements: {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Various unnecessary formatting changes, can leave them in this time I guess

list: Array<{
entitlement_id: string;
role_name: string;
bank_id: string;
}>
}
}>;
};
views: {
list: object[];
}
};
};
oauth?: {
access_token: string;
Expand Down
18 changes: 9 additions & 9 deletions src/lib/components/OpeyChat.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
let authPipOpenState = $state(false);

// async function formatAuthStatusPip(session: SessionSnapshot, consentInfo?: OBPConsentInfo): {
// const
// const
// }

async function sendMessage(text: string) {
Expand Down Expand Up @@ -255,7 +255,7 @@
*/
async function upgradeSession() {
if (!userAuthenticated) {
window.location.href = '/login/obp';
window.location.href = '/login';
return;
}

Expand Down Expand Up @@ -376,7 +376,7 @@
{#if options.displayConnectionPips}
<div class="flex flex-col items-center">
<!-- Connection Pip with Tooltip -->
<Tooltip
<Tooltip
classes="z-10"
positioning={{ placement: 'top' }}
contentBase="card bg-primary-200-800 text-xs p-1"
Expand All @@ -392,7 +392,7 @@
{#snippet content()}Opey Connection Status: {connectionStatusString}{/snippet}
</Tooltip>
<!-- Authentication Pip with Tooltip -->
<Tooltip
<Tooltip
classes="z-10"
open={authPipOpenState}
contentBase="card bg-primary-200-800 text-xs p-1"
Expand Down Expand Up @@ -440,7 +440,7 @@
alt="Opey Avatar"
class="absolute top-1/10 left-0 size-12 -translate-x-17 rounded-full drop-shadow-[-7px_7px_10px_var(--color-secondary-500)]"
/>

<!-- Text area - no bottom border radius when expanded -->
<textarea
bind:value={messageInput}
Expand All @@ -452,7 +452,7 @@
oninput={autoResize}
rows="1"
></textarea>

<!-- Single-line mode controls -->
{#if messageInput.length > 0 && !isMultiline}
<button
Expand All @@ -468,15 +468,15 @@
{@render statusPips(session, options.currentConsentInfo)}
</div>
{/if}

<!-- Footer - visually connected to textarea when multiline -->
{#if isMultiline}
<div class="flex justify-between items-center w-full p-2 pt-0 bg-primary-50 dark:bg-primary-600 rounded-b-lg">
<div>
<!-- Placeholder for future buttons (like file upload) -->
<!-- <button class="btn variant-ghost-primary">Add File +</button> -->
</div>

<div class="flex items-center gap-2">
<button
class="btn btn-primary"
Expand All @@ -485,7 +485,7 @@
>
<CircleArrowUp class="h-7 w-7" />
</button>

{@render statusPips(session, options.currentConsentInfo)}
</div>
</div>
Expand Down
Loading