Created
August 17, 2019 20:03
-
-
Save MihaelIsaev/c727df105348a01a04ddb14d3eb403b8 to your computer and use it in GitHub Desktop.
Simple authorization middleware for Vapor 3.
This file contains 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 Vapor | |
import SwifQL | |
class AuthMiddleware: Middleware { | |
/// Use it on your router like this | |
/// ```swift | |
/// let protectedRoute = router.grouped(AuthMiddleware()) | |
/// ``` | |
func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> { | |
return try request.requireToken().flatMap { try next.respond(to: $0) } | |
} | |
} | |
extension Request { | |
/// Parses token from headers | |
fileprivate func requireToken() throws -> Future<Request> { | |
let prefix = "Bearer " | |
guard let _bearerToken = http.headers.firstValue(name: .authorization), | |
_bearerToken.contains(prefix) else { | |
throw Abort(.unauthorized, reason: "Invalid authorization token") | |
} | |
let token = _bearerToken.replacingOccurrences(of: prefix, with: "") | |
return try checkToken(token: token).map { | |
try self.putUserIntoHeaders($0) | |
} | |
} | |
} | |
extension Request { | |
/// Checks if token exists in the database and retrieves a user | |
fileprivate func checkToken(token: String) throws -> Future<User> { | |
return SwifQL | |
.select(User.table.*) | |
.from(UserToken.table) | |
.join(.inner, User.table, on: \User.id == \UserToken.userId) | |
.where(\UserToken.token == token) | |
.execute(on: self, as: .psql) | |
.first(decoding: User.self) | |
.unwrap(or: Abort(.unauthorized, reason: "Invalid auth credentials")) | |
} | |
} | |
extension Request { | |
fileprivate var headerAuthDataKey: String { return "_authData" } | |
/// Puts a user model as a JSON string into headers (to retrieve it in the future) | |
fileprivate func putUserIntoHeaders(_ user: User) throws -> Request { | |
let encodedUserData = try JSONEncoder().encode(user) | |
guard let jsonString = String(data:encodedUserData, encoding: .utf8) else { | |
throw Abort(.internalServerError, reason: "Session encoding error") | |
} | |
http.headers.add(name: headerAuthDataKey, value: jsonString) | |
return self | |
} | |
} | |
extension Request { | |
/// Call it to retrieve authorized user from request headers | |
@discardableResult | |
func authorizedUser() throws -> User { | |
guard let json = http.headers[headerAuthDataKey].first, | |
let data = json.data(using: .utf8) else { | |
throw Abort(.unauthorized, reason: "Invalid auth credentials") | |
} | |
return try JSONDecoder().decode(User.self, from: data) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment