Bart Simons


A 2 post collection

Send an SMS notification upon successful SSH authentication with Twilio

 •  Filed under ssh, sms, authentication, notification, twilio

Are your always curious about who logs into your server using SSH? Me too! Sometimes I am just a little paranoid and then I come up with these (crazy) ideas: sending an SMS when somebody successfully logs in over SSH. Why? Well: why not, right?

So, what do we need?

  • A server with an SSH daemon (duh...)
  • A Twilio account

So first of all, let me explain what Twilio is: it's an awesome company that makes placing and receiving calls, sending and receiving SMS and MMS, etcetera super simple. It operates over an API so that developers can create, integrate and automate telecommunication solutions into their applications.

Before continuing, make sure you have an account and make sure you write down your account SID and authentication token, both can be found on the Twilio console page.

Now, we need to prepare our server. Once any user successfully authenticates over SSH, we want to execute a script before any shell process gets spawned. This is how I did it:

First I created a script in the /opt directory called with the following contents in it:


curl -s -X POST '' --data-urlencode 'To=+12345678901' --data-urlencode 'From=+15017250604' --data-urlencode 'Body=Somebody just succesfully SSHed into your server!' -u YOURACCOUNTSIDGOESHERE:YOURAUTHTOKENGOESHERE > /dev/null  

Make sure to replace the account SID and authentication token placeholders by the values stated in your Twilio console and replace the To and From phone numbers to your phone numbers. Also, don't forget to make the script executable:

chmod +x /opt/  

There's only one more thing left to do: modifying the SSH daemon configuration file. For me (I use Ubuntu) it was located at /opt/ssh/sshd_config.

Just add the following line to the bottom of this file:

ForceCommand /opt/; /bin/bash  

And restart your SSH server daemon with:

service sshd restart  

Try it out! For me it worked brilliantly:

It works!

Have a wonderful day! 🎉

Swift + Perfect + Authentication API Example

 •  Filed under swift, perfect, example, authentication

So you want to build your next backend API in Swift? No problem! Perfect has got you covered. Perfect is a collection of libraries to turn your next server-side Swift application into reality. It is blazingly fast and awesome!

So I decided to dive a little bit deeper into the possibilities of Swift and Perfect, and so I quickly decided that an authentication API would be a 'perfect' idea (see what I did there?) to get started. So, what do we need?

  • A computer running Linux or macOS
  • Swift 3 installed
  • A MySQL Server

That's basically all that's needed. Now we need a functional database, so I came up with a SQL table like this:

CREATE TABLE `users` (  
  `id` int(32) NOT NULL,
  `username` varchar(128) NOT NULL,
  `password` varchar(128) NOT NULL

ALTER TABLE `users`  
  ADD UNIQUE KEY `username` (`username`),
  ADD UNIQUE KEY `id` (`id`);

INSERT INTO `users` (`id`, `username`, `password`) VALUES  
(1, 'user1', '7c12772809c1c0c3deda6103b10fdfa0'),
(2, 'user2', '7c12772809c1c0c3deda6103b10fdfa0');

ALTER TABLE `users`  

Perfect. Now - for the next part - initialize a new Swift package in an empty folder:

swift package init --type executable  

This will generate an empty package, so now it's time to edit our Package.swift file. You'll need to add 2 repositories to your project, this is how I have done that:

import PackageDescription

let package = Package(  
    name: "backend-api",
    dependencies: [
        .Package(url: "", majorVersion: 2),
        .Package(url: "", majorVersion: 2)

Cool. So now it is about time to work on the real deal - programming! I've split the project into two separate swift files

  • main.swift : this is for all things related to the web server processing
  • DBOperations.swift : this is a provider to execute MySQL (prepared) statements in the form of a function

My main.swift:

import PerfectLib  
import PerfectHTTP  
import PerfectHTTPServer  
import Foundation

let server = HTTPServer()  
var routes = Routes()

server.serverPort = 8000

routes.add(method: .get, uri: "/", handler: {  
    request, response in

    response.status = HTTPResponseStatus.ok
    response.setHeader(.contentType, value: "application/json")

    response.setBody(string: "{\"status\":\"Backend server is up and running\"}")

routes.add(method: .get, uri: "/oauth", handler: {  
    request, response in

    response.setHeader(.contentType, value: "application/json")

    let username = request.param(name: "username", defaultValue: "")
    let password = request.param(name: "password", defaultValue: "")

    if ((username ?? "").isEmpty || (password ?? "").isEmpty) {
        response.status = HTTPResponseStatus.ok
        response.setBody(string: "{\"status\":\"Invalid request\"}")
    } else {
        if (username ?? "").characters.count >= 8 || (username ?? "").characters.count <= 24{
            if (password ?? "").characters.count == 32 {
                if (username ?? "").rangeOfCharacter(from: CharacterSet.alphanumerics.inverted) == nil && (password ?? "").rangeOfCharacter(from: CharacterSet.alphanumerics.inverted) == nil {
                    response.status = HTTPResponseStatus.ok
                    let queryResult: [[[String: String]]] = executeQuery(query: "SELECT * FROM users WHERE username = ?", parameters: [username ?? ""])
                    if (queryResult.count == 1) {
                        if  ((queryResult[0][2]["password"] ?? "") as String == password ?? "") {
                            response.setBody(string: "{\"status\":\"Authentication request succeeded!\"}")
                        } else {
                            response.setBody(string: "{\"status\":\"Invalid request\"}")
                    } else {
                        response.setBody(string: "{\"status\":\"Invalid request\"}")
                } else {
                    response.status = HTTPResponseStatus.ok
                    response.setBody(string: "{\"status\":\"Invalid request\"}")
            } else {
                response.status = HTTPResponseStatus.ok
                response.setBody(string: "{\"status\":\"Invalid request\"}")
        } else {
            response.status = HTTPResponseStatus.ok
            response.setBody(string: "{\"status\":\"Invalid request\"}")



do {  
    try server.start()
} catch PerfectError.networkError(let err, let msg) {
    print("Network error thrown: \(err) \(msg)")

My DBOperations.swift:

import MySQL

let sqlHost     = ""  
let sqlUser     = "app_authentication"  
let sqlPassword = "testing01"  
let sqlDB       = "app_authentication"

func executeQuery(query: String, parameters: [String]? = nil) -> [[[String: String]]] {  
    var fStore = [[[String: String]]]()

    let mysql = MySQL()
    let connection = mysql.connect(host: sqlHost, user: sqlUser, password: sqlPassword, db: sqlDB)

    guard connection else {
        return [[[String: String]]]()

    defer {

    let fQuery = MySQLStmt(mysql)

    _ = fQuery.prepare(statement: query)

    if parameters != nil {
        for parameter in parameters! {

    _ = fQuery.execute()

    let fResults = fQuery.results()

    _ = fResults.forEachRow {
        e in

        var fSubStore = [[String: String]]()

        for i in 0..<e.count {
            let fFieldName: String? = fQuery.fieldInfo(index: i)!.name
            fSubStore.append(["\(fFieldName!)": "\(e[i]!)"])


    return fStore

You need to save both of these files inside the Sources folder of your package.

To build and run your app, run this in the root of your package directory:

swift build  

Now it is time to test if everything works:
It works!

So: this is how you can build authentication APIs with Swift. Please bare in mind that all code examples given are NOT production ready. I know that it works, but I can definitely already see some points of improvements - too bad that I don't have time to fix that. This demo project is soon to be expected on my GitHub. Thanks for reading and have a nice day ;)