Verify Reddit identity before Matrix capture
This commit is contained in:
1 parent
2fd92bf82c
commit
2c533fa52a
3 files changed
+48
-14
No files matched your search
+13
-13
@@ -31,7 +31,11 @@ import { MediaSyncService } from './services/media-sync.js';
|
||||
import { RateLimiter } from './utils/rate-limiter.js';
|
||||
import { fetchWithTimeout } from './utils/fetch-with-timeout.js';
|
||||
import { WebServer } from './web/server.js';
|
||||
import { AccountAuthContext, type AuthenticatedAccount } from './services/account-auth-context.js';
|
||||
import {
|
||||
AccountAuthContext,
|
||||
authenticateCurrentAccount,
|
||||
type AuthenticatedAccount,
|
||||
} from './services/account-auth-context.js';
|
||||
|
||||
export interface AppContainer {
|
||||
config: Config;
|
||||
@@ -184,18 +188,14 @@ export async function bootstrap(config: Config, logger: Logger): Promise<AppCont
|
||||
},
|
||||
);
|
||||
|
||||
accountAuth.setAuthenticator(async (): Promise<AuthenticatedAccount | null> => {
|
||||
credentialState.latest = null;
|
||||
await tokenRefresher.refresh();
|
||||
const redditUsername = await browserClient.getAuthenticatedUsername();
|
||||
const freshCredentials = credentialState.latest as MatrixCredentials | null;
|
||||
if (!redditUsername || !freshCredentials?.userId) return null;
|
||||
return {
|
||||
accountId: freshCredentials.userId,
|
||||
redditUsername,
|
||||
matrixUserId: freshCredentials.userId,
|
||||
};
|
||||
});
|
||||
accountAuth.setAuthenticator(() => authenticateCurrentAccount({
|
||||
getRedditUsername: () => browserClient.getAuthenticatedUsername(),
|
||||
refreshMatrixCredentials: async () => {
|
||||
credentialState.latest = null;
|
||||
await tokenRefresher.refresh();
|
||||
return credentialState.latest as MatrixCredentials | null;
|
||||
},
|
||||
}));
|
||||
|
||||
if (loggedIn) {
|
||||
logger.info('Verifying full Reddit and Matrix account authentication...');
|
||||
|
||||
@@ -33,6 +33,26 @@ export interface AccountAuthContextOptions {
|
||||
now?: () => number;
|
||||
}
|
||||
|
||||
export interface CurrentAccountAuthentication {
|
||||
getRedditUsername(): Promise<string | null>;
|
||||
refreshMatrixCredentials(): Promise<{ userId: string } | null>;
|
||||
}
|
||||
|
||||
export async function authenticateCurrentAccount(
|
||||
authentication: CurrentAccountAuthentication,
|
||||
): Promise<AuthenticatedAccount | null> {
|
||||
const redditUsername = await authentication.getRedditUsername();
|
||||
if (!redditUsername) return null;
|
||||
|
||||
const credentials = await authentication.refreshMatrixCredentials();
|
||||
if (!credentials?.userId) return null;
|
||||
return {
|
||||
accountId: credentials.userId,
|
||||
redditUsername,
|
||||
matrixUserId: credentials.userId,
|
||||
};
|
||||
}
|
||||
|
||||
export const denyAccountAuth: AccountAuthGate = {
|
||||
isFullyAuthenticated: () => false,
|
||||
invalidate: () => undefined,
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { AccountAuthContext } from '../src/services/account-auth-context.js';
|
||||
import {
|
||||
AccountAuthContext,
|
||||
authenticateCurrentAccount,
|
||||
} from '../src/services/account-auth-context.js';
|
||||
import { createMockLogger } from './mockLogger.js';
|
||||
|
||||
describe('AccountAuthContext', () => {
|
||||
it('does not attempt Matrix capture until Reddit identity is authoritative', async () => {
|
||||
const refreshMatrixCredentials = vi.fn();
|
||||
|
||||
await expect(authenticateCurrentAccount({
|
||||
getRedditUsername: vi.fn().mockResolvedValue(null),
|
||||
refreshMatrixCredentials,
|
||||
})).resolves.toBeNull();
|
||||
|
||||
expect(refreshMatrixCredentials).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('fails closed until Reddit and Matrix resolve to one authenticated account', async () => {
|
||||
const auth = new AccountAuthContext(createMockLogger());
|
||||
const authenticate = vi.fn().mockResolvedValue({
|
||||
|
||||
Reference in new issue
Block a user