Login

DraftAccessible

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

API

Table of contents

Components

LoginButton

PropertyDescriptionDefault
errorTextRequired. An error text is shown if the OIDC server rejects the connection.none
loggingInTextRequired. Text read by the screen reader when the button is clicked.Default of the "loadingText" property of the LoadingSpinner
spinnerColorOptional. Colour of the loading spinner.var(--color-white)
redirectionPropsProperties appended to the url when redirecting. Read more in the documentation of the oidcClient login method.none
onClickCalled when the component is clicked and before the oidcClient.login() is called.none

| [Table 2: Properties of the LoginButton component] |

Button text is passed and rendered as a child.

LoginCallbackHandler

Table 3: Properties of the LoginCallbackHandler component
PropertyDescription
childrenOptional. Usually, child elements inform the user that a response is being processed.
onErrorRequired. The function is called when a login fails. The function is called with an OidcClientError.
onSuccessRequired. The function is called when a response is handled successfully. The function is called with a User object.

In some scenarios, when the component is rendered multiple times, usually in development mode with strict mode enabled, the onError callback is called with an error that can usually be ignored. The error indicates a login callback is already being handled. The error can be checked with the exported function isHandlingLoginCallbackError(error).

LoginProvider

Table 4: Properties of the LoginProvider component
PropertyDescription
apiTokensClientSettingsOptional. Settings for the Api tokens client. If omitted, the Api tokens client is not created.
debugOptional. If true, debugging is enabled. See Oidc client settings.
modulesOptional. An array of custom modules.
sessionPollerSettingsOptional. Settings for the Session poller. If omitted, the Session poller is not created. If default values are acceptable, an empty object is required to create the poller.
userManagerSettingsRequired. The HDS Oidc client uses oidc-client-ts to handle redirect URLs, token parsing and expiration. Both use the same userManagerSettings, but different defaults.

SessionEndedHandler

Table 5: Properties of the SessionEndedHandler component
PropertyDescription
contentRequired. Textual content with properties title, text, buttonText and closeButtonLabelText.
dialogPropsOptional. Additional DialogProps passed to the Dialog.
idsOptional. Ids for the dialog elements with properties dialog, title, and text. If omitted, ids are created.

WithAuthentication

Table 6: Properties of the WithAuthentication component
PropertyDescription
AuthorisedComponentOptional. Component to render if the user is authenticated. The user object is passed to the component.
UnauthorisedComponentOptional. Component to render if the user is not authenticated.

WithAuthenticatedUser

Table 7: Properties of the WithAuthenticatedUser component
PropertyDescription
childrenReact children. A user object is passed to each child.

WithoutAuthenticatedUser

Table 8: Properties of the WithAuthenticatedUser component
PropertyDescription
childrenReact children.

Modules

Usually, just logging in is not enough. The user must be able to fetch data from backend servers. That cannot be done unless the user's access token is exchanged for API tokens. Api tokens are sent in the headers of backend requests to authenticate the user. HDS Login modules include also API token handling. It is an independent module, but it communicates with the Oidc client and updates automatically when the user logs in or is updated. The modules emit signals that can be listened to and reacted to.

There are four modules to handle the user's needs:

  • Oidc client for user data.
  • Api tokens client for acquiring backend tokens.
  • Session poller for checking if the user's session is still valid at the Oidc provider.
  • GraphQL module for fetching data from a graphQL server. Fetching can be automatically linked to the Api Tokens client.

All modules can be used without React and without each other. But a >user object is required to get API tokens and poll the session.

Oidc client

The Oidc client uses the oidc-client-ts and has the same settings but different defaults.

Settings
Table 9: Settings of the Oidc client
PropertyDefault value
debugOptional. If true, debugging is enabled in the oidc-client-ts instance with Log.setLevel(Log.Debug) and Log.setLogger(console).
userManagerSettingsRequired, but not all options. At least authority, client_id and scope must be set.
Default userManager settings
Table 10: Default userManager settings of the Oidc client
PropertyDefault value
automaticSilentRenewtrue
includeIdTokenInSilentRenewtrue
loadUserInfotrue
monitorSessionfalse
post_logout_redirect_uriwindow.origin + /
redirect_uriwindow.origin + /callback
response_typecode
silent_redirect_uriwindow.origin + /silent_renew.html
validateSubOnSilentRenewfalse

These override the default settings of the oidc-client-ts.

Methods
Table 11: Methods of the Oidc client
PropertyDescriptionReturn value
getAmrReturns the user's amr value(s) as an array. Tunnistamo returns the amr as a string, which oidc-client-ts rejects because it violates the OIDC specification. Then the amr value is read from the decrypted id_token.Amr or undefined
getStateState of the client. See Oidc client states.State
getTokenReturns a token. The function is async because token renewal might be in progress and the current token might be outdated after that. If renewal was in progress but failed, then the old token is returned. Use isRenewing() and the exported function getUserToken(user, tokenType) to get the token synchronously and safely. The Promise will never be rejected.string or undefined
getUserReturns a user object or null.User or null
getUserManagerReturns the UserManager.UserManager
handleCallbackHandles the callback data and returns a user object - if the user is valid.Promise<User> or Promise<OidcClientError>
isAuthenticatedReturns true if the user exists and the user object passes isValidUser() check.boolean
isRenewingReturns true if the user renewal is in progress.boolean
loginCalls the authorization_endpoint with given parameters. The browser window is redirected and the returned promise is fulfilled only, if an error occurs when redirecting. Parameters are documented in the UserManager.signinRedirect of the oidc-client-ts. Used language can be set with a custom language prop.Promise<never>
logoutCalls the end_session_endpoint with given parameters. The browser window is redirected so the returned promise is never fulfilled. Parameters are documented in the UserManager.signoutRedirect of the oidc-client-ts. Used language can be set with a custom language prop.Promise<never>
renewUserFor manual user renewal. The Promise will never be rejected.Promise<User>
Other exported utility functions

The following functions can be imported and used without the Oidc client module:

Table 12: Other Oidc client and user-related functions
FunctionDescriptionArgument(s)Return value
createOidcClient(props)Creates the Oidc client.Oidc client props.OidcClient
getUserStoreKey(settings)Returns the session storage key for the user data.UserManagerSettings object.string
getUserFromStorage(settings, storage)Returns user data from storage.UserManagerSettings object, storage to get the user from. (optional, default is session storage).User or null
isUserExpired(user)Returns true if the user is expired.User object.boolean
isValidUser(user)Returns true if the user is not expired and has an access token.User object.boolean
pickUserToken(user, tokenType)Returns one of the user's tokens: access, id or refresh.User object, token type ( access, id or refresh).string or undefined
import { isUserExpired } from 'hds-react';
if (isUserExpired(user)) {
// Show an error
}
State change signals

When the user does something, the Oidc client's state changes and the UI can indicate it by reacting to the state change signals. The simplest way to react to signals is useOidcClientTracking hook. The current state can also be read with oidcClient.getState().

There are utility functions for each state change. Use these to check if the emitted signal contains that state.

Table 13: Oidc client states
StateDescriptionUtility function to check the signal
HANDLING_LOGIN_CALLBACKThe browser has returned from an Oidc provider and data is being parsed.isHandlingLoginCallbackSignal(signal)
LOGGING_INState changes to this when login() is called.isLoggingInSignal(signal)
LOGGING_OUTState changes to this when logout() is called.isLoggingOutSignal(signal)
NO_SESSIONNo valid user exists.isNoSessionSignal(signal)
VALID_SESSIONUser is found and is valid.isValidSessionSignal(signal)
Error signal types

Errors are rarely thrown; they are emitted as error signals where the error object is in the signal payload. Errors are instances of the OidcClientError. It is an extended Error object with a type property and multiple .is... getters for checking error types. The error object also contains the thrown error in error.originalError property.

Table 14: Oidc client errors
Error typeDescriptionError object property to checkUtility function to check the signal
INVALID_OR_EXPIRED_USERThe returned user object is expired or invalid.error.isInvalidUserErrorisInvalidUserErrorSignal(signal)
RENEWAL_FAILEDUser renewal failed.error.isRenewalErrorisRenewalErrorSignal(signal)
SIGNIN_ERROREmitted when handleCallback() or login() failed.error.isSignInErrorisSigninErroSignal(signal)
Event signals

The Oidc client also emits event signals when an action is complete or starting. The difference between state changes and events is the UI does not usually need to react to events.

Table 15: Oidc client events
Event typeDescriptionUtility function to check the signal
USER_UPDATEDUser data has changed. Includes also changes to null, if login or renewal fail.isUserUpdatedSignal(signal)
USER_REMOVEDUser data was removed. Happens when the user logs out or tokens expire.isUserRemovedSignal(signal)
USER_RENEWAL_STARTEDUser is being renewed. Current access_token and API tokens may be invalid until the renewal is complete.isUserRenewalStartedSignal(signal)
Dedicated signal triggers

Generic triggers for Oidc client signals. These trigger only if the signal originated from the Oidc client.

Table 16: Oidc client triggers
Generic triggersRequired signal props to trigger the listener
triggerForAllOidcClientErrorsOidc client namespace and type error.
triggerForAllOidcClientEventsOidc client namespace and type event.
triggerForAllOidcClientSignalsOidc client namespace.
triggerForAllOidcClientStateChangesOidc client namespace and type stateChange.

State change triggers require the signal to have the Oidc client namespace and type stateChange. Signal.payload.type must contain one of the states.

Table 17: Oidc client state change triggers
State change triggersRequired signal props to trigger the listener
loggingInTriggerpayload.type has a matching state.
loggingOutTriggerpayload.type has a matching state.
noSessionTriggerpayload.type has a matching state.
validSessionTriggerpayload.type has a matching state.

Event triggers require the signal to have the Oidc client namespace and type event. Signal.payload.type contains one of the events.

Table 18: Oidc client event triggers
Event triggersRequired signal props to trigger the listener
userRemovedTriggerpayload.type has a matching event.
userRenewalStartedTriggerpayload.type has a matching event.
userUpdatedTriggerpayload.type has a matching event.

Error triggers require the signal to have the Oidc client namespace and type error. Signal.payload.type contains one of the errors.

Table 19: Oidc client error triggers
Error triggersRequired signal props to trigger the listener
invalidUserErrorTriggerpayload.type has a matching error.
renewalErrorTriggerpayload.type has a matching error.
signInErrorTriggerpayload.type has a matching error.
Getting signal payloads

Sometimes the most significant part of a signal is the payload. There are predefined payload getters for the Oidc client. If the given signal is not from Oidc client or is not of the given type, the function returns null. So there is no need to pre-check the signal namespace or type.

Table 20: Oidc client signal payload getters
Payload getterReturns
getOidcClientEventPayloadnull or {type, data}. The data is usually a user object.
getOidcClientErrorPayloadnull or OidcClientError object.
getOidcClientStateChangePayloadnull or {state, previousState}.
Hooks

Oidc client hooks are listed in the hooks section.

Api tokens client

The Api tokens client listens to the Oidc client signals and waits until the user is authenticated and then fetches the API tokens. It retries if fetch fails and terminates when the maximum number of retries is reached. When the user is about to expire and renews, API tokens are also renewed automatically.

When user renewal starts, API tokens are also removed to prevent usage of API tokens that are not possibly valid anymore. Use isRenewing() to check if API tokens are being renewed, and if it returns 'false', then use the API tokens returned by getTokens().

Requirements

URL where to get the tokens from. A user's access token is also required, so the user must be authenticated to fetch API tokens.

Settings
Table 21: Api tokens client settings
PropertyDescriptionDefault value
audiencesOptional. An array of strings. Used with Helsinki Profile api token requests. One token is fetched for each audience.none
maxRetriesOptional. The number of max retries until fetching is stopped.4
queryPropsOptional. An object. Used with Helsinki Profile api token requests. Contains properties grantType and permission.none
retryIntervalOptional. Waiting time in milliseconds between failed requests.500
urlRequired. URL to the API tokens endpoint.none
Methods
Table 22: Api tokens client methods
PropertyDescriptionReturn value
clearCancels fetch and removes all data.none
fetchFetches the tokens from the given URI.Promise<Tokens>
getTokensGets locally stored tokens.Tokens
isRenewingReturns true if tokens are currently fetched.boolean
Other exported utility functions

The following functions can be imported and used without the Api tokens client module:

Table 23: Other Api tokens client-related functions
FunctionDescriptionArgument(s)Return value
createApiTokenClient(props)Creates the client.ApiTokenClientProps.ApiTokensClient
getApiTokensFromStorage(storage)Gets the API tokens from session storage.Storage to get the tokens from. (optional, default is session storage).TokenData or null
getUserReferenceFromStorage(storage)Gets the user's access token used as reference.Storage to get the reference from. (optional, default is session storage).string
removeApiTokensFromStorage(storage)Removes the API tokens from session storage.Storage to remove the tokens from. (optional, default is session storage).none
removeUserReferenceFromStorage(storage)Removes the user's access token used as reference.Storage to remove the reference from. (optional, default is session storage).none
setApiTokensToStorage(tokens, storage)Sets the API tokens to storage.Tokens, storage to set the tokens to. (optional, default is session storage).none
setUserReferenceToStorage(reference,storage)Sets the user's access token used as reference.Reference as string, tokens, storage to set the tokens to. (optional, default is session storage).none
import { getApiTokensFromStorage } from 'hds-react';
const apiTokens = getApiTokensFromStorage();
if (apiTokens) {
// Use tokens
}
State change signals

The Api tokens client has no state changes.

Error signal types

Returned errors are instances of the ApiTokensClientError. It is an extended Error object with type property and multiple .is... getters for checking error types. The error object also contains the thrown error in error.originalError property.

Table 24: Api tokens client errors
Error typeDescriptionError object property to checkUtility function to check the signal
API_TOKEN_FETCH_FAILEDFetch failed.error.isFetchErrorisApiTokensFetchFailedErrorSignal(signal)
INVALID_API_TOKENSThe returned object is an invalid json.error.isInvalidTokensErrorisInvalidApiTokensErrorSignal(signal)
INVALID_USER_FOR_API_TOKENSUser is not valid.error.isInvalidApiTokensUserErrorisInvalidApiTokensUserErrorSignal(signal)
Event signals

The Api tokens client also emits events when an action is complete or starting.

Table 25: Api tokens client events
Event typeDescriptionUtility function to check the signal
API_TOKENS_UPDATEDTokens have changed. Includes also changes to null.isApiTokensUpdatedSignal(signal)
API_TOKENS_REMOVEDTokens have been removed. Happens usually when the user object has been removed or is renewed.isApiTokensRemovedSignal(signal)
API_TOKENS_RENEWAL_STARTEDApi tokens are renewed. Happens usually right after user renewal.isApiTokensRenewalStartedSignal(signal)
Dedicated signal triggers

Triggers for Api tokens client signals. These trigger signals only originated from the Api tokens client.

Table 26: Api tokens client triggers
Generic triggersRequired signal props to trigger the listener
triggerForAllApiTokensClientErrorsApi tokens client namespace and type error.
triggerForAllApiTokensClientEventsApi tokens client namespace and type event.
triggerForAllApiTokensClientSignalsApi tokens client namespace.

Event triggers require the signal to have Api tokens client namespace and type event. Signal.payload.type contains one of the events.

Table 27: Api tokens client event triggers
Event triggersRequired signal props to trigger the listener
apiTokensRemovedTriggerpayload.type has a matching event.
apiTokensRenewalStartedTriggerpayload.type has a matching event.
apiTokensUpdatedTriggerpayload.type has a matching event.

Error triggers require the signal to have Api tokens client namespace and type error. Signal.payload.type contains one of the errors.

Table 28: Api tokens client error triggers
Error triggersRequired signal props to trigger the listener
apiTokensFetchFailedErrorTriggerpayload.type has a matching error.
invalidApiTokensErrorTriggerpayload.type has a matching error.
invalidApiTokensUserErrorTriggerpayload.type has a matching error.
Getting signal payloads

Sometimes the most significant part of a signal is the payload. There are predefined payload getters for the Api tokens client. If the given signal is not from Api tokens client or is not given type, the function returns null. So there is no need to pre-check the signal namespace or type.

Table 29: Api tokens client signal payload getters
Payload getterReturns
getApiTokensClientErrorPayloadnull or ApiTokensClientError object.
getApiTokensClientEventPayloadnull or {type, data}. The data is usually tokens.
Hooks

Api tokens client hooks are listed in the hooks section.

Session poller

The user's session could end outside of the current browser window. The Session poller calls the userinfo endpoint and notifies when it receives an unauthorized or forbidden response. Successes and other errors are ignored.

Requirements

Polling requires a user and the userManager from the Oidc client. The polling endpoint is fetched from userManager's metadata.

Settings
Table 30: Session poller settings
PropertyDescriptionDefault value
pollIntervalInMsOptional. Waiting time between polls in milliseconds. The default is one minute.60000
Methods
Table 31: Session poller methods
PropertyDescriptionReturn value
startStarts the polling. The first check is done after pollIntervalInMs is expired. Not immediately.
stopStops the polling and aborts current requests, if any.Tokens
Other exported utility functions

The following function can be imported and used without the Session poller module:

Table 32: Other Session poller functions
PropertyDescriptionArgument(s)Return value
createSessionPoller(options)Creates the poller.SessionPollerOptions.SessionPoller
import { createSessionPoller } from 'hds-react';
const sessionPoller = createSessionPoller();
State change signals

The Session poller has no state changes.

Error signal types

Returned errors are instances of the SessionPollerError. It is an extended Error object with type property and multiple .is... getters for checking error types. The error object also contains the thrown error in error.originalError property.

Table 33: Other Session poller error types
Error typeDescriptionError object property to checkUtility function to check the signal
SESSION_ENDEDThe server returned a forbidden or unauthorized status code.error.isSessionEndedisSessionEndedSignal(signal)
SESSION_POLLING_FAILEDPolling has failed and max retries have been reached.error.isSessionPollingFailureisSessionPollingFailureSignal(signal)
Event signals

The Session poller also emits events when an action is complete or starting.

Table 34: Other Session poller event types
Event typeDescriptionUtility function to check the signal
SESSION_POLLING_STARTEDPolling has started.isSessionPollerStartedSignal(signal)
SESSION_POLLING_STOPPEDPolling has stopped.isSessionPollerStoppedSignal(signal)
Dedicated signal triggers

Triggers for Session pollersignals. These trigger signals only originated from the Session poller.

Table 35: Session poller triggers
Generic triggersRequired signal props to trigger the listener
triggerForAllSessionPollerErrorsSession poller namespace and type error.
triggerForAllSessionPollerEventsSession poller namespace and type event.
triggerForAllSessionPollerSignalsSession poller namespace.

Event triggers require the signal to have a Session poller namespace and type event. Signal.payload.type contains one of the events.

Table 36: Session poller event triggers
Event triggersRequired signal props to trigger the listener
sessionPollingStartedTriggerpayload.type has a matching event.
sessionPollingStoppedTriggerpayload.type has a matching event.

Error triggers require the signal to have a Session pollernamespace and type error. Signal.payload.type contains one of the errors.

Table 37: Session poller error triggers
Error triggersRequired signal props to trigger the listener
sessionEndedTriggerpayload.type has a matching error.
sessionPollingFailedTriggerpayload.type has a matching error.
Getting signal payloads

