feat: self-refresh Matrix tokens from the account browser session
Expired Matrix bearers no longer pause an account until a new extension dump is uploaded. BrowserMatrixTokenRefresher loads chat.reddit.com in the account's persistent browser profile (whose Reddit cookies stay valid), lifts the fresh bearer off the first authenticated Matrix request, verifies it via whoami, swaps it into the live MatrixClient and persists it to the account row. Wired into the per-account runtime at boot verification, the auth-gate authenticator, and ChatService's 401 retry path. A dump is now only needed when the Reddit cookies themselves no longer authenticate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f34129666e
commit
82be197b79
7 files changed
+426
-8
No files matched your search
@@ -12,7 +12,7 @@ import {
|
||||
ActivityLogRepository,
|
||||
RecipientHistoryRepository,
|
||||
} from '../database/repositories.js';
|
||||
import { AccountContentRepository } from '../database/account-repositories.js';
|
||||
import { AccountContentRepository, RedditAccountsRepository } from '../database/account-repositories.js';
|
||||
import { RedditApiClient, RedditRateLimitError, type RedditPost } from '../reddit/api.js';
|
||||
import { RedditRequestCoordinator } from '../reddit/request-coordinator.js';
|
||||
import { OpenRouterClient } from '../ai/openrouter.js';
|
||||
@@ -28,8 +28,9 @@ import {
|
||||
} from './account-auth-context.js';
|
||||
import { ChatCache } from './chat/cache.js';
|
||||
import {
|
||||
ChatService, MatrixClient, MatrixMediaDownloader, MatrixTokenRefresher,
|
||||
MatrixUsernameResolver, type UsernameResolver,
|
||||
BrowserMatrixTokenRefresher, ChatService, MatrixAuthError, MatrixClient,
|
||||
MatrixMediaDownloader, MatrixUsernameResolver,
|
||||
type MatrixCredentials, type UsernameResolver,
|
||||
} from './chat/index.js';
|
||||
import { MediaService } from './media.js';
|
||||
import { MediaSyncService } from './media-sync.js';
|
||||
@@ -116,7 +117,29 @@ async function buildRuntime(
|
||||
}
|
||||
|
||||
const matrixClient = new MatrixClient(session.matrixCredentials, logger);
|
||||
if (await matrixClient.whoami() !== session.matrixUserId) {
|
||||
const accountsRepo = new RedditAccountsRepository(db);
|
||||
const tokenRefresher = new BrowserMatrixTokenRefresher({
|
||||
context: browserClient.getContext()!,
|
||||
expectedUserId: session.matrixUserId,
|
||||
verifyCredentials: (credentials) => new MatrixClient(credentials, logger).whoami(),
|
||||
applyCredentials: (credentials) => matrixClient.updateCredentials(credentials),
|
||||
persistCredentials: (credentials: MatrixCredentials) => {
|
||||
session.matrixCredentials = credentials;
|
||||
accountsRepo.updateMatrixCredentials(session.accountId, JSON.stringify(credentials));
|
||||
},
|
||||
logger,
|
||||
});
|
||||
const verifyMatrixSession = async (refreshOnAuthError: boolean): Promise<boolean> => {
|
||||
try {
|
||||
return await matrixClient.whoami() === session.matrixUserId;
|
||||
} catch (error) {
|
||||
if (!refreshOnAuthError || !(error instanceof MatrixAuthError)) throw error;
|
||||
logger.warn(`Stored Matrix token for u/${session.redditUsername} was rejected — refreshing from the browser session`);
|
||||
await tokenRefresher.refresh();
|
||||
return await matrixClient.whoami() === session.matrixUserId;
|
||||
}
|
||||
};
|
||||
if (!(await verifyMatrixSession(true))) {
|
||||
throw new Error(`Stored Matrix session no longer verifies for u/${session.redditUsername}`);
|
||||
}
|
||||
|
||||
@@ -125,7 +148,7 @@ async function buildRuntime(
|
||||
accountAuth.setAuthenticator(async () => {
|
||||
const current = await browserClient.getAuthenticatedIdentity();
|
||||
if (!current || matrixRedditId(session.matrixUserId) !== current.accountId.toLowerCase().replace(/^t2_/, '')) return null;
|
||||
return await matrixClient.whoami() === session.matrixUserId
|
||||
return await verifyMatrixSession(true)
|
||||
? runtimeAuthenticatedAccount(session, current)
|
||||
: null;
|
||||
});
|
||||
@@ -182,7 +205,7 @@ async function buildRuntime(
|
||||
const chatCache = new ChatCache(logger, undefined, accountContentRepo, session.accountId);
|
||||
const chatService = new ChatService(
|
||||
matrixClient, logger, successfulMessagesRepo, mediaService,
|
||||
new MatrixTokenRefresher(), usernameResolver, chatCache,
|
||||
tokenRefresher, usernameResolver, chatCache,
|
||||
messagesRepo, redditUsersRepo, accountAuth, conversationOutcomesRepo, conversationStateRepo,
|
||||
);
|
||||
await chatService.loadCache();
|
||||
|
||||
Reference in new issue
Block a user