Just a second...

Example: Register an authentication handler

The following examples use the Diffusion™ API to register a control authentication handler with the Diffusion server. The examples also include a simple or empty authentication handler.

The name by which the control authentication handler is registered must be configured in the Server.xml configuration file of the Diffusion server for the control authentication handler to be called to handle authentication requests.

JavaScript
import * as diffusion from 'diffusion';

const PASSWORDS = {
    manager: 'password',
    guest: 'asecret',
    brian: 'boru',
    another: 'apassword'
};

/**
 * An example of a control authenticator.
 *
 * This shows a simple example using a table of permitted principals with
 * their passwords. It also demonstrates how the handler can change the
 * properties of the client being authenticated.
 */
const exampleAuthenticator = {
    authenticate: (principal, credentials, sessionProperties, proposedProperties, callback) => {
        const password = PASSWORDS[principal];

        if (password !== credentials) {
            if (principal === 'manager') {
                // manager allows all proposed properties
                callback.allow(proposedProperties);
            } else if (principal === 'brian') {
                // brian is allowed all proposed properties and also gets
                // the 'super' role added
                const result = { ...proposedProperties };
                const roles = diffusion.stringToRoles(sessionProperties.ROLES);
                roles.add('super');
                result.ROLES = diffusion.rolesToString(roles);
                callback.allow(result);
            } else {
                // all others authenticated but ignoring proposed properties
                callback.allow();
            }
        } else {
            // Any principal not in the table is denied.
            callback.deny();
        }
    },
    onClose: () => {
        console.log('The authenticator has disconnected');
    },
    onError: (err) => {
        console.log('An error occurred');
    }
};

/**
 * This is a control client which registers an authentication handler with a
 * Diffusion server.
 */
async function runExample() {
    const session = await diffusion.connect({
        principal : 'admin',
        credentials: 'password'
    });

    session.security.setAuthenticator('custom-authenticator', exampleAuthenticator)
}
Java and Android
package com.pushtechnology.diffusion.examples;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import com.pushtechnology.diffusion.client.Diffusion;
import com.pushtechnology.diffusion.client.callbacks.Stream;
import com.pushtechnology.diffusion.client.features.control.clients.AuthenticationControl;
import com.pushtechnology.diffusion.client.features.control.clients.AuthenticationControl.ControlAuthenticator;
import com.pushtechnology.diffusion.client.session.Session;
import com.pushtechnology.diffusion.client.types.Credentials;

/**
 * This is a control client which registers an authentication handler with a
 * Diffusion server.
 */
public final class ControlAuthenticationClient {

    /**
     * Main entry point for the control client.
     */
    public static void main(final String[] args) throws Exception {

        // The control client links to the server using the principal 'admin', which is
        // authenticated by the system authentication handler (see etc/SystemAuthentication.store).
        // The principal must have REGISTER_HANDLER and AUTHENTICATE permissions.
        final Session session =
            Diffusion.sessions()
                .principal("admin")
                .password("password")
                .open("ws://diffusion.example.com:80");

        session.feature(AuthenticationControl.class).setAuthenticationHandler(
            "after-system-handler",
            new ExampleControlAuthenticationHandler()).get(10, TimeUnit.SECONDS);

        while (true) {
            Thread.sleep(60000);
        }
    }

    /**
     * An example of a control authentication handler.
     * <P>
     * This shows a simple example using a table of permitted principals with
     * their passwords. It also demonstrates how the handler can change the
     * properties of the client being authenticated.
     */
    private static class ExampleControlAuthenticationHandler
        extends Stream.Default
        implements ControlAuthenticator {

        private static final Map<String, byte[]> PASSWORDS = new HashMap<>();
        static {
            PASSWORDS.put("manager", "password".getBytes(Charset.forName("UTF-8")));
            PASSWORDS.put("guest", "asecret".getBytes(Charset.forName("UTF-8")));
            PASSWORDS.put("brian", "boru".getBytes(Charset.forName("UTF-8")));
            PASSWORDS.put("another", "apassword".getBytes(Charset.forName("UTF-8")));
        }

        @Override
        public void authenticate(
            String principal,
            Credentials credentials,
            Map<String, String> sessionProperties,
            Map<String, String> proposedProperties,
            Callback callback) {

            final byte[] passwordBytes = PASSWORDS.get(principal);

            if (passwordBytes != null &&
                credentials.getType() == Credentials.Type.PLAIN_PASSWORD &&
                Arrays.equals(credentials.toBytes(), passwordBytes)) {
                if ("manager".equals(principal)) {
                    // manager allows all proposed properties
                    callback.allow(proposedProperties);
                }
                else if ("brian".equals(principal)) {
                    // brian is allowed all proposed properties and also gets
                    // the 'super' role added
                    final Map<String, String> result =
                        new HashMap<>(proposedProperties);
                    final Set<String> roles =
                        Diffusion.stringToRoles(
                            sessionProperties.get(Session.ROLES));
                    roles.add("super");
                    result.put(Session.ROLES, Diffusion.rolesToString(roles));
                    callback.allow(result);
                }
                else {
                    // all others authenticated but ignoring proposed properties
                    callback.allow();
                }
            }
            else {
                // Any principal not in the table is denied.
                callback.deny();
            }
        }
    }
}
                        
