Skip to content

Instantly share code, notes, and snippets.

@tanner0101
Last active February 20, 2017 12:53
Show Gist options
  • Save tanner0101/6fe6618a215bca2383545c3501f17e45 to your computer and use it in GitHub Desktop.
Save tanner0101/6fe6618a215bca2383545c3501f17e45 to your computer and use it in GitHub Desktop.

Vapor Auth 2.0

Token (Authorization Bearer) authenication with a table holding the tokens.

Request

GET /name HTTP/1.1
Authorization: Bearer foobar
Host: 0.0.0.0:8080
Connection: close
Content-Length: 0

Basic

The most common use case: authenication with a table holding the tokens.

Schema

users tokens
id id
name token
user_id

User Model

A basic Vapor Model type with a name using the "users" table.

import Vapor

class SomeUser: Model {
    let name: String
    // all sorts of model code
}

Token Model

Basic Vapor Model representing the "tokens" table.

import Vapor

class SomeToken: Model {
    // code for representing a token type with some "tokens" table
}

Authenticatable

Conforming the user to TokenAuthenticatable requires specifying the token type.

Since both the token type and the user itself are entities, the authentication is automatically implemented using a Fluent join.

import Authentication

extension SomeUser: TokenAuthenticatable {
    typealias TokenType = SomeToken
}

Convenience

This makes it easier to access the user using just req.user()

import HTTP
import Authentication

extension Request {
    func user() throws -> SomeUser {
        return try auth.authenticated()
    }
}

Droplet Configuration

A TokenAuthenticationMiddleware must be added to protected routes. All requests that fail to authenticate will be rejected.

let drop = Droplet()

let authMiddleware = TokenAuthenticationMiddleware(SomeUser.self)
let authed = drop.grouped(authMiddleware)

authed.get("name") { req in
    // return the users name
    return try req.user().name
}

Custom key

Schema

users tokens
id id
name foo
user_id

Token Key

Should the key which contains the token on the tokens table ("token" in the previous example) be different, the protocol must be informed.

import Authentication

extension SomeUser: TokenAuthenticatable {
    typealias TokenType = SomeToken
    static let tokenKey = "foo" 
}

Custom token auth

Schema

users
id
name

Authenticate Method

To do custom authentication with the token, just set TokenType to self and implement the authenticate method.

extension SomeUser: TokenAuthenticatable {
    typealias TokenType = Self
	
    static func authenticate(_ token: Token) throws -> Self {
        // some custom method for looking up the user
    }
}
@siemensikkema
Copy link

Let's see if I understand correctly:

  • this is intended to provide a general approach to token based authentication
  • there will be no need for a specific JWT Authentication middleware
  • protectMiddleware would become obsolete
  • to support JWT authentication we could do something like:
extension SomeUser: TokenAuthenticatable {
    typealias TokenType = JWTVerifier
}

// needs better name
class JWTVerifier {
    let claims: [Claim]
    let signer: Signer
    ...
}

extension JWTVerifier: TokenAuthenticatable {
    static func authenticate(_ token: Token) throws -> JWT {
        let jwt = try JWT(token: token.token) // I heard you like tokens ...
        try jwt.verifySignature(using: ...)
        try jwt.verifyClaims(...)
        return jwt
    }
}

Possible disadvantages:

  • the user and token need to be loaded from the database on each request

Am I on the right track?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment