Events
Subscribing to Events
The Parallel experience can be initiated in two ways:
- Via a call to
login()directly - A Parallel button click on a button shown after a call to
subscribeWithButton()orshowButton()
Once that experience is initiated, users are directed through a few steps:
- The user will be asked to log in or create an account if they are not already logged in to Parallel Markets.
- The user will be asked to consent to share information (if they have not previously provided consent) with you.
- The user will be asked to provide information to fulfill any scopes you requested (if they haven't provided the information before). For instance, if you request the
accreditation_statusscope and a user does not have an active accreditation, they will be asked to complete an accreditation flow. - The user will complete the experience and the overlay will disappear (or the user will be redirected back to your site).
After these steps are completed, a few different events are fired by the SDK to provide you with the results of the user flow. You can subscribe to these events to know when a user has completed the steps.
For instance, to execute code whenever a user completes or cancels a flow:
- React Module
- ES Module
- Manual Loading
AuthStatusChangeMessage is a complete example component that subscribes to events and handles unsubscribing on unmount. SimplerAuthStatusChangeMessage demonstrates a simplification by pulling the loginStatus out of the useParallel() hook.
import { useParallel } from '@parallelmarkets/react'
const AuthStatusChangeMessage = () => {
const { parallel } = useParallel()
const [status, setStatus] = useState(null)
useEffect(() => {
// library hasn't loaded yet
if (!parallel) return
parallel.subscribe('auth.statusChange', setStatus)
// cleanup on unmount
return () => {
parallel.unsubscribe('auth.statusChange', setStatus)
}
}, [parallel])
return status ? <p>Status changed to: {status.status}</p> : null
}
// simpler version of the above using the hook's existing subscription to auth status changes
const SimplerAuthStatusChangeMessage = () => {
const { loginStatus } = useParallel()
return loginStatus ? <p>Status changed to: {loginStatus.status}</p> : null
}
// wait for the loading to finish before calling any functions
const parallel = await loadParallel({ client_id: '123', environment: 'demo', flow_type: 'overlay' })
parallel.subscribe('auth.statusChange', function (result) {
if (result.status === 'connected') {
// the user is logged in
} else if (result.status === 'not_authorized') {
// the user canceled authentication or failed
// to consent to sharing with your site
} else {
// the user authentication is unknown (i.e., the user
// is not logged in) - so show the login button
parallel.showButton()
}
})
Parallel.subscribe('auth.statusChange', function (result) {
if (result.status === 'connected') {
// the user is logged in
} else if (result.status === 'not_authorized') {
// the user canceled authentication or failed
// to consent to sharing with your site
} else {
// the user authentication is unknown (i.e., the user
// is not logged in) - so show the login button
Parallel.showButton()
}
})
The only way to know when users have successfully authorized sharing their data with your site is to subscribe to an auth event (or use the loginStatus from the useParallel React hook). This is because a user may have authorized via a redirect flow (the experience on mobile, for instance), which fires an auth.login and an auth.statusChange event.
Also note that authentication "status" refers to the user's status from the perspective of your site, not their authentication status on parallelmarkets.com (or within the experience in the iframe that is embedded or shown in an overlay).
Auth Events
The following events can be tracked using Parallel.subscribe():
List of Events
| Event | Description |
|---|---|
auth.login | Fires after a user authenticates (after a call to Parallel.login()) and completes all steps in the Parallel experience |
auth.logout | Fires after a user is logged out via a call to Parallel.logout() |
auth.statusChange | Fires when the user's authentication status changes after the full completion of the flow in a Parallel experience or when the user is logged out |
auth.authResponseChange | Fires when the authResponse object has changed, which indicates that the user's access token has changed in some way. See Event Callback Argument for more information. |
Event Callback Arguments
When events are triggered, the callback function will be called with a single argument with details about the event. The argument will be an object with the following properties:
| Property | Description |
|---|---|
| status | This will be one of:
|
| error | An error code (if an error occurred) - see this list for possible codes |
| errorDescription | An error description (if an error occurred) |
| authResponse | If a user has authenticated and consented to sharing the requested data, this will contain an access_token and refresh_token. If you'd like to make ongoing API calls from your server environment, you can now call getProfile() to get the Parallel ID for the subject (individual or business) that completed the flow. |
For instance, here's a successful authentication / consent granted object:
{
status: "connected",
authResponse: {
access_token: "MVXoULzTSdmDINFf",
token_type: "bearer",
expires_in: 86400,
refresh_token: "dmDINFfULzTSdMVXoU",
refresh_expires_in: 345600
}
}
And here's an example where a user declined consent:
{
status: 'not_authorized'
}
And here's an example object when the user is not currently logged in:
{
status: 'unknown'
}
Handling Overlay Events
When using the overlay flow, you'll want to handle three scenarios: successful completion, user cancellation, and errors.
Successful Completion
When a user completes the flow and consents to sharing data, auth.login fires with status: 'connected'. This is the signal to update your UI and fetch the user's profile:
parallel.subscribe('auth.login', function (result) {
if (result.status === 'connected') {
// Flow complete - user authenticated and consented
// Safe to hide loading state and fetch profile
hideLoadingSpinner()
parallel.getProfile((response) => {
// Store the Parallel ID and profile data
saveToBackend(response)
})
}
})
The vanilla SDK's getProfile(callback, errback) is callback-based. The React integration wraps it as a Promise, so React users can write getProfile().then((response) => ...) instead.
User Cancellation
When a user closes the overlay without completing (via the dismiss button, clicking outside, or pressing Escape), auth.statusChange fires with status: 'not_authorized'. Handle this to reset your UI:
parallel.subscribe('auth.statusChange', function (result) {
if (result.status === 'not_authorized') {
// User canceled or declined consent
// Reset loading state and optionally show message
hideLoadingSpinner()
showMessage('Verification was not completed. Click the button to try again.')
}
})
Recommended Pattern
Subscribe to both events to handle all cases:
// Handle successful completion
parallel.subscribe('auth.login', function (result) {
if (result.status === 'connected') {
onVerificationComplete(result.authResponse)
}
})
// Handle cancellation and status changes
parallel.subscribe('auth.statusChange', function (result) {
if (result.status === 'not_authorized') {
onVerificationCanceled()
} else if (result.status === 'unknown') {
onUserLoggedOut()
}
})
If your "Verify" button stays in a loading state after the overlay closes, you're likely not handling the not_authorized status. Always subscribe to auth.statusChange to catch cancellations.
Interpreting auth.statusChange Values
Handle each possible value of result.status:
connected— User completed the flow successfully.- Fetch profile data
- Update UI to show verified state
- Store the Parallel ID in your backend
not_authorized— User did not complete the flow.- Possible reasons:
- Clicked the dismiss/close button
- Clicked outside the overlay
- Pressed Escape
- Declined consent
- Reset loading state
- Show a "try again" option
- Check
result.errorfor details
- Possible reasons:
unknown— User is not logged in.- Possible reasons:
- Never started a flow
- Session expired
- Called
Parallel.logout()
- Show the login/verify button
- Hide authenticated UI elements
- Possible reasons:
Checking for Specific Errors
When status is not_authorized, check result.error and result.errorDescription for details:
parallel.subscribe('auth.statusChange', function (result) {
if (result.status === 'not_authorized') {
if (result.error === 'access_denied') {
// User explicitly declined consent
showError('Verification requires consent to share your information.')
} else {
// User closed the overlay without completing
showMessage('Verification was not completed.')
}
}
})
A required_entity_id mismatch (the email passed to login() doesn't match the record) is not reported through this event. It surfaces as an invalid_entity error on the redirect_uri query string — see Starting a Flow For a Specific Record.