Stop treating transient auth_required as home-session expiry, keep auth scope consistent across login redirect and server-rail joins, and leave /login when the in-memory user is still authenticated. Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
4.5 KiB
TypeScript
121 lines
4.5 KiB
TypeScript
import '@angular/compiler';
|
|
import { Injector, runInInjectionContext } from '@angular/core';
|
|
import {
|
|
beforeEach,
|
|
describe,
|
|
expect,
|
|
it,
|
|
vi
|
|
} from 'vitest';
|
|
import { Router } from '@angular/router';
|
|
import { Store } from '@ngrx/store';
|
|
import { of } from 'rxjs';
|
|
|
|
import { ServerDirectoryFacade } from '../../../server-directory';
|
|
import { SignalServerAuthService } from './signal-server-auth.service';
|
|
import { SignalServerAuthorizeService } from './signal-server-authorize.service';
|
|
|
|
const HOME_URL = 'https://signal.toju.app';
|
|
const FOREIGN_URL = 'https://signal-sweden.toju.app';
|
|
const homeUser = {
|
|
id: 'user-1',
|
|
username: 'alice',
|
|
displayName: 'Alice',
|
|
homeSignalServerUrl: HOME_URL
|
|
};
|
|
|
|
describe('SignalServerAuthorizeService', () => {
|
|
let router: { navigate: ReturnType<typeof vi.fn>; url: string };
|
|
let store: { select: ReturnType<typeof vi.fn> };
|
|
let serverDirectory: {
|
|
ensureServerEndpoint: ReturnType<typeof vi.fn>;
|
|
findServerByUrl: ReturnType<typeof vi.fn>;
|
|
servers: ReturnType<typeof vi.fn>;
|
|
testServer: ReturnType<typeof vi.fn>;
|
|
};
|
|
let signalServerAuth: {
|
|
ensureProvisioned: ReturnType<typeof vi.fn>;
|
|
hasValidCredential: ReturnType<typeof vi.fn>;
|
|
migrateHomeCredential: ReturnType<typeof vi.fn>;
|
|
};
|
|
let service: SignalServerAuthorizeService;
|
|
|
|
beforeEach(() => {
|
|
router = { navigate: vi.fn(() => Promise.resolve(true)), url: '/room/room-1' };
|
|
store = { select: vi.fn(() => of(homeUser)) };
|
|
serverDirectory = {
|
|
ensureServerEndpoint: vi.fn(() => ({ id: 'endpoint-1' })),
|
|
findServerByUrl: vi.fn(() => ({ id: 'endpoint-1', status: 'online' })),
|
|
servers: vi.fn(() => []),
|
|
testServer: vi.fn(() => Promise.resolve())
|
|
};
|
|
|
|
signalServerAuth = {
|
|
ensureProvisioned: vi.fn(() => Promise.resolve({ kind: 'skipped', reason: 'no-provision-secret' })),
|
|
hasValidCredential: vi.fn(() => false),
|
|
migrateHomeCredential: vi.fn()
|
|
};
|
|
|
|
const injector = Injector.create({
|
|
providers: [
|
|
SignalServerAuthorizeService,
|
|
{ provide: Router, useValue: router },
|
|
{ provide: Store, useValue: store },
|
|
{ provide: ServerDirectoryFacade, useValue: serverDirectory },
|
|
{ provide: SignalServerAuthService, useValue: signalServerAuth }
|
|
]
|
|
});
|
|
|
|
service = runInInjectionContext(injector, () => injector.get(SignalServerAuthorizeService));
|
|
});
|
|
|
|
it('returns true immediately when a valid credential already exists', async () => {
|
|
signalServerAuth.hasValidCredential.mockReturnValue(true);
|
|
|
|
await expect(service.ensureCredentialForServerUrl(FOREIGN_URL)).resolves.toBe(true);
|
|
expect(signalServerAuth.ensureProvisioned).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('never provisions or opens the authorize login page for the home signal server', async () => {
|
|
await expect(service.ensureCredentialForServerUrl(HOME_URL)).resolves.toBe(false);
|
|
|
|
expect(signalServerAuth.ensureProvisioned).not.toHaveBeenCalled();
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('restores the home credential from the legacy token store instead of provisioning', async () => {
|
|
signalServerAuth.hasValidCredential
|
|
.mockReturnValueOnce(false)
|
|
.mockReturnValue(true);
|
|
|
|
await expect(service.ensureCredentialForServerUrl(HOME_URL)).resolves.toBe(true);
|
|
|
|
expect(signalServerAuth.migrateHomeCredential).toHaveBeenCalledWith(homeUser);
|
|
expect(signalServerAuth.ensureProvisioned).not.toHaveBeenCalled();
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('treats scheme variants of the home url as the home server', async () => {
|
|
await expect(service.ensureCredentialForServerUrl('wss://signal.toju.app/')).resolves.toBe(false);
|
|
|
|
expect(signalServerAuth.ensureProvisioned).not.toHaveBeenCalled();
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('still provisions foreign servers and navigates to authorize when the secret is missing', async () => {
|
|
await expect(service.ensureCredentialForServerUrl(FOREIGN_URL)).resolves.toBe(false);
|
|
|
|
expect(signalServerAuth.ensureProvisioned).toHaveBeenCalledWith(FOREIGN_URL, homeUser);
|
|
expect(router.navigate).toHaveBeenCalledWith(['/login'], expect.objectContaining({
|
|
queryParams: expect.objectContaining({ mode: 'authorize' })
|
|
}));
|
|
});
|
|
|
|
it('returns true when foreign provisioning succeeds', async () => {
|
|
signalServerAuth.ensureProvisioned.mockResolvedValue({ kind: 'provisioned', result: {} });
|
|
|
|
await expect(service.ensureCredentialForServerUrl(FOREIGN_URL)).resolves.toBe(true);
|
|
expect(router.navigate).not.toHaveBeenCalled();
|
|
});
|
|
});
|