feat(auth): OIDC phase 4/5/6 — token refresh, logout revocation, account link

- initMatrix.ts: import the shared Session type; when a session has a refresh
  token + oidc metadata, wire a LotusOidcTokenRefresher via createClient's
  refreshToken + tokenRefreshFunction (reactive 401 refresh). Rust crypto is
  unaffected (still keyed on userId/deviceId).
- client/oidcTokenRefresher.ts: OidcTokenRefresher subclass that persists rotated
  tokens back to the fallback session.
- client/oidcLogout.ts + logoutClient: best-effort revoke access+refresh tokens at
  the issuer's revocation_endpoint on logout (tolerant of failure).
- settings/account/OidcManageAccount.tsx: MSC2965 "Manage account" deep-link,
  shown only when authMetadata is present (OIDC servers); mirrors OtherDevices.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 16:12:13 -04:00
parent dd6b0bccb3
commit 67bd05fc96
5 changed files with 141 additions and 8 deletions
@@ -5,6 +5,7 @@ import { MatrixId } from './MatrixId';
import { Profile } from './Profile';
import { ContactInformation } from './ContactInfo';
import { IgnoredUserList } from './IgnoredUserList';
import { OidcManageAccount } from './OidcManageAccount';
type AccountProps = {
requestClose: () => void;
@@ -32,6 +33,7 @@ export function Account({ requestClose }: AccountProps) {
<Box direction="Column" gap="700">
<Profile />
<MatrixId />
<OidcManageAccount />
<ContactInformation />
<IgnoredUserList />
</Box>
@@ -0,0 +1,49 @@
import React, { useCallback } from 'react';
import { Box, Chip, Text } from 'folds';
import { SequenceCard } from '../../../components/sequence-card';
import { SequenceCardStyle } from '../styles.css';
import { SettingTile } from '../../../components/setting-tile';
import { useAuthMetadata } from '../../../hooks/useAuthMetadata';
import { useAccountManagementActions } from '../../../hooks/useAccountManagement';
import { withSearchParam } from '../../../pages/pathUtils';
/**
* On OIDC/next-gen-auth servers, profile/password/sessions are managed by the
* authentication service — surface a deep-link to its account page (MSC2965
* account-management URL). Renders nothing on password/legacy-SSO servers, where
* `useAuthMetadata()` is undefined.
*/
export function OidcManageAccount() {
const authMetadata = useAuthMetadata();
const accountManagementActions = useAccountManagementActions();
const open = useCallback(() => {
const authUrl = authMetadata?.account_management_uri ?? authMetadata?.issuer;
if (!authUrl) return;
window.open(withSearchParam(authUrl, { action: accountManagementActions.profile }), '_blank');
}, [authMetadata, accountManagementActions]);
if (!authMetadata) return null;
return (
<Box direction="Column" gap="100">
<Text size="L400">Account Management</Text>
<SequenceCard
className={SequenceCardStyle}
variant="SurfaceVariant"
direction="Column"
gap="400"
>
<SettingTile
title="Manage account"
description="Your profile, password, and sessions are managed by your single sign-on provider."
after={
<Chip variant="Secondary" radii="Pill" onClick={open}>
<Text size="T200">Open</Text>
</Chip>
}
/>
</SequenceCard>
</Box>
);
}