Skip to content

Commit c56703f

Browse files
ptoffygwynne
andauthored
Update to JWTKit v5 (#149)
* Switch to using JWTKit v5 * Add Sendable conformance * [skip ci] Delete projectboard.yml * Adapt to new errors * Fix failing test * Update package for new JWTKit API * Update to use JWTKit 5 beta * Bump Vapor dependency * Fix stupid mistake * Update CODEOWNERS * Update README * Remove codecov from README --------- Co-authored-by: Gwynne Raskind <[email protected]>
1 parent 209ae87 commit c56703f

12 files changed

+309
-422
lines changed

.github/CODEOWNERS

+8-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,8 @@
1-
* @0xTim @gwynne
1+
* @ptoffy
2+
/.github/CONTRIBUTING.md @ptoffy @0xTim @gwynne
3+
/.github/workflows/*.yml @ptoffy @0xTim @gwynne
4+
/.github/workflows/test.yml @ptoffy @gwynne
5+
/.spi.yml @ptoffy @0xTim @gwynne
6+
/.gitignore @ptoffy @0xTim @gwynne
7+
/LICENSE @ptoffy @0xTim @gwynne
8+
/README.md @ptoffy @0xTim @gwynne

Package.swift

+23-15
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,37 @@
1-
// swift-tools-version:5.4
1+
// swift-tools-version:5.9
22
import PackageDescription
33

44
let package = Package(
55
name: "jwt",
66
platforms: [
7-
.macOS(.v10_15),
8-
.iOS(.v13),
9-
.tvOS(.v13),
10-
.watchOS(.v6)
7+
.macOS(.v13),
8+
.iOS(.v16),
9+
.tvOS(.v16),
10+
.watchOS(.v9),
1111
],
1212
products: [
1313
.library(name: "JWT", targets: ["JWT"]),
1414
],
1515
dependencies: [
16-
.package(url: "https://github.com/vapor/jwt-kit.git", from: "4.0.0"),
17-
.package(url: "https://github.com/vapor/vapor.git", from: "4.50.0"),
16+
.package(url: "https://github.com/vapor/jwt-kit.git", from: "5.0.0-beta.1"),
17+
.package(url: "https://github.com/vapor/vapor.git", from: "4.92.0"),
1818
],
1919
targets: [
20-
.target(name: "JWT", dependencies: [
21-
.product(name: "JWTKit", package: "jwt-kit"),
22-
.product(name: "Vapor", package: "vapor"),
23-
]),
24-
.testTarget(name: "JWTTests", dependencies: [
25-
.target(name: "JWT"),
26-
.product(name: "XCTVapor", package: "vapor"),
27-
]),
20+
.target(
21+
name: "JWT",
22+
dependencies: [
23+
.product(name: "JWTKit", package: "jwt-kit"),
24+
.product(name: "Vapor", package: "vapor"),
25+
],
26+
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
27+
),
28+
.testTarget(
29+
name: "JWTTests",
30+
dependencies: [
31+
.target(name: "JWT"),
32+
.product(name: "XCTVapor", package: "vapor"),
33+
],
34+
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
35+
),
2836
]
2937
)

README.md

+3-6
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,14 @@
1818
<a href="https://github.com/vapor/jwt/actions/workflows/test.yml">
1919
<img src="https://img.shields.io/github/actions/workflow/status/vapor/jwt/test.yml?event=push&style=plastic&logo=github&label=tests&logoColor=%23ccc" alt="Continuous Integration">
2020
</a>
21+
<a href="https://swift.org">
22+
<img src="https://design.vapor.codes/images/swift59up.svg" alt="Swift 5.9+">
23+
</a>
2124
</p>
2225
<br>
2326

2427
Support for JWT (JSON Web Tokens) in Vapor.
2528

26-
Supported versions:
27-
28-
|Version|Swift|SPM|
29-
|---|---|---|
30-
|4.0|5.4+|`from: "4.0.0"`|
31-
3229
**Original author**
3330

3431
- Siemen Sikkema, [@siemensikkema](http://github.com/siemensikkema)

Sources/JWT/Application+JWT.swift

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
1-
import Vapor
21
import JWTKit
2+
import Vapor
3+
import NIOConcurrencyHelpers
34

4-
extension Application {
5-
public var jwt: JWT {
5+
public extension Application {
6+
var jwt: JWT {
67
.init(_application: self)
78
}
89

9-
public struct JWT {
10-
private final class Storage {
11-
var signers: JWTSigners
10+
struct JWT: Sendable {
11+
private final class Storage: Sendable {
12+
private struct SendableBox: Sendable {
13+
var keys: JWTKeyCollection
14+
}
15+
16+
private let sendableBox: NIOLockedValueBox<SendableBox>
17+
18+
var keys: JWTKeyCollection {
19+
get {
20+
self.sendableBox.withLockedValue { box in
21+
box.keys
22+
}
23+
}
24+
set {
25+
self.sendableBox.withLockedValue { box in
26+
box.keys = newValue
27+
}
28+
}
29+
}
30+
1231
init() {
13-
self.signers = .init()
32+
let box = SendableBox(keys: .init())
33+
self.sendableBox = .init(box)
1434
}
1535
}
1636

@@ -20,9 +40,9 @@ extension Application {
2040

2141
public let _application: Application
2242

23-
public var signers: JWTSigners {
24-
get { self.storage.signers }
25-
set { self.storage.signers = newValue }
43+
public var keys: JWTKeyCollection {
44+
get { self.storage.keys }
45+
set { self.storage.keys = newValue }
2646
}
2747

2848
private var storage: Storage {

Sources/JWT/AsyncJWTAuthenticator.swift

-38
This file was deleted.

Sources/JWT/JWT+Apple.swift

+50-31
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,55 @@
1+
import NIOConcurrencyHelpers
12
import Vapor
23

3-
extension Request.JWT {
4-
public var apple: Apple {
4+
public extension Request.JWT {
5+
var apple: Apple {
56
.init(_jwt: self)
67
}
78

8-
public struct Apple {
9+
struct Apple: Sendable {
910
public let _jwt: Request.JWT
1011

11-
public func verify(applicationIdentifier: String? = nil) -> EventLoopFuture<AppleIdentityToken> {
12+
public func verify(
13+
applicationIdentifier: String? = nil
14+
) async throws -> AppleIdentityToken {
1215
guard let token = self._jwt._request.headers.bearerAuthorization?.token else {
1316
self._jwt._request.logger.error("Request is missing JWT bearer header.")
14-
return self._jwt._request.eventLoop.makeFailedFuture(Abort(.unauthorized))
17+
throw Abort(.unauthorized)
1518
}
16-
return self.verify(token, applicationIdentifier: applicationIdentifier)
19+
return try await self.verify(token, applicationIdentifier: applicationIdentifier)
1720
}
1821

19-
public func verify(_ message: String, applicationIdentifier: String? = nil) -> EventLoopFuture<AppleIdentityToken> {
20-
self.verify([UInt8](message.utf8), applicationIdentifier: applicationIdentifier)
22+
public func verify(
23+
_ message: String,
24+
applicationIdentifier: String? = nil
25+
) async throws -> AppleIdentityToken {
26+
try await self.verify([UInt8](message.utf8), applicationIdentifier: applicationIdentifier)
2127
}
2228

23-
public func verify<Message>(_ message: Message, applicationIdentifier: String? = nil) -> EventLoopFuture<AppleIdentityToken>
24-
where Message: DataProtocol
25-
{
26-
self._jwt._request.application.jwt.apple.signers(
27-
on: self._jwt._request
28-
).flatMapThrowing { signers in
29-
let token = try signers.verify(message, as: AppleIdentityToken.self)
30-
if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.apple.applicationIdentifier {
31-
try token.audience.verifyIntendedAudience(includes: applicationIdentifier)
32-
}
33-
return token
29+
public func verify(
30+
_ message: some DataProtocol & Sendable,
31+
applicationIdentifier: String? = nil
32+
) async throws -> AppleIdentityToken {
33+
let keys = try await self._jwt._request.application.jwt.apple.keys(on: self._jwt._request)
34+
let token = try await keys.verify(message, as: AppleIdentityToken.self)
35+
if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.apple.applicationIdentifier {
36+
try token.audience.verifyIntendedAudience(includes: applicationIdentifier)
3437
}
38+
return token
3539
}
3640
}
3741
}
3842

39-
extension Application.JWT {
40-
public var apple: Apple {
43+
public extension Application.JWT {
44+
var apple: Apple {
4145
.init(_jwt: self)
4246
}
4347

44-
public struct Apple {
48+
struct Apple: Sendable {
4549
public let _jwt: Application.JWT
4650

47-
public func signers(on request: Request) -> EventLoopFuture<JWTSigners> {
48-
self.jwks.get(on: request).flatMapThrowing {
49-
let signers = JWTSigners()
50-
try signers.use(jwks: $0)
51-
return signers
52-
}
51+
public func keys(on request: Request) async throws -> JWTKeyCollection {
52+
try await JWTKeyCollection().add(jwks: jwks.get(on: request).get())
5353
}
5454

5555
public var jwks: EndpointCache<JWKS> {
@@ -69,12 +69,31 @@ extension Application.JWT {
6969
typealias Value = Storage
7070
}
7171

72-
private final class Storage {
72+
private final class Storage: Sendable {
73+
private struct SendableBox: Sendable {
74+
var applicationIdentifier: String?
75+
}
76+
7377
let jwks: EndpointCache<JWKS>
74-
var applicationIdentifier: String?
78+
private let sendableBox: NIOLockedValueBox<SendableBox>
79+
80+
var applicationIdentifier: String? {
81+
get {
82+
self.sendableBox.withLockedValue { box in
83+
box.applicationIdentifier
84+
}
85+
}
86+
set {
87+
self.sendableBox.withLockedValue { box in
88+
box.applicationIdentifier = newValue
89+
}
90+
}
91+
}
92+
7593
init() {
7694
self.jwks = .init(uri: "https://appleid.apple.com/auth/keys")
77-
self.applicationIdentifier = nil
95+
let box = SendableBox(applicationIdentifier: nil)
96+
self.sendableBox = .init(box)
7897
}
7998
}
8099

0 commit comments

Comments
 (0)