Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
5257875a5b | |
![]() |
d7db87fcd8 | |
![]() |
079814fe35 | |
![]() |
c852499d8a | |
![]() |
63a01c6c5f | |
![]() |
b1ab5bae59 | |
![]() |
22e244a5e6 | |
![]() |
36ca47cd4a | |
![]() |
2d241ee1c7 |
|
@ -2,3 +2,4 @@
|
|||
/.build
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
.swiftpm/
|
203
Package.resolved
203
Package.resolved
|
@ -2,129 +2,75 @@
|
|||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "Auth",
|
||||
"repositoryURL": "https://github.com/vapor/auth.git",
|
||||
"package": "async-http-client",
|
||||
"repositoryURL": "https://github.com/swift-server/async-http-client.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "e6f61b403727ec124214beb3e57deff579f31d00",
|
||||
"version": "2.0.4"
|
||||
"revision": "51dc885a30ca704b02fa803099b0a9b5b38067b6",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Console",
|
||||
"repositoryURL": "https://github.com/vapor/console.git",
|
||||
"package": "async-kit",
|
||||
"repositoryURL": "https://github.com/vapor/async-kit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "74cfbea629d4aac34a97cead2447a6870af1950b",
|
||||
"version": "3.1.1"
|
||||
"revision": "d9fd2be441af6d1428b62ab694848396e7072a14",
|
||||
"version": "1.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Core",
|
||||
"repositoryURL": "https://github.com/vapor/core.git",
|
||||
"package": "console-kit",
|
||||
"repositoryURL": "https://github.com/vapor/console-kit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c64f63cb21631010952f7abfef719d376ab6a441",
|
||||
"version": "3.9.1"
|
||||
"revision": "5b91c2dc93781e4b36cb4c667972670eac90e6e7",
|
||||
"version": "4.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Crypto",
|
||||
"repositoryURL": "https://github.com/vapor/crypto.git",
|
||||
"package": "jwt-kit",
|
||||
"repositoryURL": "https://github.com/vapor/jwt-kit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "df8eb7d8ae51787b3a0628aa3975e67666da936c",
|
||||
"version": "3.3.3"
|
||||
"revision": "0c7e52ab75ddfabad539a4a2a2f9fc003e7700b7",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "DatabaseKit",
|
||||
"repositoryURL": "https://github.com/vapor/database-kit.git",
|
||||
"package": "multipart-kit",
|
||||
"repositoryURL": "https://github.com/vapor/multipart-kit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "8f352c8e66dab301ab9bfef912a01ce1361ba1e4",
|
||||
"version": "1.3.3"
|
||||
"revision": "a941d7a1d685c83df09077f6190808ff2a7f4dce",
|
||||
"version": "4.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Fluent",
|
||||
"repositoryURL": "https://github.com/vapor/fluent.git",
|
||||
"package": "open-crypto",
|
||||
"repositoryURL": "https://github.com/vapor/open-crypto.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "b915c321c6f9e83743ee5efa35a30895e1b02e51",
|
||||
"version": "3.2.0"
|
||||
"revision": "06d26edb8e28295bb7103b4f950d5ea58d634c1b",
|
||||
"version": "4.0.0-alpha.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "FluentSQLite",
|
||||
"repositoryURL": "https://github.com/vapor/fluent-sqlite.git",
|
||||
"package": "routing-kit",
|
||||
"repositoryURL": "https://github.com/vapor/routing-kit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c32f5bda84bf4ea691d19afe183d40044f579e11",
|
||||
"version": "3.0.0"
|
||||
"revision": "6c7f4b471f9662d05045d82e64e22d5572a16a82",
|
||||
"version": "4.0.0-alpha.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "HTTP",
|
||||
"repositoryURL": "https://github.com/vapor/http.git",
|
||||
"package": "swift-log",
|
||||
"repositoryURL": "https://github.com/apple/swift-log.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "3808ed0401379b6e9f4a053f03090ea9d658caa9",
|
||||
"version": "3.2.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "JWT",
|
||||
"repositoryURL": "https://github.com/vapor/jwt.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "2e225c722bf26407c1c4bd11d341e48759f46095",
|
||||
"version": "3.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Multipart",
|
||||
"repositoryURL": "https://github.com/vapor/multipart.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "f919a01c4d10a281d6236a21b0b1d1759a72b8eb",
|
||||
"version": "3.0.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Routing",
|
||||
"repositoryURL": "https://github.com/vapor/routing.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "626190ddd2bd9f967743b60ba6adaf90bbd2651c",
|
||||
"version": "3.0.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Service",
|
||||
"repositoryURL": "https://github.com/vapor/service.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "fa5b5de62bd68bcde9a69933f31319e46c7275fb",
|
||||
"version": "1.0.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SQL",
|
||||
"repositoryURL": "https://github.com/vapor/sql.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "50eaeb8f52a1ce63f1ff3880e1114dd8757a78a6",
|
||||
"version": "2.3.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "SQLite",
|
||||
"repositoryURL": "https://github.com/vapor/sqlite.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "314d9cd21165bcf14215e336a23ff8214f40e411",
|
||||
"version": "3.2.1"
|
||||
"revision": "74d7b91ceebc85daf387ebb206003f78813f71aa",
|
||||
"version": "1.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -132,8 +78,26 @@
|
|||
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "ba7970fe396e8198b84c6c1b44b38a1d4e2eb6bd",
|
||||
"version": "1.14.1"
|
||||
"revision": "ff01888051cd7efceb1bf8319c1dd3986c4bf6fc",
|
||||
"version": "2.10.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-nio-extras",
|
||||
"repositoryURL": "https://github.com/apple/swift-nio-extras.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "53808818c2015c45247cad74dc05c7a032c96a2f",
|
||||
"version": "1.3.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-nio-http2",
|
||||
"repositoryURL": "https://github.com/apple/swift-nio-http2.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "867259332c45d5405efed844f7b3997ebfa94167",
|
||||
"version": "1.7.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -141,71 +105,26 @@
|
|||
"repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "0f3999f3e3c359cc74480c292644c3419e44a12f",
|
||||
"version": "1.4.0"
|
||||
"revision": "ccf96bbe65ecc7c1558ab0dba7ffabdea5c1d31f",
|
||||
"version": "2.4.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-nio-ssl-support",
|
||||
"repositoryURL": "https://github.com/apple/swift-nio-ssl-support.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c02eec4e0e6d351cd092938cf44195a8e669f555",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-nio-zlib-support",
|
||||
"repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "37760e9a52030bb9011972c5213c3350fa9d41fd",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "TemplateKit",
|
||||
"repositoryURL": "https://github.com/vapor/template-kit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "51405c83e95e8adb09565278a5e9b959c605e56c",
|
||||
"version": "1.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "URLEncodedForm",
|
||||
"repositoryURL": "https://github.com/vapor/url-encoded-form.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "82d8d63bdb76b6dd8febe916c639ab8608dbbaed",
|
||||
"version": "1.0.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Validation",
|
||||
"repositoryURL": "https://github.com/vapor/validation.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "4de213cf319b694e4ce19e5339592601d4dd3ff6",
|
||||
"version": "2.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Vapor",
|
||||
"package": "vapor",
|
||||
"repositoryURL": "https://github.com/vapor/vapor.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "c86ada59b31c69f08a6abd4f776537cba48d5df6",
|
||||
"version": "3.3.0"
|
||||
"revision": "3d8bbd5eb2b556debbb51da8ac7357f34b7b0a8e",
|
||||
"version": "4.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "WebSocket",
|
||||
"repositoryURL": "https://github.com/vapor/websocket.git",
|
||||
"package": "websocket-kit",
|
||||
"repositoryURL": "https://github.com/vapor/websocket-kit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "d85e5b6dce4d04065865f77385fc3324f98178f6",
|
||||
"version": "1.1.2"
|
||||
"revision": "66c0ea58398f055b5a0d92b0d5f4c32ef0c02eeb",
|
||||
"version": "2.0.0-beta.1"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
// swift-tools-version:4.0
|
||||
// swift-tools-version:5.0
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "JWTVapor",
|
||||
platforms: [
|
||||
.macOS(.v10_14)
|
||||
],
|
||||
products: [
|
||||
.library(name: "JWTVapor", targets: ["JWTVapor"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/vapor/vapor.git", from: "3.3.0"),
|
||||
.package(url: "https://github.com/vapor/jwt.git", from: "3.0.0"),
|
||||
.package(url: "https://github.com/vapor/auth.git", from: "2.0.4")
|
||||
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0-beta"),
|
||||
.package(url: "https://github.com/vapor/jwt-kit.git", .revision("0c7e52ab75ddfabad539a4a2a2f9fc003e7700b7")),
|
||||
// .package(url: "https://github.com/vapor/auth.git", .branch("master"))
|
||||
],
|
||||
targets: [
|
||||
.target(name: "JWTVapor", dependencies: ["Vapor", "Authentication", "JWT"]),
|
||||
.target(name: "JWTVapor", dependencies: ["Vapor", "JWTKit"]),
|
||||
.testTarget(name: "JWTVaporTests", dependencies: ["JWTVapor"]),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Ralph Küpper on 12/10/19.
|
||||
//
|
||||
|
||||
import Vapor
|
||||
@_exported import JWTKit
|
||||
|
||||
class JWTConfig {
|
||||
var algorithm: JWTAlgorithm
|
||||
var header: JWTHeader?
|
||||
var secretVar: String
|
||||
var publicVar: String
|
||||
|
||||
public init(algorithm: JWTAlgorithm, header: JWTHeader? = nil, secretVar: String? = nil, publicVar: String? = nil) {
|
||||
self.algorithm = algorithm
|
||||
self.header = header
|
||||
self.secretVar = secretVar ?? "JWT_SECRET"
|
||||
self.publicVar = publicVar ?? "JWT_PUBLIC"
|
||||
}
|
||||
}
|
||||
|
||||
extension Application {
|
||||
|
||||
private struct JWTServiceKey: StorageKey {
|
||||
typealias Value = JWTService
|
||||
}
|
||||
|
||||
private struct JWTConfigKey: StorageKey {
|
||||
typealias Value = JWTConfig
|
||||
}
|
||||
|
||||
var jwtConfig: JWTConfig {
|
||||
if let existing = self.storage[JWTConfigKey.self] {
|
||||
return existing
|
||||
} else {
|
||||
let config = JWTConfig(algorithm: .rsa(.sha256))
|
||||
self.storage[JWTConfigKey.self] = config
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var jwtService: JWTService {
|
||||
if let existing = self.storage[JWTServiceKey.self] {
|
||||
return existing
|
||||
} else {
|
||||
var new:JWTService
|
||||
guard let publicKey = Environment.get(self.jwtConfig.publicVar) else {
|
||||
print("No value was found at the given public key environmen '\(self.jwtConfig.publicVar)'")
|
||||
exit(0)
|
||||
}
|
||||
switch self.jwtConfig.algorithm {
|
||||
case let .rsa(alorithm): new = RSAService(n: publicKey, e: "AQAB", d: Environment.get(self.jwtConfig.secretVar), header: self.jwtConfig.header, algorithm: alorithm)
|
||||
case let .hmac(alorithm): new = HMACService(key: publicKey, header: self.jwtConfig.header, algorithm: alorithm)
|
||||
case let .cert(alorithm): new = CertService(certificate: Data(publicKey.utf8), header: self.jwtConfig.header, algorithm: alorithm)
|
||||
case let .custom(closure): new = closure(self.jwtConfig.header, publicKey, Environment.get(self.jwtConfig.secretVar))
|
||||
}
|
||||
|
||||
self.storage[JWTServiceKey.self] = new
|
||||
return new
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,19 +1,23 @@
|
|||
import Foundation
|
||||
import Crypto
|
||||
import JWT
|
||||
import JWTKit
|
||||
|
||||
public final class CertService: JWTService {
|
||||
public var signer: JWTSigner
|
||||
public var header: JWTHeader?
|
||||
|
||||
public init(certificate: String, header: JWTHeader? = nil, algorithm: DigestAlgorithm = .sha256)throws {
|
||||
let key = try RSAKey.public(certificate: certificate)
|
||||
public init(certificate: Data, header: JWTHeader? = nil, algorithm: DigestAlgorithm = .sha256) {
|
||||
do {
|
||||
let key = try RSAKey.public(pem: certificate)
|
||||
|
||||
switch algorithm {
|
||||
case .sha256: self.signer = JWTSigner.rs256(key: key)
|
||||
case .sha384: self.signer = JWTSigner.rs384(key: key)
|
||||
case .sha512: self.signer = JWTSigner.rs512(key: key)
|
||||
default: throw JWTProviderError(identifier: "badRSAAlgorithm", reason: "RSA signing requires SHA256, SHA384, or SHA512 algorithm", status: .internalServerError)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
print("We cannot run this service without a key.")
|
||||
exit(0)
|
||||
}
|
||||
|
||||
self.header = header
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
import Foundation
|
||||
import Crypto
|
||||
import JWT
|
||||
import JWTKit
|
||||
|
||||
public final class HMACService: JWTService {
|
||||
public let signer: JWTSigner
|
||||
public let header: JWTHeader?
|
||||
|
||||
public init(key: String, header: JWTHeader? = nil, algorithm: DigestAlgorithm = .sha256)throws {
|
||||
public init(key: String, header: JWTHeader? = nil, algorithm: DigestAlgorithm = .sha256) {
|
||||
switch algorithm {
|
||||
case .sha256: self.signer = JWTSigner.hs256(key: Data(key.utf8))
|
||||
case .sha384: self.signer = JWTSigner.hs384(key: Data(key.utf8))
|
||||
case .sha512: self.signer = JWTSigner.hs512(key: Data(key.utf8))
|
||||
default: throw JWTProviderError(identifier: "badHMACAlgorithm", reason: "HMAC signing requires SHA256, SHA384, or SHA512 algorithm", status: .internalServerError)
|
||||
}
|
||||
|
||||
self.header = header
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Foundation
|
||||
import Vapor
|
||||
|
||||
/// Configuration required by the `JWKSService`.
|
||||
|
@ -13,12 +12,12 @@ import Vapor
|
|||
|
||||
/// `JWKSConfig` is a `Service` which gets created by the required container and is made available
|
||||
/// to `JWKSService` in its `makeService` method.
|
||||
public struct JWKSConfig: Service {
|
||||
public let jwks: String
|
||||
public struct JWKSConfig {
|
||||
public let jwks: URI
|
||||
public let following: String
|
||||
public let password: String?
|
||||
|
||||
public init(jwks: String, following: String, password: String? = nil) {
|
||||
public init(jwks: URI, following: String, password: String? = nil) {
|
||||
self.jwks = jwks
|
||||
self.following = following
|
||||
self.password = password
|
||||
|
|
|
@ -4,10 +4,9 @@ import Vapor
|
|||
/// Content (Codeable) model to read the response obtained after hitting the url specified in the
|
||||
/// `jwks` property of `JWKSConfig`.
|
||||
public struct JWKSDocumentRequest: Content {
|
||||
let jwksUrl: String
|
||||
let jwksUrl: URI
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
// Get the container
|
||||
let container = try decoder.container(keyedBy: JWKSDocumentCodingKey.self)
|
||||
|
||||
// Read the coding key passed to the decoder as part of userinfo
|
||||
|
@ -15,8 +14,18 @@ public struct JWKSDocumentRequest: Content {
|
|||
throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Failed"))
|
||||
}
|
||||
|
||||
// get the jwks url from the json response.
|
||||
self.jwksUrl = try container.decode(String.self, forKey: jwksUrlKey)
|
||||
self.jwksUrl = try URI(string: container.decode(String.self, forKey: jwksUrlKey))
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: JWKSDocumentCodingKey.self)
|
||||
|
||||
// Read the coding key passed to the decoder as part of userinfo
|
||||
guard let jwksUrlKey = encoder.userInfo[JWKSService.codingUserInfoKey] as? JWKSDocumentCodingKey else {
|
||||
throw DecodingError.dataCorrupted(.init(codingPath: [], debugDescription: "Failed"))
|
||||
}
|
||||
|
||||
try container.encode(self.jwksUrl.string, forKey: jwksUrlKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import Foundation
|
||||
import Vapor
|
||||
|
||||
public struct JWKSService: ServiceType {
|
||||
|
||||
/// config: `JWKSConfig` instance which contains all the configurations required by `JWKSService`.
|
||||
private let config: JWKSConfig
|
||||
/// The service container in which the Services are to be loaded.
|
||||
private let container: Container
|
||||
/// The client we need to pull the jwks.
|
||||
private let client: Client
|
||||
public struct JWKSService {
|
||||
static let factory: (Application) throws -> JWKSService = { _ in fatalError() }
|
||||
static let requestFactory: (Request) throws -> JWKSService = { _ in fatalError() }
|
||||
|
||||
/// Create a key that gets passed as a key in the `JSONDecoder.userInfo` dictionary and its value is
|
||||
/// value that the user passes in `JWKSConfig`'s `following`. The value passed in the `following`
|
||||
|
@ -19,52 +14,52 @@ public struct JWKSService: ServiceType {
|
|||
/// [codingUserInfoKey: JWKSDocumentCodingKey(stringValue: self.config.following)]
|
||||
static let codingUserInfoKey = CodingUserInfoKey(rawValue: "jwksUrlKey")!
|
||||
|
||||
public init(config: JWKSConfig, container: Container) {
|
||||
|
||||
/// config: `JWKSConfig` instance which contains all the configurations required by `JWKSService`.
|
||||
private let config: JWKSConfig
|
||||
|
||||
/// The client we need to pull the jwks.
|
||||
private let client: Client
|
||||
|
||||
public init(config: JWKSConfig, client: Client) {
|
||||
self.config = config
|
||||
self.container = container
|
||||
self.client = try! container.make(Client.self)
|
||||
self.client = client
|
||||
|
||||
}
|
||||
|
||||
/// Finds and returns a RSAService that matches the provided `tid` from the list of JWKS.
|
||||
/// The `tid` is obtained from the JWT Header of the incoming request, if the signing mechanism used is RSA.
|
||||
public func rsaService(forKey tid: String) throws -> Future<RSAService> {
|
||||
return client.get(self.config.jwks).flatMap { response throws -> Future<JWKSDocumentRequest> in
|
||||
|
||||
/// Create a JSONDecoder in which the codingKey could be passed as part of userInfo.
|
||||
public func rsaService(forKey tid: String) throws -> EventLoopFuture<RSAService> {
|
||||
return self.client.get(self.config.jwks).flatMap { response -> EventLoopFuture<ClientResponse> in
|
||||
let jsonDecoder = JSONDecoder()
|
||||
let jwksUrlKey = JWKSDocumentCodingKey(stringValue: self.config.following)!
|
||||
jsonDecoder.userInfo = [JWKSService.codingUserInfoKey: jwksUrlKey]
|
||||
/// return the JSON response.
|
||||
return try response.content.decode(json: JWKSDocumentRequest.self, using: jsonDecoder)
|
||||
|
||||
}.flatMap { jwksDocumentRequest throws -> Future<Response> in
|
||||
/// Make a request and return the JWKS file.
|
||||
return self.client.get(jwksDocumentRequest.jwksUrl)
|
||||
|
||||
}.flatMap { response throws -> Future<JWKSKeys> in
|
||||
do {
|
||||
let request = try response.content.decode(JWKSDocumentRequest.self, using: jsonDecoder)
|
||||
return self.client.get(request.jwksUrl)
|
||||
} catch let error {
|
||||
let eventLoop = self.client.eventLoopGroup.next()
|
||||
return eventLoop.future(error: error)
|
||||
}
|
||||
}.flatMapThrowing { response throws -> RSAService in
|
||||
/// Read the entire list of JWKS Keys.
|
||||
return try response.content.decode(JWKSKeys.self)
|
||||
let jwksKeys = try response.content.decode(JWKSKeys.self)
|
||||
|
||||
}.map { jwksKeys throws -> JWKSKey in
|
||||
/// Search for JWKSKey that corresponds to the provided `tid`
|
||||
guard let matchingJWKSKey = jwksKeys.keys.filter({ $0.kid == tid }).first else {
|
||||
throw JWTProviderError(identifier: "invalidJWKSKeys", reason: "No matching key found in JWKS file", status: .internalServerError)
|
||||
}
|
||||
return matchingJWKSKey
|
||||
|
||||
}.map { jwksKey throws -> RSAService in
|
||||
/// Create the RSAService using the JWKSKey.
|
||||
return try RSAService(n: jwksKey.n, e: jwksKey.e, d: jwksKey.d)
|
||||
|
||||
return try RSAService(n: matchingJWKSKey.n, e: matchingJWKSKey.e, d: matchingJWKSKey.d)
|
||||
}
|
||||
}
|
||||
|
||||
public static func makeService(for worker: Container) throws -> JWKSService {
|
||||
// Load the required services.
|
||||
let config = try worker.make(JWKSConfig.self)
|
||||
return JWKSService(config: config, container: worker)
|
||||
}
|
||||
// public static func makeService(for worker: Container) throws -> JWKSService {
|
||||
// // Load the required services.
|
||||
// let config = try worker.make(JWKSConfig.self)
|
||||
// return JWKSService(config: config, container: worker)
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Ralph Küpper on 12/10/19.
|
||||
//
|
||||
|
||||
import Vapor
|
||||
|
||||
|
||||
public enum JWTAlgorithm {
|
||||
case rsa(DigestAlgorithm = .sha256)
|
||||
case hmac(DigestAlgorithm = .sha256)
|
||||
case cert(DigestAlgorithm = .sha256)
|
||||
case custom((JWTHeader?, String, String?) -> JWTService)
|
||||
}
|
|
@ -1,42 +1,47 @@
|
|||
import Command
|
||||
import Console
|
||||
import ConsoleKit
|
||||
import Foundation
|
||||
|
||||
public final class NewJWTCommand<Payload>: Command where Payload: JWTPayload {
|
||||
public var arguments: [CommandArgument] = []
|
||||
public struct Signature: CommandSignature {
|
||||
static var jsonHelp: String { """
|
||||
The payload for the generated token.
|
||||
If this flag is not use, the payload passed into the command's initializer is used.
|
||||
""" }
|
||||
|
||||
public var options: [CommandOption] = [
|
||||
CommandOption.value(name: "json", help: [
|
||||
"The payload for the generated token",
|
||||
"If this flag is not use, the payload passed into the command's initializer is used"
|
||||
])
|
||||
]
|
||||
@Option(name: "json", help: Signature.jsonHelp) var json: Data?
|
||||
|
||||
public var help: [String] = ["Creates a JWT token for debugging purposes."]
|
||||
public init() { }
|
||||
}
|
||||
|
||||
public let help: String = "Creates a JWT token for debugging purposes."
|
||||
|
||||
internal let payloadCreator: () -> Payload
|
||||
internal let signer: JWTService
|
||||
|
||||
public init(payload: Payload) {
|
||||
public init(payload: Payload, signer: JWTService) {
|
||||
self.payloadCreator = { payload }
|
||||
self.signer = signer
|
||||
}
|
||||
|
||||
public init(_ handler: @escaping () -> Payload) {
|
||||
public init(signer: JWTService, _ handler: @escaping () -> Payload) {
|
||||
self.payloadCreator = handler
|
||||
self.signer = signer
|
||||
}
|
||||
|
||||
public func run(using context: CommandContext) throws -> EventLoopFuture<Void> {
|
||||
let signer = try context.container.make(JWTService.self)
|
||||
let payload: Payload
|
||||
if let json = context.options["json"]?.data(using: .utf8) {
|
||||
payload = try JSONDecoder().decode(Payload.self, from: json)
|
||||
} else {
|
||||
payload = self.payloadCreator()
|
||||
}
|
||||
public func run(using context: CommandContext, signature: Signature) throws {
|
||||
let payload: Payload =
|
||||
try signature.json.map { try JSONDecoder().decode(Payload.self, from: $0) } ??
|
||||
self.payloadCreator()
|
||||
|
||||
let token = try signer.sign(payload)
|
||||
|
||||
context.console.output("JWT Token: ", style: .info, newLine: false)
|
||||
context.console.output(token, style: .plain, newLine: true)
|
||||
|
||||
return context.container.future()
|
||||
}
|
||||
}
|
||||
|
||||
extension Data: LosslessStringConvertible {
|
||||
public init?(_ description: String) {
|
||||
self.init(description.utf8)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
import Vapor
|
||||
import Service
|
||||
@_exported import JWT
|
||||
|
||||
public class JWTProvider: Vapor.Provider {
|
||||
public static var repositoryName: String = "JWTProvider"
|
||||
|
||||
public let serviceBuilder: (String, String?)throws -> JWTService
|
||||
|
||||
public init (serviceBuilder: @escaping (String, String?)throws -> JWTService) {
|
||||
self.serviceBuilder = serviceBuilder
|
||||
}
|
||||
|
||||
public init(_ serviceType: JWTService.Type)throws {
|
||||
self.serviceBuilder = { key, d in
|
||||
switch serviceType {
|
||||
case is RSAService.Type:
|
||||
return try RSAService(n: key, e: "AQAB", d: d)
|
||||
case is HMACService.Type: return try HMACService(key: key)
|
||||
case is CertService.Type: return try CertService(certificate: key)
|
||||
default:
|
||||
throw JWTProviderError(
|
||||
identifier: "unsupportedJWTService",
|
||||
reason: "The registered JWT service is not supported. Maybe you created a custom service?",
|
||||
status: .internalServerError
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func register(_ services: inout Services) throws {
|
||||
|
||||
/// Registering a JWKSService.
|
||||
services.register(JWKSService.self)
|
||||
|
||||
let d = Environment.get("JWT_SECRET")
|
||||
guard let key = Environment.get("JWT_PUBLIC") else {
|
||||
throw JWTProviderError(identifier: "noPublicFound", reason: "No 'JWT_PUBLIC' environment variable was found", status: .internalServerError)
|
||||
}
|
||||
|
||||
let jwtService = try serviceBuilder(key, d)
|
||||
if let rsaService = jwtService as? RSAService {
|
||||
services.register(rsaService, as: JWTService.self)
|
||||
} else if let hmacService = jwtService as? HMACService {
|
||||
services.register(hmacService, as: JWTService.self)
|
||||
} else if let certService = jwtService as? CertService {
|
||||
services.register(certService, as: JWTService.self)
|
||||
} else {
|
||||
throw JWTProviderError(
|
||||
identifier: "unsupportedJWTService",
|
||||
reason: "The registered JWT service is not supported. Maybe you created a custom service?",
|
||||
status: .internalServerError
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func boot(_ worker: Container) throws {}
|
||||
|
||||
public func didBoot(_ worker: Container) throws -> EventLoopFuture<Void> { return Future.map(on: worker, { () }) }
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
import Debugging
|
||||
import Vapor
|
||||
|
||||
public struct JWTProviderError: Debuggable, AbortError, Error {
|
||||
public struct JWTProviderError: AbortError, Error {
|
||||
public let identifier: String
|
||||
public let reason: String
|
||||
public let status: HTTPResponseStatus
|
||||
|
||||
public var description: String {
|
||||
return "JWTProviderError.\(self.identifier): \(self.status) \(self.reason)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import Foundation
|
||||
import Service
|
||||
import Crypto
|
||||
import JWT
|
||||
import JWTKit
|
||||
|
||||
public protocol JWTService: Service {
|
||||
public protocol JWTService {
|
||||
var signer: JWTSigner { get }
|
||||
var header: JWTHeader? { get }
|
||||
|
||||
|
@ -11,27 +9,42 @@ public protocol JWTService: Service {
|
|||
func verify(_ token: Data)throws -> Bool
|
||||
}
|
||||
|
||||
public enum DigestAlgorithm {
|
||||
case sha256, sha384, sha512
|
||||
}
|
||||
|
||||
|
||||
extension UInt8 {
|
||||
static let period: UInt8 = 46
|
||||
}
|
||||
|
||||
extension JWTSigner {
|
||||
public func verify<S, H, P>(_ signature: S, header: H, payload: P) throws -> Bool
|
||||
where S: DataProtocol, H: DataProtocol, P: DataProtocol
|
||||
{
|
||||
let message: Data = Data(header) + Data([.period]) + Data(payload)
|
||||
guard let signature = Data(base64Encoded: Data(signature)) else {
|
||||
throw JWTError.malformedToken
|
||||
}
|
||||
return try algorithm.verify(signature, signs: message)
|
||||
}
|
||||
}
|
||||
|
||||
extension JWTService {
|
||||
public func sign<T>(_ payload: T) throws -> String where T : JWTPayload {
|
||||
guard let header = self.header else {
|
||||
throw JWTProviderError(identifier: "noHeader", reason: "Cannot sign token with a header", status: .internalServerError)
|
||||
}
|
||||
|
||||
let jwt = JWT(header: header, payload: payload)
|
||||
let data = try signer.sign(jwt)
|
||||
guard let token = String(data: data, encoding: .utf8) else {
|
||||
throw JWTProviderError(
|
||||
identifier: "tokenEncodingFailed",
|
||||
reason: "Converting access token data to a string failed with UTF-8 encoding",
|
||||
status: .internalServerError
|
||||
)
|
||||
}
|
||||
return token
|
||||
let bytes = try jwt.sign(using: self.signer)
|
||||
return String(decoding: bytes, as: UTF8.self)
|
||||
}
|
||||
|
||||
public func verify(_ token: Data)throws -> Bool {
|
||||
let parts = token.split(separator: .period)
|
||||
guard parts.count == 3 else {
|
||||
throw JWTError(identifier: "invalidJWT", reason: "Malformed JWT")
|
||||
throw JWTError.malformedToken
|
||||
}
|
||||
|
||||
let header = Data(parts[0])
|
||||
|
|
|
@ -1,32 +1,41 @@
|
|||
import Foundation
|
||||
import Crypto
|
||||
import JWT
|
||||
import CJWTKitCrypto
|
||||
import JWTKit
|
||||
|
||||
public final class RSAService: JWTService {
|
||||
public enum KeyType {
|
||||
case `public`, `private`
|
||||
}
|
||||
|
||||
public let signer: JWTSigner
|
||||
public let header: JWTHeader?
|
||||
|
||||
public init(pem: String, header: JWTHeader? = nil, type: RSAKeyType = .private, algorithm: DigestAlgorithm = .sha256)throws {
|
||||
public init(pem: Data, header: JWTHeader? = nil, type: KeyType = .private, algorithm: DigestAlgorithm = .sha256) {
|
||||
let key: RSAKey
|
||||
do {
|
||||
switch type {
|
||||
case .public: key = try RSAKey.public(pem: pem)
|
||||
case .private: key = try RSAKey.private(pem: pem)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
print("We need a key to run this service.")
|
||||
exit(0)
|
||||
}
|
||||
|
||||
switch algorithm {
|
||||
case .sha256: self.signer = JWTSigner.rs256(key: key)
|
||||
case .sha384: self.signer = JWTSigner.rs384(key: key)
|
||||
case .sha512: self.signer = JWTSigner.rs512(key: key)
|
||||
default: throw JWTProviderError(identifier: "badRSAAlgorithm", reason: "RSA signing requires SHA256, SHA384, or SHA512 algorithm", status: .internalServerError)
|
||||
}
|
||||
|
||||
self.header = header
|
||||
}
|
||||
|
||||
public init(
|
||||
secret: String,
|
||||
secret: Data,
|
||||
header: JWTHeader? = nil,
|
||||
keyBuilder: (LosslessDataConvertible)throws -> RSAKey,
|
||||
keyBuilder: (Data) throws -> RSAKey,
|
||||
signerBuilder: (RSAKey) -> JWTSigner = JWTSigner.rs256
|
||||
)throws {
|
||||
let key = try keyBuilder(secret)
|
||||
|
@ -34,17 +43,18 @@ public final class RSAService: JWTService {
|
|||
self.header = header
|
||||
}
|
||||
|
||||
public init(n: String, e: String, d: String? = nil, header: JWTHeader? = nil, algorithm: DigestAlgorithm = .sha256)throws {
|
||||
let key = try RSAKey.components(n: n, e: e, d: d)
|
||||
public init(n: String, e: String, d: String? = nil, header: JWTHeader? = nil, algorithm: DigestAlgorithm = .sha256) {
|
||||
guard let key = RSAKey(modulus: n, exponent: e, privateExponent: d) else {
|
||||
print("RSA key initialization failed")
|
||||
exit(0)
|
||||
}
|
||||
|
||||
switch algorithm {
|
||||
case .sha256: self.signer = JWTSigner.rs256(key: key)
|
||||
case .sha384: self.signer = JWTSigner.rs384(key: key)
|
||||
case .sha512: self.signer = JWTSigner.rs512(key: key)
|
||||
default: throw JWTProviderError(identifier: "badRSAAlgorithm", reason: "RSA signing requires SHA256, SHA384, or SHA512 algorithm", status: .internalServerError)
|
||||
}
|
||||
|
||||
self.header = header
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
import Authentication
|
||||
import Vapor
|
||||
import JWT
|
||||
import JWTKit
|
||||
|
||||
extension Request {
|
||||
var jwtService: JWTService {
|
||||
return self.application.jwtService
|
||||
}
|
||||
}
|
||||
|
||||
extension Request {
|
||||
public func payload<Payload>(`as` type: Payload.Type = Payload.self)throws -> Payload where Payload: JWTPayload {
|
||||
guard let token = self.http.headers.bearerAuthorization?.token else {
|
||||
guard let token = self.headers.bearerAuthorization?.token else {
|
||||
throw JWTProviderError(identifier: "missingAuthorizationHeader", reason: "'Authorization' header with bearer token is missing", status: .badRequest)
|
||||
}
|
||||
let jwt = try self.make(JWTService.self)
|
||||
let data: Data = Data(token.utf8)
|
||||
|
||||
if try jwt.verify(data) {
|
||||
return try JWT<Payload>.init(from: data, verifiedUsing: jwt.signer).payload
|
||||
if try self.jwtService.verify(data) {
|
||||
return try JWT<Payload>.init(from: data, verifiedBy: self.jwtService.signer).payload
|
||||
} else {
|
||||
throw JWTProviderError(identifier: "verificationFailed", reason: "Verification failed for JWT token", status: .forbidden)
|
||||
}
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
import Foundation
|
||||
import Vapor
|
||||
|
||||
struct JWKSMockClient: Client, ServiceType {
|
||||
var container: Container
|
||||
var requestString: String!
|
||||
|
||||
init(container: Container) {
|
||||
self.container = container
|
||||
}
|
||||
|
||||
func send(_ req: Request) -> EventLoopFuture<Response> {
|
||||
var response: String = ""
|
||||
|
||||
switch (req.http.url.absoluteString) {
|
||||
case "https://mockurl.com":
|
||||
response = JWKSResponseFixture.openIDDocumentResponse
|
||||
case "https//:mock_jwks_uri.com":
|
||||
response = JWKSResponseFixture.jwksResponse
|
||||
default:
|
||||
response = "{}"
|
||||
}
|
||||
|
||||
return Future.map(on: req) {
|
||||
req.makeResponse(response, as: .json)
|
||||
}
|
||||
}
|
||||
|
||||
public static var serviceSupports: [Any.Type] {
|
||||
return [Client.self]
|
||||
}
|
||||
|
||||
static func makeService(for worker: Container) throws -> JWKSMockClient {
|
||||
return JWKSMockClient(container: worker)
|
||||
}
|
||||
}
|
||||
//import Foundation
|
||||
//import Vapor
|
||||
//
|
||||
//struct JWKSMockClient: Client, ServiceType {
|
||||
// var container: Container
|
||||
// var requestString: String!
|
||||
//
|
||||
// init(container: Container) {
|
||||
// self.container = container
|
||||
// }
|
||||
//
|
||||
// func send(_ req: Request) -> EventLoopFuture<Response> {
|
||||
// var response: String = ""
|
||||
//
|
||||
// switch (req.http.url.absoluteString) {
|
||||
// case "https://mockurl.com":
|
||||
// response = JWKSResponseFixture.openIDDocumentResponse
|
||||
// case "https//:mock_jwks_uri.com":
|
||||
// response = JWKSResponseFixture.jwksResponse
|
||||
// default:
|
||||
// response = "{}"
|
||||
// }
|
||||
//
|
||||
// return Future.map(on: req) {
|
||||
// req.makeResponse(response, as: .json)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public static var serviceSupports: [Any.Type] {
|
||||
// return [Client.self]
|
||||
// }
|
||||
//
|
||||
// static func makeService(for worker: Container) throws -> JWKSMockClient {
|
||||
// return JWKSMockClient(container: worker)
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -1,74 +1,73 @@
|
|||
import XCTest
|
||||
import Vapor
|
||||
import JWTVapor
|
||||
import Core
|
||||
|
||||
final class JWKSServiceTests: XCTestCase {
|
||||
|
||||
static let allTests = [
|
||||
("testItThrowsErrorIfJWKSConfigIsNotRegisteredAsAService", testItThrowsErrorIfJWKSConfigIsNotRegisteredAsAService),
|
||||
("testItDoesNotThrowErrorIfJWKSConfigIsRegisteredAsAService", testItDoesNotThrowErrorIfJWKSConfigIsRegisteredAsAService),
|
||||
("testItReturnsAnRSAServiceForMentionedTid", testItReturnsAnRSAServiceForMentionedTid),
|
||||
("testItThrowsAnErrorIfNoRSAServiceIsFoundForMentionedTid", testItThrowsAnErrorIfNoRSAServiceIsFoundForMentionedTid)
|
||||
]
|
||||
|
||||
func testItThrowsErrorIfJWKSConfigIsNotRegisteredAsAService() {
|
||||
var services = Services()
|
||||
try? services.register(JWTProvider(RSAService.self))
|
||||
let app = try? Application(services: services)
|
||||
|
||||
XCTAssertThrowsError(try app?.make(JWKSService.self))
|
||||
}
|
||||
|
||||
func testItDoesNotThrowErrorIfJWKSConfigIsRegisteredAsAService() {
|
||||
var services = Services()
|
||||
let jwksMockConfig = JWKSConfig(jwks: "https://mockurl.com", following: "jwks_mock_uri_key")
|
||||
services.register(jwksMockConfig)
|
||||
try? services.register(JWTProvider(RSAService.self))
|
||||
let app = try? Application(services: services)
|
||||
|
||||
XCTAssertNoThrow(try app?.make(JWKSService.self))
|
||||
}
|
||||
|
||||
func testItReturnsAnRSAServiceForMentionedTid() {
|
||||
var services = Services.default()
|
||||
let jwksMockConfig = JWKSConfig(jwks: "https://mockurl.com", following: "jwks_mock_uri_key")
|
||||
services.register(jwksMockConfig)
|
||||
services.register(JWKSMockClient.self)
|
||||
try? services.register(JWTProvider(RSAService.self))
|
||||
|
||||
var config = Config.default()
|
||||
config.prefer(JWKSMockClient.self, for: Client.self)
|
||||
|
||||
let app = try! Application(config: config, services: services)
|
||||
|
||||
do {
|
||||
let rsaService = try app.make(JWKSService.self)
|
||||
.rsaService(forKey: "7_Zuf1tvkwLxYaHS3q6lUjUYIGw")
|
||||
.wait()
|
||||
|
||||
XCTAssertEqual(rsaService.signer.algorithm.jwtAlgorithmName, "RS256")
|
||||
|
||||
} catch {
|
||||
|
||||
XCTFail("Failed with exception \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testItThrowsAnErrorIfNoRSAServiceIsFoundForMentionedTid() {
|
||||
var services = Services.default()
|
||||
let jwksMockConfig = JWKSConfig(jwks: "https://mockurl.com", following: "jwks_mock_uri_key")
|
||||
services.register(jwksMockConfig)
|
||||
services.register(JWKSMockClient.self)
|
||||
try? services.register(JWTProvider(RSAService.self))
|
||||
|
||||
var config = Config.default()
|
||||
config.prefer(JWKSMockClient.self, for: Client.self)
|
||||
|
||||
let app = try! Application(config: config, services: services)
|
||||
|
||||
XCTAssertThrowsError(try app.make(JWKSService.self)
|
||||
.rsaService(forKey: "some_unknown_tid")
|
||||
.wait())
|
||||
}
|
||||
}
|
||||
//import XCTest
|
||||
//import Vapor
|
||||
//import JWTVapor
|
||||
//
|
||||
//final class JWKSServiceTests: XCTestCase {
|
||||
//
|
||||
// static let allTests = [
|
||||
// ("testItThrowsErrorIfJWKSConfigIsNotRegisteredAsAService", testItThrowsErrorIfJWKSConfigIsNotRegisteredAsAService),
|
||||
// ("testItDoesNotThrowErrorIfJWKSConfigIsRegisteredAsAService", testItDoesNotThrowErrorIfJWKSConfigIsRegisteredAsAService),
|
||||
// ("testItReturnsAnRSAServiceForMentionedTid", testItReturnsAnRSAServiceForMentionedTid),
|
||||
// ("testItThrowsAnErrorIfNoRSAServiceIsFoundForMentionedTid", testItThrowsAnErrorIfNoRSAServiceIsFoundForMentionedTid)
|
||||
// ]
|
||||
//
|
||||
// func testItThrowsErrorIfJWKSConfigIsNotRegisteredAsAService() {
|
||||
// var services = Services()
|
||||
// try? services.register(JWTProvider(RSAService.self))
|
||||
// let app = try? Application(services: services)
|
||||
//
|
||||
// XCTAssertThrowsError(try app?.make(JWKSService.self))
|
||||
// }
|
||||
//
|
||||
// func testItDoesNotThrowErrorIfJWKSConfigIsRegisteredAsAService() {
|
||||
// var services = Services()
|
||||
// let jwksMockConfig = JWKSConfig(jwks: "https://mockurl.com", following: "jwks_mock_uri_key")
|
||||
// services.register(jwksMockConfig)
|
||||
// try? services.register(JWTProvider(RSAService.self))
|
||||
// let app = try? Application(services: services)
|
||||
//
|
||||
// XCTAssertNoThrow(try app?.make(JWKSService.self))
|
||||
// }
|
||||
//
|
||||
// func testItReturnsAnRSAServiceForMentionedTid() {
|
||||
// var services = Services.default()
|
||||
// let jwksMockConfig = JWKSConfig(jwks: "https://mockurl.com", following: "jwks_mock_uri_key")
|
||||
// services.register(jwksMockConfig)
|
||||
// services.register(JWKSMockClient.self)
|
||||
// try? services.register(JWTProvider(RSAService.self))
|
||||
//
|
||||
// var config = Config.default()
|
||||
// config.prefer(JWKSMockClient.self, for: Client.self)
|
||||
//
|
||||
// let app = try! Application(config: config, services: services)
|
||||
//
|
||||
// do {
|
||||
// let rsaService = try app.make(JWKSService.self)
|
||||
// .rsaService(forKey: "7_Zuf1tvkwLxYaHS3q6lUjUYIGw")
|
||||
// .wait()
|
||||
//
|
||||
// XCTAssertEqual(rsaService.signer.algorithm.jwtAlgorithmName, "RS256")
|
||||
//
|
||||
// } catch {
|
||||
//
|
||||
// XCTFail("Failed with exception \(error)")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func testItThrowsAnErrorIfNoRSAServiceIsFoundForMentionedTid() {
|
||||
// var services = Services.default()
|
||||
// let jwksMockConfig = JWKSConfig(jwks: "https://mockurl.com", following: "jwks_mock_uri_key")
|
||||
// services.register(jwksMockConfig)
|
||||
// services.register(JWKSMockClient.self)
|
||||
// try? services.register(JWTProvider(RSAService.self))
|
||||
//
|
||||
// var config = Config.default()
|
||||
// config.prefer(JWKSMockClient.self, for: Client.self)
|
||||
//
|
||||
// let app = try! Application(config: config, services: services)
|
||||
//
|
||||
// XCTAssertThrowsError(try app.make(JWKSService.self)
|
||||
// .rsaService(forKey: "some_unknown_tid")
|
||||
// .wait())
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import XCTest
|
||||
import Vapor
|
||||
import JWTVapor
|
||||
|
||||
final class JWTProviderTests: XCTestCase {
|
||||
static let allTests = [("testItRegistersJWKSService", testItRegistersJWKSService)]
|
||||
|
||||
func testItRegistersJWKSService() {
|
||||
var services = Services()
|
||||
|
||||
let mockJWKSConfig = JWKSConfig(jwks: "https://mockUrl.com", following: "jwks_mock_uri")
|
||||
services.register(mockJWKSConfig)
|
||||
try? services.register(JWTProvider(RSAService.self))
|
||||
|
||||
let app = try? Application(services: services)
|
||||
let jwksService = try? app?.make(JWKSService.self)
|
||||
|
||||
XCTAssertNotNil(jwksService as Any)
|
||||
}
|
||||
}
|
||||
//import XCTest
|
||||
//import Vapor
|
||||
//import JWTVapor
|
||||
//
|
||||
//final class JWTProviderTests: XCTestCase {
|
||||
// static let allTests = [("testItRegistersJWKSService", testItRegistersJWKSService)]
|
||||
//
|
||||
// func testItRegistersJWKSService() {
|
||||
// var services = Services()
|
||||
//
|
||||
// let mockJWKSConfig = JWKSConfig(jwks: "https://mockUrl.com", following: "jwks_mock_uri")
|
||||
// services.register(mockJWKSConfig)
|
||||
// try? services.register(JWTProvider(RSAService.self))
|
||||
//
|
||||
// let app = try? Application(services: services)
|
||||
// let jwksService = try? app?.make(JWKSService.self)
|
||||
//
|
||||
// XCTAssertNotNil(jwksService as Any)
|
||||
// }
|
||||
//}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import XCTest
|
||||
|
||||
import JWTVaporTests
|
||||
import JWTKitVaporTests
|
||||
|
||||
var tests = [XCTestCaseEntry]()
|
||||
tests += JWTVaporTests.allTests()
|
||||
|
|
Loading…
Reference in New Issue