import { OidcTokenRefresher } from 'matrix-js-sdk'; import type { IdTokenClaims } from 'oidc-client-ts'; import { OidcSessionMeta, setFallbackSession } from '../app/state/sessions'; /** * OidcTokenRefresher that persists rotated tokens back to the fallback session, * so a page reload keeps the freshest access/refresh token. The matrix client * calls this automatically (reactively on a 401) when a refresh token is set. */ export class LotusOidcTokenRefresher extends OidcTokenRefresher { private readonly deviceIdRef: string; private readonly userIdRef: string; private readonly baseUrlRef: string; private readonly oidcRef: OidcSessionMeta; constructor(oidc: OidcSessionMeta, deviceId: string, userId: string, baseUrl: string) { super( oidc.issuer, oidc.clientId, oidc.redirectUri, deviceId, (oidc.idTokenClaims ?? {}) as unknown as IdTokenClaims, ); this.deviceIdRef = deviceId; this.userIdRef = userId; this.baseUrlRef = baseUrl; this.oidcRef = oidc; } // F5 — persist the new expiry so the stored `expiresAt` stays fresh across // reloads instead of going stale. The SDK invokes persistTokens synchronously // inside the refresh and passes the freshly-refreshed `expiry` (a Date) on the // tokens object at runtime, even though its published type omits it — so read // it here directly (a doRefreshAccessToken override would run too late, since // persistTokens is called before that method returns). protected async persistTokens(tokens: { accessToken: string; refreshToken?: string; expiry?: Date; }): Promise { const expiresInMs = tokens.expiry instanceof Date ? Math.max(0, tokens.expiry.getTime() - Date.now()) : undefined; setFallbackSession(tokens.accessToken, this.deviceIdRef, this.userIdRef, this.baseUrlRef, { refreshToken: tokens.refreshToken, oidc: this.oidcRef, expiresInMs, }); } }