Custom Authentication Connectors

This article presents configuration options available to the administrator and describe how to set Tigase server up to use user accounts data from a different database.

The first thing to know is that Tigase server always opens 2 separate connections to the database. One connection is used for user login data and the other is for all other user data like the user roster, vCard, private data storage, privacy lists and so on…​

In this article we still assume that Tigase server keeps user data in it’s own database and only login data is retrieved from the external database.

At the moment Tigase offers following authentication connectors:

As always the simplest way to configure the server is through the init.properties file. In the article describing this file you can find long list with all available options and all details how to handle it. For the authentication connector setup however we only need 2 options:

dataSource {
    'default-auth' () {
        uri = 'database connection url'
    }
}
authRepository {
    default () {
        cls = 'connector'
        'data-source' = 'default-auth'
    }
}

For example if you store authentication data in a drupal database on localhost your settings would be:

dataSource {
    'default-auth' () {
        uri = 'jdbc:mysql://localhost/drupal?user=user&password=passwd'
    }
}
authRepository {
    default () {
        'data-source' = 'default-auth'
    }
}

You have to use a class name if you want to attach your own authentication connector.

Default is:

authRepository {
    default {
        cls = 'tigase.db.jdbc.TigaseAuth'
    }
}

In the same exact way you can setup connector for any different database type.

For example, drupal configuration is below

authRepository {
    default {
        cls = 'tigase.db.jdbc.DrupalWPAuth'
    }
}

Or tigase-custom authentication connector.

authRepository {
    default {
        cls = 'tigase-customs'
    }
}

The different cls or classes are:

You can normally skip configuring connectors for the default Tigase database format: mysql, pgsql and derby as they are applied automatically if the parameter is missing.

One more important thing to know is that you will have to modify authRepository if you use a custom authentication connector. This is because if you retrieve user login data from the external database this external database is usually managed by an external system. User accounts are added without notifying Tigase server. Then, when the user logs in and tries to retrieve the user roster, the server can not find such a user in the roster database.

To keep user accounts in sync between the authentication database and the main user database you have to add following option to the end of the database connection URL: autoCreateUser=true.

For example:

dataSource {
    default () {
        uri = 'jdbc:mysql://localhost/tigasedb?user=nobody&password=pass&autoCreateUser=true'
    }
}

If you are interested in even further customizing your authentication connector by writing your own queries or stored procedures, please have a look at the following guides:

Tigase Auth Connector

The Tigase Auth connector with shortcut name: tigase-auth is implemented in the class: tigase.db.jdbc.TigaseAuth. It allows you to connect to any external database to perform user authentication. You can find more details how to setup a custom connector in the Custom Authentication Connectors guide.

To make this connector working you have to prepare your database to offer set of stored procedures for Tigase server to perform all the authentication actions. The best description is the example schema with all the stored procedures defined. Please refer to the Tigase SVN repository for the schema definition files.

Files with the stored procedures implementations are located in postgresql-schema-4.sql file for PostgreSQL database.

The absolute minimum of stored procedures you have to implement is:

  • TigUserLoginPlainPw - to perform user authentication. The procedure is always called when the user tries to login to the XMPP server. This is the only procedure which must be implemented and actually must work.
  • TigUserLogout - to perform user logout. The procedure is always called when the user logouts or disconnects from the server. This procedure must be implemented but it can be empty and can do nothing. It just needs to exist because Tigase expect it to exist and attempts to call it.

With these 2 above stored procedures you can only perform user login/logouts on the external database. You can’t register a user account, change user password or remove the user. In many cases this is fine as all the user management is handled by the external system.

If you however want to allow for account management via XMPP you have to implement also following procedures:

  • TigAddUserPlainPw - to add a new user account
  • TigRemoveUser - to remove existing user account
  • TigUpdatePasswordPlainPw - to change a user password for existing account

Tigase Custom Auth Connector

The Tigase Custom Auth connector with shortcut name: tigase-custom is implemented in the class: tigase.db.jdbc.TigaseCustomAuth. It allows you to connect to any external database to perform user authentication and use a custom queries for all actions.

You can find more details how to setup a custom connector in the Custom Authentication Connectors guide.

The basic configuration is very simple:

authRepository {
    default () {
        cls = 'tigase.db.jdbc.TigaseCustomAuth'
        'data-source' = 'default-auth'
    }
}

That’s it.

The connector loads correctly and starts working using predefined, default list of queries. In most cases you also might want to define your own queries in the configuration file. The shortest possible description is the following example of the content from the config.tdsl file:

This query is used to check connection to the database, whether it is still alive or not

authRepository {
    default () {
        'conn-valid-query' = 'select 1'
    }
}

This is database initialization query, normally we do not use it, especially in clustered environment

authRepository {
    default () {
        'init-db-query' = 'update tig_users set online_status = 0'
    }
}

Below query performs user authentication on the database level. The Tigase server does not need to know authentication algorithm or password encoding type, it simply passes user id (BareJID) and password in form which was received from the client, to the stored procedure. If the authentication was successful the procedure returns user bare JID or null otherwise. Tigase checks whether the JID returned from the query matches JID passed as a parameter. If they match, the authentication is successful.

authRepository {
    default () {
        'user-login-query' = '{ call TigUserLoginPlainPw(?, ?) }'
    }
}

Below query returns number of user accounts in the database, this is mainly used for the server metrics and monitoring components.

authRepository {
    default () {
      'users-count-query' = '{ call TigAllUsersCount() }'
    }
}

The Below query is used to add a new user account to the database.

authRepository {
    default () {
        'add-user-query' = '{call TigAddUserPlainPw(?, ?) }'
    }
}

Below query is used to remove existing account with all user’s data from the database.

authRepository {
    default () {
        'del-user-query' = '{ call TigRemoveUser(?) }'
    }
}

This query is used for the user authentication if user-login-query is not defined, that is if there is no database level user authentication algorithm available. In such a case the Tigase server loads user’s password from the database and compares it with data received from the client.

authRepository {
    default () {
        'get-password-query' = 'select user_pw from tig_users where user_id = ?'
    }
}

Below query is used for user password update in case user decides to change his password.

authRepository {
    default () {
        'update-password-query' = 'update tig_users set user_pw = ? where user_id = ?'
    }
}

This query is called on user logout event. Usually we use a stored procedure which records user logout time and marks user as offline in the database.

authRepository {
    default () {
        'update-logout-query' = 'update tig_users, set online_status = online_status - 1 where user_id = ?'
    }
}

This configuration specifies what non-sasl authentication mechanisms to expose to the client

authRepository {
    default () {
        'non-sasl-mechs' = [ 'password', 'digest' ]
    }
}

This setting to specify what sasl authentication mechanisms expose to the client

authRepository {
    default () {
        'sasl-mechs' = 'PLAIN,DIGEST-MD5'
    }
}

Queries are defined in the configuration file and they can be either plain SQL queries or stored procedures. If the query starts with characters: { call then the server assumes this is a stored procedure call, otherwise it is executed as a plain SQL query. Each configuration value is stripped from white characters on both ends before processing.

Please don’t use semicolon ; at the end of the query as many JDBC drivers get confused and the query may not work.

Some queries can take arguments. Arguments are marked by question marks ? in the query. Refer to the configuration parameters description for more details about what parameters are expected in each query.

This example shows how to use a stored procedure to add a user as a query with 2 required parameters (username, and password).

authRepository {
    default () {
        'add-user-query' = '{call TigAddUserPlainPw(?, ?) }'
    }
}

The same query with plain SQL parameters instead:

'add-user-query' = 'insert into users (user_id, password) values (?, ?)'

The order of the query arguments is important and must be exactly as described in specification for each parameter.

Query NameDescriptionArgumentsExample Query

conn-valid-query

Query executed periodically to ensure active connection with the database.

Takes no arguments.

select 1

init-db-query

Database initialization query which is run after the server is started.

Takes no arguments.

update tig_users set online_status = 0

add-user-query

Query adding a new user to the database.

Takes 2 arguments: (user_id (JID), password)

insert into tig_users (user_id, user_pw) values (?, ?)

del-user-query

Removes a user from the database.

Takes 1 argument: (user_id (JID))

delete from tig_users where user_id = ?

get-password-query

Retrieves user password from the database for given user_id (JID).

Takes 1 argument: (user_id (JID))

select user_pw from tig_users where user_id = ?

update-password-query

Updates (changes) password for a given user_id (JID).

Takes 2 arguments: (password, user_id (JID))

update tig_users set user_pw = ? where user_id = ?

user-login-query

Performs user login. Normally used when there is a special SP used for this purpose. This is an alternative way to a method requiring retrieving user password. Therefore at least one of those queries must be defined: user-login-query or get-password-query. If both queries are defined then user-login-query is used. Normally this method should be only used with plain text password authentication or sasl-plain. Tigase expects a result set with user_id to be returned from the query if login is successful and empty results set if the login is unsuccessful.

Takes 2 arguments: (user_id (JID), password)

select user_id from tig_users where (user_id = ?) AND (user_pw = ?)

user-logout-query

This query is called when user logs out or disconnects. It can record that event in the database.

Takes 1 argument: (user_id (JID))

update tig_users, set online_status = online_status - 1 where user_id = ?

non-sasl-mechs

Comma separated list of NON-SASL authentication mechanisms. Possible mechanisms are: password and digest. The digest mechanism can work only with get-password-query active and only when password are stored in plain text format in the database.

sasl-mechs

Comma separated list of SASL authentication mechanisms. Possible mechanisms are all mechanisms supported by Java implementation. The most common are: PLAIN, DIGEST-MD5, CRAM-MD5. "Non-PLAIN" mechanisms will work only with the get-password-query active and only when passwords are stored in plain text format in the database.

Drupal Authentication

Currently, we can only check authentication against a Drupal database at the moment. Full Drupal authentication is not implemented as of yet.

As Drupal keeps encrypted passwords in database the only possible authorization protocols are those based on PLAIN passwords.

To protect your passwords Tigase server must be used with SSL or TLS encryption.

Implementation of a Drupal database based authorization is located in tigase.db.jdbc.DrupalAuth class. Although this class is capable of adding new users to the repository I recommend to switch in-band registration off due to the caching problems in Drupal. Changes in database are not synchronized with Drupal yet. Functionality for adding new users is implemented only to ease user accounts migration from different repository types from earlier Tigase server installations.

The purpose of that implementation was to allow all accounts administration tasks from Drupal like: account creation, all accounts settings, like e-mail, full name, password changes and so on.

Tigase server uses following fields from Drupal database: name (user account name), pass (user account password), status (status of the account). Server picks up all changes instantly. If user status is not 1 then server won’t allow user to login trough XMPP even if user provides valid password.

There is no Roster management in Drupal yet. So Roster management have to be done from the XMPP client.

LDAP Authentication Connector

Tigase XMPP Server offers support for authenticating users against an LDAP server in Bind Authentication mode.

Configuration for the LDAP support is really simple you just have to add a few lines to your config.tdsl file.

authRepository {
    default () {
        cls = 'tigase.db.ldap.LdapAuthProvider'
        uri = 'ldap://ldap.tigase.com:389'
        'user-dn-pattern' = 'cn=USER_ID,ou=people,dc=tigase,dc=org'
    }
}

Please note the USER_ID element, this is a special element of the configuration which is used to authenticate particular user. Tigase LDAP connector replaces it with appropriate data during authentication. You can control what Tigase should put into this part. In your configuration you must replace this string with one of the following:

  1. %1$s - use user name only for authentication (JabberID’s localpart)
  2. %2$s - use domain name only for authentication (JabberID’s domain part)
  3. %3$s - use the whole Jabber ID (JID) for authentication

Configuration of SASL EXTERNAL

In order to enable SASL External add following line to the config.tdsl file

c2s {
    clientCertCA = '/path/to/cacert.pem'
}

File cacert.pem contains Certificate Authority certificate which is used to sign clients certificate.

Client certificate must include user’s Jabber ID as XmppAddr in subjectAltName:

As specified in RFC 3920 and updated in RFC 6120, during the stream negotiation process an XMPP client can present a certificate (a “client certificate”). If a JabberID is included in a client certificate, it is encapsulated as an id-on-xmppAddr Object Identifier (“xmppAddr”), i.e., a subjectAltName entry of type otherName with an ASN.1 Object Identifier of “id-on-xmppAddr” as specified in Section 13.7.1.4 of RFC 6120, XEP-0178.

It is possible to make client certificate required:

c2s {
    clientCertRequired = true
}

If this option will be enabled, then client must provide certificate. This certificate will be verified against clientCertCA. If client does not provide certificate or certificate will be invalid, TLS handshake will be interrupted and client will be disconnected.

Using this options does not force client to use SASL EXTERNAL. Client still may authenticate with other SASL mechanisms.