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:

If you happen to keep the user data in the same database as user authentication data you can even skip the second parameter as Tigase automatically assumes settings from the '--user-db-uri' it '--auth-db-uri' is missing.

'--auth-db-uri' stored a standard JDBC connection URL and is exactly the same as for all other settings. For example if you store authentication data in a 'drupal' database on 'localhost' the URL might look like:

--auth-db-uri = jdbc:mysql://localhost/drupal?user=user&password=passwd

'--auth-db' stored just a connector name or connector implementation class. For convenience Tigase has predefined short names for the most common connectors but you can always use the class name if you know it. You have to use a class name if you want to attach your own authentication connector. The following 2 settings are equal:

--auth-db = tigase-auth
--auth-db = tigase.db.jdbc.TigaseAuth

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

--auth-db = drupal
--auth-db = tigase-custom

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 '--user-db-uri' 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:

--user-db-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 2 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:

--auth-db = tigase-custom
--auth-db-uri = jdbc:mysql://localhost/drupal?user=user&password=passwd

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 init.properties file:

# This query is used to check connection to the database, whether it is still alive or not
basic-conf/auth-repo-params/conn-valid-query=select 1

# This is database initialization query, normally we do not use it, especially in
# clustered environment
basic-conf/auth-repo-params/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.
# The Tigase checks whether the JID returned from the query matches
# JID passed as a parameter. If they match, the authentication is successful.
basic-conf/auth-repo-params/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.
basic-conf/auth-repo-params/users-count-query={ call TigAllUsersCount() }

# Below query is used to add a new user account to the database
basic-conf/auth-repo-params/add-user-query={ call TigAddUserPlainPw(?, ?) }

# Below query is used to remove existing account with all user's data from the database
basic-conf/auth-repo-params/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.
basic-conf/auth-repo-params/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
basic-conf/auth-repo-params/update-password-query=update tig_users set user_pw = ? where user_id = ?

# Below 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
basic-conf/auth-repo-params/user-logout-query=update tig_users, set online_status = online_status - 1 where user_id = ?

# This is configuration setting to specify what non-sasl authentication mechanisms
# expose to the client
basic-conf/auth-repo-params/non-sasl-mechs=password,digest

# This is configuration setting to specify what sasl authentication mechanisms expose to the client
basic-conf/auth-repo-params/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.

The first example shows how to put a stored procedure as a query with 2 required parameters.

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.

  • 'conn-valid-query' - Query executing periodically to ensure active connection with the database.

    Takes no arguments.

    Example query: 'select 1'

  • 'init-db-query' - Database initialization query which is run after the server is started.

    Takes no arguments.

    Example query: '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)

    Example query: 'insert into tig_users (user_id, user_pw) values (?, ?)'

  • 'del-user-query' - Removes a user from the database.

    Takes 1 argument: (user_id (JID))

    Example query: '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))

    Example query: '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))

    Example query: '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)

    Example query: '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))

    Example query: '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. Application: Tigase Server

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

From version 5.1.0, rev. (build) 2881 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 init.properties file.

# LDAP Authentication connector
--auth-db = tigase.db.ldap.LdapAuthProvider
# LDAP connection URI
--auth-db-uri=ldap://ldap.tigase.com:389
# LDAP access parameters
basic-conf/auth-repo-params/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 init.properties 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.[1]

It is possible to make client certificate required:

c2s/clientCertRequired[B]=true

If this option will be enabled, then client must provide certificate. This certificate will be verified against c2s/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.