chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// OAuth sessions record the device (User-Agent) that opened them, so two
// sessions of the same app can be told apart in the Apps list. formatUserAgent
// renders a short "Browser · OS" label, and listConnectedApps surfaces the
// stored value.
import { describe, expect, it } from 'vitest';
import { formatUserAgent } from '../packages/account-ui/src/lib/userAgent.js';
import { createAccountHandlers } from '../packages/core/src/handlers/account.js';
describe('formatUserAgent', () => {
it('parses common browser + OS pairs', () => {
expect(
formatUserAgent(
'Mozilla/5.0 (Linux; Android 15; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Mobile Safari/537.36',
),
).toBe('Chrome · Android');
expect(
formatUserAgent(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15',
),
).toBe('Safari · macOS');
expect(
formatUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0',
),
).toBe('Firefox · Windows');
expect(
formatUserAgent(
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
),
).toBe('Safari · iPhone');
});
it('names common non-browser HTTP clients', () => {
expect(formatUserAgent('node')).toBe('Node.js');
expect(formatUserAgent('curl/8.7.1')).toBe('curl');
expect(formatUserAgent('okhttp/4.9.0')).toBe('OkHttp');
expect(formatUserAgent('Go-http-client/1.1')).toBe('Go');
expect(formatUserAgent('python-requests/2.32.3')).toBe('Python');
});
it('falls back and handles missing values', () => {
expect(formatUserAgent(null)).toBeNull();
expect(formatUserAgent('')).toBeNull();
// A genuinely unrecognized agent keeps its trimmed raw value.
expect(formatUserAgent('SomeUnknownBot/1.0')).toBe('SomeUnknownBot/1.0');
});
});
describe('listConnectedApps surfaces the device', () => {
it('returns the stored userAgent for a session', async () => {
const account = createAccountHandlers(
/** @type {any} */ ({
jwtSecret: 'test-secret',
sharedStorage: {
listOAuthTokensByDid: async () => [
{
did: 'did:plc:x',
clientId: 'https://client.example/metadata.json',
scope: 'atproto',
dpopJkt: 'abcdef123456',
createdAt: Date.now(),
userAgent: 'Mozilla/5.0 … Chrome/150 Mobile',
tokenId: 'ref-1',
},
],
},
}),
);
const apps = await account.listConnectedApps('did:plc:x');
expect(apps).toHaveLength(1);
expect(apps[0].userAgent).toBe('Mozilla/5.0 … Chrome/150 Mobile');
});
it('orders sessions by last access and falls back to createdAt', async () => {
const account = createAccountHandlers(
/** @type {any} */ ({
jwtSecret: 'test-secret',
sharedStorage: {
listOAuthTokensByDid: async () => [
// Authorized first, but refreshed most recently -> sorts to the top.
{
did: 'did:plc:x',
clientId: 'https://old.example/metadata.json',
createdAt: Date.parse('2026-01-01T00:00:00Z'),
updatedAt: Date.parse('2026-08-01T00:00:00Z'),
tokenId: 'ref-old',
},
// Authorized later but never refreshed since.
{
did: 'did:plc:x',
clientId: 'https://new.example/metadata.json',
createdAt: Date.parse('2026-07-01T00:00:00Z'),
updatedAt: Date.parse('2026-07-01T00:00:00Z'),
tokenId: 'ref-new',
},
// Legacy token with no updatedAt: createdAt stands in.
{
did: 'did:plc:x',
clientId: 'https://legacy.example/metadata.json',
createdAt: Date.parse('2026-06-01T00:00:00Z'),
tokenId: 'ref-legacy',
},
],
},
}),
);
const apps = await account.listConnectedApps('did:plc:x');
expect(apps.map((a) => a.clientId)).toEqual([
'https://old.example/metadata.json',
'https://new.example/metadata.json',
'https://legacy.example/metadata.json',
]);
// authorizedAt tracks first authorization; lastAccessedAt tracks the refresh.
expect(apps[0].authorizedAt).toBe('2026-01-01');
expect(apps[0].lastAccessedAt).toBe('2026-08-01T00:00:00.000Z');
// Legacy token with no updatedAt reports createdAt as its last access.
expect(apps[2].lastAccessedAt).toBe('2026-06-01T00:00:00.000Z');
});
});