Created
May 26, 2017 07:39
-
-
Save NathanFlurry/0d3b6e8bf9b83ef4724794da52329fb0 to your computer and use it in GitHub Desktop.
Like `TokenAuthenticationMiddleware`, but can declare a private and public token for a single object.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Fluent | |
import Authentication | |
/// Copy of `TokenAuthenticationMiddleware` middleware that uses classified tokens. This way, objects can use | |
/// private and public tokens. | |
public final class ClassifiedTokenAuthenticationMiddleware<U: TokenAuthenticatable>: Middleware { | |
public let isPrivate: Bool | |
public init(_ userType: U.Type = U.self, isPrivate: Bool) { | |
self.isPrivate = isPrivate | |
} | |
public func respond(to req: Request, chainingTo next: Responder) throws -> Response { | |
// Handle already authenticated | |
if req.auth.isAuthenticated(U.self) { | |
return try next.respond(to: req) | |
} | |
// Get the token | |
guard let token = req.auth.header?.bearer else { | |
throw AuthenticationError.invalidCredentials | |
} | |
// Classify token so authenticator knows where to look | |
let classifiedToken = token.classified(isPrivate: isPrivate) | |
// Do authentication | |
let u = try U.authenticate(classifiedToken) | |
req.auth.authenticate(u) | |
return try next.respond(to: req) | |
} | |
} | |
/// Extension of token in order to make it be able to depict whether it's public or private. | |
extension Token { | |
static let publicPrefix = "[public]" | |
static let privatePrefix = "[private]" | |
public func classified(isPrivate: Bool) -> Token { | |
return Token(string: "\(isPrivate ? Token.privatePrefix : Token.publicPrefix)\(string)") | |
} | |
} | |
/// Add protocol to make it simple to implement classified token authentication. | |
protocol ClassifiedTokenAuthenticatable: TokenAuthenticatable { | |
/// Key for the public token value. | |
static var publicTokenKey: String { get } | |
/// Key for the private token value. | |
static var privateTokenKey: String { get } | |
} | |
/// Add shared code to parse tokens. | |
extension ClassifiedTokenAuthenticatable { | |
static func parseToken(_ token: Token) throws -> (tokenKey: String, originalToken: String) { | |
// Find the token key for the classification | |
let tokenKey: String | |
let tokenPrefix: String | |
if token.string.hasPrefix(Token.publicPrefix) { | |
tokenKey = publicTokenKey | |
tokenPrefix = Token.publicPrefix | |
} else if token.string.hasPrefix(Token.privatePrefix) { | |
tokenKey = privateTokenKey | |
tokenPrefix = Token.privatePrefix | |
} else { | |
throw AuthenticationError.invalidBearerAuthorization | |
} | |
// Get the original token by slicing off the prefix | |
let fromIndex = token.string.index(token.string.startIndex, offsetBy: tokenPrefix.characters.count) | |
let originalToken = token.string.substring(from: fromIndex) | |
return (tokenKey, originalToken) | |
} | |
} | |
extension ClassifiedTokenAuthenticatable where Self: Entity, Self.TokenType: Entity { | |
static func authenticate(_ token: Token) throws -> Self { | |
// Parse the token | |
let (tokenKey, originalToken) = try parseToken(token) | |
// Find the user | |
guard let user = try Self.makeQuery() | |
.join(Self.TokenType.self) | |
.filter(Self.TokenType.self, tokenKey, originalToken) | |
.first() | |
else { | |
throw AuthenticationError.invalidCredentials | |
} | |
return user | |
} | |
} | |
extension ClassifiedTokenAuthenticatable where Self: Entity, Self.TokenType: Entity, Self.TokenType == Self { | |
static func authenticate(_ token: Token) throws -> Self { | |
// Parse the token | |
let (tokenKey, originalToken) = try parseToken(token) | |
// Find the user | |
guard let user = try Self.makeQuery() | |
.filter(tokenKey, originalToken) | |
.first() | |
else { | |
throw AuthenticationError.invalidCredentials | |
} | |
return user | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment