Skip to main content

JDBC Auth Provider implementation

We provide an implementation of AuthProvider which uses the Vert.x JDBCClient to perform authentication and authorisation against any JDBC compliant database.

To use this project, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-auth-jdbc-scala_2.12</artifactId>
  <version>3.4.1</version>
</dependency>
  • Gradle (in your build.gradle file):

compile 'io.vertx:vertx-auth-jdbc-scala_2.12:3.4.1'

To create an instance you first need an instance of JDBCClient. To learn how to create one of those please consult the documentation for the JDBC client.

Once you’ve got one of those you can create a JDBCAuth instance as follows:

var jdbcClient = JDBCClient.createShared(vertx, jdbcClientConfig)

var authProvider = JDBCAuth.create(vertx, jdbcClient)

Once you’ve got your instance you can authenticate and authorise with it just like any AuthProvider.

The out of the box config assumes certain queries for authentication and authorisation, these can easily be changed with the operations setAuthenticationQuery, setPermissionsQuery and setRolesQuery, if you want to use them with a different database schema.

The default implementation assumes that the password is stored in the database as a SHA-512 hash after being concatenated with a salt. It also assumes the salt is stored in the table too.

The basic data definition for the storage should look like this:

--
-- Take this script with a grain of salt and adapt it to your RDBMS
--
CREATE TABLE `user` (
  `username` VARCHAR(255) NOT NULL,
  `password` VARCHAR(255) NOT NULL,
  `password_salt` VARCHAR(255) NOT NULL
);

CREATE TABLE `user_roles` (
  `username` VARCHAR(255) NOT NULL,
  `role` VARCHAR(255) NOT NULL
);

CREATE TABLE `roles_perms` (
  `role` VARCHAR(255) NOT NULL,
  `perm` VARCHAR(255) NOT NULL
);

ALTER TABLE user ADD CONSTRAINT `pk_username` PRIMARY KEY (username);
ALTER TABLE user_roles ADD CONSTRAINT `pk_user_roles` PRIMARY KEY (username, role);
ALTER TABLE roles_perms ADD CONSTRAINT `pk_roles_perms` PRIMARY KEY (role);

ALTER TABLE user_roles ADD CONSTRAINT fk_username FOREIGN KEY (username) REFERENCES user(username);
ALTER TABLE user_roles ADD CONSTRAINT fk_roles FOREIGN KEY (role) REFERENCES roles_perms(role);

If you want to override this behaviour you can do so by providing an alternative hash strategy and setting it with setHashStrategy.

Warning
It is advised to always store your passwords as hashes in your database tables which have been created with a salt which should be stored in the row too. A strong hashing algorithm should be used. It is strongly advised never to store your passwords as plain text.

Hashing passwords

Like any application there will be a time where you need to store new users into the database. Has you have learn passwords are not stored in plain text but hashed according to the hashing strategy. The same strategy is required to hash new password before storing it to the database. Doing it is a 3 step task.

  1. Generate a salt string

  2. Hash the password given the salt string

  3. Store it to the database

var salt = auth.generateSalt()
var hash = auth.computeHash("sausages", salt)
// save to the database
conn.updateWithParamsFuture("INSERT INTO user VALUES (?, ?, ?)", new io.vertx.core.json.JsonArray().add("tim").add(hash).add(salt)).onComplete{
  case Success(result) => {
    // success!
  }
  case Failure(cause) => println("Failure")
}

Authentication

When authenticating using this implementation, it assumes username and password fields are present in the authentication info:

var authInfo = new io.vertx.core.json.JsonObject().put("username", "tim").put("password", "sausages")

authProvider.authenticateFuture(authInfo).onComplete{
  case Success(result) => {
    var user = result
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

Authorisation - Permission-Role Model

Although Vert.x auth itself does not mandate any specific model of permissions (they are just opaque strings), this implementation assumes a familiar user/role/permission model, where a user can have zero or more roles and a role can have zero or more permissions.

If validating if a user has a particular permission simply pass the permission into. isAuthorised as follows:

user.isAuthorisedFuture("commit_code").onComplete{
  case Success(result) => {
    var hasPermission = result
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

If validating that a user has a particular role then you should prefix the argument with the role prefix.

user.isAuthorisedFuture("role:manager").onComplete{
  case Success(result) => {
    var hasRole = result
  }
  case Failure(cause) => {
    println(s"$cause")
  }
}

The default role prefix is role:. You can change this with setRolePrefix. <a href="mailto:julien@julienviet.com">Julien Viet</a><a href="http://tfox.org">Tim Fox</a>