-
-
Save patchthecode/ff65a60d383156455fde604ad45674f2 to your computer and use it in GitHub Desktop.
# Snippets For how I hacked together
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 | |
// Reference: https://github.com/vapor/auth/blob/beta/Sources/Authentication/AuthenticationCache.swift | |
/// Stores authenticated objects. This should be created | |
/// using the request container as a singleton. Authenticated | |
/// objects can then be stored here by middleware and fetched | |
/// later in route closures. | |
final class AuthenticationCache: Service { | |
/// The internal storage. | |
private var storage: [ObjectIdentifier: Any] | |
/// Create a new authentication cache. | |
init() { | |
self.storage = [:] | |
} | |
/// Access the cache using types. | |
internal subscript<A>(_ type: A.Type) -> A? { | |
get { return storage[ObjectIdentifier(A.self)] as? A } | |
set { storage[ObjectIdentifier(A.self)] = newValue } | |
} | |
} | |
// MARK: Request | |
extension Request { | |
/// Authenticates the supplied instance for this request. | |
public func authenticate<A>(_ instance: A) throws { | |
let cache = try privateContainer.make(AuthenticationCache.self, for: Request.self) | |
cache[A.self] = instance | |
} | |
/// Returns the authenticated instance of the supplied type. | |
/// note: nil if no type has been authed, throws if there is a problem. | |
public func authenticated<A>(_ type: A.Type) throws -> A? { | |
let cache = try privateContainer.make(AuthenticationCache.self, for: Request.self) | |
return cache[A.self] | |
} | |
/// Returns true if the type has been authenticated. | |
public func isAuthenticated<A>(_ type: A.Type) throws -> Bool { | |
return try authenticated(A.self) != nil | |
} | |
/// Returns an instance of the supplied type. Throws if no | |
/// instance of that type has been authenticated or if there | |
/// was a problem. | |
public func requireAuthenticated<A>(_ type: A.Type) throws -> A { | |
guard let auth = try authenticated(A.self) else { | |
throw Abort(.unauthorized, reason: "\(A.self) has not been authenticated.") | |
} | |
return auth | |
} | |
} |
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 Foundation | |
import Vapor | |
import PostgreSQL | |
import FluentPostgreSQL | |
/// Called before your application initializes. | |
/// | |
/// [Learn More →](https://docs.vapor.codes/3.0/getting-started/structure/#configureswift) | |
public func configure( | |
_ config: inout Config, | |
_ env: inout Environment, | |
_ services: inout Services | |
) throws { | |
// ... | |
// Configure the rest of your application here | |
services.register { (container) -> FacebookClient in | |
let client = try container.make(Client.self, for: FacebookClient.self) | |
return FacebookClient(client: client) | |
} | |
let jwtSecret = ProcessInfo.processInfo.environment["JWT_SECRET"] ?? "secret" | |
let jwtService = JWTService(secret: jwtSecret) | |
services.register(jwtService) | |
services.register(AuthenticationCache()) | |
services.register(SecureUserMiddleware()) | |
// ... | |
} |
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 Foundation | |
import JWT | |
import Vapor | |
final class JWTService: Service { | |
var signer: JWTSigner | |
init(secret: String) { | |
signer = JWTSigner.hs512(key: Data(secret.utf8)) | |
} | |
func signUserToToken(user: User) throws -> String { | |
var jwt = JWT(payload: UserPayload(userId: user.id ?? 0)) | |
let data = try signer.sign(&jwt) | |
return String(data: data, encoding: .utf8) ?? "" | |
} | |
} |
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 JWT | |
final class SecureUserMiddleware: Middleware, Service { | |
func respond(to request: Request, chainingTo next: Responder) throws -> Future<Response> { | |
guard let authorization = request.http.headers["Authorization"], | |
let bearer = authorization.split(separator: " ").first, | |
let token = authorization.split(separator: " ").last, bearer == "Bearer" | |
else { | |
throw Abort(.unauthorized, reason: "No Authorization Header") | |
} | |
let jwtService: JWTService = try request.make() | |
let userPayload = try JWT<UserPayload>(from: String(token), verifiedUsing: jwtService.signer).payload | |
let authenticationCache = try request.make(AuthenticationCache.self, for: Request.self) | |
return request.connect(to: .psql) | |
.flatMap(to: User?.self) { conn in | |
User.find(userPayload.userId, on: conn) | |
}.flatMap(to: Response.self, { maybeUser in | |
authenticationCache[User.self] = maybeUser | |
_ = try request.requireAuthenticated(User.self) | |
return try next.respond(to: request) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment