Skip to content

Instantly share code, notes, and snippets.

@patchthecode
Forked from pedantix/JWTService.swift
Created March 3, 2018 16:10
Show Gist options
  • Save patchthecode/ff65a60d383156455fde604ad45674f2 to your computer and use it in GitHub Desktop.
Save patchthecode/ff65a60d383156455fde604ad45674f2 to your computer and use it in GitHub Desktop.
# Snippets For how I hacked together
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
}
}
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())
// ...
}
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) ?? ""
}
}
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