Custom Logic with Callbacks

Customize your login and registration behavior

Passage is designed to work out-of-the-box for most applications, but there may be times where you need to customize or add additional login and registration behavior to the authentication workflow. The passage-auth, passage-login, and passage-register elements support a beforeAuth, onSuccess, and onEvent callback.

BeforeAuth

The beforeAuth callback is executed when the user presses the Login or Register button after entering their email or phone number. A common use case for this would be collecting and validating additional user information when the user is registering for the first time.

The beforeAuth callback is set as a property on the element that accepts a function with the following prototype:

interface Metadata {
    [key: string]: boolean | string | number;
}
enum AuthCallbackType {
    Login = 'login',
    Register = 'register',
}
 
interface AuthCallbackPayload {
    authType?: AuthCallbackType;
    /** Any additional metadata provided by the user. This will only be available on registration. */
    userMetadata?: Metadata;
}
type BeforeAuthCallback = (identifier: string, payload: AuthCallbackPayload) => boolean;

When the callback is invoked, the user's identifier is provided as a parameter, along with additional user metadata if applicable and the type of authentication process. After doing additional work to validate any other requirements, the callback should return true to continue with the registration/login, or return false to cancel the registration/login.

An example of assigning this callback might look like:

const beforeAuth = (identifier: string, payload: AuthCallbackPayload) =>{
  let success = true
  if(payload.authType === AuthCallbackType.Register){
    success = validateAndStoreUserInfo(identifier, payload.metadata)
  } else (payload.authType === AuthCallbackType.Login){
    success = validateAndUpdateUserInfo(identifier)
  }
  return success
}
const passageAuth = document.querySelector("passage-auth") as PassageElement
passageAuth.beforeAuth = beforeAuth

The default behavior for this callback is to do nothing and return true.

OnSuccess

The onSuccess callback is executed when a user is successfully logged in. This allows you to customize the behavior for handling successful authentication. An example of this would be storing the authorization token to be used by your backend code, customizing where users should be redirected after a login, or setting other fields client-side.

The onSuccess callback is set as a property on the element that accepts a function with the following prototype:

interface AuthResult {
    redirectUrl: string;
    authToken: string;
    refreshToken?: string; // only if you have refresh tokens enabled.
    refreshTokenExpiration?: number; // only if you have refresh tokens enabled
}
 
enum AuthCallbackType {
    Login = 'login',
    Register = 'register',
}
 
interface AuthCallbackPayload {
    authType?: AuthCallbackType;
    /** Any additional metadata provided by the user. This will only be available on registration. */
    userMetadata?: Metadata;
}
 
type OnSuccessCallback = (authResult: AuthResult, payload: AuthCallbackPayload) => void;

If you provide a custom onSuccess callback, the auth token and redirect URL will be provided for use within the callback. The auth token will still be automatically stored for use in your application in a cookie called psg_auth_token.

An example onSuccess callback might look like:

const onSuccess = (authResult: AuthResult, payload: AuthCallbackPayload) => {
    localStorage.setItem('psg_auth_token', authResult.authToken);
    if (payload.authType === AuthCallbackType.Register) {
        window.location.href = '/getting-started';
    } else {
        window.location.href = '/dashboard';
    }
};
const passageAuth = document.querySelector('passage-auth') as PassageElement;
passageAuth.onSuccess = onSuccess;

The default behavior for this callback is to set the Passage auth token (psg_auth_token) in a cookie and in local storage, then redirect to the app's redirect URL.

OnEvent

The onEvent callback is executed when an event occurs within the element. This allows you to make the rest of your client application aware of events that occur within the Passage elements. For intance you may want to adapt your UI based on whether the user is logging in or registering.

The onEvent callback supports a number of event types that are triggered at different stages of the authentication process and when the UI of the element changes. The full set of supported events and the data they provide are:

