-
Notifications
You must be signed in to change notification settings - Fork 224
/
Copy pathDecode.swift
131 lines (102 loc) · 4.54 KB
/
Decode.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import Foundation
/// Failure reasons from decoding a JWT
public enum InvalidToken: CustomStringConvertible, Error {
/// Decoding the JWT itself failed
case decodeError(String)
/// The JWT uses an unsupported algorithm
case invalidAlgorithm
/// The issued claim has expired
case expiredSignature
/// The issued claim is for the future
case immatureSignature
/// The claim is for the future
case invalidIssuedAt
/// The audience of the claim doesn't match
case invalidAudience
/// The issuer claim failed to verify
case invalidIssuer
/// Returns a readable description of the error
public var description: String {
switch self {
case .decodeError(let error):
return "Decode Error: \(error)"
case .invalidIssuer:
return "Invalid Issuer"
case .expiredSignature:
return "Expired Signature"
case .immatureSignature:
return "The token is not yet valid (not before claim)"
case .invalidIssuedAt:
return "Issued at claim (iat) is in the future"
case .invalidAudience:
return "Invalid Audience"
case .invalidAlgorithm:
return "Unsupported algorithm or incorrect key"
}
}
}
/// Decode a JWT
public func decode(_ jwt: String, algorithms: [Algorithm], verify: Bool = true, audience: String? = nil, issuer: String? = nil) throws -> ClaimSet {
let (header, claims, signature, signatureInput) = try load(jwt)
if verify {
try claims.validate(audience: audience, issuer: issuer)
try verifySignature(algorithms, header: header, signingInput: signatureInput, signature: signature)
}
return claims
}
/// Decode a JWT
public func decode(_ jwt: String, algorithm: Algorithm, verify: Bool = true, audience: String? = nil, issuer: String? = nil) throws -> ClaimSet {
return try decode(jwt, algorithms: [algorithm], verify: verify, audience: audience, issuer: issuer)
}
/// Decode a JWT
@available(*, deprecated, message: "use decode that returns a ClaimSet instead")
public func decode(_ jwt: String, algorithms: [Algorithm], verify: Bool = true, audience: String? = nil, issuer: String? = nil) throws -> Payload {
return try decode(jwt, algorithms: algorithms, verify: verify, audience: audience, issuer: issuer).claims
}
/// Decode a JWT
@available(*, deprecated, message: "use decode that returns a ClaimSet instead")
public func decode(_ jwt: String, algorithm: Algorithm, verify: Bool = true, audience: String? = nil, issuer: String? = nil) throws -> Payload {
return try decode(jwt, algorithms: [algorithm], verify: verify, audience: audience, issuer: issuer).claims
}
// MARK: Parsing a JWT
func load(_ jwt: String) throws -> (header: JOSEHeader, payload: ClaimSet, signature: Data, signatureInput: String) {
let segments = jwt.components(separatedBy: ".")
if segments.count != 3 {
throw InvalidToken.decodeError("Not enough segments")
}
let headerSegment = segments[0]
let payloadSegment = segments[1]
let signatureSegment = segments[2]
let signatureInput = "\(headerSegment).\(payloadSegment)"
guard let headerData = base64decode(headerSegment) else {
throw InvalidToken.decodeError("Header is not correctly encoded as base64")
}
let header = (try? JSONSerialization.jsonObject(with: headerData, options: JSONSerialization.ReadingOptions(rawValue: 0))) as? Payload
if header == nil {
throw InvalidToken.decodeError("Invalid header")
}
let payloadData = base64decode(payloadSegment)
if payloadData == nil {
throw InvalidToken.decodeError("Payload is not correctly encoded as base64")
}
let payload = (try? JSONSerialization.jsonObject(with: payloadData!, options: JSONSerialization.ReadingOptions(rawValue: 0))) as? Payload
if payload == nil {
throw InvalidToken.decodeError("Invalid payload")
}
guard let signature = base64decode(signatureSegment) else {
throw InvalidToken.decodeError("Signature is not correctly encoded as base64")
}
return (header: JOSEHeader(parameters: header!), payload: ClaimSet(claims: payload!), signature: signature, signatureInput: signatureInput)
}
// MARK: Signature Verification
func verifySignature(_ algorithms: [Algorithm], header: JOSEHeader, signingInput: String, signature: Data) throws {
guard let alg = header.algorithm else {
throw InvalidToken.decodeError("Missing Algorithm")
}
let verifiedAlgorithms = try algorithms
.filter { algorithm in algorithm.description == alg }
.filter { algorithm in try algorithm.verify(signingInput, signature: signature) }
if verifiedAlgorithms.isEmpty {
throw InvalidToken.invalidAlgorithm
}
}