Sometimes the most significant part of a signal is the payload. There are predefined payload getters for the Session poller. If the given signal is not from the Session poller or is not given type, the function returns null. So there is no need to pre-check the signal namespace or type.

Table 38: Session poller signal payload getters
Payload getterReturns
getSessionPollerErrorPayloadnull or SessionPollerError object.
getSessionPollerEventPayloadnull or {type, data}. The data is usually tokens.

GraphQL Module

Loading data from a graphQL server may require user authentication and api tokens. The GraphQL module can be set to listen to the Api token client and start querying when api tokens are ready. It can pick the correct api token and set it to the query headers.

The module can also be used without api tokens and can be controlled manually.

Unlike the Api token client and Session Poller, the GraphQL Module must be manually added to the <LoginProvider>.

import { ApolloClient } from '@apollo/client/core';
import { createGraphQLModule } from 'hds-react';
import { queryDocument } from './<path to queries>';
const graphQLClient = new ApolloClient({
uri: 'https://graphql.endpoint.com/graphql/',
cache: new InMemoryCache(),
});
const options = {
graphQLClient,
query: queryDocument,
queryOptions: {
errorPolicy: 'all',
},
};
const graphQLModule = createGraphQLModule(options);
<LoginProvider {...loginProps} modules={[graphQLModule]}></LoginProvider>;
Requirements

To execute a query, the following requirements must be met:

  • A graphQLClient (ApolloClient) must be configured and passed to the GraphQL module.
  • A query document must be provided.

If the graphQL end-point requires an authentication, then the authentication headers must be set by

  • Providing them in queryOptions.header.
  • Setting them in queryHelper().
  • Requiring api tokens with options.requireApiTokens and setting options.apiTokenKey.
Important

The module props do not require a query document or a graphQLClient, but a query cannot be executed without them. Both can also be passed as arguments to the query(options). The client can also be set by calling setClient(client).

Once set, the client is stored, and there is no need to pass it again. The query document is not stored, so different queries can be used every time.

When used with Helsinki City Profile, the queryOptions.errorPolicy should be set to "all". Otherwise, queries will fail when a weakly authenticated user tries to get the profile. The resulting data will include (dismissable) errors, and an ApolloClient query will fail when the data has an error property.

The createGraphQLModule function is a Generic Typescript function with two types: createGraphQLModule<Q = GraphQLQueryResult, T = NormalizedCacheObject>. The first type, Q, defines what kind of results the query returns. The latter, T, only describes the cache type of the ApolloClient and is not usually changed.

Settings
Table 39: GraphQL module settings
PropertyTypeDescriptionDefault value
graphQLClientApollo ClientOptional property, but must be set before the query is executed. Can be set with setApolloClient or when calling a query(options).-
queryTypedDocumentNode or DocumentNodeA graphQL query document.-
queryHelper(currentOptions:QueryOptions, apiTokenClient?: ApiTokenClient, beacon?:Beacon) => QueryOptionsThis function is called before a query is executed. The options object returned by the function is used as query options.-
optionsobjectGraphQL module options.
options.autoFetchbooleanShould query be executed automatically on initialization.true
options.requireApiTokensbooleanAre api tokens required for queries.true
options.abortIfLoadingbooleanShould an on-going query be aborted when query() is called again. If false, the query() will return a pending promise, and a new query is not started.true
options.keepOldResultOnErrorbooleanShould old result be kept, if a newer query fails.false
options.apiTokensWaitTimenumberHow long in milliseconds should api tokens be awaited before rejecting a query.15 000
options.apiTokenKeystringAn object key for the api token to use in a query. The token is picked with apiTokens[key].-
queryOptionsQueryOptions excluding the query propertyOther ApolloClient queryOptions except query document.-
Methods
Table 40: GraphQL module methods
PropertyDescriptionArgument(s)Return value
cancelCancels current query. The pending query is aborted.--
clearCalls cancel() and also clears errors and results. Emits a GRAPHQL_MODULE_CLEARED event.--
getClientErrorsReturns array of query errors and/or errors in the result data.-ApolloError[]
getDataReturns the data property of last successful query result.-The result.data of latest query or undefined
getErrorReturns a query error.-ApolloError or undefined
getQueryPromiseReturns the current pending query promise or an immediately rejected promise.-Promise<ApolloQueryResult>
getResultReturns the latest query result.-ApolloQueryResult or undefined
isLoadingReturns true if a query fetch is currently active.-boolean
isPendingReturns true if the module is waiting for api tokens or query results.-boolean
queryExecutes a query.Partial<GraphQLModuleModuleProps>Promise<QueryResult>
queryCacheCalls query(options) with fetchPolicy: 'cache-only' in options.Partial<GraphQLModuleModuleProps>Promise<QueryResult>
queryServerCalls query(options) with fetchPolicy: 'network-only' in options.Partial<GraphQLModuleModuleProps>Promise<QueryResult>
setClientSets the graphQLClient.Apollo Client-
waitForApiTokensReturns a promise that is resolved when api tokens are found.timeout?: number. Time in milliseconds when the Promise is rejected. If omitted, the promise will not timeout.Promise<void>
Other exported utility functions

