This commit is contained in:
Kagami Sascha Rosylight 2023-04-02 21:59:38 +02:00
parent 82c9820ac8
commit 71f62b9d89
4 changed files with 159 additions and 2 deletions

View file

@ -127,6 +127,7 @@
"otpauth": "9.1.2", "otpauth": "9.1.2",
"parse5": "7.1.2", "parse5": "7.1.2",
"pg": "8.11.0", "pg": "8.11.0",
"pkce-challenge": "^3.1.0",
"probe-image-size": "7.2.3", "probe-image-size": "7.2.3",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"pug": "3.0.2", "pug": "3.0.2",
@ -202,6 +203,7 @@
"@types/sanitize-html": "2.9.0", "@types/sanitize-html": "2.9.0",
"@types/semver": "7.5.0", "@types/semver": "7.5.0",
"@types/sharp": "0.32.0", "@types/sharp": "0.32.0",
"@types/simple-oauth2": "^5.0.4",
"@types/sinonjs__fake-timers": "8.1.2", "@types/sinonjs__fake-timers": "8.1.2",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3", "@types/tmp": "0.2.3",
@ -219,6 +221,7 @@
"eslint-plugin-import": "2.27.5", "eslint-plugin-import": "2.27.5",
"execa": "6.1.0", "execa": "6.1.0",
"jest": "29.5.0", "jest": "29.5.0",
"jest-mock": "29.5.0" "jest-mock": "29.5.0",
"simple-oauth2": "^5.0.0"
} }
} }

View file

