[[saslcmac]]
= SASL Custom Mechanisms and Configuration
:author: Andrzej Wójcik <andrzej.wojcik@tigase.net>
:version: v2.2 July 2020. Updated for v8.0.0 and newer.

:toc:
:numbered:
:website: http://tigase.net/

*This API is available from Tigase XMPP Server version 5.2.0 or later on our current master branch.*

*In version 8.0.0 there was a major change to the API and configuration of custom SASL mechanisms.*

_Note that API is under active development. This description may be updated at any time._

== Basic SASL Configuration
SASL implementation in Tigase XMPP Server is compatible with Java API, the same exact interfaces are used.

The SASL implementation consists of following parts:

. mechanism
. CallbackHandler

[[mechconf]]
=== Mechanisms Configuration
To add a new mechanism, a new factory for the mechanism has to be implemented and registered.

The simplest way to add register a new factory is to annotate its class with `@Bean` annotation:

.Example of the registration of a SASL mechanism factory with an annotation setting id of the factory to `customSaslFactory`
[source,java]
----
@Bean(name="customSaslFactory", parent = TigaseSaslProvider.class, active = true)
public class OwnFactory implements SaslServerFactory {}
----

It can also be done by specifying the class directly for bean `customSaslFactory` in the `config.tdsl` file like in the example below:

.Example of the registration of a SASL mechanism factory with TDSL setting id of the factory to `customSaslFactory`
[source,dsl]
-----
'sess-man' () {
    'sasl-provider' () {
        customSaslFactory(class: com.example.OwnFactory) {}
    }
}
-----

The class must implement the `SaslServerFactory` interface and has public constructor without any arguments. All mechanisms returned by `getMechanismNames()` method will be registered automatically.

The default factory that is available and registered by default is `tigase.auth.TigaseSaslServerFactory` which provides `PLAIN`, `ANONYMOUS`, `EXTERNAL`, `SCRAM-SHA-1`, `SCRAM-SHA-256` and `SCRAM-SHA-512` mechanisms.

[[cbconf]]
=== CallbackHandler Configuration
The `CallbackHandler` is a helper class used for loading/retrieving authentication data from data repository and providing them to a mechanism.

To register a new callback handler you need to create a new class extending `tigase.auth.CallbackHandlerFactory` (if you wish to keep existing SASL callback handlers) or implementing `tigase.auth.CallbackHandlerFactoryIfc`.
You will need to override `create()` method to return an instance of your custom `CallbackHandler` when appropriate.

Next you need to register new implementation of `CallbackHandlerFactoryIfc`. The `config.tdsl` file should include:
[source,dsl]
-----
'sess-man' () {
    'sasl-provider' () {
        callback-handler-factory(class: com.example.OwnCallbackHandlerFactory) {}
    }
}
-----

During the authentication process, Tigase server always checks for asks callback handler factory for specific handler to selected mechanisms, and if there is no specific handler the default one is used.

[[selmech]]
=== Selecting Mechanisms Available in the Stream
The `tigase.auth.MechanismSelector` interface is used for selecting mechanisms available in a stream. Method `filterMechanisms()` should return a collection with mechanisms available based on:

. all registered SASL factories
. XMPP session data (from `XMPPResourceConnection` class)

The default selector returns mechanisms from all mechanism factories registered in `sasl-provider` `(TigaseSaslProvider)`.

It is possible to use a custom selector by specifying it's class int the `config.tdsl` file:

[source,dsl]
-----
'sess-man' () {
    'sasl-provider' () {
        'mechanism-selector'(class: com.example.OwnSelector) {}
    }
}
-----

== Logging/Authentication
After the XMPP stream is opened by a client, the server checks which SASL mechanisms are available for the XMPP session. Depending on whether the stream is encrypted or not, depending on the domain, the server can present different available authentication mechanisms. `MechanismSelector` is responsible for choosing mechanisms. List of allowed mechanisms is stored in the XMPP session object.

When the client/user begins the authentication procedure it uses one particular mechanism. It must use one of the mechanisms provided by the server as available for this session. The server checks whether mechanisms used by the client is on the list of allowed mechanisms. It the check is successful, the server creates `SaslServer` class instance and proceeds with exchanging authentication information. Authentication data is different depending on the mechanism used.

When the SASL authentication is completed without any error, Tigase server should have authorized user name or authorized BareJID. In the first case, the server automatically builds user's JID based on the domain used in the stream opening element in `to` attribute.

If, after a successful authentication, method call: `getNegotiatedProperty("IS_ANONYMOUS")` returns `Boolean.TRUE` then the user session is marked as anonymous. For valid and registered users this can be used for cases when we do not want to load any user data such as roster, vcard, privacy lists and so on. This is a performance and resource usage implication and can be useful for use cases such as support chat. The authorization is performed based on the client database but we do not need to load any XMPP specific data for the user's session.

More details about implementation can be found in the xref:cmd[custom mechanisms development] section.

// == Built-in Mechanisms
// *PLAIN*
// _TODO!_
// *ANONYMOUS*
// _TODO!_

[[cmd]]
== Custom Mechanisms Development
=== *Mechanism*

`getAuthorizationID()` method from `SaslServer` class *should* return bare JID authorized user. In case that the method returns only user name such as *romeo* for example, the server automatically appends domain name to generate a valid BareJID: _romeo@example.com_. In case the method returns a full, valid BareJID, the server does not change anything.

`handleLogin()` method from `SessionManagerHandler` will be called with user's Bare JID provided by `getAuthorizationID()` (or created later using stream domain name).

=== *CallbackHandler*

For each session authorization, the server creates a new and separate empty handler. Factory which creates handler instance allows to inject different objects to the handler, depending on interfaces implemented by the handler class:

- `AuthRepositoryAware` - injects `AuthRepository;`
- `DomainAware` - injects domain name within which the user attempts to authenticate
- `NonAuthUserRepositoryAware` - injects `NonAuthUserRepository`

=== General Remarks

`JabberIqAuth` used for non-SASL authentication mechanisms uses the same callback as the SASL mechanisms.

Methods `auth` in `Repository` interfaces will be deprecated. These interfaces will be treated as user details providers only. There will be new methods available which will allow for additional login operations on the database such as last successful login recording.
