Alchemy provides built in support for Bcrypt hashing and automatic Basic or Token authentication via Rune & Middleware.

Bcrypt

Standard practice is to never store plain text passwords in your database. Bcrypt is a password hashing function that creates a one way hash of a plaintext password. It’s an expensive process CPU-wise, so it will help protect your passwords from being easily cracked through brute forcing.

It’s simple to use, and runs asynchronously so as not to block the current thread.

let hashedPassword = try await Bcrypt.hash("password")

let isMatch = try await Bcrypt.verify("password", hashedPassword)

If you’d rather run Bcrypt synchronously and block the current thread until it’s finished, you may use hashSync() or verifySync().

let hashedPassword = Bcrypt.hashSync("password")
let isPasswordValid = Bcrypt.verifySync("password", hashedPassword) // true

Request Auth

Request makes it easy to pull Authorization information off an incoming request.

Authorization: Basic

You can access Basic auth info via .basicAuth() -> HTTPAuth.Basic?.

let request: Request = ...
if let basic = request.basicAuth() {
    print("Got basic auth. Username: \(basic.username) Password: \(basic.password)")
}

Authorization: Bearer

You can also get Bearer auth info via .bearerAuth() -> HTTPAuth.Bearer?.

let request: Request = ...
if let bearer = request.bearerAuth() {
    print("Got bearer auth with Token: \(bearer.token)")
}

Authorization: Either

You can also get any Basic or Bearer auth from the request.

let request: Request = ...
if let auth = request.getAuth() {
    switch auth {
    case .bearer(let bearer):
        print("Request had Bearer auth!")
    case .basic(let basic):
        print("Request had Basic auth!")
    }
}

Auth Middleware

Incoming Request can be automatically authorized against your Rune Models by conforming your Models to “authable” protocols and protecting routes with the generated Middleware.

Basic Auth Middleware

To authenticate via the Authorization: Basic ... headers on incoming Requests, conform your Rune Model that stores usernames and password hashes to BasicAuthable.

struct User: Model, BasicAuthable {
    var id: Int?
    let username: String
    let password: String
}

Now, put User.basicAuthMiddleware() in front of any endpoints that need basic auth. When the request comes in, the Middleware will compare the username and password in the Authorization: Basic ... headers to the username and password hash of the User model. If the credentials are valid, the Middleware will set the relevant User instance on the Request, which can then be accessed via request.get(User.self).

If the credentials aren’t valid, or there is no Authorization: Basic ... header, the Middleware will throw an HTTPError(.unauthorized).

app.use(User.basicAuthMiddleware())
app.get("/login") { req in
    let authedUser = try req.get(User.self)
    // Do something with the authorized user...
}

Note that Rune is inferring a username at column "email" and password at column "password" when verifying credentials. You may set custom columns by overriding the usernameKeyString or passwordKeyString of your Model.

struct User: Model, BasicAuthable {
    static let usernameKeyString = "username"
    static let passwordKeyString = "hashed_password"

    var id: Int?
    let username: String
    let hashedPassword: String
}

Token Auth Middleware

Similarly, to authenticate via the Authorization: Bearer ... headers on incoming Requests, conform your Rune Model that stores access token values to TokenAuthable. Note that this time, you’ll need to specify a BelongsTo relationship to the User type this token authorizes.

struct UserToken: Model, BasicAuthable {
    var id: Int?
    let value: String

    @BelongsTo var user: User
}

Like with Basic auth, put the UserToken.tokenAuthMiddleware() in front of endpoints that are protected by bearer authorization. The Middleware will automatically parse out tokens from incoming Requests and validate them via the UserToken type. If the token matches a UserToken row, the related User and UserToken will be .set() on the Request for access in a handler.

router.use(UserToken.tokenAuthMiddleware())
    .get("/todos") { req in
        let authedUser = try req.get(User.self)
        let theToken = try req.get(UserToken.self)
    }

Note that Rune is again inferring a "value" column on the UserToken to which it will compare the tokens on incoming Requests. This can be customized by overriding the valueKeyString property of your Model.

struct UserToken: Model, BasicAuthable {
    static let valueKeyString = "token_string"

    var id: Int?
    let tokenString: String

    @BelongsTo var user: User
}