@ -433,7 +433,7 @@ export class OAuth2ProviderService {
fastify.get<{ Querystring: OAuthRequestQuery }>('/oauth/authorize', async (request, reply) => { fastify.get<{ Querystring: OAuthRequestQuery }>('/oauth/authorize', async (request, reply) => {
console.log('HIT /oauth/authorize', request.query); console.log('HIT /oauth/authorize', request.query);
const oauth2 = (request.raw as any).oauth2 as (OAuth2 | undefined); const oauth2 = (request.raw as any).oauth2 as (OAuth2 | undefined);
console.log(oauth2); console.log(oauth2, request.raw.session);
if (request.query.response_type !== 'code') { if (request.query.response_type !== 'code') {
throw new Error('`response_type` parameter must be set as "code"'); throw new Error('`response_type` parameter must be set as "code"');

View file

@ -0,0 +1,94 @@
process.env.NODE_ENV = 'test';
import * as assert from 'assert';
import { port, signup, startServer } from '../utils.js';
import type { INestApplicationContext } from '@nestjs/common';
import { AuthorizationCode } from 'simple-oauth2';
import pkceChallenge from 'pkce-challenge';
import { JSDOM } from 'jsdom';
describe('OAuth', () => {
let app: INestApplicationContext;
let alice: any;
const clientPort = port + 1;
beforeAll(async () => {
app = await startServer();
alice = await signup({ username: 'alice' });
// fastify = Fastify();
}, 1000 * 60 * 2);
afterAll(async () => {
await app.close();
});
test('Full flow', async () => {
const { code_challenge, code_verifier } = pkceChallenge.default(128);
const client = new AuthorizationCode({
client: {
id: `http://127.0.0.1:${clientPort}/`,
},
auth: {
tokenHost: `http://127.0.0.1:${port}`,
tokenPath: '/oauth/token',
authorizePath: '/oauth/authorize',
},
options: {
authorizationMethod: 'body',
},
});
const redirect_uri = `http://127.0.0.1:${clientPort}/redirect`;
const authEndpoint = client.authorizeURL({
redirect_uri,
scope: 'write:notes',
state: 'state',
code_challenge,
code_challenge_method: 'S256',
});
const response = await fetch(authEndpoint);
assert.strictEqual(response.status, 200);
const cookie = response.headers.get('set-cookie');
assert.ok(cookie?.startsWith('connect.sid='));
const fragment = JSDOM.fragment(await response.text());
const transactionId = fragment.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:transaction-id"]')?.content;
assert.strictEqual(typeof transactionId, 'string');
const formData = new FormData();
formData.append('transaction_id', transactionId!);
formData.append('login_token', alice.token);
const decisionResponse = await fetch(`http://127.0.0.1:${port}/oauth/decision`, {
method: 'post',
body: new URLSearchParams({
transaction_id: transactionId!,
login_token: alice.token,
}),
redirect: 'manual',
headers: {
'content-type': 'application/x-www-form-urlencoded',
cookie: cookie!,
},
});
assert.strictEqual(decisionResponse.status, 302);
assert.ok(decisionResponse.headers.has('location'));
const location = new URL(decisionResponse.headers.get('location')!);
assert.strictEqual(location.origin + location.pathname, redirect_uri);
assert.ok(location.searchParams.has('code'));
assert.strictEqual(location.searchParams.get('state'), 'state');
const token = await client.getToken({
code: location.searchParams.get('code')!,
redirect_uri,
code_verifier,
});
assert.strictEqual(typeof token.token.access_token, 'string');
assert.strictEqual(typeof token.token.refresh_token, 'string');
assert.strictEqual(token.token.token_type, 'Bearer');
});
});

View file

@ -296,6 +296,9 @@ importers:
pg: pg:
specifier: 8.11.0 specifier: 8.11.0
version: 8.11.0 version: 8.11.0
pkce-challenge:
specifier: ^3.1.0
version: 3.1.0
probe-image-size: probe-image-size:
specifier: 7.2.3 specifier: 7.2.3
version: 7.2.3 version: 7.2.3
@ -598,6 +601,9 @@ importers:
'@types/sharp': '@types/sharp':
specifier: 0.32.0 specifier: 0.32.0
version: 0.32.0 version: 0.32.0
'@types/simple-oauth2':
specifier: ^5.0.4
version: 5.0.4
'@types/sinonjs__fake-timers': '@types/sinonjs__fake-timers':
specifier: 8.1.2 specifier: 8.1.2
version: 8.1.2 version: 8.1.2
@ -652,6 +658,9 @@ importers:
jest-mock: jest-mock:
specifier: 29.5.0 specifier: 29.5.0
version: 29.5.0 version: 29.5.0
simple-oauth2:
specifier: ^5.0.0
version: 5.0.0
packages/frontend: packages/frontend:
dependencies: dependencies:
@ -4934,6 +4943,24 @@ packages:
hashlru: 2.3.0 hashlru: 2.3.0
dev: false dev: false
/@hapi/boom@10.0.1:
resolution: {integrity: sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==}
dependencies:
'@hapi/hoek': 11.0.2
dev: true
/@hapi/bourne@3.0.0:
resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
dev: true
/@hapi/hoek@10.0.1:
resolution: {integrity: sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw==}
dev: true
/@hapi/hoek@11.0.2:
resolution: {integrity: sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==}
dev: true
/@hapi/hoek@9.3.0: /@hapi/hoek@9.3.0:
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
dev: true dev: true
@ -4944,6 +4971,14 @@ packages:
'@hapi/hoek': 9.3.0 '@hapi/hoek': 9.3.0
dev: true dev: true
/@hapi/wreck@18.0.1:
resolution: {integrity: sha512-OLHER70+rZxvDl75xq3xXOfd3e8XIvz8fWY0dqg92UvhZ29zo24vQgfqgHSYhB5ZiuFpSLeriOisAlxAo/1jWg==}
dependencies:
'@hapi/boom': 10.0.1
'@hapi/bourne': 3.0.0
'@hapi/hoek': 11.0.2
dev: true
/@humanwhocodes/config-array@0.11.10: /@humanwhocodes/config-array@0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
engines: {node: '>=10.10.0'} engines: {node: '>=10.10.0'}
@ -7964,6 +7999,10 @@ packages:
sharp: 0.32.1 sharp: 0.32.1
dev: true dev: true
/@types/simple-oauth2@5.0.4:
resolution: {integrity: sha512-4SvTfmAa1fGUa1d07j9vIiC4o92bGh0ihPXmtS05udMMmNwVIaU2nZ706cC4wI8cJxOlHD4P/d5tzqvWYd+KxA==}
dev: true
/@types/sinon@10.0.13: /@types/sinon@10.0.13:
resolution: {integrity: sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==} resolution: {integrity: sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==}
dependencies: dependencies:
@ -10609,6 +10648,10 @@ packages:
shebang-command: 2.0.0 shebang-command: 2.0.0
which: 2.0.2 which: 2.0.2
/crypto-js@4.1.1:
resolution: {integrity: sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==}
dev: false
/crypto-random-string@2.0.0: /crypto-random-string@2.0.0:
resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -17096,6 +17139,12 @@ packages:
engines: {node: '>= 6'} engines: {node: '>= 6'}
dev: true dev: true
/pkce-challenge@3.1.0:
resolution: {integrity: sha512-bQ/0XPZZ7eX+cdAkd61uYWpfMhakH3NeteUF1R8GNa+LMqX8QFAkbCLqq+AYAns1/ueACBu/BMWhrlKGrdvGZg==}
dependencies:
crypto-js: 4.1.1
dev: false
/pkg-dir@3.0.0: /pkg-dir@3.0.0:
resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -18890,6 +18939,17 @@ packages:
once: 1.4.0 once: 1.4.0
simple-concat: 1.0.1 simple-concat: 1.0.1
/simple-oauth2@5.0.0:
resolution: {integrity: sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==}
dependencies:
'@hapi/hoek': 10.0.1
'@hapi/wreck': 18.0.1
debug: 4.3.4(supports-color@8.1.1)
joi: 17.7.0
transitivePeerDependencies:
- supports-color
dev: true
/simple-swizzle@0.2.2: /simple-swizzle@0.2.2:
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
dependencies: dependencies: