update tests with updated util function

This commit is contained in:
Kagami Sascha Rosylight 2023-06-28 23:17:50 +02:00
parent 1f38d624c0
commit 93364cb922
2 changed files with 71 additions and 117 deletions

View file

@ -1,7 +1,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { signup, api, startServer, successfulApiCall, failedApiCall, uploadFile, waitFire, connectStream } from '../utils.js'; import { signup, api, startServer, successfulApiCall, failedApiCall, uploadFile, waitFire, connectStream, relativeFetch } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common'; import type { INestApplicationContext } from '@nestjs/common';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import { IncomingMessage } from 'http'; import { IncomingMessage } from 'http';
@ -218,6 +218,44 @@ describe('API', () => {
assert.ok(result.headers.get('WWW-Authenticate')?.startsWith('Bearer realm="Misskey", error="invalid_request", error_description')); assert.ok(result.headers.get('WWW-Authenticate')?.startsWith('Bearer realm="Misskey", error="invalid_request", error_description'));
}); });
describe('invalid bearer format', () => {
test('No preceding bearer', async () => {
const result = await relativeFetch('api/notes/create', {
method: 'POST',
headers: {
Authorization: alice.token,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
});
assert.strictEqual(result.status, 401);
});
test('Lowercase bearer', async () => {
const result = await relativeFetch('api/notes/create', {
method: 'POST',
headers: {
Authorization: `bearer ${alice.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
});
assert.strictEqual(result.status, 401);
});
test('No space after bearer', async () => {
const result = await relativeFetch('api/notes/create', {
method: 'POST',
headers: {
Authorization: `Bearer${alice.token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
});
assert.strictEqual(result.status, 401);
});
});
// TODO: insufficient_scope test (authテストが全然なくて書けない) // TODO: insufficient_scope test (authテストが全然なくて書けない)
}); });
}); });

View file

@ -10,7 +10,7 @@ import { AuthorizationCode, ResourceOwnerPassword, type AuthorizationTokenConfig
import pkceChallenge from 'pkce-challenge'; import pkceChallenge from 'pkce-challenge';
import { JSDOM } from 'jsdom'; import { JSDOM } from 'jsdom';
import Fastify, { type FastifyReply, type FastifyInstance } from 'fastify'; import Fastify, { type FastifyReply, type FastifyInstance } from 'fastify';
import { port, relativeFetch, signup, startServer } from '../utils.js'; import { api, port, signup, startServer } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import type { INestApplicationContext } from '@nestjs/common'; import type { INestApplicationContext } from '@nestjs/common';
@ -220,18 +220,14 @@ describe('OAuth', () => {
assert.strictEqual(token.token.token_type, 'Bearer'); assert.strictEqual(token.token.token_type, 'Bearer');
assert.strictEqual(token.token.scope, 'write:notes'); assert.strictEqual(token.token.scope, 'write:notes');
const createResponse = await relativeFetch('api/notes/create', { const createResult = await api('notes/create', { text: 'test' }, {
method: 'POST', token: token.token.access_token as string,
headers: { bearer: true,
Authorization: `Bearer ${token.token.access_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
}); });
assert.strictEqual(createResponse.status, 200); assert.strictEqual(createResult.status, 200);
const createResponseBody = await createResponse.json() as misskey.Endpoints['notes/create']['res']; const createResultBody = createResult.body as misskey.Endpoints['notes/create']['res'];
assert.strictEqual(createResponseBody.createdNote.text, 'test'); assert.strictEqual(createResultBody.createdNote.text, 'test');
}); });
test('Two concurrent flows', async () => { test('Two concurrent flows', async () => {
@ -289,31 +285,23 @@ describe('OAuth', () => {
code_verifier: pkceBob.code_verifier, code_verifier: pkceBob.code_verifier,
} as AuthorizationTokenConfigExtended); } as AuthorizationTokenConfigExtended);
const createResponseAlice = await relativeFetch('api/notes/create', { const createResultAlice = await api('notes/create', { text: 'test' }, {
method: 'POST', token: tokenAlice.token.access_token as string,
headers: { bearer: true,
Authorization: `Bearer ${tokenAlice.token.access_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
}); });
assert.strictEqual(createResponseAlice.status, 200); assert.strictEqual(createResultAlice.status, 200);
const createResponseBob = await relativeFetch('api/notes/create', { const createResultBob = await api('notes/create', { text: 'test' }, {
method: 'POST', token: tokenBob.token.access_token as string,
headers: { bearer: true,
Authorization: `Bearer ${tokenBob.token.access_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
}); });
assert.strictEqual(createResponseAlice.status, 200); assert.strictEqual(createResultAlice.status, 200);
const createResponseBodyAlice = await createResponseAlice.json() as misskey.Endpoints['notes/create']['res']; const createResultBodyAlice = await createResultAlice.body as misskey.Endpoints['notes/create']['res'];
assert.strictEqual(createResponseBodyAlice.createdNote.user.username, 'alice'); assert.strictEqual(createResultBodyAlice.createdNote.user.username, 'alice');
const createResponseBodyBob = await createResponseBob.json() as misskey.Endpoints['notes/create']['res']; const createResultBodyBob = await createResultBob.body as misskey.Endpoints['notes/create']['res'];
assert.strictEqual(createResponseBodyBob.createdNote.user.username, 'bob'); assert.strictEqual(createResultBodyBob.createdNote.user.username, 'bob');
}); });
// https://datatracker.ietf.org/doc/html/rfc7636.html // https://datatracker.ietf.org/doc/html/rfc7636.html
@ -444,15 +432,11 @@ describe('OAuth', () => {
code_verifier, code_verifier,
} as AuthorizationTokenConfigExtended); } as AuthorizationTokenConfigExtended);
const createResponse = await relativeFetch('api/notes/create', { const createResult = await api('notes/create', { text: 'test' }, {
method: 'POST', token: token.token.access_token as string,
headers: { bearer: true,
Authorization: `Bearer ${token.token.access_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
}); });
assert.strictEqual(createResponse.status, 200); assert.strictEqual(createResult.status, 200);
await assert.rejects(client.getToken({ await assert.rejects(client.getToken({
code, code,
@ -463,15 +447,11 @@ describe('OAuth', () => {
return true; return true;
}); });
const createResponse2 = await relativeFetch('api/notes/create', { const createResult2 = await api('notes/create', { text: 'test' }, {
method: 'POST', token: token.token.access_token as string,
headers: { bearer: true,
Authorization: `Bearer ${token.token.access_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
}); });
assert.strictEqual(createResponse2.status, 401); assert.strictEqual(createResult2.status, 401);
}); });
}); });
@ -610,79 +590,15 @@ describe('OAuth', () => {
} as AuthorizationTokenConfigExtended); } as AuthorizationTokenConfigExtended);
assert.strictEqual(typeof token.token.access_token, 'string'); assert.strictEqual(typeof token.token.access_token, 'string');
const createResponse = await relativeFetch('api/notes/create', { const createResult = await api('notes/create', { text: 'test' }, {
method: 'POST', token: token.token.access_token as string,
headers: { bearer: true,
Authorization: `Bearer ${token.token.access_token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
}); });
assert.strictEqual(createResponse.status, 403); assert.strictEqual(createResult.status, 403);
assert.ok(createResult.headers.get('WWW-Authenticate')?.startsWith('Bearer realm="Misskey", error="insufficient_scope", error_description'));
}); });
}); });
// https://datatracker.ietf.org/doc/html/rfc6750.html
test('Authorization header', async () => {
const { code_challenge, code_verifier } = await pkceChallenge(128);
const { client, code } = await fetchAuthorizationCode(alice, 'write:notes', code_challenge);
const token = await client.getToken({
code,
redirect_uri,
code_verifier,
} as AuthorizationTokenConfigExtended);
// Pattern 1: No preceding "Bearer "
let createResponse = await relativeFetch('api/notes/create', {
method: 'POST',
headers: {
Authorization: token.token.access_token as string,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
});
assert.strictEqual(createResponse.status, 401);
// Pattern 2: Incorrect token
createResponse = await relativeFetch('api/notes/create', {
method: 'POST',
headers: {
Authorization: `Bearer ${(token.token.access_token as string).slice(0, -1)}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
});
// https://datatracker.ietf.org/doc/html/rfc6750.html#section-3.1
// "The access token provided is expired, revoked, malformed, or
// invalid for other reasons. The resource SHOULD respond with
// the HTTP 401 (Unauthorized) status code."
assert.strictEqual(createResponse.status, 401);
let wwwAuthenticate = createResponse.headers.get('WWW-Authenticate');
assert.ok(wwwAuthenticate?.startsWith('Bearer realm="Misskey", error="invalid_token"'));
// Pattern 3: No token
createResponse = await relativeFetch('api/notes/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ text: 'test' }),
});
wwwAuthenticate = createResponse.headers.get('WWW-Authenticate');
// https://datatracker.ietf.org/doc/html/rfc6750.html#section-3.1
// "If the request lacks any authentication information (e.g., the client
// was unaware that authentication is necessary or attempted using an
// unsupported authentication method), the resource server SHOULD NOT
// include an error code or other error information."
assert.strictEqual(createResponse.status, 401);
assert.strictEqual(wwwAuthenticate, 'Bearer realm="Misskey"');
});
// https://datatracker.ietf.org/doc/html/rfc6749.html#section-3.1.2.4 // https://datatracker.ietf.org/doc/html/rfc6749.html#section-3.1.2.4
// "If an authorization request fails validation due to a missing, // "If an authorization request fails validation due to a missing,
// invalid, or mismatching redirection URI, the authorization server // invalid, or mismatching redirection URI, the authorization server