Skip to content

Commit

Permalink
Migrate to SwiftTesting (#197)
Browse files Browse the repository at this point in the history
* Migrate ClaimTests

* Migrate ECDSATests

* Fix import

* Migrate EdDSATests

* Migrate PSSTests

* Update JWTKitTests

* Update JWTKitTests

* Update RSA Tests

* Migrate vendor token tests

* Update X5CTests

* Move to Swift Testing

* Tidy up for Swift 6

* Use latest Xcode for iOS

* Remove import

---------

Co-authored-by: Paul Toffoloni <[email protected]>
Co-authored-by: Paul Toffoloni <[email protected]>
  • Loading branch information
3 people authored Oct 3, 2024
1 parent bba4fad commit 5c073a0
Show file tree
Hide file tree
Showing 24 changed files with 1,135 additions and 1,173 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ jobs:
if: ${{ !(github.event.pull_request.draft || false) }}
runs-on: macos-latest
steps:
- name: Select appropriate Xcode version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: 16.0
- name: Check out JWTKit
uses: actions/checkout@v4
- name: Run iOS Tests
Expand Down
3 changes: 1 addition & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,5 @@ let package = Package(
.copy("TestCertificates")
]
),
],
swiftLanguageModes: [.v6]
]
)
47 changes: 0 additions & 47 deletions [email protected]

This file was deleted.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<a href="LICENSE"><img src="https://design.vapor.codes/images/mitlicense.svg" alt="MIT License"></a>
<a href="https://github.com/vapor/jwt-kit/actions/workflows/test.yml"><img src="https://img.shields.io/github/actions/workflow/status/vapor/jwt-kit/test.yml?event=push&style=plastic&logo=github&label=tests&logoColor=%23ccc" alt="Continuous Integration"></a>
<a href="https://codecov.io/github/vapor/jwt-kit"><img src="https://img.shields.io/codecov/c/github/vapor/jwt-kit?style=plastic&logo=codecov&label=codecov"></a>
<a href="https://swift.org"><img src="https://design.vapor.codes/images/swift510up.svg" alt="Swift 5.10+"></a>
<a href="https://swift.org"><img src="https://design.vapor.codes/images/swift60up.svg" alt="Swift 6.0+"></a>
<a href="https://www.swift.org/sswg/incubation-process.html"><img src="https://design.vapor.codes/images/sswg-graduated.svg" alt="SSWG Incubation Level: Graduated"></a>
</p>
<br>
Expand All @@ -23,7 +23,7 @@
Use the SPM string to easily include the dependendency in your `Package.swift` file

```swift
.package(url: "https://github.com/vapor/jwt-kit.git", from: "5.0.0-beta.rc")
.package(url: "https://github.com/vapor/jwt-kit.git", from: "5.0.0-rc")
```

and add it to your target's dependencies:
Expand All @@ -36,7 +36,7 @@ and add it to your target's dependencies:
### Supported Platforms

JWTKit supports all platforms supported by Swift 5.10 and later, with the exception of Windows.
JWTKit supports all platforms supported by Swift 6 and later, with the exception of Windows.

## Overview

Expand Down
2 changes: 1 addition & 1 deletion Sources/JWTKit/Docs.docc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ and add it to your target's dependencies:

### Supported Platforms

JWTKit supports all platforms supported by Swift 5.10 and later, with the exception of Windows.
JWTKit supports all platforms supported by Swift 6 and later, with the exception of Windows.

## Overview

Expand Down
16 changes: 12 additions & 4 deletions Sources/JWTKit/JWTError.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Foundation

