Skip to content

Commit d303591

Browse files
committed
Use ErrorType and throws for decoding
1 parent 924e5ba commit d303591

File tree

3 files changed

+55
-70
lines changed

3 files changed

+55
-70
lines changed

JWT/Decode.swift

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Foundation
22

33

44
/// Failure reasons from decoding a JWT
5-
public enum InvalidToken : CustomStringConvertible {
5+
public enum InvalidToken : CustomStringConvertible, ErrorType {
66
/// Decoding the JWT itself failed
77
case DecodeError(String)
88

@@ -46,34 +46,25 @@ public enum InvalidToken : CustomStringConvertible {
4646
}
4747

4848

49-
/// Result from decoding a JWT
50-
public enum DecodeResult {
51-
/// Decoding succeeded
52-
case Success(Payload)
53-
54-
/// Decoding failed, take a look at the invalid token reason
55-
case Failure(InvalidToken)
56-
}
57-
5849
/// Decode a JWT
59-
public func decode(jwt:String, algorithms:[Algorithm], verify:Bool = true, audience:String? = nil, issuer:String? = nil) -> DecodeResult {
50+
public func decode(jwt:String, algorithms:[Algorithm], verify:Bool = true, audience:String? = nil, issuer:String? = nil) throws -> Payload {
6051
switch load(jwt) {
6152
case let .Success(header, payload, signature, signatureInput):
6253
if verify {
6354
if let failure = validateClaims(payload, audience: audience, issuer: issuer) ?? verifySignature(algorithms, header: header, signingInput: signatureInput, signature: signature) {
64-
return .Failure(failure)
55+
throw failure
6556
}
6657
}
6758

68-
return .Success(payload)
59+
return payload
6960
case .Failure(let failure):
70-
return .Failure(failure)
61+
throw failure
7162
}
7263
}
7364

7465
/// Decode a JWT
75-
public func decode(jwt:String, algorithm:Algorithm, verify:Bool = true, audience:String? = nil, issuer:String? = nil) -> DecodeResult {
76-
return decode(jwt, algorithms: [algorithm], verify: verify, audience: audience, issuer: issuer)
66+
public func decode(jwt:String, algorithm:Algorithm, verify:Bool = true, audience:String? = nil, issuer:String? = nil) throws -> Payload {
67+
return try decode(jwt, algorithms: [algorithm], verify: verify, audience: audience, issuer: issuer)
7768
}
7869

7970
// MARK: Parsing a JWT

JWTTests/JWTTests.swift

Lines changed: 42 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class JWTEncodeTests : XCTestCase {
1515
builder.issuer = "fuller.li"
1616
}
1717

18-
assertSuccess(JWT.decode(jwt, algorithm: algorithm)) { payload in
18+
assertSuccess(try JWT.decode(jwt, algorithm: algorithm)) { payload in
1919
XCTAssertEqual(payload as NSDictionary, ["iss": "fuller.li"])
2020
}
2121
}
@@ -76,58 +76,58 @@ class JWTPayloadBuilder : XCTestCase {
7676
class JWTDecodeTests : XCTestCase {
7777
func testDecodingValidJWT() {
7878
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiS3lsZSJ9.zxm7xcp1eZtZhp4t-nlw09ATQnnFKIiSN83uG8u6cAg"
79-
let result = JWT.decode(jwt, algorithm: .HS256("secret"))
80-
assertSuccess(result) { payload in
79+
80+
assertSuccess(try JWT.decode(jwt, algorithm: .HS256("secret"))) { payload in
8181
XCTAssertEqual(payload as NSDictionary, ["name": "Kyle"])
8282
}
8383
}
8484

8585
func testFailsToDecodeInvalidStringWithoutThreeSegments() {
86-
assertDecodeError(decode("a.b", algorithm: .None), error: "Not enough segments")
86+
assertDecodeError(try decode("a.b", algorithm: .None), error: "Not enough segments")
8787
}
8888

8989
// MARK: Disable verify
9090

9191
func testDisablingVerify() {
9292
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w"
93-
assertSuccess(decode(jwt, algorithm: .None, verify:false, issuer:"fuller.li"))
93+
assertSuccess(try decode(jwt, algorithm: .None, verify:false, issuer:"fuller.li"))
9494
}
9595

9696
// MARK: Issuer claim
9797

9898
func testSuccessfulIssuerValidation() {
9999
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmdWxsZXIubGkifQ.d7B7PAQcz1E6oNhrlxmHxHXHgg39_k7X7wWeahl8kSQ"
100-
assertSuccess(decode(jwt, algorithm: .HS256("secret"), issuer:"fuller.li")) { payload in
100+
assertSuccess(try decode(jwt, algorithm: .HS256("secret"), issuer:"fuller.li")) { payload in
101101
XCTAssertEqual(payload as NSDictionary, ["iss": "fuller.li"])
102102
}
103103
}
104104

105105
func testIncorrectIssuerValidation() {
106106
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmdWxsZXIubGkifQ.wOhJ9_6lx-3JGJPmJmtFCDI3kt7uMAMmhHIslti7ryI"
107-
assertFailure(decode(jwt, algorithm: .HS256("secret"), issuer:"querykit.org"))
107+
assertFailure(try decode(jwt, algorithm: .HS256("secret"), issuer:"querykit.org"))
108108
}
109109

110110
func testMissingIssuerValidation() {
111111
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w"
112-
assertFailure(decode(jwt, algorithm: .HS256("secret"), issuer:"fuller.li"))
112+
assertFailure(try decode(jwt, algorithm: .HS256("secret"), issuer:"fuller.li"))
113113
}
114114

115115
// MARK: Expiration claim
116116

117117
func testExpiredClaim() {
118118
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0MjgxODg0OTF9.cy6b2szsNkKnHFnz2GjTatGjoHBTs8vBKnPGZgpp91I"
119-
assertFailure(decode(jwt, algorithm: .HS256("secret")))
119+
assertFailure(try decode(jwt, algorithm: .HS256("secret")))
120120
}
121121

122122
func testInvalidExpiaryClaim() {
123123
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOlsiMTQyODE4ODQ5MSJdfQ.OwF-wd3THjxrEGUhh6IdnNhxQZ7ydwJ3Z6J_dfl9MBs"
124-
assertFailure(decode(jwt, algorithm: .HS256("secret")))
124+
assertFailure(try decode(jwt, algorithm: .HS256("secret")))
125125
}
126126

127127
func testUnexpiredClaim() {
128128
// If this just started failing, hello 2024!
129129
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjgxODg0OTF9.EW7k-8Mvnv0GpvOKJalFRLoCB3a3xGG3i7hAZZXNAz0"
130-
assertSuccess(decode(jwt, algorithm: .HS256("secret"))) { payload in
130+
assertSuccess(try decode(jwt, algorithm: .HS256("secret"))) { payload in
131131
XCTAssertEqual(payload as NSDictionary, ["exp": 1728188491])
132132
}
133133
}
@@ -136,131 +136,127 @@ class JWTDecodeTests : XCTestCase {
136136

137137
func testNotBeforeClaim() {
138138
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0MjgxODk3MjB9.jFT0nXAJvEwyG6R7CMJlzNJb7FtZGv30QRZpYam5cvs"
139-
assertSuccess(decode(jwt, algorithm: .HS256("secret"))) { payload in
139+
assertSuccess(try decode(jwt, algorithm: .HS256("secret"))) { payload in
140140
XCTAssertEqual(payload as NSDictionary, ["nbf": 1428189720])
141141
}
142142
}
143143

144144
func testInvalidNotBeforeClaim() {
145145
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOlsxNDI4MTg5NzIwXX0.PUL1FQubzzJa4MNXe2D3d5t5cMaqFr3kYlzRUzly-C8"
146-
assertDecodeError(decode(jwt, algorithm: .HS256("secret")), error: "Not before claim (nbf) must be an integer")
146+
assertDecodeError(try decode(jwt, algorithm: .HS256("secret")), error: "Not before claim (nbf) must be an integer")
147147
}
148148

149149
func testUnmetNotBeforeClaim() {
150150
// If this just started failing, hello 2024!
151151
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3MjgxODg0OTF9.Tzhu1tu-7BXcF5YEIFFE1Vmg4tEybUnaz58FR4PcblQ"
152-
assertFailure(decode(jwt, algorithm: .HS256("secret")))
152+
assertFailure(try decode(jwt, algorithm: .HS256("secret")))
153153
}
154154

155155
// MARK: Issued at claim
156156

157157
func testIssuedAtClaimInThePast() {
158158
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MjgxODk3MjB9.I_5qjRcCUZVQdABLwG82CSuu2relSdIyJOyvXWUAJh4"
159-
assertSuccess(decode(jwt, algorithm: .HS256("secret"))) { payload in
159+
assertSuccess(try decode(jwt, algorithm: .HS256("secret"))) { payload in
160160
XCTAssertEqual(payload as NSDictionary, ["iat": 1428189720])
161161
}
162162
}
163163

164164
func testIssuedAtClaimInTheFuture() {
165165
// If this just started failing, hello 2024!
166166
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MjgxODg0OTF9.owHiJyJmTcW1lBW5y_Rz3iBfSbcNiXlbZ2fY9qR7-aU"
167-
assertFailure(decode(jwt, algorithm: .HS256("secret")))
167+
assertFailure(try decode(jwt, algorithm: .HS256("secret")))
168168
}
169169

170170
func testInvalidIssuedAtClaim() {
171171
// If this just started failing, hello 2024!
172172
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOlsxNzI4MTg4NDkxXX0.ND7QMWtLkXDXH38OaXM3SQgLo3Z5TNgF_pcfWHV_alQ"
173-
assertDecodeError(decode(jwt, algorithm: .HS256("secret")), error: "Issued at claim (iat) must be an integer")
173+
assertDecodeError(try decode(jwt, algorithm: .HS256("secret")), error: "Issued at claim (iat) must be an integer")
174174
}
175175

176176
// MARK: Audience claims
177177

178178
func testAudiencesClaim() {
179179
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibWF4aW5lIiwia2F0aWUiXX0.-PKvdNLCClrWG7CvesHP6PB0-vxu-_IZcsYhJxBy5JM"
180-
assertSuccess(decode(jwt, algorithm: .HS256("secret"), audience:"maxine")) { payload in
180+
assertSuccess(try decode(jwt, algorithm: .HS256("secret"), audience:"maxine")) { payload in
181181
XCTAssertEqual(payload as NSDictionary, ["aud": ["maxine", "katie"]])
182182
}
183183
}
184184

185185
func testAudienceClaim() {
186186
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJreWxlIn0.dpgH4JOwueReaBoanLSxsGTc7AjKUvo7_M1sAfy_xVE"
187-
assertSuccess(decode(jwt, algorithm: .HS256("secret"), audience:"kyle")) { payload in
187+
assertSuccess(try decode(jwt, algorithm: .HS256("secret"), audience:"kyle")) { payload in
188188
XCTAssertEqual(payload as NSDictionary, ["aud": "kyle"])
189189
}
190190
}
191191

192192
func testMismatchAudienceClaim() {
193193
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJreWxlIn0.VEB_n06pTSLlTXPFkc46ARADJ9HXNUBUPo3VhL9RDe4" // kyle
194-
assertFailure(decode(jwt, algorithm: .HS256("secret"), audience:"maxine"))
194+
assertFailure(try decode(jwt, algorithm: .HS256("secret"), audience:"maxine"))
195195
}
196196

197197
func testMissingAudienceClaim() {
198198
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w"
199-
assertFailure(decode(jwt, algorithm: .HS256("secret"), audience:"kyle"))
199+
assertFailure(try decode(jwt, algorithm: .HS256("secret"), audience:"kyle"))
200200
}
201201

202202
// MARK: Signature verification
203203

204204
func testNoneAlgorithm() {
205205
let jwt = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ0ZXN0IjoiaW5nIn0."
206-
assertSuccess(decode(jwt, algorithm:.None)) { payload in
206+
assertSuccess(try decode(jwt, algorithm:.None)) { payload in
207207
XCTAssertEqual(payload as NSDictionary, ["test": "ing"])
208208
}
209209
}
210210

211211
func testNoneFailsWithSecretAlgorithm() {
212212
let jwt = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ0ZXN0IjoiaW5nIn0."
213-
assertFailure(decode(jwt, algorithm: .HS256("secret")))
213+
assertFailure(try decode(jwt, algorithm: .HS256("secret")))
214214
}
215215

216216
func testMatchesAnyAlgorithm() {
217217
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w."
218-
assertFailure(decode(jwt, algorithms: [.HS256("anothersecret"), .HS256("secret")]))
218+
assertFailure(try decode(jwt, algorithms: [.HS256("anothersecret"), .HS256("secret")]))
219219
}
220220

221221
func testHS384Algorithm() {
222222
let jwt = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.lddiriKLoo42qXduMhCTKZ5Lo3njXxOC92uXyvbLyYKzbq4CVVQOb3MpDwnI19u4"
223-
assertSuccess(decode(jwt, algorithm: .HS384("secret"))) { payload in
223+
assertSuccess(try decode(jwt, algorithm: .HS384("secret"))) { payload in
224224
XCTAssertEqual(payload as NSDictionary, ["some": "payload"])
225225
}
226226
}
227227

228228
func testHS512Algorithm() {
229229
let jwt = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.WTzLzFO079PduJiFIyzrOah54YaM8qoxH9fLMQoQhKtw3_fMGjImIOokijDkXVbyfBqhMo2GCNu4w9v7UXvnpA"
230-
assertSuccess(decode(jwt, algorithm: .HS512("secret"))) { payload in
230+
assertSuccess(try decode(jwt, algorithm: .HS512("secret"))) { payload in
231231
XCTAssertEqual(payload as NSDictionary, ["some": "payload"])
232232
}
233233
}
234234
}
235235

236236
// MARK: Helpers
237237

238-
func assertSuccess(result:DecodeResult, closure:(Payload -> ())? = nil) {
239-
switch result {
240-
case .Success(let payload):
241-
if let closure = closure {
242-
closure(payload)
243-
}
244-
case .Failure(let failure):
245-
XCTFail("Failed to decode while expecting success. \(failure)")
246-
break
238+
func assertSuccess(@autoclosure decoder:() throws -> Payload, closure:(Payload -> ())? = nil) {
239+
do {
240+
let payload = try decoder()
241+
closure?(payload)
242+
} catch {
243+
XCTFail("Failed to decode while expecting success. \(error)")
247244
}
248245
}
249246

250-
func assertFailure(result:DecodeResult, closure:(InvalidToken -> ())? = nil) {
251-
switch result {
252-
case .Success:
253-
XCTFail("Decoded when expecting a failure.")
254-
case .Failure(let failure):
255-
if let closure = closure {
256-
closure(failure)
257-
}
258-
break
247+
func assertFailure(@autoclosure decoder:() throws -> Payload, closure:(InvalidToken -> ())? = nil) {
248+
do {
249+
_ = try decoder()
250+
XCTFail("Decoding succeeded, expected a failure.")
251+
} catch let error as InvalidToken {
252+
closure?(error)
253+
} catch {
254+
XCTFail("Unexpected error")
259255
}
260256
}
261257

262-
func assertDecodeError(result:DecodeResult, error:String) {
263-
assertFailure(result) { failure in
258+
func assertDecodeError(@autoclosure decoder:() throws -> Payload, error:String) {
259+
assertFailure(try decoder()) { failure in
264260
switch failure {
265261
case .DecodeError(let decodeError):
266262
if decodeError != error {

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,18 @@ JWT.encode(.HS256("secret")) { builder in
3939
When decoding a JWT, you must supply one or more algorithms and keys.
4040

4141
```swift
42-
let result = JWT.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w", algorithm: .HS256("secret"))
43-
44-
switch result {
45-
case .Success(let payload):
46-
print(payload)
47-
case .Failure(let failure):
48-
print("decoding failed \(failure)")
42+
do {
43+
let payload = try JWT.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w", algorithm: .HS256("secret"))
44+
print(payload)
45+
} catch {
46+
print("Failed to decode JWT: \(error)")
4947
}
5048
```
5149

5250
When the JWT may be signed with one out of many algorithms or keys:
5351

5452
```swift
55-
let result = JWT.decode("eyJh...5w", algorithms: [.HS256("secret"), .HS256("secret2"), .HS512("secure")])
53+
try JWT.decode("eyJh...5w", algorithms: [.HS256("secret"), .HS256("secret2"), .HS512("secure")])
5654
```
5755

5856
#### Supported claims

0 commit comments

Comments
 (0)