From 2cf91a03c4bf4656619b70be74b341ec2412bd7c Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Sun, 3 Nov 2024 22:45:43 +0000 Subject: [PATCH 1/8] Update SIWA test with new token and testing the body so we know when it expires --- .gitignore | 1 + Tests/JWTTests/JWTTests.swift | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dd69a89..4febda1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ Package.resolved DerivedData .swiftpm Tests/LinuxMain.swift +.vscode/ diff --git a/Tests/JWTTests/JWTTests.swift b/Tests/JWTTests/JWTTests.swift index ed2e981..1b6e070 100644 --- a/Tests/JWTTests/JWTTests.swift +++ b/Tests/JWTTests/JWTTests.swift @@ -262,15 +262,17 @@ struct JWTTests { var headers = HTTPHeaders() headers.bearerAuthorization = .init( token: """ - eyJraWQiOiJmaDZCczhDIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiZGV2LnRpbWMuc2l3YS1kZW1vLlRJTGlPUyIsImV4cCI6MTcwODUxNTY3NiwiaWF0IjoxNzA4NDI5Mjc2LCJzdWIiOiIwMDE1NDIuYjA0MTAwYzUxYWNiNDhkM2E1NzA2ODRmMTdkNjM5NGQuMTYwMyIsImNfaGFzaCI6ImFxQjM1RXR1bWFtVUg0VjZBYklmaXciLCJlbWFpbCI6Ijh5c2JjaHZjMm1AcHJpdmF0ZXJlbGF5LmFwcGxlaWQuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzX3ByaXZhdGVfZW1haWwiOnRydWUsImF1dGhfdGltZSI6MTcwODQyOTI3Niwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlLCJyZWFsX3VzZXJfc3RhdHVzIjoyfQ.E4SmBvvsr-L1f4rbwoXIg23XJEdA6WQxLfT6Z0TaFRTNbufuUtvG41MwJvf62T3HdCsY1VXlhdVYmTNbzqCuax6CUObue2ndx6osInDzfTkzysx17eUeCaG1XCfq9mScuVgW8xh3ZPfIeQdsII-MnP8ZG7q-CAxf6soSza_BKrrw4TArvEXrjbZO7FI1U2K72JtVZ118wcuEWfv8JO-FWFOHgWzJujqxI_7ayVG-mQfZitmYXv5ws-stZMxA0RvIbuYLWAksI6-ehYEgeEQa6NzzcJNWm3oArB0ithQE59fqFDoKCwpLchBMANz3tmNpN194Rc4ppL-niIDWFE-0Ug + eyJraWQiOiJGZnRPTlR4b0VnIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiZGV2LnRpbWMuc2l3YS1kZW1vLlRJTGlPUyIsImV4cCI6MTczMDc2MDAxOCwiaWF0IjoxNzMwNjczNjE4LCJzdWIiOiIwMDE1NDIuYjA0MTAwYzUxYWNiNDhkM2E1NzA2ODRmMTdkNjM5NGQuMTYwMyIsImNfaGFzaCI6IlUxc1d0Z1dfWTZSb3d1a09WRzJmNEEiLCJlbWFpbCI6Ijh5c2JjaHZjMm1AcHJpdmF0ZXJlbGF5LmFwcGxlaWQuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzX3ByaXZhdGVfZW1haWwiOnRydWUsImF1dGhfdGltZSI6MTczMDY3MzYxOCwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.fb-e48W_zMGfT0LqciYnBUBy7KxVaV5JC5VV4HFhhpK8yz0AUxeYHmXpkvt1gLPNnjd3c-fzMS0hUR-NiffgYuNs3qSFXSenb4BwYdDIuMXElggUPX3j6HU2TV-JYsTFl4tZpgnFs_0_56pscaJzQONCdrZdKJiD0lmtum7D-doH43aKflV-pAMXSZTCli9HwRNeZikmbY6wBS5Ltg4VI5Z8Usge4eS2HINdHIPSCadYf858pZ8huAaj5Jm4t_5j988khwgqBMc9haTHZgiUpK7SZDePuRsAAQQVCXnRsuibxFX66ugo5BEEKCdK-xg66iAstb_mC_628gMrybC-_w """) try await app.test(.GET, "test", headers: headers) { res async in #expect(res.status == .unauthorized) + #expect(res.body.string.contains("expired")) } try await app.test(.GET, "test2", headers: headers) { res async in #expect(res.status == .unauthorized) + #expect(res.body.string.contains("expired")) } } } From c0e1f99ab96960bc0865420edd58cb1e4066e1f3 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Tue, 5 Nov 2024 16:11:50 +0000 Subject: [PATCH 2/8] Wrap JWTError for conformance --- Sources/JWT/JWT+Apple.swift | 18 +++++++++++++----- Sources/JWT/JWTError+Vapor.swift | 9 ++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Sources/JWT/JWT+Apple.swift b/Sources/JWT/JWT+Apple.swift index e50555f..31c1bf9 100644 --- a/Sources/JWT/JWT+Apple.swift +++ b/Sources/JWT/JWT+Apple.swift @@ -1,5 +1,6 @@ import NIOConcurrencyHelpers import Vapor +import JWTKit extension Request.JWT { public var apple: Apple { @@ -30,12 +31,19 @@ extension Request.JWT { _ message: some DataProtocol & Sendable, applicationIdentifier: String? = nil ) async throws -> AppleIdentityToken { - let keys = try await self._jwt._request.application.jwt.apple.keys(on: self._jwt._request) - let token = try await keys.verify(message, as: AppleIdentityToken.self) - if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.apple.applicationIdentifier { - try token.audience.verifyIntendedAudience(includes: applicationIdentifier) + do { + let keys = try await self._jwt._request.application.jwt.apple.keys(on: self._jwt._request) + let token = try await keys.verify(message, as: AppleIdentityToken.self) + if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.apple.applicationIdentifier { + try token.audience.verifyIntendedAudience(includes: applicationIdentifier) + } + return token + } catch { + if let jwtKitError = error as? JWTError { + throw JWTErrorWrapper(underlying: jwtKitError) + } + throw error } - return token } } } diff --git a/Sources/JWT/JWTError+Vapor.swift b/Sources/JWT/JWTError+Vapor.swift index 3338eb4..c7eb79d 100644 --- a/Sources/JWT/JWTError+Vapor.swift +++ b/Sources/JWT/JWTError+Vapor.swift @@ -1,7 +1,14 @@ import Vapor -extension JWTError: @retroactive AbortError { +// Wrap JWTKit's error so we have better control over it, can set a reason etc +public struct JWTErrorWrapper: AbortError { + let underlying: JWTError + public var status: HTTPResponseStatus { .unauthorized } + + public var reason: String { + underlying.reason ?? status.description + } } From ddd1269fdd52b000752b23a41b694186dcee0283 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Tue, 5 Nov 2024 16:14:11 +0000 Subject: [PATCH 3/8] Wrap more errors --- Sources/JWT/JWT+FirebaseAuth.swift | 37 ++++++++++++++++++------------ Sources/JWT/JWT+Google.swift | 31 +++++++++++++++---------- Sources/JWT/JWT+Microsoft.swift | 17 ++++++++++---- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/Sources/JWT/JWT+FirebaseAuth.swift b/Sources/JWT/JWT+FirebaseAuth.swift index 71c0969..3182f0e 100644 --- a/Sources/JWT/JWT+FirebaseAuth.swift +++ b/Sources/JWT/JWT+FirebaseAuth.swift @@ -30,24 +30,31 @@ extension Request.JWT { _ message: some DataProtocol & Sendable, applicationIdentifier: String? = nil ) async throws -> FirebaseAuthIdentityToken { - let keys = try await self._jwt._request.application.jwt.firebaseAuth.keys(on: self._jwt._request) - let token = try await keys.verify(message, as: FirebaseAuthIdentityToken.self) - if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.firebaseAuth.applicationIdentifier { - try token.audience.verifyIntendedAudience(includes: applicationIdentifier) - guard token.audience.value.first == applicationIdentifier else { - throw JWTError.claimVerificationFailure( - failedClaim: token.audience, - reason: "Audience claim does not match expected value" - ) + do { + let keys = try await self._jwt._request.application.jwt.firebaseAuth.keys(on: self._jwt._request) + let token = try await keys.verify(message, as: FirebaseAuthIdentityToken.self) + if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.firebaseAuth.applicationIdentifier { + try token.audience.verifyIntendedAudience(includes: applicationIdentifier) + guard token.audience.value.first == applicationIdentifier else { + throw JWTError.claimVerificationFailure( + failedClaim: token.audience, + reason: "Audience claim does not match expected value" + ) + } + guard token.issuer.value == "https://securetoken.google.com/\(applicationIdentifier)" else { + throw JWTError.claimVerificationFailure( + failedClaim: token.issuer, + reason: "Issuer claim does not match expected value" + ) + } } - guard token.issuer.value == "https://securetoken.google.com/\(applicationIdentifier)" else { - throw JWTError.claimVerificationFailure( - failedClaim: token.issuer, - reason: "Issuer claim does not match expected value" - ) + return token + } catch { + if let jwtKitError = error as? JWTError { + throw JWTErrorWrapper(underlying: jwtKitError) } + throw error } - return token } } } diff --git a/Sources/JWT/JWT+Google.swift b/Sources/JWT/JWT+Google.swift index 4860e87..747ab4f 100644 --- a/Sources/JWT/JWT+Google.swift +++ b/Sources/JWT/JWT+Google.swift @@ -33,20 +33,27 @@ extension Request.JWT { applicationIdentifier: String? = nil, gSuiteDomainName: String? = nil ) async throws -> GoogleIdentityToken { - let keys = try await self._jwt._request.application.jwt.google.keys(on: self._jwt._request) - let token = try await keys.verify(message, as: GoogleIdentityToken.self) - if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.google.applicationIdentifier { - try token.audience.verifyIntendedAudience(includes: applicationIdentifier) - } - if let gSuiteDomainName = gSuiteDomainName ?? self._jwt._request.application.jwt.google.gSuiteDomainName { - guard let hd = token.hostedDomain, hd.value == gSuiteDomainName else { - throw JWTError.claimVerificationFailure( - failedClaim: token.hostedDomain, - reason: "Hosted domain claim does not match gSuite domain name" - ) + do { + let keys = try await self._jwt._request.application.jwt.google.keys(on: self._jwt._request) + let token = try await keys.verify(message, as: GoogleIdentityToken.self) + if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.google.applicationIdentifier { + try token.audience.verifyIntendedAudience(includes: applicationIdentifier) + } + if let gSuiteDomainName = gSuiteDomainName ?? self._jwt._request.application.jwt.google.gSuiteDomainName { + guard let hd = token.hostedDomain, hd.value == gSuiteDomainName else { + throw JWTError.claimVerificationFailure( + failedClaim: token.hostedDomain, + reason: "Hosted domain claim does not match gSuite domain name" + ) + } + } + return token + } catch { + if let jwtKitError = error as? JWTError { + throw JWTErrorWrapper(underlying: jwtKitError) } + throw error } - return token } } } diff --git a/Sources/JWT/JWT+Microsoft.swift b/Sources/JWT/JWT+Microsoft.swift index 125ea46..d4931c0 100644 --- a/Sources/JWT/JWT+Microsoft.swift +++ b/Sources/JWT/JWT+Microsoft.swift @@ -30,12 +30,19 @@ extension Request.JWT { _ message: some DataProtocol & Sendable, applicationIdentifier: String? = nil ) async throws -> MicrosoftIdentityToken { - let keys = try await self._jwt._request.application.jwt.microsoft.keys(on: self._jwt._request) - let token = try await keys.verify(message, as: MicrosoftIdentityToken.self) - if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.microsoft.applicationIdentifier { - try token.audience.verifyIntendedAudience(includes: applicationIdentifier) + do { + let keys = try await self._jwt._request.application.jwt.microsoft.keys(on: self._jwt._request) + let token = try await keys.verify(message, as: MicrosoftIdentityToken.self) + if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.microsoft.applicationIdentifier { + try token.audience.verifyIntendedAudience(includes: applicationIdentifier) + } + return token + } catch { + if let jwtKitError = error as? JWTError { + throw JWTErrorWrapper(underlying: jwtKitError) + } + throw error } - return token } } } From b8ebf37ebefed8344d2aeb3ab884c31433544ddb Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Tue, 5 Nov 2024 16:20:08 +0000 Subject: [PATCH 4/8] Better workaround --- Sources/JWT/JWT+Apple.swift | 17 ++++---------- Sources/JWT/JWT+FirebaseAuth.swift | 37 ++++++++++++------------------ Sources/JWT/JWT+Google.swift | 31 ++++++++++--------------- Sources/JWT/JWT+Microsoft.swift | 17 ++++---------- Sources/JWT/JWTError+Vapor.swift | 11 ++++----- 5 files changed, 41 insertions(+), 72 deletions(-) diff --git a/Sources/JWT/JWT+Apple.swift b/Sources/JWT/JWT+Apple.swift index 31c1bf9..5c9c913 100644 --- a/Sources/JWT/JWT+Apple.swift +++ b/Sources/JWT/JWT+Apple.swift @@ -31,19 +31,12 @@ extension Request.JWT { _ message: some DataProtocol & Sendable, applicationIdentifier: String? = nil ) async throws -> AppleIdentityToken { - do { - let keys = try await self._jwt._request.application.jwt.apple.keys(on: self._jwt._request) - let token = try await keys.verify(message, as: AppleIdentityToken.self) - if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.apple.applicationIdentifier { - try token.audience.verifyIntendedAudience(includes: applicationIdentifier) - } - return token - } catch { - if let jwtKitError = error as? JWTError { - throw JWTErrorWrapper(underlying: jwtKitError) - } - throw error + let keys = try await self._jwt._request.application.jwt.apple.keys(on: self._jwt._request) + let token = try await keys.verify(message, as: AppleIdentityToken.self) + if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.apple.applicationIdentifier { + try token.audience.verifyIntendedAudience(includes: applicationIdentifier) } + return token } } } diff --git a/Sources/JWT/JWT+FirebaseAuth.swift b/Sources/JWT/JWT+FirebaseAuth.swift index 3182f0e..71c0969 100644 --- a/Sources/JWT/JWT+FirebaseAuth.swift +++ b/Sources/JWT/JWT+FirebaseAuth.swift @@ -30,31 +30,24 @@ extension Request.JWT { _ message: some DataProtocol & Sendable, applicationIdentifier: String? = nil ) async throws -> FirebaseAuthIdentityToken { - do { - let keys = try await self._jwt._request.application.jwt.firebaseAuth.keys(on: self._jwt._request) - let token = try await keys.verify(message, as: FirebaseAuthIdentityToken.self) - if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.firebaseAuth.applicationIdentifier { - try token.audience.verifyIntendedAudience(includes: applicationIdentifier) - guard token.audience.value.first == applicationIdentifier else { - throw JWTError.claimVerificationFailure( - failedClaim: token.audience, - reason: "Audience claim does not match expected value" - ) - } - guard token.issuer.value == "https://securetoken.google.com/\(applicationIdentifier)" else { - throw JWTError.claimVerificationFailure( - failedClaim: token.issuer, - reason: "Issuer claim does not match expected value" - ) - } + let keys = try await self._jwt._request.application.jwt.firebaseAuth.keys(on: self._jwt._request) + let token = try await keys.verify(message, as: FirebaseAuthIdentityToken.self) + if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.firebaseAuth.applicationIdentifier { + try token.audience.verifyIntendedAudience(includes: applicationIdentifier) + guard token.audience.value.first == applicationIdentifier else { + throw JWTError.claimVerificationFailure( + failedClaim: token.audience, + reason: "Audience claim does not match expected value" + ) } - return token - } catch { - if let jwtKitError = error as? JWTError { - throw JWTErrorWrapper(underlying: jwtKitError) + guard token.issuer.value == "https://securetoken.google.com/\(applicationIdentifier)" else { + throw JWTError.claimVerificationFailure( + failedClaim: token.issuer, + reason: "Issuer claim does not match expected value" + ) } - throw error } + return token } } } diff --git a/Sources/JWT/JWT+Google.swift b/Sources/JWT/JWT+Google.swift index 747ab4f..4860e87 100644 --- a/Sources/JWT/JWT+Google.swift +++ b/Sources/JWT/JWT+Google.swift @@ -33,27 +33,20 @@ extension Request.JWT { applicationIdentifier: String? = nil, gSuiteDomainName: String? = nil ) async throws -> GoogleIdentityToken { - do { - let keys = try await self._jwt._request.application.jwt.google.keys(on: self._jwt._request) - let token = try await keys.verify(message, as: GoogleIdentityToken.self) - if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.google.applicationIdentifier { - try token.audience.verifyIntendedAudience(includes: applicationIdentifier) - } - if let gSuiteDomainName = gSuiteDomainName ?? self._jwt._request.application.jwt.google.gSuiteDomainName { - guard let hd = token.hostedDomain, hd.value == gSuiteDomainName else { - throw JWTError.claimVerificationFailure( - failedClaim: token.hostedDomain, - reason: "Hosted domain claim does not match gSuite domain name" - ) - } - } - return token - } catch { - if let jwtKitError = error as? JWTError { - throw JWTErrorWrapper(underlying: jwtKitError) + let keys = try await self._jwt._request.application.jwt.google.keys(on: self._jwt._request) + let token = try await keys.verify(message, as: GoogleIdentityToken.self) + if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.google.applicationIdentifier { + try token.audience.verifyIntendedAudience(includes: applicationIdentifier) + } + if let gSuiteDomainName = gSuiteDomainName ?? self._jwt._request.application.jwt.google.gSuiteDomainName { + guard let hd = token.hostedDomain, hd.value == gSuiteDomainName else { + throw JWTError.claimVerificationFailure( + failedClaim: token.hostedDomain, + reason: "Hosted domain claim does not match gSuite domain name" + ) } - throw error } + return token } } } diff --git a/Sources/JWT/JWT+Microsoft.swift b/Sources/JWT/JWT+Microsoft.swift index d4931c0..125ea46 100644 --- a/Sources/JWT/JWT+Microsoft.swift +++ b/Sources/JWT/JWT+Microsoft.swift @@ -30,19 +30,12 @@ extension Request.JWT { _ message: some DataProtocol & Sendable, applicationIdentifier: String? = nil ) async throws -> MicrosoftIdentityToken { - do { - let keys = try await self._jwt._request.application.jwt.microsoft.keys(on: self._jwt._request) - let token = try await keys.verify(message, as: MicrosoftIdentityToken.self) - if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.microsoft.applicationIdentifier { - try token.audience.verifyIntendedAudience(includes: applicationIdentifier) - } - return token - } catch { - if let jwtKitError = error as? JWTError { - throw JWTErrorWrapper(underlying: jwtKitError) - } - throw error + let keys = try await self._jwt._request.application.jwt.microsoft.keys(on: self._jwt._request) + let token = try await keys.verify(message, as: MicrosoftIdentityToken.self) + if let applicationIdentifier = applicationIdentifier ?? self._jwt._request.application.jwt.microsoft.applicationIdentifier { + try token.audience.verifyIntendedAudience(includes: applicationIdentifier) } + return token } } } diff --git a/Sources/JWT/JWTError+Vapor.swift b/Sources/JWT/JWTError+Vapor.swift index c7eb79d..a4449f0 100644 --- a/Sources/JWT/JWTError+Vapor.swift +++ b/Sources/JWT/JWTError+Vapor.swift @@ -1,14 +1,11 @@ import Vapor -// Wrap JWTKit's error so we have better control over it, can set a reason etc -public struct JWTErrorWrapper: AbortError { - let underlying: JWTError +extension JWTError: @retroactive AbortError { + public var reason: String { + self.description + } public var status: HTTPResponseStatus { .unauthorized } - - public var reason: String { - underlying.reason ?? status.description - } } From f1ed0bf476af242b9c6a397f7b55cae96772c6f6 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Tue, 5 Nov 2024 16:21:49 +0000 Subject: [PATCH 5/8] Remove unused import --- Sources/JWT/JWT+Apple.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/JWT/JWT+Apple.swift b/Sources/JWT/JWT+Apple.swift index 5c9c913..e50555f 100644 --- a/Sources/JWT/JWT+Apple.swift +++ b/Sources/JWT/JWT+Apple.swift @@ -1,6 +1,5 @@ import NIOConcurrencyHelpers import Vapor -import JWTKit extension Request.JWT { public var apple: Apple { From 006b5710ed159fd810beac34090f4bf78dd560d1 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Tue, 5 Nov 2024 17:04:54 +0000 Subject: [PATCH 6/8] Fix lint error --- Sources/JWT/JWTError+Vapor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/JWT/JWTError+Vapor.swift b/Sources/JWT/JWTError+Vapor.swift index a4449f0..7990c1a 100644 --- a/Sources/JWT/JWTError+Vapor.swift +++ b/Sources/JWT/JWTError+Vapor.swift @@ -4,7 +4,7 @@ extension JWTError: @retroactive AbortError { public var reason: String { self.description } - + public var status: HTTPResponseStatus { .unauthorized } From e81248d86ccf4c88eb8a42aeab6002dfd0e1c3e5 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Wed, 13 Nov 2024 11:13:25 +0100 Subject: [PATCH 7/8] Do it 'correctly' --- Sources/JWT/JWTError+Vapor.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/JWT/JWTError+Vapor.swift b/Sources/JWT/JWTError+Vapor.swift index 7990c1a..ee5ca1f 100644 --- a/Sources/JWT/JWTError+Vapor.swift +++ b/Sources/JWT/JWTError+Vapor.swift @@ -1,11 +1,11 @@ import Vapor extension JWTError: @retroactive AbortError { - public var reason: String { - self.description - } - public var status: HTTPResponseStatus { .unauthorized } + + @_implements(AbortError, reason) public var abortErrorReason: String { + self.reason ?? self.description + } } From a0fcf8d0c61b88128e0d4c562d0fd115429ef483 Mon Sep 17 00:00:00 2001 From: Tim <0xtimc@gmail.com> Date: Wed, 13 Nov 2024 13:54:05 +0100 Subject: [PATCH 8/8] Fix linting --- Sources/JWT/JWTError+Vapor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/JWT/JWTError+Vapor.swift b/Sources/JWT/JWTError+Vapor.swift index ee5ca1f..b1d3675 100644 --- a/Sources/JWT/JWTError+Vapor.swift +++ b/Sources/JWT/JWTError+Vapor.swift @@ -5,7 +5,7 @@ extension JWTError: @retroactive AbortError { .unauthorized } - @_implements(AbortError, reason) public var abortErrorReason: String { + @_implements(AbortError,reason) public var abortErrorReason: String { self.reason ?? self.description } }