enum OnEventType {
    // when element is fully rendered
    onLoaded = 'onLoaded',
    // when the main login screen is shown
    onShowLogin = 'onShowLogin',
    // when the main register screen is shown
    onShowRegister = 'onShowRegister',
    // when the register passkey screen is shown
    // renamed from onRegisterDevice
    onRegisterPasskey = 'onRegisterPasskey',
    // when the login passkey screen is shown
    onLoginPasskey = 'onLoginPasskey',
    // when identity is verified
    onVerifyIdentity = 'onVerifyIdentity',
    // when the login magic link screen is shown
    onMagicLinkLogin = 'onMagicLinkLogin',
    // when the register magic link screen is shown
    onMagicLinkRegister = 'onMagicLinkRegister',
    // the polling event, for cross-session magic links
    onMagicLinkActivated = 'onMagicLinkActivated',
    // when the magic link activate screen is shown
    onMagicLinkActivateSuccess = 'onMagicLinkActivateSuccess',
    // when the login OTP screen is shown
    onOneTimePasscodeLogin = 'onOneTimePasscodeLogin',
    // when the register OTP screen is shown
    onOneTimePasscodeRegister = 'onOneTimePasscodeRegister',
    // when an OTP is activated
    onOneTimePasscodeActivated = 'onOneTimePasscodeActivated',
    // when the addPasskey screen is shown
    onAddPasskey = 'onAddPasskey',
}
type OnLoadedEvent = { type: OnEventType.onLoaded };
type OnShowLoginEvent = { type: OnEventType.onShowLogin };
type OnShowRegisterEvent = { type: OnEventType.onShowRegister };
type OnRegisterPasskeyEvent = { type: OnEventType.onRegisterPasskey; data: { identifier: string } };
type OnLoginPasskeyEvent = { type: OnEventType.onLoginPasskey; data: { identifier: string } };
type OnVerifyIdentityEvent = {
    type: OnEventType.onVerifyIdentity;
    data: { identifier: string; identifierExists: boolean };
};
type OnMagicLinkLoginEvent = { type: OnEventType.onMagicLinkLogin; data: { identifier: string } };
type OnMagicLinkRegisterEvent = { type: OnEventType.onMagicLinkRegister; data: { identifier: string } };
type OnMagicLinkActivatedEvent = { type: OnEventType.onMagicLinkActivated };
type OnMagicLinkActivateSuccessEvent = { type: OnEventType.onMagicLinkActivateSuccess };
type OnOneTimePasscodeLoginEvent = { type: OnEventType.onOneTimePasscodeLogin; data: { identifier: string } };
type OnOneTimePasscodeRegisterEvent = {
    type: OnEventType.onOneTimePasscodeRegister;
    data: { identifier: string };
};
type OnOneTimePasscodeActivatedEvent = { type: OnEventType.onOneTimePasscodeActivated };
type OnAddPasskeyEvent = { type: OnEventType.onAddPasskey };

The onEvent callback is set as a property on the element that accepts a function with the following prototype:

type PassageElementEvent =
    | OnLoadedEvent
    | OnShowLoginEvent
    | OnShowRegisterEvent
    | OnRegisterPasskeyEvent
    | OnLoginPasskeyEvent
    | OnVerifyIdentityEvent
    | OnMagicLinkLoginEvent
    | OnMagicLinkRegisterEvent
    | OnMagicLinkActivatedEvent
    | OnMagicLinkActivateSuccessEvent
    | OnOneTimePasscodeLoginEvent
    | OnOneTimePasscodeRegisterEvent
    | OnOneTimePasscodeActivatedEvent
    | OnAddPasskeyEvent;
type OnEventCallback = (event: PassageElementEvent) => void;

An example onEvent callback might look like:

const onEvent = (event: PassageElementEvent): void => {
    switch (event.type) {
        case OnEventType.onLoaded:
            console.log('Element is finished loading and ready to use');
            break;
        case OnEventType.onShowRegister:
            console.log('Showing register screen');
            break;
        case OnEventType.onRegisterPasskey:
            console.log('Passkey created for', event.data.identifier);
            break;
        default:
            break;
    }
};
const passageAuth = document.querySelector('passage-auth') as PassageElement;
passageAuth.onEvent = onEvent;

If there is an additional element event you'd like to see supported, please let us know by contating us at support@passage.id.