From a688bd1061297fa51ae59ee7b0d5c50cb8bde284 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Mon, 10 Apr 2023 17:48:45 +0200 Subject: [PATCH] more discovery test --- .../src/server/oauth/OAuth2ProviderService.ts | 2 +- packages/backend/test/e2e/oauth.ts | 223 +++++++++++------- 2 files changed, 134 insertions(+), 91 deletions(-) diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts index f6fbdbdd8..68056587e 100644 --- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts +++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts @@ -511,7 +511,7 @@ export class OAuth2ProviderService { const clientUrl = validateClientId(clientId); - if (process.env.NODE_ENV !== 'test') { + if (process.env.NODE_ENV !== 'test' || process.env.MISSKEY_TEST_DISALLOW_LOOPBACK === '1') { const lookup = await dns.lookup(clientUrl.hostname); if (ipaddr.parse(lookup.address).range() === 'loopback') { throw new Error('client_id unexpectedly resolves to loopback IP.'); diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts index f56846f30..bc8e62ec7 100644 --- a/packages/backend/test/e2e/oauth.ts +++ b/packages/backend/test/e2e/oauth.ts @@ -29,7 +29,7 @@ function getClient(): AuthorizationCode<'client_id'> { }); } -function getMeta(html: string): { transactionId: string | undefined, clientName: string | undefined } | undefined { +function getMeta(html: string): { transactionId: string | undefined, clientName: string | undefined } { const fragment = JSDOM.fragment(html); return { transactionId: fragment.querySelector('meta[name="misskey:oauth:transaction-id"]')?.content, @@ -68,6 +68,11 @@ describe('OAuth', () => { beforeAll(async () => { app = await startServer(); + alice = await signup({ username: 'alice' }); + }, 1000 * 60 * 2); + + beforeEach(async () => { + process.env.MISSKEY_TEST_DISALLOW_LOOPBACK = ''; fastify = Fastify(); fastify.get('/', async (request, reply) => { reply.send(` @@ -77,12 +82,13 @@ describe('OAuth', () => { `); }); await fastify.listen({ port: clientPort }); - - alice = await signup({ username: 'alice' }); - }, 1000 * 60 * 2); + }); afterAll(async () => { await app.close(); + }); + + afterEach(async () => { await fastify.close(); }); @@ -104,7 +110,7 @@ describe('OAuth', () => { const meta = getMeta(await response.text()); assert.strictEqual(typeof meta.transactionId, 'string'); - assert.strictEqual(meta?.clientName, 'Misklient'); + assert.strictEqual(meta.clientName, 'Misklient'); const decisionResponse = await fetchDecision(cookie!, meta.transactionId!, alice); assert.strictEqual(decisionResponse.status, 302); @@ -602,123 +608,139 @@ describe('OAuth', () => { }); describe('Client Information Discovery', () => { - test('Read HTTP header', async () => { - await fastify.close(); + describe('Redirection', () => { + test('Read HTTP header', async () => { + await fastify.close(); - fastify = Fastify(); - fastify.get('/', async (request, reply) => { - reply.header('Link', '; rel="redirect_uri"'); - reply.send(` + fastify = Fastify(); + fastify.get('/', async (request, reply) => { + reply.header('Link', '; rel="redirect_uri"'); + reply.send(`
Misklient `); + }); + await fastify.listen({ port: clientPort }); + + const client = getClient(); + + const response = await fetch(client.authorizeURL({ + redirect_uri, + scope: 'write:notes', + state: 'state', + code_challenge: 'code', + code_challenge_method: 'S256', + })); + assert.strictEqual(response.status, 200); }); - await fastify.listen({ port: clientPort }); - const client = getClient(); + test('Mixed links', async () => { + await fastify.close(); - const response = await fetch(client.authorizeURL({ - redirect_uri, - scope: 'write:notes', - state: 'state', - code_challenge: 'code', - code_challenge_method: 'S256', - })); - assert.strictEqual(response.status, 200); - }); - - test('Mixed links', async () => { - await fastify.close(); - - fastify = Fastify(); - fastify.get('/', async (request, reply) => { - reply.header('Link', '; rel="redirect_uri"'); - reply.send(` + fastify = Fastify(); + fastify.get('/', async (request, reply) => { + reply.header('Link', '; rel="redirect_uri"'); + reply.send(`
Misklient `); + }); + await fastify.listen({ port: clientPort }); + + const client = getClient(); + + const response = await fetch(client.authorizeURL({ + redirect_uri, + scope: 'write:notes', + state: 'state', + code_challenge: 'code', + code_challenge_method: 'S256', + })); + assert.strictEqual(response.status, 200); }); - await fastify.listen({ port: clientPort }); - const client = getClient(); + test('Multiple items in Link header', async () => { + await fastify.close(); - const response = await fetch(client.authorizeURL({ - redirect_uri, - scope: 'write:notes', - state: 'state', - code_challenge: 'code', - code_challenge_method: 'S256', - })); - assert.strictEqual(response.status, 200); - }); - - test('Multiple items in Link header', async () => { - await fastify.close(); - - fastify = Fastify(); - fastify.get('/', async (request, reply) => { - reply.header('Link', '; rel="redirect_uri",; rel="redirect_uri"'); - reply.send(` + fastify = Fastify(); + fastify.get('/', async (request, reply) => { + reply.header('Link', '; rel="redirect_uri",; rel="redirect_uri"'); + reply.send(`
Misklient `); + }); + await fastify.listen({ port: clientPort }); + + const client = getClient(); + + const response = await fetch(client.authorizeURL({ + redirect_uri, + scope: 'write:notes', + state: 'state', + code_challenge: 'code', + code_challenge_method: 'S256', + })); + assert.strictEqual(response.status, 200); }); - await fastify.listen({ port: clientPort }); - const client = getClient(); + test('Multiple items in HTML', async () => { + await fastify.close(); - const response = await fetch(client.authorizeURL({ - redirect_uri, - scope: 'write:notes', - state: 'state', - code_challenge: 'code', - code_challenge_method: 'S256', - })); - console.log(await response.text()); - assert.strictEqual(response.status, 200); - }); - - test('Multiple items in HTML', async () => { - await fastify.close(); - - fastify = Fastify(); - fastify.get('/', async (request, reply) => { - reply.send(` + fastify = Fastify(); + fastify.get('/', async (request, reply) => { + reply.send(`
Misklient `); + }); + await fastify.listen({ port: clientPort }); + + const client = getClient(); + + const response = await fetch(client.authorizeURL({ + redirect_uri, + scope: 'write:notes', + state: 'state', + code_challenge: 'code', + code_challenge_method: 'S256', + })); + assert.strictEqual(response.status, 200); }); - await fastify.listen({ port: clientPort }); - const client = getClient(); + test('No item', async () => { + await fastify.close(); - const response = await fetch(client.authorizeURL({ - redirect_uri, - scope: 'write:notes', - state: 'state', - code_challenge: 'code', - code_challenge_method: 'S256', - })); - assert.strictEqual(response.status, 200); - }); - - test('No item', async () => { - await fastify.close(); - - fastify = Fastify(); - fastify.get('/', async (request, reply) => { - reply.send(` + fastify = Fastify(); + fastify.get('/', async (request, reply) => { + reply.send(`
Misklient `); + }); + await fastify.listen({ port: clientPort }); + + const client = getClient(); + + const response = await fetch(client.authorizeURL({ + redirect_uri, + scope: 'write:notes', + state: 'state', + code_challenge: 'code', + code_challenge_method: 'S256', + })); + // TODO: status code + assert.strictEqual(response.status, 500); }); - await fastify.listen({ port: clientPort }); + }); + + test('Disallow loopback', async () => { + process.env.MISSKEY_TEST_DISALLOW_LOOPBACK = '1'; const client = getClient(); - const response = await fetch(client.authorizeURL({ redirect_uri, scope: 'write:notes', @@ -729,11 +751,32 @@ describe('OAuth', () => { // TODO: status code assert.strictEqual(response.status, 500); }); + + test('Missing name', async () => { + await fastify.close(); + + fastify = Fastify(); + fastify.get('/', async (request, reply) => { + reply.header('Link', '; rel="redirect_uri"'); + reply.send(); + }); + await fastify.listen({ port: clientPort }); + + const client = getClient(); + + const response = await fetch(client.authorizeURL({ + redirect_uri, + scope: 'write:notes', + state: 'state', + code_challenge: 'code', + code_challenge_method: 'S256', + })); + assert.strictEqual(response.status, 200); + assert.strictEqual(getMeta(await response.text()).clientName, `http://127.0.0.1:${clientPort}/`); + }); }); // TODO: authorizing two users concurrently // TODO: Error format required by OAuth spec - - // TODO: Client Information Discovery (use http header, loopback check, missing name or redirection uri) });