Skip to content

Commit 2e27a4f

Browse files
authored
Fix conversion of boolean claim values (#193)
1 parent e858cae commit 2e27a4f

File tree

2 files changed

+80
-8
lines changed

2 files changed

+80
-8
lines changed

JWTDecode/JWTDecode.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,17 @@ public struct Claim {
7373

7474
/// Value of the claim as `Bool`.
7575
public var boolean: Bool? {
76-
return self.value as? Bool
76+
// This is necessary because Core Foundation's JSON deserialization turns JSON booleans into CFBoolean values,
77+
// which get wrapped in NSNumber –a Foundation type. But integers and floats also get wrapped in NSNumber, and
78+
// thus Swift will happily bridge a NSNumber containing a '1' or '1.0' to a 'true' Bool, and a '0' or '0.0' to
79+
// a 'false' Bool.
80+
// So, to find out if the deserialized claim value is really a CFBoolean or not, we need to bypass its NSNumber
81+
// wrapper and check its Core Foundation type directly. We do so by comparing its Core Foundation type ID to
82+
// that of CFBoolean.
83+
if let value = self.value as CFTypeRef?, CFGetTypeID(value) == CFBooleanGetTypeID() {
84+
return self.value as? Bool
85+
}
86+
return nil
7787
}
7888

7989
/// Value of the claim as `Double`.
@@ -138,7 +148,8 @@ private func decodeJWTPart(_ value: String) throws -> [String: Any] {
138148
throw JWTDecodeError.invalidBase64URL(value)
139149
}
140150

141-
guard let json = try? JSONSerialization.jsonObject(with: bodyData, options: []), let payload = json as? [String: Any] else {
151+
guard let json = try? JSONSerialization.jsonObject(with: bodyData, options: []),
152+
let payload = json as? [String: Any] else {
142153
throw JWTDecodeError.invalidJSON(value)
143154
}
144155

JWTDecodeTests/JWTDecodeSpec.swift

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,18 @@ class JWTDecodeSpec: QuickSpec {
147147

148148
describe("custom claim") {
149149
beforeEach {
150-
sut = jwt(withBody: ["sub": UUID().uuidString, "custom_string_claim": "Shawarma Friday!", "custom_integer_claim": 10, "custom_double_claim": 3.4, "custom_double_string_claim": "1.3", "custom_true_boolean_claim": true, "custom_false_boolean_claim": false])
150+
sut = jwt(withBody: ["sub": UUID().uuidString,
151+
"custom_string_claim": "Shawarma Friday!",
152+
"custom_integer_claim": 10,
153+
"custom_integer_claim_0": 0,
154+
"custom_integer_claim_1": 1,
155+
"custom_integer_claim_string": "13",
156+
"custom_double_claim": 3.1,
157+
"custom_double_claim_0.0": 0.0,
158+
"custom_double_claim_1.0": 1.0,
159+
"custom_double_claim_string": "1.3",
160+
"custom_boolean_claim_true": true,
161+
"custom_boolean_claim_false": false])
151162
}
152163

153164
it("should return claim by name") {
@@ -175,18 +186,68 @@ class JWTDecodeSpec: QuickSpec {
175186
expect(claim.boolean).to(beNil())
176187
}
177188

189+
it("should return '0' integer claim") {
190+
let claim = sut["custom_integer_claim_0"]
191+
expect(claim.string).to(beNil())
192+
expect(claim.array).to(beNil())
193+
expect(claim.integer) == 0
194+
expect(claim.double) == 0.0
195+
expect(claim.date) == Date(timeIntervalSince1970: 0)
196+
expect(claim.boolean).to(beNil())
197+
}
198+
199+
it("should return '1' integer claim") {
200+
let claim = sut["custom_integer_claim_1"]
201+
expect(claim.string).to(beNil())
202+
expect(claim.array).to(beNil())
203+
expect(claim.integer) == 1
204+
expect(claim.double) == 1.0
205+
expect(claim.date) == Date(timeIntervalSince1970: 1)
206+
expect(claim.boolean).to(beNil())
207+
}
208+
209+
it("should return integer as string claim") {
210+
let claim = sut["custom_integer_claim_string"]
211+
expect(claim.string) == "13"
212+
expect(claim.array) == ["13"]
213+
expect(claim.integer) == 13
214+
expect(claim.double) == 13.0
215+
expect(claim.date) == Date(timeIntervalSince1970: 13)
216+
expect(claim.boolean).to(beNil())
217+
}
218+
178219
it("should return double claim") {
179220
let claim = sut["custom_double_claim"]
180221
expect(claim.string).to(beNil())
181222
expect(claim.array).to(beNil())
182223
expect(claim.integer) == 3
183-
expect(claim.double) == 3.4
184-
expect(claim.date) == Date(timeIntervalSince1970: 3.4)
224+
expect(claim.double) == 3.1
225+
expect(claim.date) == Date(timeIntervalSince1970: 3.1)
226+
expect(claim.boolean).to(beNil())
227+
}
228+
229+
it("should return '0.0' double claim") {
230+
let claim = sut["custom_double_claim_0.0"]
231+
expect(claim.string).to(beNil())
232+
expect(claim.array).to(beNil())
233+
expect(claim.integer) == 0
234+
expect(claim.double) == 0.0
235+
expect(claim.date) == Date(timeIntervalSince1970: 0)
236+
expect(claim.boolean).to(beNil())
237+
}
238+
239+
it("should return '1.0' double claim") {
240+
let claim = sut["custom_double_claim_1.0"]
241+
expect(claim.string).to(beNil())
242+
expect(claim.array).to(beNil())
243+
expect(claim.integer) == 1
244+
expect(claim.double) == 1.0
245+
expect(claim.date) == Date(timeIntervalSince1970: 1)
185246
expect(claim.boolean).to(beNil())
186247
}
187248

188249
it("should return double as string claim") {
189-
let claim = sut["custom_double_string_claim"]
250+
let claim = sut["custom_double_claim_string"]
190251
expect(claim.string) == "1.3"
191252
expect(claim.array) == ["1.3"]
192253
expect(claim.integer).to(beNil())
@@ -196,7 +257,7 @@ class JWTDecodeSpec: QuickSpec {
196257
}
197258

198259
it("should return true boolean claim") {
199-
let claim = sut["custom_true_boolean_claim"]
260+
let claim = sut["custom_boolean_claim_true"]
200261
expect(claim.string).to(beNil())
201262
expect(claim.array).to(beNil())
202263
expect(claim.integer).to(beNil())
@@ -206,7 +267,7 @@ class JWTDecodeSpec: QuickSpec {
206267
}
207268

208269
it("should return false boolean claim") {
209-
let claim = sut["custom_false_boolean_claim"]
270+
let claim = sut["custom_boolean_claim_false"]
210271
expect(claim.string).to(beNil())
211272
expect(claim.array).to(beNil())
212273
expect(claim.integer).to(beNil())

0 commit comments

Comments
 (0)