diff --git a/Sources/CredentialsJWT/CredentialsJWT.swift b/Sources/CredentialsJWT/CredentialsJWT.swift index d72c680..c958cac 100644 --- a/Sources/CredentialsJWT/CredentialsJWT.swift +++ b/Sources/CredentialsJWT/CredentialsJWT.swift @@ -27,8 +27,10 @@ import LoggerAPI A plugin for Kitura-Credentials supporting authentication using [JSON Web Tokens](https://jwt.io/). This plugin requires that the following HTTP headers are present on a request: - - `X-token-type`: must be `JWT` - - `Authorization`: the JWT string, optionally prefixed with `Bearer`. + - `Authorization`: the JWT string, optionally prefixed with `Bearer` + + If you wish to use multiple Credentials plugins, then additionally the header: + - `X-token-type`: must equal `JWT`. The [Swift-JWT](https://github.com/IBM-Swift/Swift-JWT) library is used to decode JWT strings. To successfully decode it, you must specify the `Claims` that will @@ -169,6 +171,10 @@ public class CredentialsJWT: CredentialsPluginProtocol { /// Authenticate incoming request using a JWT. /// + /// Behaviour depends on the presence (and value) of the `X-token-type` header: + /// - `X-token-type: JWT`: Expects a valid JWT string in the `Authorization` header. + /// - no `X-token-type` header: Attempts to extract a valid JWT string from the `Authorization` header, but will defer to other plugins (rather than failing authentication). + /// /// - Parameter request: The `RouterRequest` object used to get information /// about the request. /// - Parameter response: The `RouterResponse` object used to respond to the @@ -185,8 +191,9 @@ public class CredentialsJWT: CredentialsPluginProtocol { onFailure: @escaping (HTTPStatusCode?, [String:String]?) -> Void, onPass: @escaping (HTTPStatusCode?, [String:String]?) -> Void, inProgress: @escaping () -> Void) { - - if let type = request.headers["X-token-type"], type == name { + + let noTokenType = (request.headers["X-token-type"] == nil) + if noTokenType || request.headers["X-token-type"] == .some(self.name) { if let rawToken = request.headers["Authorization"] { if rawToken.hasPrefix("Bearer") { let rawTokenParts = rawToken.split(separator: " ", maxSplits: 2) @@ -243,14 +250,25 @@ public class CredentialsJWT: CredentialsPluginProtocol { self.usersCache?.setObject(newCacheElement, forKey: key) onSuccess(userProfile) } catch { - Log.info("JWT can't be verified: \(error)") - onFailure(nil, nil) + // Authorization header did not contain a valid JWT + if (noTokenType) { + // No X-token-type header: Allow other plugins to attempt to authenticate the Authorization header + onPass(nil, nil) + } else { + Log.info("JWT can't be verified: \(error)") + onFailure(nil, nil) + } } } else { // No Authorization header - Log.debug("Missing authorization header") - onFailure(nil, nil) + if (noTokenType) { + // No X-token-type header: Allow other plugins to authenticate + onPass(nil, nil) + } else { + Log.debug("Missing authorization header") + onFailure(nil, nil) + } } } else { diff --git a/Sources/CredentialsJWT/TypeSafeJWT.swift b/Sources/CredentialsJWT/TypeSafeJWT.swift index ec86f9a..1120b7f 100644 --- a/Sources/CredentialsJWT/TypeSafeJWT.swift +++ b/Sources/CredentialsJWT/TypeSafeJWT.swift @@ -102,22 +102,30 @@ extension JWT: TypeSafeCredentials { } } + /// Attempts to authenticate a request with an Authorization header containing a JWT. + /// The possible outcomes depend on the `X-token-type` header: + /// - If `X-token-type` is set to `JWT`, then this will either succeed or fail. + /// - If `X-token-type` is set to another value, this will pass (defer to other type-safe + /// middlewares in the case of multi-auth). + /// - If `X-token-type` is not set, then this and will either succeed or pass. public static func authenticate(request: RouterRequest, response: RouterResponse, onSuccess: @escaping (JWT) -> Void, onFailure: @escaping (HTTPStatusCode?, [String : String]?) -> Void, onSkip: @escaping (HTTPStatusCode?, [String : String]?) -> Void) { - // Check whether this request declares that a Google token is being supplied - guard let type = request.headers["X-token-type"], type == "JWT" else { + // Check whether this request declares that a JWT is being supplied. + let tokenHeader = request.headers["X-token-type"] + let noTokenType = (tokenHeader == nil) + if let tokenHeader = tokenHeader, tokenHeader != "JWT" { return onSkip(nil, nil) } // Check whether a token has been supplied guard let authHeader = request.headers["Authorization"] else { - return onFailure(nil, nil) + return noTokenType ? onSkip(nil, nil) : onFailure(nil, nil) } // Unpack the token from the header let authParts = authHeader.split(separator: " ", maxSplits: 2) guard authParts.count == 2, authParts[0] == "Bearer" else { - return onFailure(nil, nil) + return noTokenType ? onSkip(nil, nil) : onFailure(nil, nil) } let token = String(authParts[1]) @@ -129,7 +137,7 @@ extension JWT: TypeSafeCredentials { guard let verifier = TypeSafeJWT.verifier, let jwt = try? JWT(jwtString: token, verifier: verifier) else { - return onFailure(nil, nil) + return noTokenType ? onSkip(nil, nil) : onFailure(nil, nil) } saveInCache(profile: jwt, token: token) onSuccess(jwt) diff --git a/Tests/CredentialsJWTTests/CredentialsJWTTests.swift b/Tests/CredentialsJWTTests/CredentialsJWTTest.swift similarity index 100% rename from Tests/CredentialsJWTTests/CredentialsJWTTests.swift rename to Tests/CredentialsJWTTests/CredentialsJWTTest.swift diff --git a/Tests/CredentialsJWTTests/Middlewares/MyDelegate.swift b/Tests/CredentialsJWTTests/Middlewares/MyDelegate.swift new file mode 100644 index 0000000..bb69944 --- /dev/null +++ b/Tests/CredentialsJWTTests/Middlewares/MyDelegate.swift @@ -0,0 +1,14 @@ +import Credentials + +// A UserProfileDelegate for the route accessed in testDelegateToken. Custom claims +// 'fullName' and 'email' are applied to the UserProfile 'displayName' and 'emails' +// fields. +struct MyDelegate: UserProfileDelegate { + func update(userProfile: UserProfile, from dictionary: [String:Any]) { + // `userProfile.id` already contains `id` + userProfile.displayName = dictionary["fullName"]! as! String + let email = UserProfile.UserProfileEmail(value: dictionary["email"]! as! String, type: "home") + userProfile.emails = [email] + } +} + diff --git a/Tests/CredentialsJWTTests/GoogleToken.swift b/Tests/CredentialsJWTTests/Middlewares/TestGoogleToken.swift similarity index 60% rename from Tests/CredentialsJWTTests/GoogleToken.swift rename to Tests/CredentialsJWTTests/Middlewares/TestGoogleToken.swift index cc1778b..1a90408 100644 --- a/Tests/CredentialsJWTTests/GoogleToken.swift +++ b/Tests/CredentialsJWTTests/Middlewares/TestGoogleToken.swift @@ -20,23 +20,20 @@ import LoggerAPI import Credentials import Foundation -// CLASS TAKEN FROM CREDENTIALS GOOGLE AND USED ONLY IN TESTS. -public class CredentialsGoogleToken: CredentialsPluginProtocol { +// Simplified copy of CredentialsGoogleToken, only used in tests. +class TestCredentialsGoogleToken: CredentialsPluginProtocol { - public var usersCache: NSCache? + var usersCache: NSCache? - public var name: String { - return "GoogleToken" - } + var name: String { return "GoogleToken" } - public var redirecting: Bool { - return false - } - public func authenticate(request: RouterRequest, response: RouterResponse, - options: [String:Any], onSuccess: @escaping (UserProfile) -> Void, - onFailure: @escaping (HTTPStatusCode?, [String:String]?) -> Void, - onPass: @escaping (HTTPStatusCode?, [String:String]?) -> Void, - inProgress: @escaping () -> Void) { + var redirecting: Bool { return false } + + func authenticate(request: RouterRequest, response: RouterResponse, + options: [String:Any], onSuccess: @escaping (UserProfile) -> Void, + onFailure: @escaping (HTTPStatusCode?, [String:String]?) -> Void, + onPass: @escaping (HTTPStatusCode?, [String:String]?) -> Void, + inProgress: @escaping () -> Void) { if let type = request.headers["X-token-type"], type == name { if request.headers["access_token"] != nil { let googleProfile = UserProfile(id: "TestGoogle", displayName: "TestGoogle", provider: "GoogleToken") diff --git a/Tests/CredentialsJWTTests/Middlewares/TestHTTPBasic.swift b/Tests/CredentialsJWTTests/Middlewares/TestHTTPBasic.swift new file mode 100644 index 0000000..a8a71be --- /dev/null +++ b/Tests/CredentialsJWTTests/Middlewares/TestHTTPBasic.swift @@ -0,0 +1,146 @@ +/** + * Copyright IBM Corporation 2016-2019 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import Kitura +import KituraNet +import Credentials + +import Foundation + +// Simplified copy of CredentialsHTTP, only used in tests. +class TestCredentialsHTTPBasic : CredentialsPluginProtocol { + + var name: String { return "HTTPBasic" } + + var redirecting: Bool { return false } + + var usersCache: NSCache? + + typealias VerifyPassword = (String, String, @escaping (UserProfile?) -> Void) -> Void + private var verifyPassword: VerifyPassword + + init (verifyPassword: @escaping VerifyPassword, realm: String?=nil) { + self.verifyPassword = verifyPassword + } + + func authenticate (request: RouterRequest, response: RouterResponse, + options: [String:Any], onSuccess: @escaping (UserProfile) -> Void, + onFailure: @escaping (HTTPStatusCode?, [String:String]?) -> Void, + onPass: @escaping (HTTPStatusCode?, [String:String]?) -> Void, + inProgress: @escaping () -> Void) { + + var authorization : String + if let user = request.urlURL.user, let password = request.urlURL.password { + authorization = user + ":" + password + } + else { + let options = Data.Base64DecodingOptions(rawValue: 0) + + guard let authorizationHeader = request.headers["Authorization"] else { + onPass(.unauthorized, ["WWW-Authenticate" : "Basic realm=\"Users\""]) + return + } + + let authorizationHeaderComponents = authorizationHeader.components(separatedBy: " ") + guard authorizationHeaderComponents.count == 2, + authorizationHeaderComponents[0] == "Basic", + let decodedData = Data(base64Encoded: authorizationHeaderComponents[1], options: options), + let userAuthorization = String(data: decodedData, encoding: .utf8) else { + onPass(.unauthorized, ["WWW-Authenticate" : "Basic realm=\"Users\""]) + return + } + + authorization = userAuthorization as String + } + + let credentials = authorization.split(separator: ":", maxSplits: 1) + guard credentials.count == 2 else { + onFailure(.badRequest, nil) + return + } + + let userid = String(credentials[0]) + let password = String(credentials[1]) + + verifyPassword(userid, password) { userProfile in + if let userProfile = userProfile { + onSuccess(userProfile) + } + else { + onFailure(.unauthorized, ["WWW-Authenticate" : "Basic realm=\"Users\""]) + } + } + } +} + +protocol TypeSafeHTTPBasic : TypeSafeCredentials { + static var realm: String { get } + static func verifyPassword(username: String, password: String, callback: @escaping (Self?) -> Void) -> Void +} + +extension TypeSafeHTTPBasic { + public var provider: String { return "HTTPBasic" } + public static var realm: String { return "User" } + + public static func authenticate(request: RouterRequest, response: RouterResponse, onSuccess: @escaping (Self) -> Void, onFailure: @escaping (HTTPStatusCode?, [String : String]?) -> Void, onSkip: @escaping (HTTPStatusCode?, [String : String]?) -> Void) { + + let userid: String + let password: String + if let requestUser = request.urlURL.user, let requestPassword = request.urlURL.password { + userid = requestUser + password = requestPassword + } else { + guard let authorizationHeader = request.headers["Authorization"] else { + return onSkip(.unauthorized, ["WWW-Authenticate" : "Basic realm=\"" + realm + "\""]) + } + + let authorizationHeaderComponents = authorizationHeader.components(separatedBy: " ") + guard authorizationHeaderComponents.count == 2, + authorizationHeaderComponents[0] == "Basic", + let decodedData = Data(base64Encoded: authorizationHeaderComponents[1], options: Data.Base64DecodingOptions(rawValue: 0)), + let userAuthorization = String(data: decodedData, encoding: .utf8) else { + return onSkip(.unauthorized, ["WWW-Authenticate" : "Basic realm=\"" + realm + "\""]) + } + let credentials = userAuthorization.components(separatedBy: ":") + guard credentials.count >= 2 else { + return onFailure(.badRequest, nil) + } + userid = credentials[0] + password = credentials[1] + } + + verifyPassword(username: userid, password: password) { selfInstance in + if let selfInstance = selfInstance { + onSuccess(selfInstance) + } else { + onFailure(.unauthorized, ["WWW-Authenticate" : "Basic realm=\"" + self.realm + "\""]) + } + } + } +} + +// Trivial implementation of a type-safe basic authentication that only allows +// the user "John" with password "12345". +struct TestBasicAuthedUser: TypeSafeHTTPBasic { + static func verifyPassword(username: String, password: String, callback: @escaping (TestBasicAuthedUser?) -> Void) { + if username == "John" && password == "12345" { + callback(TestBasicAuthedUser(id: username)) + } else { + callback(nil) + } + } + var id: String +} diff --git a/Tests/CredentialsJWTTests/Middlewares/TestMultiAuth.swift b/Tests/CredentialsJWTTests/Middlewares/TestMultiAuth.swift new file mode 100644 index 0000000..a5162b8 --- /dev/null +++ b/Tests/CredentialsJWTTests/Middlewares/TestMultiAuth.swift @@ -0,0 +1,40 @@ +/** + * Copyright IBM Corporation 2019 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import SwiftJWT +import Credentials +import CredentialsJWT + +// Trivial multi-auth credentials that authenticates using either JWT or Basic +// credentials. +struct TestMultiAuth: TypeSafeMultiCredentials { + var id: String + var provider: String + var profile: JWT? + + static var authenticationMethods: [TypeSafeCredentials.Type] = [JWT.self, TestBasicAuthedUser.self] + + init(successfulAuth: TypeSafeCredentials) { + self.id = successfulAuth.id + self.provider = successfulAuth.provider + switch(successfulAuth.self) { + case let jwt as JWT: + self.profile = jwt + default: + self.profile = nil + } + } +} diff --git a/Tests/CredentialsJWTTests/Models/TestClaims.swift b/Tests/CredentialsJWTTests/Models/TestClaims.swift new file mode 100644 index 0000000..7ee2bb9 --- /dev/null +++ b/Tests/CredentialsJWTTests/Models/TestClaims.swift @@ -0,0 +1,56 @@ +/** + * Copyright IBM Corporation 2019 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +import SwiftJWT + +// A claims structure that will be used for the tests. The `sub` claim holds the users name. +struct TestClaims: Claims, Equatable { + + var sub: String + + // Testing requirement: Equatable + static func == (lhs: TestClaims, rhs: TestClaims) -> Bool { + return + lhs.sub == rhs.sub + } +} + +// An alternate claims structure that will be used for the tests. +// The `username` holds the user's identity. +struct TestAlternateClaims: Claims, Equatable { + + var username: String + + // Testing requirement: Equatable + static func == (lhs: TestAlternateClaims, rhs: TestAlternateClaims) -> Bool { + return + lhs.username == rhs.username + } +} + +// A claims structure containing custom claims that should be extracted as part of the +// UserProfile. The `id` holds the user's identity. +struct TestDelegateClaims: Claims, Equatable { + let id: Int + let fullName: String + let email: String + + // Testing requirement: Equatable + static func == (lhs: TestDelegateClaims, rhs: TestDelegateClaims) -> Bool { + return + lhs.id == rhs.id && lhs.fullName == rhs.fullName && lhs.email == rhs.email + } +} diff --git a/Tests/CredentialsJWTTests/Models/User.swift b/Tests/CredentialsJWTTests/Models/User.swift new file mode 100644 index 0000000..44d6f2d --- /dev/null +++ b/Tests/CredentialsJWTTests/Models/User.swift @@ -0,0 +1,10 @@ +// A User structure that can be passed into the generate jwt route. +struct User: Codable { + let name: String + let email: String? + init(name: String, email: String? = nil) { + self.name = name + self.email = email + } +} + diff --git a/Tests/CredentialsJWTTests/TestRawRouteJWT.swift b/Tests/CredentialsJWTTests/TestRawRouteJWT.swift index 9af1696..24e709f 100644 --- a/Tests/CredentialsJWTTests/TestRawRouteJWT.swift +++ b/Tests/CredentialsJWTTests/TestRawRouteJWT.swift @@ -25,59 +25,6 @@ import Dispatch @testable import CredentialsJWT - -// A claims structure that will be used for the tests. -// The `sub` claim holds the user's identity. -struct TestClaims: Claims, Equatable { - - var sub: String - - // Testing requirement: Equatable - static func == (lhs: TestClaims, rhs: TestClaims) -> Bool { - return - lhs.sub == rhs.sub - } -} - -// An alternate claims structure that will be used for the tests. -// The `username` holds the user's identity. -struct TestAlternateClaims: Claims, Equatable { - - var username: String - - // Testing requirement: Equatable - static func == (lhs: TestAlternateClaims, rhs: TestAlternateClaims) -> Bool { - return - lhs.username == rhs.username - } -} - -// A claims structure containing custom claims that should be extracted as part of the -// UserProfile. The `id` holds the user's identity. -struct TestDelegateClaims: Claims, Equatable { - let id: Int - let fullName: String - let email: String - - // Testing requirement: Equatable - static func == (lhs: TestDelegateClaims, rhs: TestDelegateClaims) -> Bool { - return - lhs.id == rhs.id && lhs.fullName == rhs.fullName && lhs.email == rhs.email - } -} - -// A UserProfileDelegate for the route accessed in testDelegateToken. Custom claims -// 'fullName' and 'email' are applied to the UserProfile 'displayName' and 'emails' -// fields. -struct MyDelegate: UserProfileDelegate { - func update(userProfile: UserProfile, from dictionary: [String:Any]) { - // `userProfile.id` already contains `id` - userProfile.displayName = dictionary["fullName"]! as! String - let email = UserProfile.UserProfileEmail(value: dictionary["email"]! as! String, type: "home") - userProfile.emails = [email] - } -} - // Sets option for CredentialsJWT to allow username to be used instead of sub, used in the alternate // credentials tests. let jwtOptions: [String:Any] = [CredentialsJWTOptions.subject: "username"] @@ -93,16 +40,6 @@ let delegateCredentials = CredentialsJWT(verifier: .hs256(ke class TestRawRouteJWT : XCTestCase { - // A User structure that can be passed into the generate jwt route. - struct User: Codable { - let name: String - let email: String? - init(name: String, email: String? = nil) { - self.name = name - self.email = email - } - } - // Initiliasting the 3 users names and token variables. // The actual String is generated in the setUp function before each test. @@ -135,7 +72,8 @@ class TestRawRouteJWT : XCTestCase { ("testTokenTimeToLive", testTokenTimeToLive), ("testSkipAuthentication", testSkipAuthentication), ("testMissingTokenType", testMissingTokenType), - ("testMissingAccessToken", testMissingAccessToken) + ("testMissingAccessToken", testMissingAccessToken), + ("testPassOnMissingTokenType", testPassOnMissingTokenType), ] } @@ -234,7 +172,6 @@ class TestRawRouteJWT : XCTestCase { return expectation.fulfill() } self.jwtString4 = body - print(self.jwtString4) } catch { XCTFail("Couldn't read string from response") expectation.fulfill() @@ -476,14 +413,14 @@ class TestRawRouteJWT : XCTestCase { } - // Tests that when a request to a raw route that includes this middleware does not - // contain an X-token-type header, the middleware skips authentication and a - // second handler is instead invoked, after which authorization fails. + // Tests that when a request to a raw route that includes this middleware contains + // a valid Authorization header, but does not contain an X-token-type header, the + // middleware attempts authentication anyway and authentication succeeds. func testMissingTokenType() { performServerTest(router: router) { expectation in self.performRequest(method: "get", path: "/rawtokenauth", callback: { response in XCTAssertNotNil(response, "ERROR!!! ClientRequest response object was nil") - XCTAssertEqual(response?.statusCode, HTTPStatusCode.unauthorized, "HTTP Status code was \(String(describing: response?.statusCode))") + XCTAssertEqual(response?.statusCode, HTTPStatusCode.OK, "HTTP Status code was \(String(describing: response?.statusCode))") expectation.fulfill() }, headers: ["Authorization" : "Bearer " + self.jwtString]) } @@ -502,6 +439,31 @@ class TestRawRouteJWT : XCTestCase { } } + // Tests that CredentialsJWT will successfully defer to other plugins after + // speculatively attempting to authenticate a request containing an Authorization + // header but no `X-token-type` header. + // In this case, we have registered CredentialsHTTPBasic after CredentialsJWT. + func testPassOnMissingTokenType() { + guard let httpBasicCredentials = "John:12345".data(using: .utf8)?.base64EncodedString() else { + return XCTFail("Couldn't create credentials string") + } + performServerTest(router: router) { expectation in + self.performRequest(method: "get", path: "/rawtokenauth", callback: { response in + guard let response = response else { + return XCTFail("ERROR!!! ClientRequest response object was nil") + } + XCTAssertEqual(response.statusCode, HTTPStatusCode.OK, "HTTP Status code was \(response.statusCode)") + do { + let responseString = try response.readString() + XCTAssertEqual(responseString, "John") + } catch { + XCTFail("Unable to read response string") + } + expectation.fulfill() + }, headers: ["Authorization" : "Basic " + httpBasicCredentials]) + } + } + // Function that creates the raw routes for the router. static func setupRawRouter() -> Router { let router = Router() @@ -511,8 +473,18 @@ class TestRawRouteJWT : XCTestCase { let altTokenCredentials = Credentials() let delegateTokenCredentials = Credentials() + // Simple verifier that expects test values + let httpBasicVerifier: TestCredentialsHTTPBasic.VerifyPassword = { user, pass, callback in + if user == "John" && pass == "12345" { + callback(UserProfile(id: user, displayName: user, provider: "basic")) + } else { + callback(nil) + } + } + tokenCredentials.register(plugin: jwtCredentials) - tokenCredentials.register(plugin: CredentialsGoogleToken()) + tokenCredentials.register(plugin: TestCredentialsGoogleToken()) + tokenCredentials.register(plugin: TestCredentialsHTTPBasic(verifyPassword: httpBasicVerifier)) altTokenCredentials.register(plugin: altCredentials) delegateTokenCredentials.register(plugin: delegateCredentials) diff --git a/Tests/CredentialsJWTTests/TestTypeSafeJWT.swift b/Tests/CredentialsJWTTests/TestTypeSafeJWT.swift index 6ccd315..0e32cac 100644 --- a/Tests/CredentialsJWTTests/TestTypeSafeJWT.swift +++ b/Tests/CredentialsJWTTests/TestTypeSafeJWT.swift @@ -25,18 +25,6 @@ import LoggerAPI class TestTypeSafeJWT : XCTestCase { - // A claims structure that will be used for the tests. The `sub` claim holds the users name. - struct TestClaims: Claims, Equatable { - - var sub: String - - // Testing requirement: Equatable - static func == (lhs: TestClaims, rhs: TestClaims) -> Bool { - return - lhs.sub == rhs.sub - } - } - // A User structure that can be passed into the generate jwt route. struct User: Codable { var name: String @@ -74,7 +62,10 @@ class TestTypeSafeJWT : XCTestCase { ("testTwoInCache", testTwoInCache), ("testCacheEviction", testCacheEviction), ("testCachedProfile", testCachedProfile), - ("testMissingTokenType", testMissingTokenType), + ("testGoodToken", testGoodToken), + ("testBadToken", testBadToken), + ("testMissingTokenTypeJWT", testMissingTokenTypeJWT), + ("testMissingTokenTypeBasic", testMissingTokenTypeBasic), ("testMissingAccessToken", testMissingAccessToken) ] @@ -102,7 +93,7 @@ class TestTypeSafeJWT : XCTestCase { do { guard let body = try response?.readString(), let responseData = body.data(using: .utf8) else { XCTFail("Couldn't read response") - return + return expectation.fulfill() } self.jwtToken = try JSONDecoder().decode(AccessToken.self, from: responseData) self.jwtString = self.jwtToken.accessToken @@ -125,7 +116,7 @@ class TestTypeSafeJWT : XCTestCase { do { guard let body = try response?.readString(), let responseData = body.data(using: .utf8) else { XCTFail("Couldn't read response") - return + return expectation.fulfill() } do { self.jwtToken2 = try JSONDecoder().decode(AccessToken.self, from: responseData) @@ -153,7 +144,7 @@ class TestTypeSafeJWT : XCTestCase { do { guard let body = try response?.readString(), let responseData = body.data(using: .utf8) else { XCTFail("Couldn't read response") - return + return expectation.fulfill() } do { self.jwtToken3 = try JSONDecoder().decode(AccessToken.self, from: responseData) @@ -262,7 +253,7 @@ class TestTypeSafeJWT : XCTestCase { do { guard let body = try response?.readString(), let profileJSON = body.data(using: .utf8) else { XCTFail("No response body") - return + return expectation.fulfill() } let profile = try JSONDecoder().decode(JWT.self, from: profileJSON) XCTAssertEqual(profile.claims, jwt.claims, "Body \(profile) is not equal to \(jwt)") @@ -277,21 +268,47 @@ class TestTypeSafeJWT : XCTestCase { } } - // Tests that when a request to a Codable route that includes this middleware does not - // contain the matching X-token-type header, the middleware skips authentication and a - // second handler is instead invoked. - func testMissingTokenType() { + // Tests that when a request to a Codable route that includes this middleware receives + // an X-token-type header of 'JWT' and a valid JWT string in the Authorization header, + // authentication succeeds. + func testGoodToken() { performServerTest(router: router) { expectation in - self.performRequest(method: "get", path: "/multipleHandlers", callback: {response in + self.performRequest(method: "get", path: "/singleHandler", callback: {response in + XCTAssertNotNil(response, "ERROR!!! ClientRequest response object was nil") + XCTAssertEqual(response?.statusCode, .OK) + expectation.fulfill() + }, headers: ["Authorization" : "Bearer " + self.jwtString, "X-token-type" : "JWT"]) + } + } + + // Tests that when a request to a Codable route that includes this middleware receives + // an X-token-type header of 'JWT' and an invalid JWT string in the Authorization + // header, authentication fails. + func testBadToken() { + performServerTest(router: router) { expectation in + self.performRequest(method: "get", path: "/singleHandler", callback: {response in + XCTAssertNotNil(response, "ERROR!!! ClientRequest response object was nil") + XCTAssertEqual(response?.statusCode, .unauthorized) + expectation.fulfill() + }, headers: ["Authorization" : "Bearer of bad news", "X-token-type" : "JWT"]) + } + } + + // Tests that when a request is made to a Codable route that includes this middleware + // as one of multiple authentication methods, and the request does not contain the + // X-token-type header, the middleware successfully authenticates a JWT token. + func testMissingTokenTypeJWT() { + performServerTest(router: router) { expectation in + self.performRequest(method: "get", path: "/multipleAuth", callback: {response in XCTAssertNotNil(response, "ERROR!!! ClientRequest response object was nil") XCTAssertEqual(response?.statusCode, HTTPStatusCode.OK, "HTTP Status code was \(String(describing: response?.statusCode))") do { guard let body = try response?.readString(), let profileJSON = body.data(using: .utf8) else { XCTFail("No response body") - return + return expectation.fulfill() } let testResponse = try JSONDecoder().decode(JWT.self, from: profileJSON) - let expectedResponse = JWT(claims: TestClaims(sub: "Test")) + let expectedResponse = JWT(claims: TestClaims(sub: self.testUser.name)) XCTAssertEqual(testResponse.claims, expectedResponse.claims, "Response from second handler did not contain expected data") } catch { XCTFail("Could not decode response: \(error)") @@ -301,12 +318,30 @@ class TestTypeSafeJWT : XCTestCase { } } + // Tests that when a request is made to a Codable route that includes this middleware + // as one of multiple authentication methods, and the request does not contain the + // X-token-type header, the JWT middleware skips and allows Basic auth to proceed. + func testMissingTokenTypeBasic() { + guard let httpBasicCredentials = "John:12345".data(using: .utf8)?.base64EncodedString() else { + return XCTFail("Couldn't create credentials string") + } + performServerTest(router: router) { expectation in + self.performRequest(method: "get", path: "/multipleAuth", callback: {response in + XCTAssertNotNil(response, "ERROR!!! ClientRequest response object was nil") + // For the purposes of detecting which method of authentication was used, we + // expect .accepted from the multipleAuth route with successful basic auth. + XCTAssertEqual(response?.statusCode, HTTPStatusCode.accepted) + expectation.fulfill() + }, headers: ["Authorization" : "Basic " + httpBasicCredentials]) + } + } + // Tests that when a request to a Codable route that includes this middleware contains // the matching X-token-type header, but does not supply 'Authorization', the middleware // fails authentication and returns unauthorized. func testMissingAccessToken() { performServerTest(router: router) { expectation in - self.performRequest(method: "get", path: "/multipleHandlers", callback: {response in + self.performRequest(method: "get", path: "/singleHandler", callback: {response in XCTAssertNotNil(response, "ERROR!!! ClientRequest response object was nil") XCTAssertEqual(response?.statusCode, HTTPStatusCode.unauthorized, "HTTP Status code was \(String(describing: response?.statusCode))") expectation.fulfill() @@ -317,12 +352,13 @@ class TestTypeSafeJWT : XCTestCase { // Function that creates the codable routes for the router. static func setupCodableRouter() -> Router { let router = Router() - + let key = "".data(using: .utf8)! + TypeSafeJWT.verifier = .hs256(key: key) + // Route that generates a jwt from a given User's name. router.post("/generatejwt") { (user: User, respondWith: (AccessToken?, RequestError?) -> Void) in var jwt = JWT(claims: TestClaims(sub: user.name)) - guard let key = "".data(using: .utf8), - let signedJWT = try? jwt.sign(using: .hs256(key: key)) + guard let signedJWT = try? jwt.sign(using: .hs256(key: key)) else { return respondWith(nil, .internalServerError) } @@ -333,14 +369,15 @@ class TestTypeSafeJWT : XCTestCase { respondWith(profile, nil) } - router.get("/multipleHandlers") { (profile: JWT, respondWith: (JWT?, RequestError?) -> Void) in + router.get("/multipleAuth") { (auth: TestMultiAuth, respondWith: (JWT?, RequestError?) -> Void) in + guard let profile = auth.profile else { + // Indicate that request was successful, using .accepted to indicate + // that basic authentication was used instead of JWT. + return respondWith(nil, .accepted) + } respondWith(profile, nil) } - router.get("/multipleHandlers") { (respondWith: (JWT?, RequestError?) -> Void) in - respondWith(JWT(claims: TestClaims(sub: "Test")), nil) - } - return router } diff --git a/docs/Classes.html b/docs/Classes.html index 8eacff9..36e8220 100644 --- a/docs/Classes.html +++ b/docs/Classes.html @@ -111,8 +111,13 @@

Classes

This plugin requires that the following HTTP headers are present on a request:

    -
  • X-token-type: must be JWT
  • -
  • Authorization: the JWT string, optionally prefixed with Bearer.
  • +
  • Authorization: the JWT string, optionally prefixed with Bearer
  • +
+ +

If you wish to use multiple Credentials plugins, then additionally the header:

+ +
    +
  • X-token-type: must equal JWT.

The Swift-JWT library is used to @@ -211,7 +216,7 @@

Declaration

diff --git a/docs/Classes/CredentialsJWT.html b/docs/Classes/CredentialsJWT.html index 74b9534..81770b9 100644 --- a/docs/Classes/CredentialsJWT.html +++ b/docs/Classes/CredentialsJWT.html @@ -95,8 +95,13 @@

CredentialsJWT

This plugin requires that the following HTTP headers are present on a request:

    -
  • X-token-type: must be JWT
  • -
  • Authorization: the JWT string, optionally prefixed with Bearer.
  • +
  • Authorization: the JWT string, optionally prefixed with Bearer
  • +
+ +

If you wish to use multiple Credentials plugins, then additionally the header:

+ +
    +
  • X-token-type: must equal JWT.

The Swift-JWT library is used to @@ -418,6 +423,13 @@

Declaration

Authenticate incoming request using a JWT.

+

Behaviour depends on the presence (and value) of the X-token-type header:

+ +
    +
  • X-token-type: JWT: Expects a valid JWT string in the Authorization header.
  • +
  • no X-token-type header: Attempts to extract a valid JWT string from the Authorization header, but will defer to other plugins (rather than failing authentication).

  • +
+

Declaration

@@ -444,7 +456,7 @@

Parameters

The RouterRequest object used to get information - about the request.

+ about the request.

@@ -457,7 +469,7 @@

Parameters

The RouterResponse object used to respond to the - request.

+ request.

@@ -506,7 +518,7 @@

Parameters

The closure to invoke when the plugin doesn’t recognize the - authentication token in the request.

+ authentication token in the request.

@@ -519,7 +531,7 @@

Parameters

The closure to invoke to cause a redirect to the login page in the - case of redirecting authentication.

+ case of redirecting authentication.

@@ -537,7 +549,7 @@

Parameters

diff --git a/docs/Extensions.html b/docs/Extensions.html index d86e29f..d6b4867 100644 --- a/docs/Extensions.html +++ b/docs/Extensions.html @@ -128,7 +128,7 @@

Declaration

diff --git a/docs/Extensions/JWT.html b/docs/Extensions/JWT.html index 1d5ee10..0bee210 100644 --- a/docs/Extensions/JWT.html +++ b/docs/Extensions/JWT.html @@ -155,9 +155,9 @@

Declaration

  • @@ -165,7 +165,16 @@

    Declaration

    - +

    Attempts to authenticate a request with an Authorization header containing a JWT. +The possible outcomes depend on the X-token-type header:

    + +
      +
    • If X-token-type is set to JWT, then this will either succeed or fail.
    • +
    • If X-token-type is set to another value, this will pass (defer to other type-safe +middlewares in the case of multi-auth).
    • +
    • If X-token-type is not set, then this and will either succeed or pass.
    • +
    +

    Declaration

    @@ -189,7 +198,7 @@

    Declaration

    diff --git a/docs/Structs.html b/docs/Structs.html index b1fcfa6..0701f22 100644 --- a/docs/Structs.html +++ b/docs/Structs.html @@ -163,7 +163,7 @@

    Declaration

    diff --git a/docs/Structs/CredentialsJWTOptions.html b/docs/Structs/CredentialsJWTOptions.html index 994c039..5fddd6a 100644 --- a/docs/Structs/CredentialsJWTOptions.html +++ b/docs/Structs/CredentialsJWTOptions.html @@ -161,7 +161,7 @@

    Declaration

    diff --git a/docs/Structs/TypeSafeJWT.html b/docs/Structs/TypeSafeJWT.html index dff828d..60c04a8 100644 --- a/docs/Structs/TypeSafeJWT.html +++ b/docs/Structs/TypeSafeJWT.html @@ -191,7 +191,7 @@

    Declaration

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Classes.html b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Classes.html index 8eacff9..36e8220 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Classes.html +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Classes.html @@ -111,8 +111,13 @@

    Classes

    This plugin requires that the following HTTP headers are present on a request:

      -
    • X-token-type: must be JWT
    • -
    • Authorization: the JWT string, optionally prefixed with Bearer.
    • +
    • Authorization: the JWT string, optionally prefixed with Bearer
    • +
    + +

    If you wish to use multiple Credentials plugins, then additionally the header:

    + +
      +
    • X-token-type: must equal JWT.

    The Swift-JWT library is used to @@ -211,7 +216,7 @@

    Declaration

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Classes/CredentialsJWT.html b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Classes/CredentialsJWT.html index 74b9534..81770b9 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Classes/CredentialsJWT.html +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Classes/CredentialsJWT.html @@ -95,8 +95,13 @@

    CredentialsJWT

    This plugin requires that the following HTTP headers are present on a request:

      -
    • X-token-type: must be JWT
    • -
    • Authorization: the JWT string, optionally prefixed with Bearer.
    • +
    • Authorization: the JWT string, optionally prefixed with Bearer
    • +
    + +

    If you wish to use multiple Credentials plugins, then additionally the header:

    + +
      +
    • X-token-type: must equal JWT.

    The Swift-JWT library is used to @@ -418,6 +423,13 @@

    Declaration

    Authenticate incoming request using a JWT.

    +

    Behaviour depends on the presence (and value) of the X-token-type header:

    + +
      +
    • X-token-type: JWT: Expects a valid JWT string in the Authorization header.
    • +
    • no X-token-type header: Attempts to extract a valid JWT string from the Authorization header, but will defer to other plugins (rather than failing authentication).

    • +
    +

    Declaration

    @@ -444,7 +456,7 @@

    Parameters

    The RouterRequest object used to get information - about the request.

    + about the request.

    @@ -457,7 +469,7 @@

    Parameters

    The RouterResponse object used to respond to the - request.

    + request.

    @@ -506,7 +518,7 @@

    Parameters

    The closure to invoke when the plugin doesn’t recognize the - authentication token in the request.

    + authentication token in the request.

    @@ -519,7 +531,7 @@

    Parameters

    The closure to invoke to cause a redirect to the login page in the - case of redirecting authentication.

    + case of redirecting authentication.

    @@ -537,7 +549,7 @@

    Parameters

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Extensions.html b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Extensions.html index d86e29f..d6b4867 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Extensions.html +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Extensions.html @@ -128,7 +128,7 @@

    Declaration

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Extensions/JWT.html b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Extensions/JWT.html index 1d5ee10..0bee210 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Extensions/JWT.html +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Extensions/JWT.html @@ -155,9 +155,9 @@

    Declaration

  • @@ -165,7 +165,16 @@

    Declaration

    - +

    Attempts to authenticate a request with an Authorization header containing a JWT. +The possible outcomes depend on the X-token-type header:

    + +
      +
    • If X-token-type is set to JWT, then this will either succeed or fail.
    • +
    • If X-token-type is set to another value, this will pass (defer to other type-safe +middlewares in the case of multi-auth).
    • +
    • If X-token-type is not set, then this and will either succeed or pass.
    • +
    +

    Declaration

    @@ -189,7 +198,7 @@

    Declaration

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs.html b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs.html index b1fcfa6..0701f22 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs.html +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs.html @@ -163,7 +163,7 @@

    Declaration

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs/CredentialsJWTOptions.html b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs/CredentialsJWTOptions.html index 994c039..5fddd6a 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs/CredentialsJWTOptions.html +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs/CredentialsJWTOptions.html @@ -161,7 +161,7 @@

    Declaration

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs/TypeSafeJWT.html b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs/TypeSafeJWT.html index dff828d..60c04a8 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs/TypeSafeJWT.html +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/Structs/TypeSafeJWT.html @@ -191,7 +191,7 @@

    Declaration

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/index.html b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/index.html index 405cc3b..d5f8fe8 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/index.html +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/index.html @@ -230,7 +230,7 @@

    License

    diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/search.json b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/search.json index 26d466c..fc72baa 100644 --- a/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/search.json +++ b/docs/docsets/CredentialsJWT.docset/Contents/Resources/Documents/search.json @@ -1 +1 @@ -{"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V8verifier05SwiftB011JWTVerifierVSgvpZ":{"name":"verifier","abstract":"

    The verifier to use when verifying tokens. This must be configured before tokens can be successfully authenticated,","parent_name":"TypeSafeJWT"},"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V9cacheSizeSivpZ":{"name":"cacheSize","abstract":"

    The maximum size of the token cache. Defaults to 0, which is unlimited.

    ","parent_name":"TypeSafeJWT"},"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V15tokenTimeToLiveSdSgvpZ":{"name":"tokenTimeToLive","abstract":"

    The length of time this token should be deemed valid before it must be verified again. Defaults to nil, which is unlimited.

    ","parent_name":"TypeSafeJWT"},"Structs/CredentialsJWTOptions.html#/s:14CredentialsJWT0A10JWTOptionsV7subjectSSvpZ":{"name":"subject","abstract":"

    Determines the JWT claim that will be used for the identity of the user, (default is ‘sub’).

    ","parent_name":"CredentialsJWTOptions"},"Structs/CredentialsJWTOptions.html#/s:14CredentialsJWT0A10JWTOptionsV19userProfileDelegateSSvpZ":{"name":"userProfileDelegate","abstract":"

    An implementation of Credentials.UserProfileDelegate to update user profile.

    ","parent_name":"CredentialsJWTOptions"},"Structs/CredentialsJWTOptions.html":{"name":"CredentialsJWTOptions","abstract":"

    A list of options for authentication with JWT.

    "},"Structs/TypeSafeJWT.html":{"name":"TypeSafeJWT","abstract":"

    Represents the configuration for TypeSafeJWT authenication: the verification method, and the cache parameters."},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E2idSSvp":{"name":"id","abstract":"

    Note: This field does not apply to Type-safe JWT credentials. Use the JWT claims instead.

    ","parent_name":"JWT"},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E8providerSSvp":{"name":"provider","abstract":"

    Answers JWT.

    ","parent_name":"JWT"},"Extensions/JWT.html#/s:11Credentials08TypeSafeA0P12authenticate7request8response9onSuccess0G7Failure0G4Skipy6Kitura13RouterRequestC_AJ0L8ResponseCyxcy0K3Net14HTTPStatusCodeOSg_SDyS2SGSgtcyAR_ATtctFZ":{"name":"authenticate(request:response:onSuccess:onFailure:onSkip:)","parent_name":"JWT"},"Extensions/JWT.html":{"name":"JWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC4nameSSvp":{"name":"name","abstract":"

    The name of the plugin: JWT.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC11redirectingSbvp":{"name":"redirecting","abstract":"

    An indication as to whether the plugin is redirecting or not. This plugin is not redirecting.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC15tokenTimeToLiveSdSgvp":{"name":"tokenTimeToLive","abstract":"

    The time in seconds since the user profile was generated that the access token will be considered valid","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC19userProfileDelegate0A004UserdE0_pSgvp":{"name":"userProfileDelegate","abstract":"

    A delegate for UserProfile manipulation. Use this to further populate the profile using","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC8verifier7options15tokenTimeToLiveAByxG05SwiftB011JWTVerifierV_SDySSypGSgSdSgtcfc":{"name":"init(verifier:options:tokenTimeToLive:)","abstract":"

    Initialize a CredentialsJWT instance. Upon first receipt, a JWT will be verified to ensure the signature is valid,","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC10usersCacheSo7NSCacheCySo8NSStringC0A004BaseD7ElementCGSgvp":{"name":"usersCache","abstract":"

    User profile cache.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC12authenticate7request8response7options9onSuccess0G7Failure0G4Pass10inProgressy6Kitura13RouterRequestC_AK0N8ResponseCSDySSypGy0A011UserProfileCcy0M3Net14HTTPStatusCodeOSg_SDyS2SGSgtcyAW_AYtcyyctF":{"name":"authenticate(request:response:options:onSuccess:onFailure:onPass:inProgress:)","abstract":"

    Authenticate incoming request using a JWT.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html":{"name":"CredentialsJWT","abstract":"

    A plugin for Kitura-Credentials supporting authentication using JSON Web Tokens.

    "},"Classes.html":{"name":"Classes","abstract":"

    The following classes are available globally.

    "},"Extensions.html":{"name":"Extensions","abstract":"

    The following extensions are available globally.

    "},"Structs.html":{"name":"Structures","abstract":"

    The following structures are available globally.

    "}} \ No newline at end of file +{"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V8verifier05SwiftB011JWTVerifierVSgvpZ":{"name":"verifier","abstract":"

    The verifier to use when verifying tokens. This must be configured before tokens can be successfully authenticated,","parent_name":"TypeSafeJWT"},"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V9cacheSizeSivpZ":{"name":"cacheSize","abstract":"

    The maximum size of the token cache. Defaults to 0, which is unlimited.

    ","parent_name":"TypeSafeJWT"},"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V15tokenTimeToLiveSdSgvpZ":{"name":"tokenTimeToLive","abstract":"

    The length of time this token should be deemed valid before it must be verified again. Defaults to nil, which is unlimited.

    ","parent_name":"TypeSafeJWT"},"Structs/CredentialsJWTOptions.html#/s:14CredentialsJWT0A10JWTOptionsV7subjectSSvpZ":{"name":"subject","abstract":"

    Determines the JWT claim that will be used for the identity of the user, (default is ‘sub’).

    ","parent_name":"CredentialsJWTOptions"},"Structs/CredentialsJWTOptions.html#/s:14CredentialsJWT0A10JWTOptionsV19userProfileDelegateSSvpZ":{"name":"userProfileDelegate","abstract":"

    An implementation of Credentials.UserProfileDelegate to update user profile.

    ","parent_name":"CredentialsJWTOptions"},"Structs/CredentialsJWTOptions.html":{"name":"CredentialsJWTOptions","abstract":"

    A list of options for authentication with JWT.

    "},"Structs/TypeSafeJWT.html":{"name":"TypeSafeJWT","abstract":"

    Represents the configuration for TypeSafeJWT authenication: the verification method, and the cache parameters."},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E2idSSvp":{"name":"id","abstract":"

    Note: This field does not apply to Type-safe JWT credentials. Use the JWT claims instead.

    ","parent_name":"JWT"},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E8providerSSvp":{"name":"provider","abstract":"

    Answers JWT.

    ","parent_name":"JWT"},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E12authenticate7request8response9onSuccess0G7Failure0G4Skipy6Kitura13RouterRequestC_AK0L8ResponseCyACyxGcy0K3Net14HTTPStatusCodeOSg_SDyS2SGSgtcyAT_AVtctFZ":{"name":"authenticate(request:response:onSuccess:onFailure:onSkip:)","abstract":"

    Attempts to authenticate a request with an Authorization header containing a JWT.","parent_name":"JWT"},"Extensions/JWT.html":{"name":"JWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC4nameSSvp":{"name":"name","abstract":"

    The name of the plugin: JWT.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC11redirectingSbvp":{"name":"redirecting","abstract":"

    An indication as to whether the plugin is redirecting or not. This plugin is not redirecting.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC15tokenTimeToLiveSdSgvp":{"name":"tokenTimeToLive","abstract":"

    The time in seconds since the user profile was generated that the access token will be considered valid","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC19userProfileDelegate0A004UserdE0_pSgvp":{"name":"userProfileDelegate","abstract":"

    A delegate for UserProfile manipulation. Use this to further populate the profile using","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC8verifier7options15tokenTimeToLiveAByxG05SwiftB011JWTVerifierV_SDySSypGSgSdSgtcfc":{"name":"init(verifier:options:tokenTimeToLive:)","abstract":"

    Initialize a CredentialsJWT instance. Upon first receipt, a JWT will be verified to ensure the signature is valid,","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC10usersCacheSo7NSCacheCySo8NSStringC0A004BaseD7ElementCGSgvp":{"name":"usersCache","abstract":"

    User profile cache.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC12authenticate7request8response7options9onSuccess0G7Failure0G4Pass10inProgressy6Kitura13RouterRequestC_AK0N8ResponseCSDySSypGy0A011UserProfileCcy0M3Net14HTTPStatusCodeOSg_SDyS2SGSgtcyAW_AYtcyyctF":{"name":"authenticate(request:response:options:onSuccess:onFailure:onPass:inProgress:)","abstract":"

    Authenticate incoming request using a JWT.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html":{"name":"CredentialsJWT","abstract":"

    A plugin for Kitura-Credentials supporting authentication using JSON Web Tokens.

    "},"Classes.html":{"name":"Classes","abstract":"

    The following classes are available globally.

    "},"Extensions.html":{"name":"Extensions","abstract":"

    The following extensions are available globally.

    "},"Structs.html":{"name":"Structures","abstract":"

    The following structures are available globally.

    "}} \ No newline at end of file diff --git a/docs/docsets/CredentialsJWT.docset/Contents/Resources/docSet.dsidx b/docs/docsets/CredentialsJWT.docset/Contents/Resources/docSet.dsidx index aafc307..4c2d0b7 100644 Binary files a/docs/docsets/CredentialsJWT.docset/Contents/Resources/docSet.dsidx and b/docs/docsets/CredentialsJWT.docset/Contents/Resources/docSet.dsidx differ diff --git a/docs/docsets/CredentialsJWT.tgz b/docs/docsets/CredentialsJWT.tgz index bb957ac..40b5a65 100644 Binary files a/docs/docsets/CredentialsJWT.tgz and b/docs/docsets/CredentialsJWT.tgz differ diff --git a/docs/index.html b/docs/index.html index 405cc3b..d5f8fe8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -230,7 +230,7 @@

    License

    diff --git a/docs/search.json b/docs/search.json index 26d466c..fc72baa 100644 --- a/docs/search.json +++ b/docs/search.json @@ -1 +1 @@ -{"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V8verifier05SwiftB011JWTVerifierVSgvpZ":{"name":"verifier","abstract":"

    The verifier to use when verifying tokens. This must be configured before tokens can be successfully authenticated,","parent_name":"TypeSafeJWT"},"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V9cacheSizeSivpZ":{"name":"cacheSize","abstract":"

    The maximum size of the token cache. Defaults to 0, which is unlimited.

    ","parent_name":"TypeSafeJWT"},"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V15tokenTimeToLiveSdSgvpZ":{"name":"tokenTimeToLive","abstract":"

    The length of time this token should be deemed valid before it must be verified again. Defaults to nil, which is unlimited.

    ","parent_name":"TypeSafeJWT"},"Structs/CredentialsJWTOptions.html#/s:14CredentialsJWT0A10JWTOptionsV7subjectSSvpZ":{"name":"subject","abstract":"

    Determines the JWT claim that will be used for the identity of the user, (default is ‘sub’).

    ","parent_name":"CredentialsJWTOptions"},"Structs/CredentialsJWTOptions.html#/s:14CredentialsJWT0A10JWTOptionsV19userProfileDelegateSSvpZ":{"name":"userProfileDelegate","abstract":"

    An implementation of Credentials.UserProfileDelegate to update user profile.

    ","parent_name":"CredentialsJWTOptions"},"Structs/CredentialsJWTOptions.html":{"name":"CredentialsJWTOptions","abstract":"

    A list of options for authentication with JWT.

    "},"Structs/TypeSafeJWT.html":{"name":"TypeSafeJWT","abstract":"

    Represents the configuration for TypeSafeJWT authenication: the verification method, and the cache parameters."},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E2idSSvp":{"name":"id","abstract":"

    Note: This field does not apply to Type-safe JWT credentials. Use the JWT claims instead.

    ","parent_name":"JWT"},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E8providerSSvp":{"name":"provider","abstract":"

    Answers JWT.

    ","parent_name":"JWT"},"Extensions/JWT.html#/s:11Credentials08TypeSafeA0P12authenticate7request8response9onSuccess0G7Failure0G4Skipy6Kitura13RouterRequestC_AJ0L8ResponseCyxcy0K3Net14HTTPStatusCodeOSg_SDyS2SGSgtcyAR_ATtctFZ":{"name":"authenticate(request:response:onSuccess:onFailure:onSkip:)","parent_name":"JWT"},"Extensions/JWT.html":{"name":"JWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC4nameSSvp":{"name":"name","abstract":"

    The name of the plugin: JWT.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC11redirectingSbvp":{"name":"redirecting","abstract":"

    An indication as to whether the plugin is redirecting or not. This plugin is not redirecting.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC15tokenTimeToLiveSdSgvp":{"name":"tokenTimeToLive","abstract":"

    The time in seconds since the user profile was generated that the access token will be considered valid","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC19userProfileDelegate0A004UserdE0_pSgvp":{"name":"userProfileDelegate","abstract":"

    A delegate for UserProfile manipulation. Use this to further populate the profile using","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC8verifier7options15tokenTimeToLiveAByxG05SwiftB011JWTVerifierV_SDySSypGSgSdSgtcfc":{"name":"init(verifier:options:tokenTimeToLive:)","abstract":"

    Initialize a CredentialsJWT instance. Upon first receipt, a JWT will be verified to ensure the signature is valid,","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC10usersCacheSo7NSCacheCySo8NSStringC0A004BaseD7ElementCGSgvp":{"name":"usersCache","abstract":"

    User profile cache.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC12authenticate7request8response7options9onSuccess0G7Failure0G4Pass10inProgressy6Kitura13RouterRequestC_AK0N8ResponseCSDySSypGy0A011UserProfileCcy0M3Net14HTTPStatusCodeOSg_SDyS2SGSgtcyAW_AYtcyyctF":{"name":"authenticate(request:response:options:onSuccess:onFailure:onPass:inProgress:)","abstract":"

    Authenticate incoming request using a JWT.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html":{"name":"CredentialsJWT","abstract":"

    A plugin for Kitura-Credentials supporting authentication using JSON Web Tokens.

    "},"Classes.html":{"name":"Classes","abstract":"

    The following classes are available globally.

    "},"Extensions.html":{"name":"Extensions","abstract":"

    The following extensions are available globally.

    "},"Structs.html":{"name":"Structures","abstract":"

    The following structures are available globally.

    "}} \ No newline at end of file +{"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V8verifier05SwiftB011JWTVerifierVSgvpZ":{"name":"verifier","abstract":"

    The verifier to use when verifying tokens. This must be configured before tokens can be successfully authenticated,","parent_name":"TypeSafeJWT"},"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V9cacheSizeSivpZ":{"name":"cacheSize","abstract":"

    The maximum size of the token cache. Defaults to 0, which is unlimited.

    ","parent_name":"TypeSafeJWT"},"Structs/TypeSafeJWT.html#/s:14CredentialsJWT08TypeSafeB0V15tokenTimeToLiveSdSgvpZ":{"name":"tokenTimeToLive","abstract":"

    The length of time this token should be deemed valid before it must be verified again. Defaults to nil, which is unlimited.

    ","parent_name":"TypeSafeJWT"},"Structs/CredentialsJWTOptions.html#/s:14CredentialsJWT0A10JWTOptionsV7subjectSSvpZ":{"name":"subject","abstract":"

    Determines the JWT claim that will be used for the identity of the user, (default is ‘sub’).

    ","parent_name":"CredentialsJWTOptions"},"Structs/CredentialsJWTOptions.html#/s:14CredentialsJWT0A10JWTOptionsV19userProfileDelegateSSvpZ":{"name":"userProfileDelegate","abstract":"

    An implementation of Credentials.UserProfileDelegate to update user profile.

    ","parent_name":"CredentialsJWTOptions"},"Structs/CredentialsJWTOptions.html":{"name":"CredentialsJWTOptions","abstract":"

    A list of options for authentication with JWT.

    "},"Structs/TypeSafeJWT.html":{"name":"TypeSafeJWT","abstract":"

    Represents the configuration for TypeSafeJWT authenication: the verification method, and the cache parameters."},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E2idSSvp":{"name":"id","abstract":"

    Note: This field does not apply to Type-safe JWT credentials. Use the JWT claims instead.

    ","parent_name":"JWT"},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E8providerSSvp":{"name":"provider","abstract":"

    Answers JWT.

    ","parent_name":"JWT"},"Extensions/JWT.html#/s:8SwiftJWT0B0V011CredentialsB0E12authenticate7request8response9onSuccess0G7Failure0G4Skipy6Kitura13RouterRequestC_AK0L8ResponseCyACyxGcy0K3Net14HTTPStatusCodeOSg_SDyS2SGSgtcyAT_AVtctFZ":{"name":"authenticate(request:response:onSuccess:onFailure:onSkip:)","abstract":"

    Attempts to authenticate a request with an Authorization header containing a JWT.","parent_name":"JWT"},"Extensions/JWT.html":{"name":"JWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC4nameSSvp":{"name":"name","abstract":"

    The name of the plugin: JWT.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC11redirectingSbvp":{"name":"redirecting","abstract":"

    An indication as to whether the plugin is redirecting or not. This plugin is not redirecting.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC15tokenTimeToLiveSdSgvp":{"name":"tokenTimeToLive","abstract":"

    The time in seconds since the user profile was generated that the access token will be considered valid","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC19userProfileDelegate0A004UserdE0_pSgvp":{"name":"userProfileDelegate","abstract":"

    A delegate for UserProfile manipulation. Use this to further populate the profile using","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC8verifier7options15tokenTimeToLiveAByxG05SwiftB011JWTVerifierV_SDySSypGSgSdSgtcfc":{"name":"init(verifier:options:tokenTimeToLive:)","abstract":"

    Initialize a CredentialsJWT instance. Upon first receipt, a JWT will be verified to ensure the signature is valid,","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC10usersCacheSo7NSCacheCySo8NSStringC0A004BaseD7ElementCGSgvp":{"name":"usersCache","abstract":"

    User profile cache.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html#/s:14CredentialsJWTAAC12authenticate7request8response7options9onSuccess0G7Failure0G4Pass10inProgressy6Kitura13RouterRequestC_AK0N8ResponseCSDySSypGy0A011UserProfileCcy0M3Net14HTTPStatusCodeOSg_SDyS2SGSgtcyAW_AYtcyyctF":{"name":"authenticate(request:response:options:onSuccess:onFailure:onPass:inProgress:)","abstract":"

    Authenticate incoming request using a JWT.

    ","parent_name":"CredentialsJWT"},"Classes/CredentialsJWT.html":{"name":"CredentialsJWT","abstract":"

    A plugin for Kitura-Credentials supporting authentication using JSON Web Tokens.

    "},"Classes.html":{"name":"Classes","abstract":"

    The following classes are available globally.

    "},"Extensions.html":{"name":"Extensions","abstract":"

    The following extensions are available globally.

    "},"Structs.html":{"name":"Structures","abstract":"

    The following structures are available globally.

    "}} \ No newline at end of file