Just a second...

Example: Register an authentication handler

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

Change the connection URL in the example to that of your Diffusion Cloud service and the name the handler registers with, example-handler to be either before-system-handler or after-system-handler depending on when you want the authentication handler to be called.

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)
}
.NET
/**
 * Copyright © 2021 - 2023 DiffusionData 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.
 */

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using PushTechnology.ClientInterface.Client.Callbacks;
using PushTechnology.ClientInterface.Client.Factories;
using PushTechnology.ClientInterface.Client.Features.Control.Clients;
using PushTechnology.ClientInterface.Client.Security.Authentication;
using PushTechnology.DiffusionCore.Client.Types;
using static System.Console;
using PushTechnology.ClientInterface.Client.Session;
using System;

namespace PushTechnology.ClientInterface.Example {
    /// <summary>
    /// Implementation of a client which authenticates other sessions using
    /// a registered authentication handler.
    /// </summary>
    public sealed class AuthenticationControl{
        public async Task AuthenticationControlExample(string serverUrl) {
            // Connect as a control session
            var session = Diffusion.Sessions.Principal( "control" ).Password( "password" )
                .CertificateValidation((cert, chain, errors) => CertificateValidationResult.ACCEPT)
                .Open(serverUrl);

            WriteLine("Opening control session.");

            IRegistration registration = null;

            try
            {
                registration = await session.AuthenticationControl.SetAuthenticationHandlerAsync(
                    "before-system-handler", new Authenticator(), CancellationToken.None );

                WriteLine("Authentication handler registered. Authenticator created.");

                Diffusion.Sessions.Principal("client")
                    .Credentials(Diffusion.Credentials.Password("password"))
                    .CertificateValidation((cert, chain, errors) 
                        => CertificateValidationResult.ACCEPT)
                    .Open(serverUrl, new SessionOpenCallback());

                await Task.Delay(TimeSpan.FromMilliseconds(2000), CancellationToken.None );
            } catch ( TaskCanceledException ) {
                //Task was cancelled; 
            } finally {
                WriteLine("Closing control session.");

                await registration.CloseAsync();
                session.Close();
            }
        }

        /// <summary>
        /// Callback used when a session is opened using ISessionFactory.Open
        /// </summary>
        private sealed class SessionOpenCallback : ISessionOpenCallback
        {
            public void OnError(ErrorReason errorReason) 
                => WriteLine($"An error occurred: {errorReason}");

            public void OnOpened(ISession session)
            {
                WriteLine("Other session opened.");

                session.Close();

                WriteLine("Other session closed.");
            }
        }

        /// <summary>
        /// Basic implementation of the control authenticator.
        /// </summary>
        private sealed class Authenticator : IControlAuthenticator {
            /// <summary>
            /// Method which decides whether a connection attempt should be allowed, denied or
            /// if another authenticator should evaluate this request.
            /// </summary>
            /// <param name="principal">The session principal.</param>
            /// <param name="credentials">The credentials.</param>
            /// <param name="sessionProperties">The session properties.</param>
            /// <param name="proposedProperties">The client proposed properties.</param>
            /// <param name="callback">The callback.</param>
            public void Authenticate(
                string principal,
                ICredentials credentials,
                IReadOnlyDictionary<string, string> sessionProperties,
                IReadOnlyDictionary<string, string> proposedProperties,
                IAuthenticatorCallback callback ) {

                switch ( principal ) {
                case "admin": {
                        WriteLine( "Authenticator allowing connection with proposed properties." );
                        callback.Allow( proposedProperties );
                        break;
                    }
                case "client": {
                        WriteLine( "Authenticator allowing connection with no properties." );
                        callback.Allow();
                        break;
                    }
                case "block": {
                        WriteLine( "Authenticator denying connection." );
                        callback.Deny();
                        break;
                    }
                default: {
                        WriteLine( "Authenticator abstaining." );
                        callback.Abstain();
                        break;
                    }
                }
            }

            /// <summary>
            /// Notification of authenticator closure.
            /// </summary>
            public void OnClose() => WriteLine( "Authenticator closed." );

            /// <summary>
            /// Notification of error.
            /// </summary>
            /// <param name="errorReason">Error reason.</param>
            public void OnError( ErrorReason errorReason ) 
                => WriteLine( $"Authenticator received an error: {errorReason}" );
        }
    }
}
Java and Android
/*******************************************************************************
 * Copyright (C) 2023 DiffusionData 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.
 *******************************************************************************/
package com.pushtechnology.client.sdk.manual;

import com.pushtechnology.diffusion.client.Diffusion;
import com.pushtechnology.diffusion.client.callbacks.ErrorReason;
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.AuthenticationException;
import com.pushtechnology.diffusion.client.session.Session;
import com.pushtechnology.diffusion.client.session.SessionFactory;
import com.pushtechnology.diffusion.client.types.Credentials;

import java.util.Map;

/**
 * This is a control client which registers an authentication handler with a
 * Diffusion server.
 * <P>
 * This uses the 'AuthenticationControl' feature.
 *
 * @author DiffusionData Limited
 */
public final class AuthenticationControlExample {

    public static void main(String[] args) {

        // create a SessionFactory and establish a control session
        final SessionFactory sessions = Diffusion.sessions();

        final Session controlSession = sessions
            .principal("control")
            .password("password")
            .open("ws://localhost:8080");

        // set our custom authenticator as the before-system-handler
        controlSession.feature(AuthenticationControl.class)
            .setAuthenticationHandler("before-system-handler", new MyAuthenticator()).join();

        // try to connect with a principal that is not allowed by our custom authenticator
        try {
            sessions.principal("client").open("ws://localhost:8080");
        }
        catch (AuthenticationException e) {
            System.out.println("This should fail: " + e.getMessage());
        }

        // connect with a principal that is allowed by our custom authenticator
        final Session session = sessions
            .principal("diffusion_client")
            .password("password")
            .open("ws://localhost:8080");

        System.out.printf("Connected as %s with id %s\n", session.getPrincipal(), session.getSessionId());

        session.close();
        controlSession.close();
    }

    private static final class MyAuthenticator implements ControlAuthenticator {

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

            // only allow connections from principals with the 'diffusion_' prefix
            if (!principal.startsWith("diffusion_")) {
                System.out.println("Principal does not begin with diffusion_ prefix. Connection Rejected.");
                callback.deny();
                return;
            }

            System.out.println("Principal begins with diffusion_ prefix. Connection Accepted.");
            callback.allow();
        }

        @Override
        public void onClose() { }

        @Override
        public void onError(ErrorReason errorReason) { }
    }
}
C
/**
 * Copyright © 2021 - 2023 DiffusionData 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 Diffusion Cloud . Diffusion Cloud service URLs end in diffusion.cloud