diff --git a/src/jwt.ts b/src/jwt.ts index 44d8097..bc82ab5 100644 --- a/src/jwt.ts +++ b/src/jwt.ts @@ -54,12 +54,13 @@ export class JWTcontext { public async verify(jwt: string, audience?: string | string[], issuer?: string | string[]): Promise> { log.debug('Verify JWT') - log.trace('Issuers :', issuer) + log.trace('Issuer :', issuer) + log.trace('Audience :', audience) const key = this.get_key(false) try { - let payload = await jose.jwtVerify(jwt, key) + let payload = await jose.jwtVerify(jwt, key, {audience, issuer}) return Result.ok(payload.payload.message as T) } catch(e) { log.warn('JWT verification failed') diff --git a/test/jwt.test.ts b/test/jwt.test.ts index 31e8fcd..9df8637 100644 --- a/test/jwt.test.ts +++ b/test/jwt.test.ts @@ -1,10 +1,16 @@ -import {beforeAll, expect, test} from 'bun:test' -import {JWTcontext, type JWTalgorithm} from '../src/jwt' +import {beforeAll, describe, expect, setSystemTime, test} from 'bun:test' +import {JWTcontext, type JWTalgorithm, type Key} from '../src/jwt' + +let k!: Key; +let c!: JWTcontext; const algs: JWTalgorithm[] = ["HS256", "HS512", "ES256", "ES512", "EdDSA"] const contexts: Map = new Map() beforeAll(async () => { + k = (await JWTcontext.gen_key("HS256")) as Key + c = new JWTcontext(k, "HS256") + for (const alg of algs) { const key = await JWTcontext.gen_key(alg as JWTalgorithm) expect(key).not.toBeUndefined() @@ -28,10 +34,103 @@ test('Base case', async () => { } }) -test.todo("Multiple audience can be verified") -test.todo("Multiple issuer can be verified") -test.todo("Wrong audience is rejected") -test.todo("Wrong issuer is rejected") -test.todo("Expired JWT is rejected") -test.todo("Wrong key won't decrypt") -test.todo("tampered JWT are rejected (test 3 parts) (TODO : decode ?)") +describe("Audience verification", () => { + const cases : [string|string[]|undefined, string|string[]|undefined, boolean][] = [ + // undefined at verify means we don't enforce that field + [undefined, undefined, true], + ["value", undefined, true], + [["value", "other"], undefined, true], + + [undefined, "value", false], + [undefined, ["value", "other"], false], + + ["value", "value", true], + ["value", ["value", "other"], true], + ["value", "yeet", false], + ["value", ["yeet", "other"], false], + + [["value", "other"], "value", true], + [["value", "other"], ["value", "yeet"], true], + [["value", "other"], ["value", "other"], true], + [["yeet", "other"], "value", false], + [["value", "other"], ["yeet", "yaat"], false], + ] + + for (const [at_sign, at_verify, result] of cases) { + test(`${at_sign} and ${at_verify} ${result ? 'should' : "shouldn't"} work`, async () => { + const message = "Yeet" + const context = [...contexts.values()][0] + + const jwt = await context.sign(message, false, undefined, at_sign) + const res = await context.verify(jwt, at_verify) + if (result) { + res.expect("The JWT should be valid") + } else { + res.expect_err("The JWT shouldn't be valid") + } + }) + } +}) +describe("Issuer verification", () => { + const cases: [string|undefined, string|string[]|undefined, boolean][] = [ + // undefined at verify means don't enforce the field + [undefined, undefined, true], + ["value", undefined, true], + + [undefined, "value", false], + [undefined, ["value", "other"], false], + + ["value", "value", true], + ["value", ["value", "other"], true], + ["value", "yeet", false], + ["value", ["yeet", "other"], false], + ] + + for (const [at_sign, at_verify, result] of cases) { + test(`${at_sign} and ${at_verify} ${result ? 'should' : "shouldn't"} work`, async () => { + const message = "Yaat" + const context = [...contexts.values()][0] + + const jwt = await context.sign(message, false, undefined, undefined, at_sign) + const res = await context.verify(jwt, undefined, at_verify) + if (result) { + res.expect("The JWT should be valid") + } else { + res.expect_err("The JWT shouldn't be valid") + } + }) + } +}) +test("Expired JWT is rejected", async () => { + const message = "yeet" + + const jwt = await c.sign(message, false, "5min") + + const today = new Date() + today.setDate(today.getDate() + 1) + setSystemTime(today) + + const res = await c.verify(jwt) + res.expect_err("Shouldn't verify expired JWT") +}) +test("Wrong key won't decrypt", async () => { + const alg = "HS256" + const k2 = await JWTcontext.gen_key(alg, false) + const c2 = new JWTcontext(k2, alg) + + const message = "yeet" + const jwt = await c.sign(message) + const res = await c2.verify(jwt) + + res.expect_err("Shouldn't verify with a different key") +}) +test("tampered JWT are rejected", async () => { + const message = "yeet" + let jwt = await c.sign(message) + + if (jwt[0] === "a") jwt = "b" + jwt.substring(1) + else jwt = "a" + jwt.substring(1) + + const res = await c.verify(jwt) + res.expect_err("Shouldn't verify a tampered JWT") +})