@@ -36,6 +36,52 @@ public enum InvalidToken : Printable {
36
36
}
37
37
}
38
38
39
+ /// The supported Algorithms
40
+ public enum Algorithm : Printable {
41
+ /// No Algorithm, i-e, insecure
42
+ case None
43
+
44
+ /// HMAC using SHA-256 hash algorithm
45
+ case HS256( String )
46
+
47
+ static func algorithm( name: String , key: String ? ) -> Algorithm ? {
48
+ if name == " none " {
49
+ return Algorithm . None
50
+ } else if let key = key {
51
+ if name == " HS256 " {
52
+ return . HS256( key)
53
+ }
54
+ }
55
+
56
+ return nil
57
+ }
58
+
59
+ public var description : String {
60
+ switch self {
61
+ case . None:
62
+ return " none "
63
+ case . HS256( let key) :
64
+ return " HS256 "
65
+ }
66
+ }
67
+
68
+ func sign( message: String ) -> String {
69
+ switch self {
70
+ case . None:
71
+ return " "
72
+
73
+ case . HS256( let key) :
74
+ let mac = Authenticator . HMAC ( key: key. dataUsingEncoding ( NSUTF8StringEncoding, allowLossyConversion: false ) !, variant: . sha256)
75
+ let result = mac. authenticate ( message. dataUsingEncoding ( NSUTF8StringEncoding, allowLossyConversion: false ) !) !
76
+ return base64encode ( result)
77
+ }
78
+ }
79
+
80
+ func verify( message: String , signature: NSData ) -> Bool {
81
+ return sign ( message) == base64encode ( signature)
82
+ }
83
+ }
84
+
39
85
public enum DecodeResult {
40
86
case Success( Payload )
41
87
case Failure( InvalidToken )
@@ -60,8 +106,7 @@ public func decode(jwt:String, key:String? = nil, verify:Bool = true, audience:S
60
106
61
107
62
108
/// Encoding a payload
63
-
64
- public func encode( payload: Payload , key: String ) -> String {
109
+ public func encode( payload: Payload , algorithm: Algorithm ) -> String {
65
110
func encode( payload: Payload ) -> String ? {
66
111
if let data = NSJSONSerialization . dataWithJSONObject ( payload, options: NSJSONWritingOptions ( 0 ) , error: nil ) {
67
112
return base64encode ( data)
@@ -70,13 +115,10 @@ public func encode(payload:Payload, key:String) -> String {
70
115
return nil
71
116
}
72
117
73
- let algorithm = " HS256 "
74
- let header = encode ( [ " typ " : " JWT " , " alg " : algorithm] ) !
118
+ let header = encode ( [ " typ " : " JWT " , " alg " : algorithm. description] ) !
75
119
let payload = encode ( payload) !
76
120
let signingInput = " \( header) . \( payload) "
77
- let mac = Authenticator . HMAC ( key: key. dataUsingEncoding ( NSUTF8StringEncoding, allowLossyConversion: true ) !, variant: . sha256)
78
- let result = mac. authenticate ( signingInput. dataUsingEncoding ( NSUTF8StringEncoding, allowLossyConversion: false ) !) !
79
- let signature = base64encode ( result)
121
+ let signature = algorithm. sign ( signingInput)
80
122
return " \( signingInput) . \( signature) "
81
123
}
82
124
@@ -156,80 +198,16 @@ func load(jwt:String) -> LoadResult {
156
198
157
199
func verifySignature( header: Payload , signingInput: String , signature: NSData , key: String ? ) -> InvalidToken ? {
158
200
if let alg = header [ " alg " ] as? String {
159
- switch alg {
160
- case " none " :
201
+ if let algoritm = Algorithm . algorithm ( alg, key : key ) {
202
+ if algoritm . verify ( signingInput , signature : signature ) {
161
203
return nil
162
- case " HS256 " :
163
- if let key = key? . dataUsingEncoding ( NSUTF8StringEncoding, allowLossyConversion: false ) {
164
- let mac = Authenticator . HMAC ( key: key, variant: . sha256)
165
- let result = mac. authenticate ( signingInput. dataUsingEncoding ( NSUTF8StringEncoding, allowLossyConversion: false ) !)
166
- if result! == signature {
167
- return nil
168
- } else {
169
- return . DecodeError( " Signature verification failed " )
170
- }
171
- }
172
- break
173
- default :
174
- break
204
+ } else {
205
+ return . DecodeError( " Signature verification failed " )
206
+ }
175
207
}
176
208
177
209
return . InvalidAlgorithm
178
210
}
179
211
180
212
return . DecodeError( " Missing Algorithm " )
181
213
}
182
-
183
- // MARK: Claim Validation
184
-
185
- func validateAudience( payload: Payload , audience: String ? ) -> InvalidToken ? {
186
- if let audience = audience {
187
- if let aud = payload [ " aud " ] as? [ String ] {
188
- if !contains( aud, audience) {
189
- return . InvalidAudience
190
- }
191
- } else if let aud = payload [ " aud " ] as? String {
192
- if aud != audience {
193
- return . InvalidAudience
194
- }
195
- } else {
196
- return . DecodeError( " Invalid audience claim, must be a string or an array of strings " )
197
- }
198
- }
199
-
200
- return nil
201
- }
202
-
203
- func validateIssuer( payload: Payload , issuer: String ? ) -> InvalidToken ? {
204
- if let issuer = issuer {
205
- if let iss = payload [ " iss " ] as? String {
206
- if iss != issuer {
207
- return . InvalidIssuer
208
- }
209
- } else {
210
- return . InvalidIssuer
211
- }
212
- }
213
-
214
- return nil
215
- }
216
-
217
- func validateDate( payload: Payload , key: String , comparison: NSComparisonResult , failure: InvalidToken , decodeError: String ) -> InvalidToken ? {
218
- if let timestamp = payload [ key] as? NSTimeInterval {
219
- let date = NSDate ( timeIntervalSince1970: timestamp)
220
- if date. compare ( NSDate ( ) ) == comparison {
221
- return failure
222
- }
223
- } else if let timestamp: AnyObject = payload [ key] {
224
- return . DecodeError( decodeError)
225
- }
226
-
227
- return nil
228
- }
229
-
230
- func validateClaims( payload: Payload , audience: String ? , issuer: String ? ) -> InvalidToken ? {
231
- return validateIssuer ( payload, issuer) ?? validateAudience ( payload, audience) ??
232
- validateDate ( payload, " exp " , . OrderedAscending, . ExpiredSignature, " Expiration time claim (exp) must be an integer " ) ??
233
- validateDate ( payload, " nbf " , . OrderedDescending, . ImmatureSignature, " Not before claim (nbf) must be an integer " ) ??
234
- validateDate ( payload, " iat " , . OrderedDescending, . InvalidIssuedAt, " Issued at claim (iat) must be an integer " )
235
- }
0 commit comments