Login

DraftAccessible

Login components include React components, context and hooks for handling user authorisation, api tokens and session polling.

A work in progress!
The HDS Login system is a set of components the HDS team is currently making. This means that this component is subject to change, and we don't recommend using it in production.

Creating and connecting your own module

If there is a need for a deeper connection to other modules, a custom module can be created. Modules can emit signals. Modules are not React-specific, just plain javascript. A module passed to the Beacon must have the following properties:

Table 1: Custom module properties
PropertyDescription
connect(beacon)Function called when a module is added to the Beacon.
namespaceString. A unique namespace.
import { createNamespacedBeacon, ConnectedModule } from 'hds-react';
const createMyCustomModule = (): ConnectedModule => {
const customBeacon = createNamespacedBeacon('myModule');
const listener = (signal) => {
// This is listenening everything
};
return {
namespace: helperNamespace,
connect: (targetBeacon) => {
customBeacon.storeBeacon(targetBeacon);
customBeacon.addListener(createTriggerForAllSignalTypes(), listener);
},
};
};

Connecting your module

Modules are automatically connected to other modules when they are passed in the modules property of the LoginProvider.

Module.connect() is called and when all modules are connected, an init signal is emitted for each module. This is done by the Beacon.

import { createNamespacedBeacon, ConnectedModule } from 'hds-react';
const myModule = createMyCustomModule()
const providerProperties = {..., modules=[myModule]}
<LoginProvider {...providerProperties}>
<Content />
</LoginProvider>

Custom namespaced beacons for modules

Modules must emit signals in their namespace. Beacon has only one emit function. To make emitting easier, the createNamespacedBeacon returns a utility where all emitted signals have a pre-set namespace and there are emit functions for different signal types.

This utility also handles cases where listeners are added before the module is connected to the Beacon.

It also ignores signals from its namespace when listening to generic signals.

Table 2: Custom beacon utility functions
NameDescriptionReturn values
addListener(signalProps, listener)Adds a listener for given signal props. Module's own signals are filtered out if the namespace is for all signals.A disposer function
emit(signal)Calls the beacon.emit, if the Beacon is connected.none
emitError(payload)Emits an error with the given payload.Context module or undefined
emitEvent(payload, data)Emits an event with the given type and data in the payload.Object
emitStateChange(state,previousState)Emits a stateChange with the given state and previousState in the payload.none
storeBeacon(beacon)Beacon. Call this from the module.connect(). Stores the beacon, so the module does not have to store it.none
import { createNamespacedBeacon, ConnectedModule } from 'hds-react';
const createMyCustomModule = (): ConnectedModule => {
let state = 'none';
const customBeacon = createNamespacedBeacon('myModule');
const listener = (signal) => {
if (isErrorSignal) {
newState = 'error';
customBeacon.emitStateChange(newState, state);
state = newState;
}
};
return {
namespace: helperNamespace,
connect: (targetBeacon) => {
customBeacon.storeBeacon(targetBeacon);
customBeacon.addListener(createTriggerForAllSignalTypes(), listener);
},
};
};

Emitting signals

The following example is for custom modules because components cannot emit signals.

// Beacon is not imported, this example assumes it is in the scope
const error = new OidcClientError('Failed', oidcClientError.SIGNIN_ERROR),
// This is the same as
beacon.emit({
type: errorSignalType,
namespace: 'teamHDS',
payload: error,
});
// This
beacon.emit(createErrorSignal('teamHDS', error));
// and this - if custom, namespaced beacon is created for 'teamHDS'
customBeacon.emitError(error);

Listening to signals

A signal listener is a function that receives one argument: the signal. A listener can listen to all signals or just one type of signal with a certain namespace. Listeners can be even more specific and listen to signals with certain payloads. In short, a listener can listen to any properties of the signal and is triggered when all properties match.

The listener is called only if the emitted signal matches the given props.

For example, if the trigger props (the first argument) passed to beacon.addListener(trigger, listener) is { type:'error' }, the listener (second argument) is called when the emitted signal has a matching type. It does not matter what other props the signal has.

If the trigger props are { namespace:'myModule', payload:{type:'click'} }, the emitted signal must have those properties with the same, exact values. Other properties are not checked.

The trigger can also be a function. Internally all triggers are converted to functions.

const listener = (signal) => {
// Do something with the signal.
};
// Listen to all error signals
const trigger = { type: 'error' };
const disposer = beacon.addListener(trigger, listener);
// The listener can be removed by calling the returned disposer
disposer();