Skip to content

Commit 40e9748

Browse files
authored
Merge pull request #2 from vanioinformatika/review
Code review for ts-rewrite
2 parents ee5ed68 + ef21cd0 commit 40e9748

File tree

10 files changed

+329
-318
lines changed

10 files changed

+329
-318
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
All notable changes to this project will be documented in this file.
44
This project adheres to [Semantic Versioning](http://semver.org/).
55

6+
## Next release
7+
8+
- Add `options` object to `Handler`'s constructor
9+
- Make `verify` method's return value generic
10+
611
## 2.0.0 2017.08.09
712

813
- Complete reimplementation in TypeScript

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ function privkeyResolver (keyId) {
3333

3434
const jwtHandler = new jwt.JwtHandler('myproject', pubkeyResolver, privkeyResolver)
3535

36+
// or with options object
37+
38+
const jwtHandler = new jwt.JwtHandler({
39+
debugNamePrefix: 'myproject',
40+
pubkeyResolver: pubkeyResolver,
41+
privkeyResolver: privkeyResolver,
42+
})
43+
3644
// Verifying JWT tokens
3745
jwtHandler.verify(jwtRaw)
3846
.then(jwtBody => {

index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export {TokenExpiredError, NotBeforeError, JsonWebTokenError } from "jsonwebtoken"
1+
export {TokenExpiredError, NotBeforeError, JsonWebTokenError} from "jsonwebtoken"
22
export {JwtHandler, PubkeyResolver, PrivkeyResolver, PubkeyData, PrivkeyData} from "./src/JwtHandler"
33
export {MissingKeyIdError} from "./src/MissingKeyIdError"
44
export {UnknownKeyIdError} from "./src/UnknownKeyIdError"

src/JwtHandler.spec.ts

Lines changed: 159 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,183 +1,187 @@
11
import * as chai from "chai"
22
import * as chaiAsPromised from "chai-as-promised"
33
import * as jwt from "jsonwebtoken"
4-
chai.use(chaiAsPromised)
5-
const { expect } = chai
6-
74
import "mocha"
85

9-
import { JwtHandler, PrivkeyData, PubkeyData } from "./JwtHandler"
10-
import { MissingKeyIdError } from "./MissingKeyIdError"
11-
import { UnknownKeyIdError } from "./UnknownKeyIdError"
6+
import {JwtHandler, PrivkeyData, PubkeyData} from "./JwtHandler"
7+
import {MissingKeyIdError} from "./MissingKeyIdError"
8+
import {UnknownKeyIdError} from "./UnknownKeyIdError"
9+
10+
chai.use(chaiAsPromised)
11+
const {expect} = chai
1212

1313
const debugNamePrefix = "test"
1414

1515
// fixtures
1616
const keyId = "abc1234"
1717
const tokenBody = {
18-
iat: Math.floor(Date.now() / 1000),
19-
dummy: "dummy",
20-
iss: "issuer1",
21-
aud: "audience1",
18+
iat: Math.floor(Date.now() / 1000),
19+
dummy: "dummy",
20+
iss: "issuer1",
21+
aud: "audience1",
2222
}
23+
2324
function pubkeyResolver(pubkeyId: string): PubkeyData {
24-
return (pubkeyId === keyId) ? {cert, alg: "RS256"} : null
25+
return (pubkeyId === keyId) ? {cert, alg: "RS256"} : null
2526
}
27+
2628
function privkeyResolver(privkeyId: string): PrivkeyData {
27-
return (privkeyId === keyId) ? {key: privateKey, passphrase: privateKeyPass, alg: "RS256"} : null
29+
return (privkeyId === keyId) ? {key: privateKey, passphrase: privateKeyPass, alg: "RS256"} : null
2830
}
2931

3032
describe("JwtHandler", () => {
3133

32-
describe("extractKeyId", () => {
33-
const jwtHandler = new JwtHandler(debugNamePrefix, pubkeyResolver, privkeyResolver)
34-
it("should return the correct key id from the given JWT", () => {
35-
const jwtRaw = generateJwt(keyId, tokenBody)
36-
expect(jwtHandler.extractKeyId(jwtRaw)).to.equal(keyId)
37-
})
38-
39-
it("should be rejected with MissingKeyIdError if the JWT does not contain a kid property in the header", () => {
40-
const jwtRaw = generateJwt(null, tokenBody)
41-
expect(() => jwtHandler.extractKeyId(jwtRaw)).to.throw(MissingKeyIdError)
42-
})
43-
44-
it("should be rejected with JsonWebTokenError if the JWT header is not JSON", () => {
45-
// tslint:disable-next-line:max-line-length
46-
const jwtRaw = "76576576werwerwterertertert.7868765348765zurtiueziuerziutziuzeriuziuwtzuizi34986349.345765347654376543765735765tzreztwrwueruz"
47-
expect(() => jwtHandler.extractKeyId(jwtRaw)).to.throw(jwt.JsonWebTokenError)
48-
})
49-
50-
it("should be rejected with JsonWebTokenError if the JWT is complete garbage", () => {
51-
// tslint:disable-next-line:max-line-length
52-
const jwtRaw = "!\\76576576w!!--///erwerwterertertertöüóAAŐÚÉÁŰ<<7868765348765>> zurtiueziuerz{ }iutziuzer*****iuziuwtzuizi34986349345765347654376543765735765+++====tzreztwrwueruz"
53-
expect(() => jwtHandler.extractKeyId(jwtRaw)).to.throw(jwt.JsonWebTokenError)
54-
})
55-
})
56-
57-
describe("verify", () => {
58-
const jwtHandler = new JwtHandler(debugNamePrefix, pubkeyResolver, privkeyResolver)
59-
it("should return the JWT body if passed a valid JWT", (done) => {
60-
const jwtRaw = generateJwt(keyId, tokenBody)
61-
expect(
62-
jwtHandler.verify(jwtRaw)
63-
).to.eventually.deep.equal(tokenBody).notify(done)
64-
})
65-
66-
it("should return the JWT body if passed a valid JWT and validation options that matches the JWT", (done) => {
67-
const jwtRaw = generateJwt(keyId, tokenBody)
68-
expect(
69-
jwtHandler.verify(jwtRaw, {issuer: tokenBody.iss})
70-
).to.eventually.deep.equal(tokenBody).notify(done)
71-
})
72-
73-
it("should be rejected with JsonWebTokenError if the validation options do not match", (done) => {
74-
const jwtRaw = generateJwt(keyId, tokenBody)
75-
expect(
76-
jwtHandler.verify(jwtRaw, {issuer: "expected_issuer"})
77-
).to.eventually.rejectedWith(jwt.JsonWebTokenError).notify(done)
78-
})
79-
80-
it("should be rejected with TokenExpiredError if the JWT is already expired", (done) => {
81-
const tokenBodyExpired = {
82-
iat: Math.floor(Date.now() / 1000),
83-
dummy: "dummy",
84-
exp: Math.floor(Date.now() / 1000) - 5000,
85-
}
86-
const jwtRaw = generateJwt(keyId, tokenBodyExpired)
87-
expect(
88-
jwtHandler.verify(jwtRaw)
89-
).to.eventually.rejectedWith(jwt.TokenExpiredError).notify(done)
90-
})
91-
92-
it("should be rejected with NotBeforeError if the is not yet valid", (done) => {
93-
const tokenBodyNbf = {
94-
iat: Math.floor(Date.now() / 1000),
95-
dummy: "dummy",
96-
nbf: Math.floor(Date.now() / 1000) + 1000,
97-
}
98-
const jwtRaw = generateJwt(keyId, tokenBodyNbf)
99-
expect(
100-
jwtHandler.verify(jwtRaw)
101-
).to.eventually.rejectedWith(jwt.NotBeforeError).notify(done)
102-
})
103-
104-
it("should be rejected with JsonWebTokenError if the JWT is empty", (done) => {
105-
expect(
106-
jwtHandler.verify("")
107-
).to.be.rejectedWith(jwt.JsonWebTokenError).notify(done)
108-
})
109-
110-
it("should be rejected with JsonWebTokenError if the JWT is null", (done) => {
111-
expect(
112-
jwtHandler.verify(null as any)
113-
).to.be.rejectedWith(jwt.JsonWebTokenError).notify(done)
114-
})
115-
116-
it("should be rejected with JsonWebTokenError if the JWT is undefined", (done) => {
117-
expect(
118-
jwtHandler.verify(undefined as any)
119-
).to.be.rejectedWith(jwt.JsonWebTokenError).notify(done)
120-
})
121-
122-
it("should be rejected with MissingKeyIdError if the JWT does not contain a kid property in the header", (done) => {
123-
const jwtRaw = generateJwt(null, tokenBody)
124-
expect(
125-
jwtHandler.verify(jwtRaw)
126-
).to.eventually.rejectedWith(MissingKeyIdError).notify(done)
127-
})
128-
129-
it("should be rejected with UnknownKeyIdError if the key id is unknown", (done) => {
130-
const jwtRaw = generateJwt("unknown-key-id", tokenBody)
131-
expect(
132-
jwtHandler.verify(jwtRaw)
133-
).to.eventually.rejectedWith(UnknownKeyIdError).notify(done)
134-
})
135-
136-
it("should be throw an Error if no pubkey resolver is specified", (done) => {
137-
const jwtHandlerNoPubkeyResolver = new JwtHandler(debugNamePrefix, null, privkeyResolver)
138-
const jwtRaw = generateJwt(keyId, tokenBody)
139-
expect(
140-
jwtHandlerNoPubkeyResolver.verify(jwtRaw)
141-
).to.eventually.rejectedWith(Error).notify(done)
142-
})
143-
})
144-
145-
describe("create", () => {
146-
const jwtHandler = new JwtHandler(debugNamePrefix, pubkeyResolver, privkeyResolver)
147-
it("should create a valid JWT if called with a key id that exists", (done) => {
148-
jwtHandler.create(tokenBody, keyId)
34+
describe("extractKeyId", () => {
35+
const jwtHandler = new JwtHandler(debugNamePrefix, pubkeyResolver, privkeyResolver)
36+
it("should return the correct key id from the given JWT", () => {
37+
const jwtRaw = generateJwt(keyId, tokenBody)
38+
expect(jwtHandler.extractKeyId(jwtRaw)).to.equal(keyId)
39+
})
40+
41+
it("should be rejected with MissingKeyIdError if the JWT does not contain a kid property in the header", () => {
42+
const jwtRaw = generateJwt(null, tokenBody)
43+
expect(() => jwtHandler.extractKeyId(jwtRaw)).to.throw(MissingKeyIdError)
44+
})
45+
46+
it("should be rejected with JsonWebTokenError if the JWT header is not JSON", () => {
47+
// tslint:disable-next-line:max-line-length
48+
const jwtRaw = "76576576werwerwterertertert.7868765348765zurtiueziuerziutziuzeriuziuwtzuizi34986349.345765347654376543765735765tzreztwrwueruz"
49+
expect(() => jwtHandler.extractKeyId(jwtRaw)).to.throw(jwt.JsonWebTokenError)
50+
})
51+
52+
it("should be rejected with JsonWebTokenError if the JWT is complete garbage", () => {
53+
// tslint:disable-next-line:max-line-length
54+
const jwtRaw = "!\\76576576w!!--///erwerwterertertertöüóAAŐÚÉÁŰ<<7868765348765>> zurtiueziuerz{ }iutziuzer*****iuziuwtzuizi34986349345765347654376543765735765+++====tzreztwrwueruz"
55+
expect(() => jwtHandler.extractKeyId(jwtRaw)).to.throw(jwt.JsonWebTokenError)
56+
})
57+
})
58+
59+
describe("verify", () => {
60+
const jwtHandler = new JwtHandler(debugNamePrefix, pubkeyResolver, privkeyResolver)
61+
it("should return the JWT body if passed a valid JWT", (done) => {
62+
const jwtRaw = generateJwt(keyId, tokenBody)
63+
expect(
64+
jwtHandler.verify(jwtRaw)
65+
).to.eventually.deep.equal(tokenBody).notify(done)
66+
})
67+
68+
it("should return the JWT body if passed a valid JWT and validation options that matches the JWT", (done) => {
69+
const jwtRaw = generateJwt(keyId, tokenBody)
70+
expect(
71+
jwtHandler.verify(jwtRaw, {issuer: tokenBody.iss})
72+
).to.eventually.deep.equal(tokenBody).notify(done)
73+
})
74+
75+
it("should be rejected with JsonWebTokenError if the validation options do not match", (done) => {
76+
const jwtRaw = generateJwt(keyId, tokenBody)
77+
expect(
78+
jwtHandler.verify(jwtRaw, {issuer: "expected_issuer"})
79+
).to.eventually.rejectedWith(jwt.JsonWebTokenError).notify(done)
80+
})
81+
82+
it("should be rejected with TokenExpiredError if the JWT is already expired", (done) => {
83+
const tokenBodyExpired = {
84+
iat: Math.floor(Date.now() / 1000),
85+
dummy: "dummy",
86+
exp: Math.floor(Date.now() / 1000) - 5000,
87+
}
88+
const jwtRaw = generateJwt(keyId, tokenBodyExpired)
89+
expect(
90+
jwtHandler.verify(jwtRaw)
91+
).to.eventually.rejectedWith(jwt.TokenExpiredError).notify(done)
92+
})
93+
94+
it("should be rejected with NotBeforeError if the is not yet valid", (done) => {
95+
const tokenBodyNbf = {
96+
iat: Math.floor(Date.now() / 1000),
97+
dummy: "dummy",
98+
nbf: Math.floor(Date.now() / 1000) + 1000,
99+
}
100+
const jwtRaw = generateJwt(keyId, tokenBodyNbf)
101+
expect(
102+
jwtHandler.verify(jwtRaw)
103+
).to.eventually.rejectedWith(jwt.NotBeforeError).notify(done)
104+
})
105+
106+
it("should be rejected with JsonWebTokenError if the JWT is empty", (done) => {
107+
expect(
108+
jwtHandler.verify("")
109+
).to.be.rejectedWith(jwt.JsonWebTokenError).notify(done)
110+
})
111+
112+
it("should be rejected with JsonWebTokenError if the JWT is null", (done) => {
113+
expect(
114+
jwtHandler.verify(null as any)
115+
).to.be.rejectedWith(jwt.JsonWebTokenError).notify(done)
116+
})
117+
118+
it("should be rejected with JsonWebTokenError if the JWT is undefined", (done) => {
119+
expect(
120+
jwtHandler.verify(undefined as any)
121+
).to.be.rejectedWith(jwt.JsonWebTokenError).notify(done)
122+
})
123+
124+
// tslint:disable-next-line:max-line-length
125+
it("should be rejected with MissingKeyIdError if the JWT does not contain a kid property in the header", (done) => {
126+
const jwtRaw = generateJwt(null, tokenBody)
127+
expect(
128+
jwtHandler.verify(jwtRaw)
129+
).to.eventually.rejectedWith(MissingKeyIdError).notify(done)
130+
})
131+
132+
it("should be rejected with UnknownKeyIdError if the key id is unknown", (done) => {
133+
const jwtRaw = generateJwt("unknown-key-id", tokenBody)
134+
expect(
135+
jwtHandler.verify(jwtRaw)
136+
).to.eventually.rejectedWith(UnknownKeyIdError).notify(done)
137+
})
138+
139+
it("should be throw an Error if no pubkey resolver is specified", (done) => {
140+
const jwtHandlerNoPubkeyResolver = new JwtHandler(debugNamePrefix, null, privkeyResolver)
141+
const jwtRaw = generateJwt(keyId, tokenBody)
142+
expect(
143+
jwtHandlerNoPubkeyResolver.verify(jwtRaw)
144+
).to.eventually.rejectedWith(Error).notify(done)
145+
})
146+
})
147+
148+
describe("create", () => {
149+
const jwtHandler = new JwtHandler(debugNamePrefix, pubkeyResolver, privkeyResolver)
150+
it("should create a valid JWT if called with a key id that exists", (done) => {
151+
jwtHandler.create(tokenBody, keyId)
149152
.then((result) => {
150-
expect(
151-
result.match(/^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/)
152-
).to.be.instanceof(Array)
153-
done()
153+
expect(
154+
result.match(/^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/)
155+
).to.be.instanceof(Array)
156+
done()
154157
})
158+
})
159+
it("should be rejected with UnknownKeyIdError with a key id that does not exist", (done) => {
160+
const keyIdUnknown = "unknown-key-id"
161+
expect(
162+
jwtHandler.create(tokenBody, keyIdUnknown)
163+
).to.be.rejectedWith(UnknownKeyIdError).notify(done)
164+
})
165+
it("should be rejected with Error if no privkey resolver is specified", (done) => {
166+
const jwtHandlerNoPrivkeyResolver = new JwtHandler(debugNamePrefix, pubkeyResolver, null)
167+
expect(
168+
jwtHandlerNoPrivkeyResolver.create(tokenBody, keyId)
169+
).to.be.rejectedWith(Error).notify(done)
170+
})
171+
// it("should be rejected with UnknownKeyIdError with a null key id", (done) => {
172+
// expect(
173+
// jwtHandler.create(tokenBody, null)
174+
// ).to.be.rejectedWith(UnknownKeyIdError).notify(done)
175+
// })
155176
})
156-
it("should be rejected with UnknownKeyIdError with a key id that does not exist", (done) => {
157-
const keyIdUnknown = "unknown-key-id"
158-
expect(
159-
jwtHandler.create(tokenBody, keyIdUnknown)
160-
).to.be.rejectedWith(UnknownKeyIdError).notify(done)
161-
})
162-
it("should be rejected with Error if no privkey resolver is specified", (done) => {
163-
const jwtHandlerNoPrivkeyResolver = new JwtHandler(debugNamePrefix, pubkeyResolver, null)
164-
expect(
165-
jwtHandlerNoPrivkeyResolver.create(tokenBody, keyId)
166-
).to.be.rejectedWith(Error).notify(done)
167-
})
168-
// it("should be rejected with UnknownKeyIdError with a null key id", (done) => {
169-
// expect(
170-
// jwtHandler.create(tokenBody, null)
171-
// ).to.be.rejectedWith(UnknownKeyIdError).notify(done)
172-
// })
173-
})
174177
})
175178

176179
function generateJwt(kid: string | null, body: object) {
177-
const header = kid ? {kid} : undefined
178-
return jwt.sign(body, {
179-
key: privateKey, passphrase: privateKeyPass}, {algorithm: "RS256", header}
180-
)
180+
const header = kid ? {kid} : undefined
181+
return jwt.sign(body, {
182+
key: privateKey, passphrase: privateKeyPass,
183+
}, {algorithm: "RS256", header}
184+
)
181185
}
182186

183187
const cert = `

0 commit comments

Comments
 (0)