Skip to content

Commit d596741

Browse files
feat: Add leeway to validateClaims (#38)
1 parent dbbec19 commit d596741

File tree

3 files changed

+41
-28
lines changed

3 files changed

+41
-28
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,17 +178,18 @@ The supported algorithms for signing and verifying JWTs are:
178178

179179
### Validate claims
180180

181-
The `validateClaims` function validates the Standard Date claims of a JWT instance.
181+
The `validateClaims` function validates the standard `Date` claims of a JWT instance.
182182
The following claims are validated if they are present in the `Claims` object:
183183
- exp (expiration date)
184184
- nbf (not before date)
185185
- iat (issued at date)
186186

187187
The method returns `ValidateClaimsResult` - an struct that list the various reasons for validation failure.
188188
If the validation succeeds `ValidateClaimsResult.success` is returned.
189+
The `leeway` parameter is the `TimeInterval` in seconds that a standard `Date` claim will be valid outside of the specified time. This can be used to account for clock skew between issuers and verifiers.
189190

190191
```swift
191-
let validationResult = verified.validateClaims()
192+
let validationResult = verified.validateClaims(leeway: 10)
192193
if validationResult != .success {
193194
print("Claims validation failed: ", validationResult)
194195
}

Sources/SwiftJWT/JWT.swift

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -112,38 +112,24 @@ public struct JWT<T: Claims>: Codable {
112112
/// This function checks that the "exp" (expiration time) is in the future
113113
/// and the "iat" (issued at) and "nbf" (not before) headers are in the past,
114114
///
115+
/// - Parameter leeway: The time in seconds that the JWT can be invalid but still accepted to account for clock differences.
115116
/// - Returns: A value of `ValidateClaimsResult`.
116-
public func validateClaims() -> ValidateClaimsResult {
117-
if let _ = claims.exp {
118-
if let expirationDate = claims.exp {
119-
if expirationDate < Date() {
120-
return .expired
121-
}
122-
}
123-
else {
124-
return .invalidExpiration
117+
public func validateClaims(leeway: TimeInterval = 0) -> ValidateClaimsResult {
118+
if let expirationDate = claims.exp {
119+
if expirationDate + leeway < Date() {
120+
return .expired
125121
}
126122
}
127123

128-
if let _ = claims.nbf {
129-
if let notBeforeDate = claims.nbf {
130-
if notBeforeDate > Date() {
131-
return .notBefore
132-
}
133-
}
134-
else {
135-
return .invalidNotBefore
124+
if let notBeforeDate = claims.nbf {
125+
if notBeforeDate > Date() + leeway {
126+
return .notBefore
136127
}
137128
}
138129

139-
if let _ = claims.iat {
140-
if let issuedAtDate = claims.iat {
141-
if issuedAtDate > Date() {
142-
return .issuedAt
143-
}
144-
}
145-
else {
146-
return .invalidIssuedAt
130+
if let issuedAtDate = claims.iat {
131+
if issuedAtDate > Date() + leeway {
132+
return .issuedAt
147133
}
148134
}
149135

Tests/SwiftJWTTests/TestJWT.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ class TestJWT: XCTestCase {
112112
("testJWTCoderCycleKeyID", testJWTCoderCycleKeyID),
113113
("testJWT", testJWT),
114114
("testJWTUsingHMAC", testJWTUsingHMAC),
115-
("testMicroProfile", testMicroProfile)
115+
("testMicroProfile", testMicroProfile),
116+
("testValidateClaims", testValidateClaims),
117+
("testValidateClaimsLeeway", testValidateClaimsLeeway),
116118
]
117119
}
118120

@@ -553,6 +555,30 @@ class TestJWT: XCTestCase {
553555
XCTFail("Failed to decode")
554556
}
555557
}
558+
559+
func testValidateClaims() {
560+
var jwt = JWT(claims: TestClaims(name:"Kitura"))
561+
jwt.claims.exp = Date()
562+
XCTAssertEqual(jwt.validateClaims(), .expired, "Validation failed")
563+
jwt.claims.exp = nil
564+
jwt.claims.iat = Date(timeIntervalSinceNow: 10)
565+
XCTAssertEqual(jwt.validateClaims(), .issuedAt, "Validation failed")
566+
jwt.claims.iat = nil
567+
jwt.claims.nbf = Date(timeIntervalSinceNow: 10)
568+
XCTAssertEqual(jwt.validateClaims(), .notBefore, "Validation failed")
569+
}
570+
571+
func testValidateClaimsLeeway() {
572+
var jwt = JWT(claims: TestClaims(name:"Kitura"))
573+
jwt.claims.exp = Date()
574+
XCTAssertEqual(jwt.validateClaims(leeway: 20), .success, "Validation failed")
575+
jwt.claims.exp = nil
576+
jwt.claims.iat = Date(timeIntervalSinceNow: 10)
577+
XCTAssertEqual(jwt.validateClaims(leeway: 20), .success, "Validation failed")
578+
jwt.claims.iat = nil
579+
jwt.claims.nbf = Date(timeIntervalSinceNow: 10)
580+
XCTAssertEqual(jwt.validateClaims(leeway: 20), .success, "Validation failed")
581+
}
556582
}
557583

558584
func read(fileName: String) -> Data {

0 commit comments

Comments
 (0)