Login
Login components include React components, context and hooks for handling user authorisation, api tokens, session polling and GraphQL queries.
API
Table of contents
- Components
- Modules
- Oidc client hooks
- Api tokens client hooks
- Session poller hooks
- GraphQL module hooks
- Generic signal hooks
- Beacon
- Signals
Components
LoginButton
Property | Description | Default |
---|---|---|
errorText | Required. An error text is shown if the OIDC server rejects the connection. | none |
loggingInText | Required. Text read by the screen reader when the button is clicked. | Default of the "loadingText" property of the LoadingSpinner |
spinnerColor | Optional. Colour of the loading spinner. | var(--color-white) |
redirectionProps | Properties appended to the url when redirecting. Read more in the documentation of the oidcClient login method. | none |
onClick | Called 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
Property | Description |
---|---|
children | Optional. Usually, child elements inform the user that a response is being processed. |
onError | Required. The function is called when a login fails. The function is called with an OidcClientError. |
onSuccess | Required. 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
Property | Description |
---|---|
apiTokensClientSettings | Optional. Settings for the Api tokens client. If omitted, the Api tokens client is not created. |
debug | Optional. If true, debugging is enabled. See Oidc client settings. |
modules | Optional. An array of custom modules. |
sessionPollerSettings | Optional. 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. |
userManagerSettings | Required. 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
Property | Description |
---|---|
content | Required. Textual content with properties title , text , buttonText and closeButtonLabelText . |
dialogProps | Optional. Additional DialogProps passed to the Dialog. |
ids | Optional. Ids for the dialog elements with properties dialog , title , and text . If omitted, ids are created. |
WithAuthentication
Property | Description |
---|---|
AuthorisedComponent | Optional. Component to render if the user is authenticated. The user object is passed to the component. |
UnauthorisedComponent | Optional. Component to render if the user is not authenticated. |
WithAuthenticatedUser
Property | Description |
---|---|
children | React children. A user object is passed to each child. |
WithoutAuthenticatedUser
Property | Description |
---|---|
children | React 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
Property | Default value |
---|---|
debug | Optional. If true, debugging is enabled in the oidc-client-ts instance with Log.setLevel(Log.Debug) and Log.setLogger(console) . |
userManagerSettings | Required, but not all options. At least authority , client_id and scope must be set. |
Default userManager settings
Property | Default value |
---|---|
automaticSilentRenew | true |
includeIdTokenInSilentRenew | true |
loadUserInfo | true |
monitorSession | false |
post_logout_redirect_uri | window.origin + / |
redirect_uri | window.origin + /callback |
response_type | code |
silent_redirect_uri | window.origin + /silent_renew.html |
validateSubOnSilentRenew | false |
These override the default settings of the oidc-client-ts.
Methods
Property | Description | Return value |
---|---|---|
getAmr | Returns 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 |
getState | State of the client. See Oidc client states. | State |
getToken | Returns 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 |
getUser | Returns a user object or null . | User or null |
getUserManager | Returns the UserManager. | UserManager |
handleCallback | Handles the callback data and returns a user object - if the user is valid. | Promise<User> or Promise<OidcClientError> |
isAuthenticated | Returns true if the user exists and the user object passes isValidUser() check. | boolean |
isRenewing | Returns true if the user renewal is in progress. | boolean |
login | Calls 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> |
logout | Calls 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> |
renewUser | For 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:
Function | Description | Argument(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.
State | Description | Utility function to check the signal |
---|---|---|
HANDLING_LOGIN_CALLBACK | The browser has returned from an Oidc provider and data is being parsed. | isHandlingLoginCallbackSignal(signal) |
LOGGING_IN | State changes to this when login() is called. | isLoggingInSignal(signal) |
LOGGING_OUT | State changes to this when logout() is called. | isLoggingOutSignal(signal) |
NO_SESSION | No valid user exists. | isNoSessionSignal(signal) |
VALID_SESSION | User 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.
Error type | Description | Error object property to check | Utility function to check the signal |
---|---|---|---|
INVALID_OR_EXPIRED_USER | The returned user object is expired or invalid. | error.isInvalidUserError | isInvalidUserErrorSignal(signal) |
RENEWAL_FAILED | User renewal failed. | error.isRenewalError | isRenewalErrorSignal(signal) |
SIGNIN_ERROR | Emitted when handleCallback() or login() failed. | error.isSignInError | isSigninErroSignal(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.
Event type | Description | Utility function to check the signal |
---|---|---|
USER_UPDATED | User data has changed. Includes also changes to null , if login or renewal fail. | isUserUpdatedSignal(signal) |
USER_REMOVED | User data was removed. Happens when the user logs out or tokens expire. | isUserRemovedSignal(signal) |
USER_RENEWAL_STARTED | User 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
.
Generic triggers | Required signal props to trigger the listener |
---|---|
triggerForAllOidcClientErrors | Oidc client namespace and type error . |
triggerForAllOidcClientEvents | Oidc client namespace and type event . |
triggerForAllOidcClientSignals | Oidc client namespace. |
triggerForAllOidcClientStateChanges | Oidc 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.
State change triggers | Required signal props to trigger the listener |
---|---|
loggingInTrigger | payload.type has a matching state. |
loggingOutTrigger | payload.type has a matching state. |
noSessionTrigger | payload.type has a matching state. |
validSessionTrigger | payload.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.
Event triggers | Required signal props to trigger the listener |
---|---|
userRemovedTrigger | payload.type has a matching event. |
userRenewalStartedTrigger | payload.type has a matching event. |
userUpdatedTrigger | payload.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.
Error triggers | Required signal props to trigger the listener |
---|---|
invalidUserErrorTrigger | payload.type has a matching error. |
renewalErrorTrigger | payload.type has a matching error. |
signInErrorTrigger | payload.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.
Payload getter | Returns |
---|---|
getOidcClientEventPayload | null or {type, data} . The data is usually a user object. |
getOidcClientErrorPayload | null or OidcClientError object. |
getOidcClientStateChangePayload | null 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
Property | Description | Default value |
---|---|---|
audiences | Optional. An array of strings. Used with Helsinki Profile api token requests. One token is fetched for each audience. | none |
maxRetries | Optional. The number of max retries until fetching is stopped. | 4 |
queryProps | Optional. An object. Used with Helsinki Profile api token requests. Contains properties grantType and permission . | none |
retryInterval | Optional. Waiting time in milliseconds between failed requests. | 500 |
url | Required. URL to the API tokens endpoint. | none |
Methods
Property | Description | Return value |
---|---|---|
clear | Cancels fetch and removes all data. | none |
fetch | Fetches the tokens from the given URI. | Promise<Tokens> |
getTokens | Gets locally stored tokens. | Tokens |
isRenewing | Returns 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:
Function | Description | Argument(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.
Error type | Description | Error object property to check | Utility function to check the signal |
---|---|---|---|
API_TOKEN_FETCH_FAILED | Fetch failed. | error.isFetchError | isApiTokensFetchFailedErrorSignal(signal) |
INVALID_API_TOKENS | The returned object is an invalid json. | error.isInvalidTokensError | isInvalidApiTokensErrorSignal(signal) |
INVALID_USER_FOR_API_TOKENS | User is not valid. | error.isInvalidApiTokensUserError | isInvalidApiTokensUserErrorSignal(signal) |
Event signals
The Api tokens client
also emits events when an action is complete or starting.
Event type | Description | Utility function to check the signal |
---|---|---|
API_TOKENS_UPDATED | Tokens have changed. Includes also changes to null . | isApiTokensUpdatedSignal(signal) |
API_TOKENS_REMOVED | Tokens have been removed. Happens usually when the user object has been removed or is renewed. | isApiTokensRemovedSignal(signal) |
API_TOKENS_RENEWAL_STARTED | Api 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
.
Generic triggers | Required signal props to trigger the listener |
---|---|
triggerForAllApiTokensClientErrors | Api tokens client namespace and type error . |
triggerForAllApiTokensClientEvents | Api tokens client namespace and type event . |
triggerForAllApiTokensClientSignals | Api 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.
Event triggers | Required signal props to trigger the listener |
---|---|
apiTokensRemovedTrigger | payload.type has a matching event. |
apiTokensRenewalStartedTrigger | payload.type has a matching event. |
apiTokensUpdatedTrigger | payload.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.
Error triggers | Required signal props to trigger the listener |
---|---|
apiTokensFetchFailedErrorTrigger | payload.type has a matching error. |
invalidApiTokensErrorTrigger | payload.type has a matching error. |
invalidApiTokensUserErrorTrigger | payload.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.
Payload getter | Returns |
---|---|
getApiTokensClientErrorPayload | null or ApiTokensClientError object. |
getApiTokensClientEventPayload | null 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
Property | Description | Default value |
---|---|---|
pollIntervalInMs | Optional. Waiting time between polls in milliseconds. The default is one minute. | 60000 |
Methods
Property | Description | Return value |
---|---|---|
start | Starts the polling. The first check is done after pollIntervalInMs is expired. Not immediately. | |
stop | Stops 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:
Property | Description | Argument(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.
Error type | Description | Error object property to check | Utility function to check the signal |
---|---|---|---|
SESSION_ENDED | The server returned a forbidden or unauthorized status code. | error.isSessionEnded | isSessionEndedSignal(signal) |
SESSION_POLLING_FAILED | Polling has failed and max retries have been reached. | error.isSessionPollingFailure | isSessionPollingFailureSignal(signal) |
Event signals
The Session poller
also emits events when an action is complete or starting.
Event type | Description | Utility function to check the signal |
---|---|---|
SESSION_POLLING_STARTED | Polling has started. | isSessionPollerStartedSignal(signal) |
SESSION_POLLING_STOPPED | Polling has stopped. | isSessionPollerStoppedSignal(signal) |
Dedicated signal triggers
Triggers for Session poller
signals. These trigger signals only originated from the Session poller
.
Generic triggers | Required signal props to trigger the listener |
---|---|
triggerForAllSessionPollerErrors | Session poller namespace and type error . |
triggerForAllSessionPollerEvents | Session poller namespace and type event . |
triggerForAllSessionPollerSignals | Session poller namespace. |
Event triggers require the signal to have a Session poller
namespace and type event
.
Signal.payload.type
contains one of the events.
Event triggers | Required signal props to trigger the listener |
---|---|
sessionPollingStartedTrigger | payload.type has a matching event. |
sessionPollingStoppedTrigger | payload.type has a matching event. |
Error triggers require the signal to have a Session poller
namespace and type error
.
Signal.payload.type
contains one of the errors.
Error triggers | Required signal props to trigger the listener |
---|---|
sessionEndedTrigger | payload.type has a matching error. |
sessionPollingFailedTrigger | payload.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.
Payload getter | Returns |
---|---|
getSessionPollerErrorPayload | null or SessionPollerError object. |
getSessionPollerEventPayload | null 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 settingoptions.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
Property | Type | Description | Default value |
---|---|---|---|
graphQLClient | Apollo Client | Optional property, but must be set before the query is executed. Can be set with setApolloClient or when calling a query(options) . | - |
query | TypedDocumentNode or DocumentNode | A graphQL query document. | - |
queryHelper | (currentOptions:QueryOptions, apiTokenClient?: ApiTokenClient, beacon?:Beacon) => QueryOptions | This function is called before a query is executed. The options object returned by the function is used as query options. | - |
options | object | GraphQL module options. | |
options.autoFetch | boolean | Should query be executed automatically on initialization. | true |
options.requireApiTokens | boolean | Are api tokens required for queries. | true |
options.abortIfLoading | boolean | Should 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.keepOldResultOnError | boolean | Should old result be kept, if a newer query fails. | false |
options.apiTokensWaitTime | number | How long in milliseconds should api tokens be awaited before rejecting a query. | 15 000 |
options.apiTokenKey | string | An object key for the api token to use in a query. The token is picked with apiTokens[key] . | - |
queryOptions | QueryOptions excluding the query property | Other ApolloClient queryOptions except query document. | - |
Methods
Property | Description | Argument(s) | Return value |
---|---|---|---|
cancel | Cancels current query. The pending query is aborted. | - | - |
clear | Calls cancel() and also clears errors and results. Emits a GRAPHQL_MODULE_CLEARED event. | - | - |
getClientErrors | Returns array of query errors and/or errors in the result data. | - | ApolloError[] |
getData | Returns the data property of last successful query result. | - | The result.data of latest query or undefined |
getError | Returns a query error. | - | ApolloError or undefined |
getQueryPromise | Returns the current pending query promise or an immediately rejected promise. | - | Promise<ApolloQueryResult> |
getResult | Returns the latest query result. | - | ApolloQueryResult or undefined |
isLoading | Returns true if a query fetch is currently active. | - | boolean |
isPending | Returns true if the module is waiting for api tokens or query results. | - | boolean |
query | Executes a query. | Partial<GraphQLModuleModuleProps> | Promise<QueryResult> |
queryCache | Calls query(options) with fetchPolicy: 'cache-only' in options. | Partial<GraphQLModuleModuleProps> | Promise<QueryResult> |
queryServer | Calls query(options) with fetchPolicy: 'network-only' in options. | Partial<GraphQLModuleModuleProps> | Promise<QueryResult> |
setClient | Sets the graphQLClient. | Apollo Client | - |
waitForApiTokens | Returns 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:
Function | Description | Argument(s) | Return value |
---|---|---|---|
createGraphQLModule(options) | Creates the module. | GraphQLModuleModuleProps . | GraphQLModule |
mergeQueryOptionContexts(options, context) | Deep merges given context to queryOptions . | QueryOptions, QueryOptions['context'] | QueryOptions |
mergeHeadersToQueryOptions | Deep merges given headers to queryOptions.context . | QueryOptions, Record | QueryOptions |
mergeAuthorizationHeaderToQueryOptions | Sets authorization: <given header> to queryOptions.context.headers . Typically the value is Bearer: <token> . | QueryOptions, string | QueryOptions |
mergeQueryOptions | Deep merges given queryOptions to another queryOptions object | QueryOptions, QueryOptions | QueryOptions |
setBearerToQueryOptions | Shortcut for calling mergeAuthorizationHeaderToQueryOptions(options, 'Bearer: <token>') . | QueryOptions, string | QueryOptions |
appendFetchOptions | Appends 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.
Error type | Description | Error object property to check | Utility function to check the signal |
---|---|---|---|
GRAPHQL_LOAD_FAILED | Query failed. | error.isLoadError | isGraphQLModuleLoadFailedSignal(signal) |
GRAPHQL_NO_CLIENT | Query failed, because client is not provided. | error.isNoClientError | isGraphQLModuleNoClientErrorSignal(signal) |
GRAPHQL_NO_API_TOKENS | Api tokens are required, but not found or fetching them timed out. | error.isNoApiTokensError | isGraphQLModuleNoApiTokensErrorSignal(signal) |
Event signals
The GraphQL module
also emits events when an action is complete or starting.
Event type | Description | Utility function to check the signal |
---|---|---|
GRAPHQL_MODULE_LOADING | Query has started. | isGraphQLModuleLoadingSignal(signal) |
GRAPHQL_MODULE_LOAD_SUCCESS | Query was successful. | isGraphQLModuleLoadSuccessSignal(signal) |
GRAPHQL_MODULE_LOAD_ABORTED | Query was aborted. | isGraphQLModuleLoadAbortedSignal(signal) |
GRAPHQL_MODULE_CLEARED | The module was cleared. | isGraphQLModuleClearedSignal(signal) |
Dedicated signal triggers
Triggers for GraphQL module
signals. These trigger signals only originated from the GraphQL module
.
Generic triggers | Required signal props to trigger the listener |
---|---|
triggerForAllGraphQLModuleErrors | GraphQL module namespace and type error . |
triggerForAllGraphQLModuleEvents | GraphQL module namespace and type event . |
triggerForAllGraphQLModuleSignals | GraphQL module namespace. |
Event triggers require the signal to have a GraphQL module
namespace and type event
.
Signal.payload.type
contains one of the events.
Event triggers | Required signal props to trigger the listener |
---|---|
graphQLModuleClearedTrigger | payload.type has a matching event. |
graphQLModuleLoadAbortedTrigger | payload.type has a matching event. |
graphQLModuleLoadingTrigger | payload.type has a matching event. |
graphQLModuleLoadSuccessTrigger | payload.type has a matching event. |
Error triggers require the signal to have a GraphQL module
namespace and type error
.
Signal.payload.type
contains one of the errors.
Error triggers | Required signal props to trigger the listener |
---|---|
loadFailedErrorTrigger | payload.type has a matching error. |
noApiTokensErrorTrigger | payload.type has a matching error. |
noClientErrorTrigger | payload.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.
Payload getter | Returns |
---|---|
getGraphQLModuleErrorPayload | null or GraphQLModuleError object. |
getGraphQLModuleEventPayload | null or {type, data} . |
Oidc client hooks
Hook | Description | Return value |
---|---|---|
useAuthenticatedUser | Returns a user object, if it is valid and authenticated and passes isValidUser() check. | User or null |
useCachedAmr | Returns the user's amr value. It is cached because in some cases it must be decrypted from id_token . | string[] |
useOidcClient | Returns the Oidc client . | OidcClient |
useOidcClientTracking | Returns 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
Hook | Description | Return value |
---|---|---|
useApiTokens | Returns functions for checking tokens and the status of renewal. | {getStoredApiTokens(), isRenewing()} |
useApiTokensClient | Returns the Api tokens client . | ApiTokensClient |
useApiTokensClientTracking | Returns 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
Hook | Description | Return value |
---|---|---|
useSessionPoller | Returns the Session poller . | SessionPoller |
useSessionPollerTracking | Returns 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
Hook | Description | Return value |
---|---|---|
useGraphQLModule | Returns the GraphQL module . | GraphQLModule |
useGraphQLModuleTracking | Returns [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] |
useGraphQL | Mimics 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
Hook | Arguments | Return 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
Name | Arguments | Return 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:
Property | Description | Value type |
---|---|---|
context | Optional. 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 |
namespace | Required. The namespace is the name of the module that emitted the signal. | string |
payload | Optional. The payload contains metadata. For example type of the event. A payload can also be an Error object. | unknown |
type | Required. Type of the signal. Can be any string, but HDS emits init , error , event and stateChange signals. | string |
Signal types and payloads
Signal type | Importable variable | Payload |
---|---|---|
error | errorSignalType | Payload is an error object. This way payload.type matches error.type . |
event | eventSignalType | Usually {type:event type} indicating what kind of event is emitted. May also have data property containing for example User or Tokens. |
init | initSignalType | Emitted when all modules are connected and everything is ready. Emitted only once. Note that modules may emit other signals before this. |
stateChange | stateChangeSignalType | {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:
- Oidc client triggers.
- Api tokens client triggers.
- Session poller triggers.
- GraphQL module triggers.
- Generic 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.
Trigger creator | Arguments | Default |
---|---|---|
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.
Function | Description |
---|---|
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.
Function | Description |
---|---|
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.
Function | Description |
---|---|
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
Function | Description |
---|---|
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.
Argument | Type | Description | Default |
---|---|---|---|
arrayOfTriggers | Object[] | Array of signals/signal props to match. Each array signal is checked once and removed when it matches. | none |
beaconInstance | Beacon | Beacon instance where listeners are added. | none |
options | Object | Optional. | none |
options.allowSkipping | boolean | If 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.strictOrder | boolean | If true, signals are not tracked in strict order. If true and signals are received in the wrong order, the promise is rejected. | false |
options.rejectOn | Object[] | 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: