Skip to content

Commit a678be2

Browse files
committed
refactor: Make JOSEHeader Codable
1 parent 69866e1 commit a678be2

File tree

7 files changed

+75
-78
lines changed

7 files changed

+75
-78
lines changed

JWT.xcodeproj/project.pbxproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
271E10811F90253300B5033C /* JWA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271E107F1F90253300B5033C /* JWA.swift */; };
1212
271E10821F90253300B5033C /* JWA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271E107F1F90253300B5033C /* JWA.swift */; };
1313
271E10831F90253300B5033C /* JWA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271E107F1F90253300B5033C /* JWA.swift */; };
14-
271E10851F90274A00B5033C /* JOSEHeaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271E10841F90274A00B5033C /* JOSEHeaderTests.swift */; };
1514
271E10891F90334B00B5033C /* CompactJSONEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271E10881F90334B00B5033C /* CompactJSONEncoder.swift */; };
1615
271E108B1F9034B100B5033C /* CompactJSONDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271E108A1F9034B100B5033C /* CompactJSONDecoder.swift */; };
16+
271E108D1F9041C400B5033C /* CompactJSONDecoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271E108C1F9041C400B5033C /* CompactJSONDecoderTests.swift */; };
17+
271E108F1F9042E900B5033C /* CompactJSONEncoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 271E108E1F9042E900B5033C /* CompactJSONEncoderTests.swift */; };
1718
273010FF1F33EABA00219C35 /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 273010FE1F33EABA00219C35 /* HMAC.swift */; };
1819
273011001F33EABA00219C35 /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 273010FE1F33EABA00219C35 /* HMAC.swift */; };
1920
273011011F33EABA00219C35 /* HMAC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 273010FE1F33EABA00219C35 /* HMAC.swift */; };
@@ -70,9 +71,10 @@
7071

7172
/* Begin PBXFileReference section */
7273
271E107F1F90253300B5033C /* JWA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWA.swift; sourceTree = "<group>"; };
73-
271E10841F90274A00B5033C /* JOSEHeaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JOSEHeaderTests.swift; sourceTree = "<group>"; };
7474
271E10881F90334B00B5033C /* CompactJSONEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactJSONEncoder.swift; sourceTree = "<group>"; };
7575
271E108A1F9034B100B5033C /* CompactJSONDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactJSONDecoder.swift; sourceTree = "<group>"; };
76+
271E108C1F9041C400B5033C /* CompactJSONDecoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactJSONDecoderTests.swift; sourceTree = "<group>"; };
77+
271E108E1F9042E900B5033C /* CompactJSONEncoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactJSONEncoderTests.swift; sourceTree = "<group>"; };
7678
273010FE1F33EABA00219C35 /* HMAC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMAC.swift; sourceTree = "<group>"; };
7779
273011041F33FC5F00219C35 /* HMACCommonCrypto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMACCommonCrypto.swift; sourceTree = "<group>"; };
7880
273011091F33FC9100219C35 /* HMACCryptoSwift.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMACCryptoSwift.swift; sourceTree = "<group>"; };
@@ -204,8 +206,9 @@
204206
279D63AB1AD07FFF0024E2BC /* Tests */ = {
205207
isa = PBXGroup;
206208
children = (
209+
271E108C1F9041C400B5033C /* CompactJSONDecoderTests.swift */,
210+
271E108E1F9042E900B5033C /* CompactJSONEncoderTests.swift */,
207211
279D63AE1AD07FFF0024E2BC /* JWTTests.swift */,
208-
271E10841F90274A00B5033C /* JOSEHeaderTests.swift */,
209212
279D63AC1AD07FFF0024E2BC /* Supporting Files */,
210213
);
211214
name = Tests;
@@ -467,8 +470,9 @@
467470
isa = PBXSourcesBuildPhase;
468471
buildActionMask = 2147483647;
469472
files = (
473+
271E108F1F9042E900B5033C /* CompactJSONEncoderTests.swift in Sources */,
474+
271E108D1F9041C400B5033C /* CompactJSONDecoderTests.swift in Sources */,
470475
279D63AF1AD07FFF0024E2BC /* JWTTests.swift in Sources */,
471-
271E10851F90274A00B5033C /* JOSEHeaderTests.swift in Sources */,
472476
);
473477
runOnlyForDeploymentPostprocessing = 0;
474478
};

Sources/JWT/CompactJSONDecoder.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
class CompactJSONDecoder: JSONDecoder {
22
override func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
33
guard let string = String(data: data, encoding: .ascii) else {
4-
fatalError()
4+
throw InvalidToken.decodeError("data should contain only ASCII characters")
55
}
66

7-
return try super.decode(type, from: base64decode(string)!)
7+
return try decode(type, from: string)
8+
}
9+
10+
func decode<T>(_ type: T.Type, from string: String) throws -> T where T : Decodable {
11+
guard let decoded = base64decode(string) else {
12+
throw InvalidToken.decodeError("data should be a valid base64 string")
13+
}
14+
15+
return try super.decode(type, from: decoded)
816
}
917
}

Sources/JWT/Decode.swift

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,8 @@ func load(_ jwt: String) throws -> (header: JOSEHeader, payload: ClaimSet, signa
8888
let signatureSegment = segments[2]
8989
let signatureInput = "\(headerSegment).\(payloadSegment)"
9090

91-
guard let headerData = base64decode(headerSegment) else {
92-
throw InvalidToken.decodeError("Header is not correctly encoded as base64")
93-
}
94-
95-
let header = (try? JSONSerialization.jsonObject(with: headerData, options: JSONSerialization.ReadingOptions(rawValue: 0))) as? Payload
96-
if header == nil {
97-
throw InvalidToken.decodeError("Invalid header")
98-
}
91+
let decoder = CompactJSONDecoder()
92+
let header = try decoder.decode(JOSEHeader.self, from: headerSegment)
9993

10094
let payloadData = base64decode(payloadSegment)
10195
if payloadData == nil {
@@ -111,7 +105,7 @@ func load(_ jwt: String) throws -> (header: JOSEHeader, payload: ClaimSet, signa
111105
throw InvalidToken.decodeError("Signature is not correctly encoded as base64")
112106
}
113107

114-
return (header: JOSEHeader(parameters: header!), payload: ClaimSet(claims: payload!), signature: signature, signatureInput: signatureInput)
108+
return (header: header, payload: ClaimSet(claims: payload!), signature: signature, signatureInput: signatureInput)
115109
}
116110

117111
// MARK: Signature Verification

Sources/JWT/JOSEHeader.swift

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,24 @@
99
import Foundation
1010

1111

12-
struct JOSEHeader {
13-
var parameters: [String: Any]
14-
15-
init(parameters: [String: Any]) {
16-
self.parameters = parameters
12+
struct JOSEHeader: Codable {
13+
var type: String?
14+
var algorithm: String?
15+
16+
init(from decoder: Decoder) throws {
17+
let container = try decoder.container(keyedBy: CodingKeys.self)
18+
type = try container.decodeIfPresent(String.self, forKey: .type)
19+
algorithm = try container.decodeIfPresent(String.self, forKey: .algorithm)
1720
}
1821

19-
var algorithm: String? {
20-
get {
21-
return parameters["alg"] as? String
22-
}
23-
24-
set {
25-
parameters["alg"] = newValue
26-
}
22+
func encode(to encoder: Encoder) throws {
23+
var container = encoder.container(keyedBy: CodingKeys.self)
24+
try container.encodeIfPresent(type, forKey: .type)
25+
try container.encodeIfPresent(algorithm, forKey: .algorithm)
2726
}
2827

29-
var type: String? {
30-
get {
31-
return parameters["typ"] as? String
32-
}
33-
34-
set {
35-
parameters["typ"] = newValue
36-
}
28+
enum CodingKeys: String, CodingKey {
29+
case type = "typ"
30+
case algorithm = "alg"
3731
}
3832
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import XCTest
2+
@testable import JWT
3+
4+
class CompactJSONDecodable: Decodable {
5+
let key: String
6+
}
7+
8+
class CompactJSONDecoderTests: XCTestCase {
9+
let decoder = CompactJSONDecoder()
10+
11+
func testDecoder() throws {
12+
let expected = "eyJrZXkiOiJ2YWx1ZSJ9".data(using: .ascii)!
13+
let value = try decoder.decode(CompactJSONDecodable.self, from: expected)
14+
XCTAssertEqual(value.key, "value")
15+
}
16+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import XCTest
2+
@testable import JWT
3+
4+
class CompactJSONEncodable: Encodable {
5+
let key: String
6+
7+
init(key: String) {
8+
self.key = key
9+
}
10+
}
11+
12+
class CompactJSONEncoderTests: XCTestCase {
13+
let encoder = CompactJSONEncoder()
14+
15+
func testEncode() throws {
16+
let value = CompactJSONEncodable(key: "value")
17+
18+
let encoded = try encoder.encode(value)
19+
20+
XCTAssertEqual(encoded, "eyJrZXkiOiJ2YWx1ZSJ9".data(using: .ascii)!)
21+
}
22+
}
23+

Tests/JWTTests/JOSEHeaderTests.swift

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)