The following functions can be imported and used without the GraphQL module module:

Table 41: Other GraphQL module functions
FunctionDescriptionArgument(s)Return value
createGraphQLModule(options)Creates the module.GraphQLModuleModuleProps.GraphQLModule
mergeQueryOptionContexts(options, context)Deep merges given context to queryOptions.QueryOptions, QueryOptions['context']QueryOptions
mergeHeadersToQueryOptionsDeep merges given headers to queryOptions.context.QueryOptions, RecordQueryOptions
mergeAuthorizationHeaderToQueryOptionsSets authorization: <given header> to queryOptions.context.headers. Typically the value is Bearer: <token>.QueryOptions, stringQueryOptions
mergeQueryOptionsDeep merges given queryOptions to another queryOptions objectQueryOptions, QueryOptionsQueryOptions
setBearerToQueryOptionsShortcut for calling mergeAuthorizationHeaderToQueryOptions(options, 'Bearer: <token>').QueryOptions, stringQueryOptions
appendFetchOptionsAppends fetchOptions to queryOptions.context.QueryOptions, Partial<RequestInit>QueryOptions
State change signals

The GraphQL module has no state changes.

Error signal types

Returned errors are instances of the GraphQLModuleError. It is an extended Error object with type property and multiple .is... getters for checking error types. The error object also contains the thrown error in error.originalError property.

Table 42: GraphQL module error signal types
Error typeDescriptionError object property to checkUtility function to check the signal
GRAPHQL_LOAD_FAILEDQuery failed.error.isLoadErrorisGraphQLModuleLoadFailedSignal(signal)
GRAPHQL_NO_CLIENTQuery failed, because client is not provided.error.isNoClientErrorisGraphQLModuleNoClientErrorSignal(signal)
GRAPHQL_NO_API_TOKENSApi tokens are required, but not found or fetching them timed out.error.isNoApiTokensErrorisGraphQLModuleNoApiTokensErrorSignal(signal)
Event signals

The GraphQL module also emits events when an action is complete or starting.

Table 43: Other GraphQL module event types
Event typeDescriptionUtility function to check the signal
GRAPHQL_MODULE_LOADINGQuery has started.isGraphQLModuleLoadingSignal(signal)
GRAPHQL_MODULE_LOAD_SUCCESSQuery was successful.isGraphQLModuleLoadSuccessSignal(signal)
GRAPHQL_MODULE_LOAD_ABORTEDQuery was aborted.isGraphQLModuleLoadAbortedSignal(signal)
GRAPHQL_MODULE_CLEAREDThe module was cleared.isGraphQLModuleClearedSignal(signal)
Dedicated signal triggers

Triggers for GraphQL modulesignals. These trigger signals only originated from the GraphQL module.

Table 44: GraphQL module triggers
Generic triggersRequired signal props to trigger the listener
triggerForAllGraphQLModuleErrorsGraphQL module namespace and type error.
triggerForAllGraphQLModuleEventsGraphQL module namespace and type event.
triggerForAllGraphQLModuleSignalsGraphQL module namespace.

Event triggers require the signal to have a GraphQL module namespace and type event. Signal.payload.type contains one of the events.

Table 45: GraphQL module event triggers
Event triggersRequired signal props to trigger the listener
graphQLModuleClearedTriggerpayload.type has a matching event.
graphQLModuleLoadAbortedTriggerpayload.type has a matching event.
graphQLModuleLoadingTriggerpayload.type has a matching event.
graphQLModuleLoadSuccessTriggerpayload.type has a matching event.

Error triggers require the signal to have a GraphQL modulenamespace and type error. Signal.payload.type contains one of the errors.

Table 46: GraphQL module error triggers
Error triggersRequired signal props to trigger the listener
loadFailedErrorTriggerpayload.type has a matching error.
noApiTokensErrorTriggerpayload.type has a matching error.
noClientErrorTriggerpayload.type has a matching error.
Getting signal payloads

Sometimes the most significant part of a signal is the payload. There are predefined payload getters for the GraphQL module. If the given signal is not from the GraphQL module or is not given type, the function returns null. So there is no need to pre-check the signal namespace or type.

Table 47: GraphQL module signal payload getters
Payload getterReturns
getGraphQLModuleErrorPayloadnull or GraphQLModuleError object.
getGraphQLModuleEventPayloadnull or {type, data}.

Oidc client hooks

Table 48: Oidc client hooks
HookDescriptionReturn value
useAuthenticatedUserReturns a user object, if it is valid and authenticated and passes isValidUser() check.User or null
useCachedAmrReturns the user's amr value. It is cached because in some cases it must be decrypted from id_token.string[]
useOidcClientReturns the Oidc client.OidcClient
useOidcClientTrackingReturns an array of [Signal, resetFunction, oidcClient instance]. The hook forces the component using it to re-render each time the listener is triggered.[Signal or undefined, ()=>void, OidcClient]

