> Markdown version of [Session Expiration](https://vaadin.com/docs/next/hilla/guides/security/session-expiration). Section index: [llms.txt](https://vaadin.com/docs/next/hilla/llms.txt)

# Handling Session Expiration

You can use the built-in [middleware](https://vaadin.com/docs/next/hilla/guides/client-middleware.md) `InvalidSessionMiddleWare` to detect when a user session expires.

This middleware requires a function as a constructor parameter (type of `OnInvalidSessionCallback`). The function should return a promise of `LoginResult`, containing the metadata of a login result, including:

- `error`

  Indicates whether the login attempt has failed.

- `token`

  In the event of a successful login, this is the cross-site request forgery (CSRF) prevention token, which can be extracted from the `index.html` page. See [CSRF protection of browser-callable services](https://vaadin.com/docs/next/hilla/guides/security/configuring.md#csrf) for more information.

- `errorTitle`

  A short text describing a login error.

- `errorMessage`

  A more detailed explanation of the login error.

## <a id="example"></a>Example

As an example, you can use the `InvalidSessionMiddleware` to show a login view to the user.

Configure the `ConnectClient` with the middleware, and let the callback open a login overlay:

`frontend/connect-client.ts`

`connect-client.ts`

```typescript
const client = new ConnectClient({
  prefix: 'connect',
  middlewares: [
    new InvalidSessionMiddleware(async () => {
      const { promptLogin } = await import('./SessionExpiredLoginOverlay');
      return promptLogin();
    }),
  ],
});

export default client;
```

The overlay reuses the `login` function from the [`useAuth` hook](https://vaadin.com/docs/next/hilla/guides/security/spring-login.md), and resolves the promise the middleware is waiting on once the user has signed in again:

`frontend/auth/SessionExpiredLoginOverlay.tsx`

`SessionExpiredLoginOverlay.tsx`

```tsx
const sessionExpired = signal(false);
const waiting: Array<(result: LoginResult) => void> = [];

// Called by the InvalidSessionMiddleware once per request that ran into the
// expired session. Opens the overlay, and resolves every waiting caller once
// the user has signed in again, so that all of those calls can be retried.
export async function promptLogin(): Promise<LoginResult> {
  sessionExpired.value = true;
  return new Promise((resolve) => {
    waiting.push(resolve);
  });
}

export default function SessionExpiredLoginOverlay() {
  const { login } = useAuth();
  const hasError = useSignal(false);

  return (
    <LoginOverlay
      opened={sessionExpired.value}
      error={hasError.value}
      noForgotPassword
      onErrorChanged={(event) => {
        hasError.value = event.detail.value;
      }}
      onLogin={async ({ detail: { username, password } }) => {
        const result = await login(username, password);
        hasError.value = result.error;
        if (!result.error) {
          sessionExpired.value = false;
          waiting.splice(0).forEach((resolve) => resolve(result));
        }
      }}
    />
  );
}
```

Render `SessionExpiredLoginOverlay` once in your main layout, so that it’s mounted for every view. It stays closed until the middleware reports an expired session, and the service call that triggered it proceeds after a successful login.
