Skip to content

Commit 414f5b7

Browse files
committed
Algorithms should take Data not a String
Closes #37
1 parent 3c357d7 commit 414f5b7

File tree

4 files changed

+53
-55
lines changed

4 files changed

+53
-55
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# JSON Web Token Changelog
22

3+
## Master
4+
5+
### Breaking
6+
7+
- Algorithms now take `Data` instead of a `String`. This improves the API
8+
allowing you to use keys that cannot be serialised as a String.
9+
10+
You can easily convert a String to Data such as in the following example:
11+
12+
```swift
13+
.hs256("secret".data(using: .utf8)!)
14+
```
15+
316
## 2.0.0
417

518
This release adds support for Swift 3.0.

JWTTests/JWTTests.swift

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1+
import Foundation
12
import XCTest
23
import JWT
34

45
class JWTEncodeTests : XCTestCase {
56
func testEncodingJWT() {
67
let payload = ["name": "Kyle"] as Payload
7-
let jwt = JWT.encode(payload, algorithm: .hs256("secret"))
8+
let jwt = JWT.encode(payload, algorithm: .hs256("secret".data(using: .utf8)!))
89
let fixture = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiS3lsZSJ9.zxm7xcp1eZtZhp4t-nlw09ATQnnFKIiSN83uG8u6cAg"
910
XCTAssertEqual(jwt, fixture)
1011
}
1112

1213
func testEncodingWithBuilder() {
13-
let algorithm = Algorithm.hs256("secret")
14+
let algorithm = Algorithm.hs256("secret".data(using: .utf8)!)
1415
let jwt = JWT.encode(algorithm) { builder in
1516
builder.issuer = "fuller.li"
1617
}
@@ -77,7 +78,7 @@ class JWTDecodeTests : XCTestCase {
7778
func testDecodingValidJWT() {
7879
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiS3lsZSJ9.zxm7xcp1eZtZhp4t-nlw09ATQnnFKIiSN83uG8u6cAg"
7980

80-
assertSuccess(try JWT.decode(jwt, algorithm: .hs256("secret"))) { payload in
81+
assertSuccess(try JWT.decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!))) { payload in
8182
XCTAssertEqual(payload as NSDictionary, ["name": "Kyle"])
8283
}
8384
}
@@ -97,45 +98,45 @@ class JWTDecodeTests : XCTestCase {
9798

9899
func testSuccessfulIssuerValidation() {
99100
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmdWxsZXIubGkifQ.d7B7PAQcz1E6oNhrlxmHxHXHgg39_k7X7wWeahl8kSQ"
100-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"), issuer:"fuller.li")) { payload in
101+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!), issuer:"fuller.li")) { payload in
101102
XCTAssertEqual(payload as NSDictionary, ["iss": "fuller.li"])
102103
}
103104
}
104105

105106
func testIncorrectIssuerValidation() {
106107
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmdWxsZXIubGkifQ.wOhJ9_6lx-3JGJPmJmtFCDI3kt7uMAMmhHIslti7ryI"
107-
assertFailure(try decode(jwt, algorithm: .hs256("secret"), issuer:"querykit.org"))
108+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!), issuer:"querykit.org"))
108109
}
109110

110111
func testMissingIssuerValidation() {
111112
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w"
112-
assertFailure(try decode(jwt, algorithm: .hs256("secret"), issuer:"fuller.li"))
113+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!), issuer:"fuller.li"))
113114
}
114115

115116
// MARK: Expiration claim
116117

117118
func testExpiredClaim() {
118119
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0MjgxODg0OTF9.cy6b2szsNkKnHFnz2GjTatGjoHBTs8vBKnPGZgpp91I"
119-
assertFailure(try decode(jwt, algorithm: .hs256("secret")))
120+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!)))
120121
}
121122

122123
func testInvalidExpiaryClaim() {
123124
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOlsiMTQyODE4ODQ5MSJdfQ.OwF-wd3THjxrEGUhh6IdnNhxQZ7ydwJ3Z6J_dfl9MBs"
124-
assertFailure(try decode(jwt, algorithm: .hs256("secret")))
125+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!)))
125126
}
126127

127128
func testUnexpiredClaim() {
128129
// If this just started failing, hello 2024!
129130
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjgxODg0OTF9.EW7k-8Mvnv0GpvOKJalFRLoCB3a3xGG3i7hAZZXNAz0"
130-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"))) { payload in
131+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!))) { payload in
131132
XCTAssertEqual(payload as NSDictionary, ["exp": 1728188491])
132133
}
133134
}
134135

135136
func testUnexpiredClaimString() {
136137
// If this just started failing, hello 2024!
137138
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIxNzI4MTg4NDkxIn0.y4w7lNLrfRRPzuNUfM-ZvPkoOtrTU_d8ZVYasLdZGpk"
138-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"))) { payload in
139+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!))) { payload in
139140
XCTAssertEqual(payload as NSDictionary, ["exp": "1728188491"])
140141
}
141142
}
@@ -144,81 +145,81 @@ class JWTDecodeTests : XCTestCase {
144145

145146
func testNotBeforeClaim() {
146147
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0MjgxODk3MjB9.jFT0nXAJvEwyG6R7CMJlzNJb7FtZGv30QRZpYam5cvs"
147-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"))) { payload in
148+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!))) { payload in
148149
XCTAssertEqual(payload as NSDictionary, ["nbf": 1428189720])
149150
}
150151
}
151152

152153
func testNotBeforeClaimString() {
153154
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOiIxNDI4MTg5NzIwIn0.qZsj36irdmIAeXv6YazWDSFbpuxHtEh4Deof5YTpnVI"
154-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"))) { payload in
155+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!))) { payload in
155156
XCTAssertEqual(payload as NSDictionary, ["nbf": "1428189720"])
156157
}
157158
}
158159

159160
func testInvalidNotBeforeClaim() {
160161
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOlsxNDI4MTg5NzIwXX0.PUL1FQubzzJa4MNXe2D3d5t5cMaqFr3kYlzRUzly-C8"
161-
assertDecodeError(try decode(jwt, algorithm: .hs256("secret")), error: "Not before claim (nbf) must be an integer")
162+
assertDecodeError(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!)), error: "Not before claim (nbf) must be an integer")
162163
}
163164

164165
func testUnmetNotBeforeClaim() {
165166
// If this just started failing, hello 2024!
166167
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3MjgxODg0OTF9.Tzhu1tu-7BXcF5YEIFFE1Vmg4tEybUnaz58FR4PcblQ"
167-
assertFailure(try decode(jwt, algorithm: .hs256("secret")))
168+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!)))
168169
}
169170

170171
// MARK: Issued at claim
171172

172173
func testIssuedAtClaimInThePast() {
173174
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MjgxODk3MjB9.I_5qjRcCUZVQdABLwG82CSuu2relSdIyJOyvXWUAJh4"
174-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"))) { payload in
175+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!))) { payload in
175176
XCTAssertEqual(payload as NSDictionary, ["iat": 1428189720])
176177
}
177178
}
178179

179180
func testIssuedAtClaimInThePastString() {
180181
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIxNDI4MTg5NzIwIn0.M8veWtsY52oBwi7LRKzvNnzhjK0QBS8Su1r0atlns2k"
181-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"))) { payload in
182+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!))) { payload in
182183
XCTAssertEqual(payload as NSDictionary, ["iat": "1428189720"])
183184
}
184185
}
185186

186187
func testIssuedAtClaimInTheFuture() {
187188
// If this just started failing, hello 2024!
188189
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MjgxODg0OTF9.owHiJyJmTcW1lBW5y_Rz3iBfSbcNiXlbZ2fY9qR7-aU"
189-
assertFailure(try decode(jwt, algorithm: .hs256("secret")))
190+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!)))
190191
}
191192