C
/**
 * Copyright © 2021 Push Technology Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <stdio.h>
#include <stdlib.h>

#ifndef WIN32
        #include <unistd.h>
#else
        #define sleep(x) Sleep(1000 * x)
#endif

#include "diffusion.h"

/*
 * Authentication handlers are registered with a name, which is typically specified in
 * Server.xml
 *
 * Two handler names are provided by default;
 *      - before-system-handler
 *      - after-system-handler
 *
 * Additional handlers may be specified for Diffusion through the Server.xml file
 * and an accompanying Java class that implements the AuthenticationHandler interface.
 *
 * This example will:
 *      - Deny all anonymous connections
 *      - Allow connections where the principal and credentials is in USERS
 *      - Abstain from all other decisions, thereby letting Diffusion and other
 *        authentication handlers decide what to do
 */
typedef struct user_credentials_s {
        const char *username;
        const char *password;
} USER_CREDENTIALS_T;


// Username/password pairs that this handler accepts.
static const USER_CREDENTIALS_T USERS[] = {
        { "manager", "password" },
        { "guest", "asecret" },
        { "brian", "boru" },
        { "another", "apassword" },
        { NULL, NULL }
};


static int on_authentication_handler_active(
        SESSION_T *session,
        const DIFFUSION_REGISTRATION_T *registered_handler)
{
        // authentication handler is now active
        return HANDLER_SUCCESS;
}


static int on_authentication_handler_error(const DIFFUSION_ERROR_T *error)
{
        // An error has occurred in the authentication handler
        return HANDLER_SUCCESS;
}


static void on_authentication_handler_close(void)
{
        // Authentication handler has been closed
}


static int on_registration_error(
        SESSION_T *session,
        const DIFFUSION_ERROR_T *error)
{
        // An error has occurred while registering the authentication handler
        return HANDLER_SUCCESS;
}


static int on_authenticate(
        SESSION_T *session,
        const char *principal,
        const CREDENTIALS_T *credentials,
        const HASH_T *session_properties,
        const HASH_T *proposed_session_properties,
        const DIFFUSION_AUTHENTICATOR_T *authenticator)
{
        if(principal == NULL || strlen(principal) == 0) {
                // Denying anonymous connection (no principal)
                diffusion_authenticator_deny(session, authenticator, NULL);
                return HANDLER_SUCCESS;
        }
        if(credentials == NULL) {
                // No credentials specified, abstaining
                // We're not an authority for this type of authentication so
                // abstain in case some other registered authentication handler can deal
                // with the request.
                diffusion_authenticator_abstain(session, authenticator, NULL);
                return HANDLER_SUCCESS;
        }
        if(credentials->type != PLAIN_PASSWORD) {
                // Credentials are not PLAIN_PASSWORD, abstaining
                diffusion_authenticator_abstain(session, authenticator, NULL);
                return HANDLER_SUCCESS;
        }

        char *password = calloc(credentials->data->len + 1, sizeof(char));
        memmove(password, credentials->data->data, credentials->data->len);

        int auth_decided = 0;
        int i = 0;
        while(USERS[i].username != NULL) {
                if(strcmp(USERS[i].username, principal) == 0 &&
                   strcmp(USERS[i].password, password) == 0) {
                        // Allow
                        diffusion_authenticator_allow(session, authenticator, NULL);
                        auth_decided = 1;
                        break;
                }
                i++;
        }

        if(auth_decided == 0) {
                // Abstain
                diffusion_authenticator_abstain(session, authenticator, NULL);
        }

        free(password);
        return HANDLER_SUCCESS;
}


int main(int argc, char** argv)
{
        const char *url = "ws://localhost:8080";
        const char *principal = "admin";
        const char *password = "password";

        CREDENTIALS_T *credentials = credentials_create_password(password);

        // Create a session, synchronously
        DIFFUSION_ERROR_T error = { 0 };
        SESSION_T *session = session_create(
                url,
                principal,
                credentials,
                NULL,
                NULL,
                &error);
        if (session == NULL) {
                fprintf(stderr, "TEST: Failed to create session\n");
                fprintf(stderr, "ERR : %s\n", error.message);
                return EXIT_FAILURE;
        }

        // create the authentication handler
        DIFFUSION_AUTHENTICATION_HANDLER_T handler = {
                .handler_name = "before-system-handler",
                .on_active = on_authentication_handler_active,
                .on_authenticate = on_authenticate,
                .on_error = on_authentication_handler_error,
                .on_close = on_authentication_handler_close
        };

        // set the authentication handler
        DIFFUSION_AUTHENTICATION_HANDLER_PARAMS_T params = {
                .handler = &handler,
                .on_error = on_registration_error
        };

        diffusion_set_authentication_handler(session, params);

        // Wait a while before closing the session
        sleep(5);

        // Close the session, and release resources and memory
        session_close(session, NULL);
        session_free(session);

        credentials_free(credentials);

        return EXIT_SUCCESS;
}

Change the URL from that provided in the example to the URL of the Diffusion server.