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.diffusion.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 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