Split PKCE verification test

This commit is contained in:
Kagami Sascha Rosylight 2023-06-04 13:30:18 +02:00
parent 9022971fb9
commit c25836bc1a
2 changed files with 45 additions and 48 deletions

View file

@ -143,7 +143,6 @@ class OAuth2Store {
@Injectable() @Injectable()
export class OAuth2ProviderService { export class OAuth2ProviderService {
// #provider: Provider;
#server = oauth2orize.createServer({ #server = oauth2orize.createServer({
store: new OAuth2Store(), store: new OAuth2Store(),
}); });

View file

@ -1,3 +1,8 @@
/**
* Basic OAuth tests to make sure the library is correctly integrated to Misskey
* and not regressed by version updates or potential migration to another library.
*/
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
@ -28,7 +33,7 @@ interface AuthorizationParamsExtended {
} }
interface AuthorizationTokenConfigExtended extends AuthorizationTokenConfig { interface AuthorizationTokenConfigExtended extends AuthorizationTokenConfig {
code_verifier: string; code_verifier: string | undefined;
} }
function getClient(): AuthorizationCode<'client_id'> { function getClient(): AuthorizationCode<'client_id'> {
@ -282,59 +287,52 @@ describe('OAuth', () => {
assert.strictEqual((await response.json() as OAuthErrorResponse).error, 'invalid_request'); assert.strictEqual((await response.json() as OAuthErrorResponse).error, 'invalid_request');
}); });
test('Verify PKCE', async () => { // Use precomputed challenge/verifier set here for deterministic test
// Use precomputed challenge/verifier set for this one for deterministic test const code_challenge = '4w2GDuvaxXlw2l46k5PFIoIcTGHdzw2i3hrn-C_Q6f7u0-nTYKd-beVEYy9XinYsGtAix.Nnvr.GByD3lAii2ibPRsSDrZgIN0YQb.kfevcfR9aDKoTLyOUm4hW4ABhs';
const code_challenge = '4w2GDuvaxXlw2l46k5PFIoIcTGHdzw2i3hrn-C_Q6f7u0-nTYKd-beVEYy9XinYsGtAix.Nnvr.GByD3lAii2ibPRsSDrZgIN0YQb.kfevcfR9aDKoTLyOUm4hW4ABhs'; const code_verifier = 'Ew8VSBiH59JirLlg7ocFpLQ6NXuFC1W_rn8gmRzBKc8';
const code_verifier = 'Ew8VSBiH59JirLlg7ocFpLQ6NXuFC1W_rn8gmRzBKc8';
const client = getClient(); const tests: Record<string, string | undefined> = {
'Code followed by some junk code': code_verifier + 'x',
'Clipped code': code_verifier.slice(0, 80),
'Some part of code is replaced': code_verifier.slice(0, -10) + 'x'.repeat(10),
'No verifier': undefined,
};
const response = await fetch(client.authorizeURL({ describe('Verify PKCE', () => {
redirect_uri, for (const [title, code_verifier] of Object.entries(tests)) {
scope: 'write:notes', test(title, async () => {
state: 'state', const client = getClient();
code_challenge,
code_challenge_method: 'S256',
} as AuthorizationParamsExtended));
assert.strictEqual(response.status, 200);
const decisionResponse = await fetchDecisionFromResponse(response, alice); const response = await fetch(client.authorizeURL({
assert.strictEqual(decisionResponse.status, 302); redirect_uri,
scope: 'write:notes',
state: 'state',
code_challenge,
code_challenge_method: 'S256',
} as AuthorizationParamsExtended));
assert.strictEqual(response.status, 200);
const code = new URL(decisionResponse.headers.get('location')!).searchParams.get('code')!; // TODO: this fetch-decision-code checks are everywhere, maybe get a helper for this.
assert.ok(!!code); const decisionResponse = await fetchDecisionFromResponse(response, alice);
assert.strictEqual(decisionResponse.status, 302);
// Pattern 1: code followed by some junk code const code = new URL(decisionResponse.headers.get('location')!).searchParams.get('code')!;
await assert.rejects(client.getToken({ assert.ok(!!code);
code,
redirect_uri,
code_verifier: code_verifier + 'x',
} as AuthorizationTokenConfigExtended));
// TODO: The following patterns may fail only because of pattern 1's failure. Let's split them. await assert.rejects(client.getToken({
code,
redirect_uri,
code_verifier,
} as AuthorizationTokenConfigExtended));
// Pattern 2: clipped code // And now the code is invalidated by the previous failure
await assert.rejects(client.getToken({ await assert.rejects(client.getToken({
code, code,
redirect_uri, redirect_uri,
code_verifier: code_verifier.slice(0, 80), code_verifier,
} as AuthorizationTokenConfigExtended)); } as AuthorizationTokenConfigExtended));
});
// Pattern 3: Some part of code is replaced }
await assert.rejects(client.getToken({
code,
redirect_uri,
code_verifier: code_verifier.slice(0, -10) + 'x'.repeat(10),
} as AuthorizationTokenConfigExtended));
// TODO: pattern 4: no code_verifier
// And now the code is invalidated by the previous failures
await assert.rejects(client.getToken({
code,
redirect_uri,
code_verifier,
} as AuthorizationTokenConfigExtended));
}); });
}); });