The usage of the Oidc client hooks is described in the usage section.

Api tokens client hooks

Table 49: Api tokens client hooks
HookDescriptionReturn value
useApiTokensReturns functions for checking tokens and the status of renewal.{getStoredApiTokens(), isRenewing()}
useApiTokensClientReturns the Api tokens client.ApiTokensClient
useApiTokensClientTrackingReturns an array of [Signal, resetFunction, apiTokensClient instance]. The hook forces the component using it to re-render each time the listener is triggered.[Signal or undefined, ()=>void, ApiTokensClient]

The usage of the Api tokens client hooks is described in the usage section.

Session poller hooks

Table 50: Session poller hooks
HookDescriptionReturn value
useSessionPollerReturns the Session poller.SessionPoller
useSessionPollerTrackingReturns an array of [Signal, resetFunction, sessionPoller instance]. The hook forces the component using it to re-render each time the listener is triggered.[Signal or undefined, ()=>void, SessionPoller]

The usage of the Session poller hooks is described in the usage section.

GraphQL module hooks

Table 51: GraphQL module hooks
HookDescriptionReturn value
useGraphQLModuleReturns the GraphQL module.GraphQLModule
useGraphQLModuleTrackingReturns [Signal, resetFunction, GraphQL module instance]. The hook forces the component using it to re-render each time the listener is triggered.[signal, reset function, GraphQL module instance]
useGraphQLMimics the useLazyQuery hook of the Apollo GraphQL library. The hook forces the component using it to re-render each time the module emits a signal.[query, {data, error, loading, refetch}]

The usage of the GraphQL module hooks is described in the usage section.

Generic signal hooks

Table 52: Generic signal listener hooks
HookArgumentsReturn value
useSignalListener(listener)The listener function. There are no trigger props. The listener is always called when any signal is emitted in any namespace. The listener should return true if the component should re-render.An array of [Signal or undefined, ()=>void]. Latter is a reset function that clears the signal and re-renders the component.
useSignalTrackingWithCallback(triggerProps, callback)Signal props need to match the incoming signal to trigger the callback function. The hook never causes a re-render. The callback must do it.none
useSignalTrackingWithReturnValue(triggerProps)Signal props need to match the incoming signal to trigger re-rendering. The hook creates a trigger from the props and re-renders if the trigger returns true.Same as above.

Important! The listener passed to useSignalListener must be memoized or the hook will attach a new listener on each render because the props changed. The old one is disposed of but to avoid unnecessary listeners, use memoization with useMemo or useCallback. Note that all triggerFor... functions are constants and do not need memoization.

useSignalTrackingWithReturnValue and useSignalListener store their arguments in React refs, so memoization is not needed.

The usage of the signal hooks is described in the usage section.

Beacon

The beacon is the controller of all signals and listeners. It emits all signals and connects all modules. It also stores all modules. Modules are added and connected by calling beacon.addContext(module). The beacon then calls module.connect() with the beacon as the only argument. After that, the module can listen to and emit signals.

Beacon methods
Table 53: Beacon methods
NameArgumentsReturn values
addListener(signalProps, trigger)Signal props that trigger the listener or ready-made trigger function.Disposer function that removes the listener.
addSignalContext(module)Adds a module and its context. The connect method of the added module is called immediately.Disposer function to remove context.
clear()Removes all data.none
emit(signal)A signal to emit.none
getAllSignalContextsAsObject()Returns all stored contexts as on object of {[context.namespace]:context}.Object
getSignalContext(module)Returns context for the given namespace.Context module or undefined.
Important

Like in React, once a signal (an event in React) is emitted, it is destroyed after all listeners have been called. Copy the object if it is needed for later use.

Signals

Signals have a type property to separate, for example errors, from other signals. Signals are easier to create with utility functions than by manually setting all properties.

The signal object

A signal is an object with the following properties:

Table 54: Signal object properties
PropertyDescriptionValue type
contextOptional. The emitting module instance. For example an Oidc client instance. This is not required, but if missing, the beacon will set it by finding the context with signal.namespace.object
namespaceRequired. The namespace is the name of the module that emitted the signal.string
payloadOptional. The payload contains metadata. For example type of the event. A payload can also be an Error object.unknown
typeRequired. Type of the signal. Can be any string, but HDS emits init, error, event and stateChange signals.string
Signal types and payloads
Table 55: Signal types and payloads
Signal typeImportable variablePayload
errorerrorSignalTypePayload is an error object. This way payload.type matches error.type.
eventeventSignalTypeUsually {type:event type} indicating what kind of event is emitted. May also have data property containing for example User or Tokens.
initinitSignalTypeEmitted when all modules are connected and everything is ready. Emitted only once. Note that modules may emit other signals before this.
stateChangestateChangeSignalType{state, previousState} New state and the previous state, if any.

Custom modules may emit any other kind of signal with different payloads.

Emitting signals

