Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update for JWTKit v5 #157

Merged
merged 11 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ on:
push:
branches:
- main

jobs:
unit-tests:
uses: vapor/ci/.github/workflows/run-unit-tests.yml@main
with:
with_coverage: false
with_tsan: true
warnings_as_errors: true
with_linting: true
12 changes: 5 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.10
// swift-tools-version:6.0
import PackageDescription

let package = Package(
Expand All @@ -10,10 +10,10 @@ let package = Package(
.watchOS(.v9),
],
products: [
.library(name: "JWT", targets: ["JWT"]),
.library(name: "JWT", targets: ["JWT"])
],
dependencies: [
.package(url: "https://github.com/vapor/jwt-kit.git", from: "5.0.0-rc.1"),
.package(url: "https://github.com/vapor/jwt-kit.git", from: "5.0.0"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.101.0"),
],
targets: [
Expand All @@ -22,16 +22,14 @@ let package = Package(
dependencies: [
.product(name: "JWTKit", package: "jwt-kit"),
.product(name: "Vapor", package: "vapor"),
],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
]
),
.testTarget(
name: "JWTTests",
dependencies: [
.target(name: "JWT"),
.product(name: "XCTVapor", package: "vapor"),
],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")]
]
),
]
)
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,18 @@
<br>

Support for JWT (JSON Web Tokens) in Vapor.
This package integrates [JWTKit](https://github.com/vapor/jwt-kit.git) with Vapor.

**Original author**
### Installation

- Siemen Sikkema, [@siemensikkema](http://github.com/siemensikkema)
Use the SPM string to easily include the package in your `Package.swift` file.

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

and add it to your target's dependencies:

```swift
.product(name: "JWT", package: "jwt")
```
14 changes: 7 additions & 7 deletions Sources/JWT/Application+JWT.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import JWTKit
import Vapor
import NIOConcurrencyHelpers
import Vapor

public extension Application {
var jwt: JWT {
extension Application {
public var jwt: JWT {
.init(_application: self)
}

struct JWT: Sendable {
public struct JWT: Sendable {
private final class Storage: Sendable {
private struct SendableBox: Sendable {
var keys: JWTKeyCollection
}

private let sendableBox: NIOLockedValueBox<SendableBox>

var keys: JWTKeyCollection {
get {
self.sendableBox.withLockedValue { box in
Expand All @@ -27,7 +27,7 @@ public extension Application {
}
}
}

init() {
let box = SendableBox(keys: .init())
self.sendableBox = .init(box)
Expand Down
12 changes: 6 additions & 6 deletions Sources/JWT/JWT+Apple.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import NIOConcurrencyHelpers
import Vapor

public extension Request.JWT {
var apple: Apple {
extension Request.JWT {
public var apple: Apple {
.init(_jwt: self)
}

struct Apple: Sendable {
public struct Apple: Sendable {
public let _jwt: Request.JWT

public func verify(
Expand Down Expand Up @@ -40,12 +40,12 @@ public extension Request.JWT {
}
}

public extension Application.JWT {
var apple: Apple {
extension Application.JWT {
public var apple: Apple {
.init(_jwt: self)
}

struct Apple: Sendable {
public struct Apple: Sendable {
public let _jwt: Application.JWT

public func keys(on request: Request) async throws -> JWTKeyCollection {
Expand Down
12 changes: 6 additions & 6 deletions Sources/JWT/JWT+Google.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import NIOConcurrencyHelpers
import Vapor

public extension Request.JWT {
var google: Google {
extension Request.JWT {
public var google: Google {
.init(_jwt: self)
}

struct Google: Sendable {
public struct Google: Sendable {
public let _jwt: Request.JWT

public func verify(
Expand Down Expand Up @@ -51,12 +51,12 @@ public extension Request.JWT {
}
}

public extension Application.JWT {
var google: Google {
extension Application.JWT {
public var google: Google {
.init(_jwt: self)
}

struct Google: Sendable {
public struct Google: Sendable {
public let _jwt: Application.JWT

public func keys(on request: Request) async throws -> JWTKeyCollection {
Expand Down
18 changes: 9 additions & 9 deletions Sources/JWT/JWT+Microsoft.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import NIOConcurrencyHelpers
import Vapor

public extension Request.JWT {
var microsoft: Microsoft {
extension Request.JWT {
public var microsoft: Microsoft {
.init(_jwt: self)
}

struct Microsoft {
public struct Microsoft {
public let _jwt: Request.JWT

public func verify(
Expand Down Expand Up @@ -40,12 +40,12 @@ public extension Request.JWT {
}
}

public extension Application.JWT {
var microsoft: Microsoft {
extension Application.JWT {
public var microsoft: Microsoft {
.init(_jwt: self)
}

struct Microsoft {
public struct Microsoft {
public let _jwt: Application.JWT

public func keys(on request: Request) async throws -> JWTKeyCollection {
Expand All @@ -55,7 +55,7 @@ public extension Application.JWT {
public var jwks: EndpointCache<JWKS> {
self.storage.jwks
}

public var jwksEndpoint: URI {
get {
self.storage.jwksEndpoint
Expand Down Expand Up @@ -87,7 +87,7 @@ public extension Application.JWT {
}

private let sendableBox: NIOLockedValueBox<SendableBox>

var jwks: EndpointCache<JWKS> {
get {
self.sendableBox.withLockedValue { box in
Expand All @@ -113,7 +113,7 @@ public extension Application.JWT {
}
}
}

var jwksEndpoint: URI {
get {
self.sendableBox.withLockedValue { box in
Expand Down
11 changes: 5 additions & 6 deletions Sources/JWT/JWTAuthenticator.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import Vapor

public extension JWTPayload where Self: Authenticatable {
static func authenticator() -> AsyncAuthenticator {
extension JWTPayload where Self: Authenticatable {
public static func authenticator() -> AsyncAuthenticator {
JWTPayloadAuthenticator<Self>()
}
}

private struct JWTPayloadAuthenticator<Payload>: JWTAuthenticator
where Payload: JWTPayload & Authenticatable
{
where Payload: JWTPayload & Authenticatable {
func authenticate(jwt: Payload, for request: Request) async throws {
request.auth.login(jwt)
}
Expand All @@ -19,8 +18,8 @@ public protocol JWTAuthenticator: AsyncBearerAuthenticator {
func authenticate(jwt: Payload, for request: Request) async throws
}

public extension JWTAuthenticator {
func authenticate(bearer: BearerAuthorization, for request: Request) async throws {
extension JWTAuthenticator {
public func authenticate(bearer: BearerAuthorization, for request: Request) async throws {
try await self.authenticate(jwt: request.jwt.verify(bearer.token), for: request)
}
}
2 changes: 1 addition & 1 deletion Sources/JWT/JWTError+Vapor.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vapor

extension JWTError: AbortError {
extension JWTError: @retroactive AbortError {
public var status: HTTPResponseStatus {
.unauthorized
}
Expand Down
18 changes: 7 additions & 11 deletions Sources/JWT/Request+JWT.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import JWTKit
import Vapor

public extension Request {
var jwt: JWT {
extension Request {
public var jwt: JWT {
.init(_request: self)
}

struct JWT: Sendable {
public struct JWT: Sendable {
public let _request: Request

@discardableResult
public func verify<Payload>(as _: Payload.Type = Payload.self) async throws -> Payload
where Payload: JWTPayload
{
where Payload: JWTPayload {
guard let token = self._request.headers.bearerAuthorization?.token else {
self._request.logger.error("Request is missing JWT bearer header")
throw Abort(.unauthorized)
Expand All @@ -22,21 +21,18 @@ public extension Request {

@discardableResult
public func verify<Payload>(_ message: String, as _: Payload.Type = Payload.self) async throws -> Payload
where Payload: JWTPayload
{
where Payload: JWTPayload {
try await self.verify([UInt8](message.utf8), as: Payload.self)
}

@discardableResult
public func verify<Payload>(_ message: some DataProtocol & Sendable, as _: Payload.Type = Payload.self) async throws -> Payload
where Payload: JWTPayload
{
where Payload: JWTPayload {
try await self._request.application.jwt.keys.verify(message, as: Payload.self)
}

public func sign<Payload>(_ jwt: Payload, kid: JWKIdentifier? = nil, header: JWTHeader = .init()) async throws -> String
where Payload: JWTPayload
{
where Payload: JWTPayload {
return try await self._request.application.jwt.keys.sign(jwt, kid: kid, header: header)
}
}
Expand Down
7 changes: 7 additions & 0 deletions Tests/JWTTests/Helpers/ByteBuffer+string.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Vapor

extension ByteBuffer {
var string: String {
.init(decoding: self.readableBytesView, as: UTF8.self)
}
}
10 changes: 10 additions & 0 deletions Tests/JWTTests/Helpers/isLoggingConfigured.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Vapor

let isLoggingConfigured: Bool = {
LoggingSystem.bootstrap { label in
var handler = StreamLogHandler.standardOutput(label: label)
handler.logLevel = .debug
return handler
}
return true
}()
9 changes: 9 additions & 0 deletions Tests/JWTTests/Helpers/withApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Testing
import Vapor

func withApp(_ body: (Application) async throws -> Void) async throws {
let app = try await Application.make(.testing)
try #require(isLoggingConfigured == true)
try await body(app)
try await app.asyncShutdown()
}
Loading