forked from kylef/JSONWebToken.swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDecode.swift
140 lines (111 loc) · 4.13 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
132
133
134
135
136
137
138
139
140
import Foundation
/// Failure reasons from decoding a JWT
public enum InvalidToken : Printable {
/// 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"
}
}
}
/// Result from decoding a JWT
public enum DecodeResult {
/// Decoding succeeded
case Success(Payload)
/// Decoding failed, take a look at the invalid token reason
case Failure(InvalidToken)
}
/// Decode a JWT
public func decode(jwt:String, algorithms:[Algorithm], verify:Bool = true, audience:String? = nil, issuer:String? = nil) -> DecodeResult {
switch load(jwt) {
case let .Success(header, payload, signature, signatureInput):
if verify {
if let failure = validateClaims(payload, audience, issuer) ?? verifySignature(algorithms, header, signatureInput, signature) {
return .Failure(failure)
}
}
return .Success(payload)
case .Failure(let failure):
return .Failure(failure)
}
}
/// Decode a JWT
public func decode(jwt:String, algorithm:Algorithm, verify:Bool = true, audience:String? = nil, issuer:String? = nil) -> DecodeResult {
return decode(jwt, [algorithm], verify: verify, audience: audience, issuer: issuer)
}
// MARK: Parsing a JWT
enum LoadResult {
case Success(header:Payload, payload:Payload, signature:NSData, signatureInput:String)
case Failure(InvalidToken)
}
func load(jwt:String) -> LoadResult {
let segments = jwt.componentsSeparatedByString(".")
if segments.count != 3 {
return .Failure(.DecodeError("Not enough segments"))
}
let headerSegment = segments[0]
let payloadSegment = segments[1]
let signatureSegment = segments[2]
let signatureInput = "\(headerSegment).\(payloadSegment)"
let headerData = base64decode(headerSegment)
if headerData == nil {
return .Failure(.DecodeError("Header is not correctly encoded as base64"))
}
let header = NSJSONSerialization.JSONObjectWithData(headerData!, options: NSJSONReadingOptions(0), error: nil) as? Payload
if header == nil {
return .Failure(.DecodeError("Invalid header"))
}
let payloadData = base64decode(payloadSegment)
if payloadData == nil {
return .Failure(.DecodeError("Payload is not correctly encoded as base64"))
}
let payload = NSJSONSerialization.JSONObjectWithData(payloadData!, options: NSJSONReadingOptions(0), error: nil) as? Payload
if payload == nil {
return .Failure(.DecodeError("Invalid payload"))
}
let signature = base64decode(signatureSegment)
if signature == nil {
return .Failure(.DecodeError("Signature is not correctly encoded as base64"))
}
return .Success(header:header!, payload:payload!, signature:signature!, signatureInput:signatureInput)
}
// MARK: Signature Verification
func verifySignature(algorithms:[Algorithm], header:Payload, signingInput:String, signature:NSData) -> InvalidToken? {
if let alg = header["alg"] as? String {
let matchingAlgorithms = filter(algorithms) { algorithm in algorithm.description == alg }
let results = map(matchingAlgorithms) { algorithm in algorithm.verify(signingInput, signature: signature) }
let successes = filter(results) { $0 }
if successes.count > 0 {
return nil
}
return .InvalidAlgorithm
}
return .DecodeError("Missing Algorithm")
}