Only custom modules can emit signals. See the custom module documentation.

See also custom namespaced beacons.

Important

If a listener is added while signaling and it is triggered by the currently emitted signal, a loop could be created. This could occur when using hooks and not memoizing listeners. Avoid emitting signals inside listeners.

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.

Instead of writing signal types as strings, it is better to use predefined triggers:

Generic triggers

Generic create... functions create trigger functions from given props. Creator functions are not objects, so they can be used to create triggers for multiple namespaces, payloads and so on.

Table 56: Generic trigger creators
Trigger creatorArgumentsDefault
createInitTriggerProps(namespace)Namespace to listen to. Listens only to init signals.all namespaces
createErrorTriggerProps(namespace,type)Namespace and payload.type to listen to. Listens only to error signals.all namespaces
createEventTriggerProps(namespace,type)Namespace and payload.type to listen to. Listens only to event signals.all namespaces
createStateChangeTriggerProps(namespace, state, previousState)Namespace to listen to. Given state and previousState must also match. Both are optional.all namespaces
createTriggerForAllNamespaces()Listens to all signals in any namespace.none
createTriggerPropsForAllSignals(namespace)Namespace to listen to. If omitted listens to all signals.all namespaces

See also hooks.

Generic signal utility functions
Create

Signals are easier to create with functions than by manually setting all properties.

Table 57: Generic signal creators
FunctionDescription
createErrorSignal(namespace, payload)Creates an error signal with the given namespace and error payload.
createEventSignal(namespace, payload)Creates an event signal with the given namespace and event payload.
createStateChangeSignal(namespace, state, previousState)Creates a stateChange signal with the given namespace and stateChange payload.
Check

Check the signal type or namespace with these utilities.

Table 58: Generic signal checks
FunctionDescription
isInitSignal(signal)Returns true if the given signal is an init signal.
isErrorSignal(signal)Returns true if the given signal is an error signal.
isEventSignal(signal)Returns true if the given signal is an event signal.
isNamespacedSignal(signal, namespace)Returns true if the given signal has given namespace.
isStateChangeSignal(signal)Returns true if the given signal is a stateChange signal.
Get payloads

Returns signal payload if the signal type is a match.

Table 59: Generic payloads getters
FunctionDescription
getErrorSignalPayload(signal)Returns the payload if the given signal is an error signal.
getEventSignalPayload(signal)Returns true if the given signal is an event signal.
getStateChangeSignalPayload(signal)Returns true if the given signal is a stateChange signal.
Convert and filter
Table 60: Convert and filter signals
FunctionDescription
convertSignalToTrigger(signal)Removes the context from the signal if the property exists. The given signal is not mutated.
filterSignals(arrayOfSignals, matchingProps)Filters signals from the array. Returns signals which match given props.
waitForSignals

The function returns a promise, which is resolved when the given signal(s) have been detected. Or it rejects the promise when a signal from an optional rejection list is given.

The function can be used for racing conditions, for example waiting for a user renewal success or error, whichever comes first.

Or wait for a longer set of emitted signals from the start of user renewal to the end of API token renewal.

The promise is created with waitForSignals(beaconInstance, arrayOfTriggers, options).

When the promise is resolved, it returns an array of received signals. If it is rejected, an error is returned.

Table 61: waitForSignals utility
ArgumentTypeDescriptionDefault
arrayOfTriggersObject[]Array of signals/signal props to match. Each array signal is checked once and removed when it matches.none
beaconInstanceBeaconBeacon instance where listeners are added.none
optionsObject Optional.none
options.allowSkippingbooleanIf true, and triggers are in order A, B, C, D and signal "C" is received. "A", "B", "C" are all removed from the array and are not checked again.true
options.strictOrderbooleanIf true, signals are not tracked in strict order. If true and signals are received in the wrong order, the promise is rejected.false
options.rejectOnObject[]Array of signals/signal props, which cause promise rejection, if a match is found.none

There is no timeout. The promise can be rejected by setting a custom signal type to options.rejectOn and emit a matching signal.

import { waitForSignals, userRenewalStartedTrigger } from 'hds-react';
const createMyCustomModule = (): ConnectedModule => {
let beacon: Beacon | undefined;
const myCustomRejectSignal = { type: 'reject', namespace: 'myNamespace' };
const listener = (signal) => {
setTimeout(() => {
beacon.emit(myCustomRejectSignal);
}, 20000);
waitForSignals(beacon, [isUserUpdatedSignal], {
rejectOn: [isUserRemovedSignal, isRenewalErrorSignal, myCustomRejectSignal],
})
.then((signals) => {
//
})
.catch((error) => {
//
});
};
return {
namespace: helperNamespace,
connect: (targetBeacon) => {
beacon = targetBeacon;
beacon.addListener(userRenewalStartedTrigger, listener);
},
};
};
Object types from oidc-client-ts

The Oidc client module uses the oidc-client-ts. Some of its objects are returned from the module. Their documentation can be found in its repository: