~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
82 lines · 2.9 KB Raw History
// The forge auth wiring: the client metadata document, the login redirect
// against a stubbed authorization server, the session cookie the callback
// sets, and the owner-only procedure refusing a stranger's session.

import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { createAppViewStore } from '@pdsjs/appview/store';
import Database from 'better-sqlite3';
import { afterEach, describe, expect, it } from 'vitest';
import { createForgeAuth } from '../src/auth.js';
import { createRemoteAccount } from '../src/remote.js';
import { createGitHostServer } from '../src/server.js';

const PDS_PORT = 2522;
const PDS_BASE = `http://127.0.0.1:${PDS_PORT}`;
const HOST_PORT = 2523;
const ORIGIN = `http://127.0.0.1:${HOST_PORT}`;
const DID = 'did:plc:forgeowner';

/** @type {Array<{close: () => Promise<void>}>} */
const closers = [];
/** @type {string[]} */
const dirs = [];

afterEach(async () => {
  for (const closer of closers.splice(0)) {
    await closer.close().catch(() => {});
  }
  for (const dir of dirs.splice(0))
    rmSync(dir, { recursive: true, force: true });
});

describe('forge auth', () => {
  it('serves the client metadata document the client_id names', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'git-host-auth-'));
    dirs.push(dir);
    const auth = createForgeAuth({ origin: ORIGIN, accountDid: DID });
    const server = createGitHostServer({
      account: createRemoteAccount({
        service: PDS_BASE,
        account: DID,
        fetch: async () => Response.json({ did: DID, handle: 'forge.test' }),
      }),
      store: createAppViewStore(new Database(':memory:')),
      auth,
      origin: ORIGIN,
    });
    closers.push(server);
    await server.listen(HOST_PORT);

    const doc = await fetch(`${ORIGIN}/oauth/client-metadata.json`).then(
      (r) => /** @type {any} */ (r.json()),
    );
    expect(doc.client_id).toBe(`${ORIGIN}/oauth/client-metadata.json`);
    expect(doc.redirect_uris).toEqual([`${ORIGIN}/oauth/callback`]);
    expect(doc.dpop_bound_access_tokens).toBe(true);
  });

  it('refuses an owner procedure without a session, and serves reads', async () => {
    const dir = mkdtempSync(join(tmpdir(), 'git-host-auth-'));
    dirs.push(dir);
    const server = createGitHostServer({
      account: createRemoteAccount({
        service: PDS_BASE,
        account: DID,
        fetch: async () => Response.json({ did: DID, handle: 'forge.test' }),
      }),
      store: createAppViewStore(new Database(':memory:')),
      origin: ORIGIN,
    });
    closers.push(server);
    await server.listen(HOST_PORT);

    const refusal = await fetch(`${ORIGIN}/xrpc/dev.pdsjs.git.createRepo`, {
      method: 'POST',
      headers: { 'content-type': 'application/json', origin: ORIGIN },
      body: JSON.stringify({ name: 'proj' }),
    });
    expect(refusal.status).toBe(401);
  });
});