192193
func testInvalidIssuedAtClaim() {
193194
// If this just started failing, hello 2024!
194195
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOlsxNzI4MTg4NDkxXX0.ND7QMWtLkXDXH38OaXM3SQgLo3Z5TNgF_pcfWHV_alQ"
195-
assertDecodeError(try decode(jwt, algorithm: .hs256("secret")), error: "Issued at claim (iat) must be an integer")
196+
assertDecodeError(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!)), error: "Issued at claim (iat) must be an integer")
196197
}
197198

198199
// MARK: Audience claims
199200

200201
func testAudiencesClaim() {
201202
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsibWF4aW5lIiwia2F0aWUiXX0.-PKvdNLCClrWG7CvesHP6PB0-vxu-_IZcsYhJxBy5JM"
202-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"), audience:"maxine")) { payload in
203+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!), audience:"maxine")) { payload in
203204
XCTAssertEqual(payload as NSDictionary, ["aud": ["maxine", "katie"]])
204205
}
205206
}
206207

207208
func testAudienceClaim() {
208209
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJreWxlIn0.dpgH4JOwueReaBoanLSxsGTc7AjKUvo7_M1sAfy_xVE"
209-
assertSuccess(try decode(jwt, algorithm: .hs256("secret"), audience:"kyle")) { payload in
210+
assertSuccess(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!), audience:"kyle")) { payload in
210211
XCTAssertEqual(payload as NSDictionary, ["aud": "kyle"])
211212
}
212213
}
213214

214215
func testMismatchAudienceClaim() {
215216
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJreWxlIn0.VEB_n06pTSLlTXPFkc46ARADJ9HXNUBUPo3VhL9RDe4" // kyle
216-
assertFailure(try decode(jwt, algorithm: .hs256("secret"), audience:"maxine"))
217+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!), audience:"maxine"))
217218
}
218219

219220
func testMissingAudienceClaim() {
220221
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w"
221-
assertFailure(try decode(jwt, algorithm: .hs256("secret"), audience:"kyle"))
222+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!), audience:"kyle"))
222223
}
223224

224225
// MARK: Signature verification
@@ -232,24 +233,24 @@ class JWTDecodeTests : XCTestCase {
232233

233234
func testNoneFailsWithSecretAlgorithm() {
234235
let jwt = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ0ZXN0IjoiaW5nIn0."
235-
assertFailure(try decode(jwt, algorithm: .hs256("secret")))
236+
assertFailure(try decode(jwt, algorithm: .hs256("secret".data(using: .utf8)!)))
236237
}
237238

238239
func testMatchesAnyAlgorithm() {
239240
let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w."
240-
assertFailure(try decode(jwt, algorithms: [.hs256("anothersecret"), .hs256("secret")]))
241+
assertFailure(try decode(jwt, algorithms: [.hs256("anothersecret".data(using: .utf8)!), .hs256("secret".data(using: .utf8)!)]))
241242
}
242243

243244
func testHS384Algorithm() {
244245
let jwt = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.lddiriKLoo42qXduMhCTKZ5Lo3njXxOC92uXyvbLyYKzbq4CVVQOb3MpDwnI19u4"
245-
assertSuccess(try decode(jwt, algorithm: .hs384("secret"))) { payload in
246+
assertSuccess(try decode(jwt, algorithm: .hs384("secret".data(using: .utf8)!))) { payload in
246247
XCTAssertEqual(payload as NSDictionary, ["some": "payload"])
247248
}
248249
}
249250

250251
func testHS512Algorithm() {
251252
let jwt = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.WTzLzFO079PduJiFIyzrOah54YaM8qoxH9fLMQoQhKtw3_fMGjImIOokijDkXVbyfBqhMo2GCNu4w9v7UXvnpA"
252-
assertSuccess(try decode(jwt, algorithm: .hs512("secret"))) { payload in
253+
assertSuccess(try decode(jwt, algorithm: .hs512("secret".data(using: .utf8)!))) { payload in
253254
XCTAssertEqual(payload as NSDictionary, ["some": "payload"])
254255
}
255256
}

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ import JWT
2121
### Encoding a claim
2222

2323
```swift
24-
JWT.encode(["my": "payload"], algorithm: .HS256("secret"))
24+
JWT.encode(["my": "payload"], algorithm: .hs256("secret".data(using: .utf8)!))
2525
```
2626

2727
#### Building a JWT with the builder pattern
2828

2929
```swift
30-
JWT.encode(.hs256("secret")) { builder in
30+
JWT.encode(.hs256("secret".data(using: .utf8))) { builder in
3131
builder.issuer = "fuller.li"
3232
builder.issuedAt = Date()
3333
builder["custom"] = "Hi"
@@ -40,7 +40,7 @@ When decoding a JWT, you must supply one or more algorithms and keys.
4040

4141
```swift
4242
do {
43-
let payload = try JWT.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w", algorithm: .hs256("secret"))
43+
let payload = try JWT.decode("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.2_8pWJfyPup0YwOXK7g9Dn0cF1E3pdn299t4hSeJy5w", algorithm: .hs256("secret".data(using: .utf8)!))
4444
print(payload)
4545
} catch {
4646
print("Failed to decode JWT: \(error)")
@@ -50,7 +50,11 @@ do {
5050
When the JWT may be signed with one out of many algorithms or keys:
5151

5252
```swift
53-
try JWT.decode("eyJh...5w", algorithms: [.hs256("secret"), .hs256("secret2"), .hs512("secure")])
53+
try JWT.decode("eyJh...5w", algorithms: [
54+
.hs256("secret".data(using: .utf8)!),
55+
.hs256("secret2".data(using: .utf8)!),
56+
.hs512("secure".data(using: .utf8)!)
57+
])
5458
```
5559

5660
#### Supported claims

Sources/JWT.swift

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,13 @@ public enum Algorithm : CustomStringConvertible {
99
case none
1010

1111
/// HMAC using SHA-256 hash algorithm
12-
case hs256(String)
12+
case hs256(Data)
1313

1414
/// HMAC using SHA-384 hash algorithm
15-
case hs384(String)
15+
case hs384(Data)
1616

1717
/// HMAC using SHA-512 hash algorithm
18-
case hs512(String)
19-
20-
static func algorithm(_ name:String, key:String?) -> Algorithm? {
21-
if name == "none" {
22-
if key != nil {
23-
return nil // We don't allow nil when we configured a key
24-
}
25-
return Algorithm.none
26-
} else if let key = key {
27-
if name == "HS256" {
28-
return .hs256(key)
29-
} else if name == "HS384" {
30-
return .hs384(key)
31-
} else if name == "HS512" {
32-
return .hs512(key)
33-
}
34-
}
35-
36-
return nil
37-
}
18+
case hs512(Data)
3819

3920
public var description:String {
4021
switch self {
@@ -51,10 +32,9 @@ public enum Algorithm : CustomStringConvertible {
5132

5233
/// Sign a message using the algorithm
5334
func sign(_ message:String) -> String {
54-
func signHS(_ key:String, variant:CryptoSwift.HMAC.Variant) -> String {
55-
let keyData = key.data(using: String.Encoding.utf8, allowLossyConversion: false)!
35+
func signHS(_ key: Data, variant:CryptoSwift.HMAC.Variant) -> String {
5636
let messageData = message.data(using: String.Encoding.utf8, allowLossyConversion: false)!
57-
let mac = HMAC(key: keyData.bytes, variant:variant)
37+
let mac = HMAC(key: key.bytes, variant:variant)
5838
let result: [UInt8]
5939
do {
6040
result = try mac.authenticate(messageData.bytes)

0 commit comments

Comments
 (0)