Skip to content

Commit

Permalink
Merge pull request #20 from skelpo/ready-to-use
Browse files Browse the repository at this point in the history
Make the middleware usable and add tests
  • Loading branch information
proggeramlug committed Mar 16, 2020
2 parents 302b0f1 + 1154e0d commit 2acd0e1
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 213 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: test
on:
- pull_request
jobs:
#jwtmiddleware_macos:
# runs-on: macos-latest
# env:
# DEVELOPER_DIR: /Applications/Xcode_11.4_beta.app/Contents/Developer
# steps:
# - uses: actions/checkout@v2
# - run: brew install vapor/tap/vapor-beta
# - run: xcrun swift test --enable-test-discovery --sanitize=thread
jwtmiddleware_xenial:
container:
image: vapor/swift:5.2-xenial
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: swift test --enable-test-discovery --sanitize=thread
jwtmiddleware_bionic:
container:
image: vapor/swift:5.2-bionic
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: swift test --enable-test-discovery --sanitize=thread

2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
/.build
/Packages
/*.xcodeproj
/.swiftpm
Package.resolved
160 changes: 0 additions & 160 deletions Package.resolved

This file was deleted.

17 changes: 11 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// swift-tools-version:5.1

// swift-tools-version:5.2
import PackageDescription

let package = Package(
Expand All @@ -11,11 +10,17 @@ let package = Package(
.library(name: "JWTMiddleware", targets: ["JWTMiddleware"]),
],
dependencies: [
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0-beta.3"),
.package(url: "https://github.com/vapor/jwt.git", from: "4.0.0-beta.2"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0-rc"),
.package(url: "https://github.com/vapor/jwt.git", from: "4.0.0-rc"),
],
targets: [
.target(name: "JWTMiddleware", dependencies: ["Vapor", "JWT"]),
.testTarget(name: "JWTMiddlewareTests", dependencies: ["JWTMiddleware"])
.target(name: "JWTMiddleware", dependencies: [
.product(name: "Vapor", package: "vapor"),
.product(name: "JWT", package: "jwt"),
]),
.testTarget(name: "JWTMiddlewareTests", dependencies: [
.byName(name: "JWTMiddleware"),
.product(name: "XCTVapor", package: "vapor"),
])
]
)
22 changes: 8 additions & 14 deletions Sources/JWTMiddleware/JWTMiddleware.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Vapor
import JWT

final class JWTMiddleware<T: JWTPayload>: Middleware {
init() { }
public final class JWTMiddleware<T: JWTPayload>: Middleware {
public init() {}

func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
public func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {

guard let token = request.headers.bearerAuthorization?.token.utf8 else {
return request.eventLoop.makeFailedFuture(Abort(.unauthorized, reason: "Missing authorization bearer header"))
Expand All @@ -24,19 +24,13 @@ final class JWTMiddleware<T: JWTPayload>: Middleware {

}

extension AnyHashable {
static let payload: String = "jwt_payload"
}

extension Request {
var loggedIn: Bool {
if (self.userInfo[.payload] as? JWTPayload) != nil {
return true
}
return false
private struct PayloadKey: StorageKey {
typealias Value = JWTPayload
}

var payload: JWTPayload {
get { self.userInfo[.payload] as! JWTPayload }
set { self.userInfo[.payload] = newValue }
get { self.storage[PayloadKey.self]! }
set { self.storage[PayloadKey.self] = newValue }
}
}
29 changes: 0 additions & 29 deletions Sources/JWTMiddleware/Payload.swift

This file was deleted.

84 changes: 80 additions & 4 deletions Tests/JWTMiddlewareTests/JWTMiddlewareTests.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,86 @@
import XCTest
import JWT
import Vapor
import XCTVapor
import CNIOBoringSSL
@testable import JWTMiddleware
@testable import JWTKit

struct TestPayload: JWTPayload {
let id: UUID
let exp: TimeInterval

init(id: UUID, exp: TimeInterval) {
self.id = id
self.exp = exp
}

func verify(using signer: JWTSigner) throws {
_ = SubjectClaim(value: self.id.uuidString) // Nothing to verify here.
try ExpirationClaim(value: Date(timeIntervalSince1970: self.exp)).verifyNotExpired()
}
}

final class JWTMiddlewareTests: XCTestCase {
func testExample() {}

static var allTests = [
("testExample", testExample),
]
var tester: Application!

override func setUpWithError() throws {
// CryptoKit only generates EC keys and I don't know how to turn the raw representation into JWKS.
var exp: BIGNUM = .init(); CNIOBoringSSL_BN_set_u64(&exp, 0x10001)
var rsa: RSA = .init(); CNIOBoringSSL_RSA_generate_key_ex(&rsa, 4096, &exp, nil)

let dBytes: [UInt8] = .init(unsafeUninitializedCapacity: Int(CNIOBoringSSL_BN_num_bytes(rsa.d))) { $1 = CNIOBoringSSL_BN_bn2bin(rsa.d, $0.baseAddress!) }
let nBytes: [UInt8] = .init(unsafeUninitializedCapacity: Int(CNIOBoringSSL_BN_num_bytes(rsa.n))) { $1 = CNIOBoringSSL_BN_bn2bin(rsa.n, $0.baseAddress!) }
struct LocalJWKS: Codable {
struct LocalJWK: Codable { let kty, d, e, n, use, kid, alg: String }
let keys: [LocalJWK]
}
let keyset = LocalJWKS(keys: [.init(kty: "RSA", d: String(bytes: dBytes.base64URLEncodedBytes(), encoding: .utf8)!, e: "AQAB", n: String(bytes: nBytes.base64URLEncodedBytes(), encoding: .utf8)!, use: "sig", kid: "jwttest", alg: "RS256")])
let json = try JSONEncoder().encode(keyset)

tester = Application(.testing)
try tester.jwt.signers.use(jwksJSON: String(data: json, encoding: .utf8)!)
}

override func tearDownWithError() throws {
tester?.shutdown()
}

func testPayloadValidationUnexpired() throws {
let testPayload = TestPayload(id: UUID(), exp: Date(timeIntervalSinceNow: 10.0).timeIntervalSince1970)

tester.middleware.use(JWTMiddleware<TestPayload>())
tester.get("hello") { _ in "world" }

let token = try tester.jwt.signers.sign(testPayload, kid: "jwttest")

_ = try XCTUnwrap(tester.testable(method: .inMemory).test(.GET, "/hello", headers: ["Authorization": "Bearer \(token)"]) { res in
XCTAssertEqual(res.body.string, "world")
})
}

func testPayloadValidationExpired() throws {
let testPayload = TestPayload(id: UUID(), exp: Date(timeIntervalSinceNow: -10.0).timeIntervalSince1970)

tester.middleware.use(JWTMiddleware<TestPayload>())
tester.get("hello") { _ in "world" }

let token = try tester.jwt.signers.sign(testPayload, kid: "jwttest")

_ = try XCTUnwrap(tester.testable(method: .inMemory).test(.GET, "/hello", headers: ["Authorization": "Bearer \(token)"]) { res in
XCTAssertEqual(res.status, .unauthorized)

struct JWTErrorResponse: Codable {
let error: Bool
let reason: String
}

guard let content = try? XCTUnwrap(res.content.decode(JWTErrorResponse.self)) else {
return
}
XCTAssertEqual(content.error, true)
XCTAssertEqual(content.reason, "exp claim verification failed: expired")
})
}
}

0 comments on commit 2acd0e1

Please sign in to comment.