/// JWT error type.
public struct JWTError: Error, Sendable {
public struct ErrorType: Sendable, Hashable, CustomStringConvertible {
enum Base: String, Sendable {
public struct JWTError: Error, Sendable, Equatable {
public struct ErrorType: Sendable, Hashable, CustomStringConvertible, Equatable {
enum Base: String, Sendable, Equatable {
case claimVerificationFailure
case signingAlgorithmFailure
case malformedToken
Expand Down Expand Up @@ -46,7 +46,7 @@ public struct JWTError: Error, Sendable {
}
}

private struct Backing: Sendable {
private struct Backing: Sendable, Equatable {
fileprivate let errorType: ErrorType
fileprivate let name: String?
fileprivate let reason: String?
Expand Down Expand Up @@ -75,6 +75,10 @@ public struct JWTError: Error, Sendable {
self.failedClaim = failedClaim
self.curve = curve
}

static func == (lhs: JWTError.Backing, rhs: JWTError.Backing) -> Bool {
lhs.errorType == rhs.errorType
}
}

private var backing: Backing
Expand Down Expand Up @@ -143,6 +147,10 @@ public struct JWTError: Error, Sendable {
public static func generic(identifier: String, reason: String) -> Self {
.init(backing: .init(errorType: .generic, reason: reason))
}

public static func == (lhs: JWTError, rhs: JWTError) -> Bool {
lhs.backing == rhs.backing
}
}

extension JWTError: CustomStringConvertible {
Expand Down
44 changes: 28 additions & 16 deletions Sources/JWTKit/X5C/X5CVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public struct X5CVerifier: Sendable {
guard !rootCertificates.isEmpty else {
throw JWTError.invalidX5CChain(reason: "No root certificates provided")
}
try self.init(rootCertificates: rootCertificates.map { try X509.Certificate(pemEncoded: $0) })
try self.init(
rootCertificates: rootCertificates.map { try X509.Certificate(pemEncoded: $0) })
}

/// Create a new X5CVerifier trusting `rootCertificates`.
Expand All @@ -46,7 +47,9 @@ public struct X5CVerifier: Sendable {
guard !rootCertificates.isEmpty else {
throw JWTError.invalidX5CChain(reason: "No root certificates provided")
}
try self.init(rootCertificates: rootCertificates.map { try X509.Certificate(derEncoded: [UInt8]($0)) })
try self.init(
rootCertificates: rootCertificates.map { try X509.Certificate(derEncoded: [UInt8]($0)) }
)
}

/// Verify a chain of certificates against the trusted root certificates.
Expand All @@ -69,11 +72,14 @@ public struct X5CVerifier: Sendable {
/// - Returns: A `X509.VerificationResult` indicating the result of the verification.
public func verifyChain(
certificates: [Certificate],
@PolicyBuilder policy: () throws -> some VerifierPolicy = { RFC5280Policy(validationTime: Date()) }
@PolicyBuilder policy: () throws -> some VerifierPolicy = {
RFC5280Policy(validationTime: Date())
}
) async throws -> X509.VerificationResult {
let untrustedChain = CertificateStore(certificates)
var verifier = try Verifier(rootCertificates: trustedStore, policy: policy)
let result = await verifier.validate(leafCertificate: certificates[0], intermediates: untrustedChain)
let result = await verifier.validate(
leafCertificate: certificates[0], intermediates: untrustedChain)
return result
}

Expand Down Expand Up @@ -118,8 +124,7 @@ public struct X5CVerifier: Sendable {
_ token: some DataProtocol,
as _: Payload.Type = Payload.self
) async throws -> Payload
where Payload: JWTPayload
{
where Payload: JWTPayload {
try await verifyJWS(token, as: Payload.self, jsonDecoder: .defaultForJWT)
}

Expand All @@ -136,17 +141,19 @@ public struct X5CVerifier: Sendable {
_ token: some DataProtocol,
as _: Payload.Type = Payload.self,
jsonDecoder: any JWTJSONDecoder,
@PolicyBuilder policy: () throws -> some VerifierPolicy = { RFC5280Policy(validationTime: Date()) }
@PolicyBuilder policy: () throws -> some VerifierPolicy = {
RFC5280Policy(validationTime: Date())
}
) async throws -> Payload
where Payload: JWTPayload
{
where Payload: JWTPayload {
// Parse the JWS header to get the header
let parser = DefaultJWTParser(jsonDecoder: jsonDecoder)
let (header, payload, _) = try parser.parse(token, as: Payload.self)

// Ensure the algorithm used is ES256, as it's the only supported one (for now)
guard let headerAlg = header.alg, headerAlg == "ES256" else {
throw JWTError.invalidX5CChain(reason: "Unsupported algorithm: \(String(describing: header.alg))")
throw JWTError.invalidX5CChain(
reason: "Unsupported algorithm: \(String(describing: header.alg))")
}

// Ensure the x5c header parameter is present and not empty
Expand All @@ -167,7 +174,7 @@ public struct X5CVerifier: Sendable {
}

// Setup an untrusted chain using the intermediate certificates
let untrustedChain = CertificateStore(certificates.dropFirst().dropLast())
let untrustedChain = CertificateStore(certificates.dropFirst())

let date: Date
// Some JWT implementations have the sign date in the payload.
Expand All @@ -179,13 +186,18 @@ public struct X5CVerifier: Sendable {
}

// Setup the verifier using the predefined trusted store
var verifier = try Verifier(rootCertificates: trustedStore, policy: {
try policy()
RFC5280Policy(validationTime: date)
})
var verifier = try Verifier(
rootCertificates: trustedStore,
policy: {
try policy()
RFC5280Policy(validationTime: date)
})

// Validate the leaf certificate against the trusted store
let result = await verifier.validate(leafCertificate: certificates[0], intermediates: untrustedChain)
let result = await verifier.validate(
leafCertificate: certificates[0],
intermediates: untrustedChain
)

if case let .couldNotValidate(failures) = result {
throw JWTError.invalidX5CChain(reason: "\(failures)")
Expand Down
Loading

0 comments on commit 5c073a0

Please sign in to comment.