@schibsted/account-sdk-browser
    Preparing search index...

    @schibsted/account-sdk-browser

    Schibsted Account SDK for browsers

    Browser SDK for websites that use Schibsted Account for login, session checks, user identifiers, the simplified login widget, and access checks.

    Create a singleton SDK instance with createAccountSdk:

    import { createAccountSdk } from '@schibsted/account-sdk-browser';

    const account = createAccountSdk({
    clientId: '56e9a5d1eee0000000000000',
    redirectUri: 'https://awesomenews.site/callback',
    sessionDomain: 'https://id.awesomenews.site',
    env: 'PRE',
    defaultProductIds: ['product-id'],
    });

    Required configuration:

    • clientId: your Schibsted Account client id
    • redirectUri: the callback URL registered for your client
    • sessionDomain: your client-configured Session Service domain

    Optional configuration:

    • env: defaults to PRE; supported keys include LOCAL, DEV, PRE, PRO, PRO_NO, PRO_FI, and PRO_DK
    • callbackBeforeRedirect: called before a full-page session refresh redirect
    • defaultProductIds: product or feature ids used when hasAccess() is called without arguments
    • log: receives SDK debug log lines
    • varnish: enables Varnish cookie handling
    • window: overrides the browser window object, mostly useful in tests

    createAccountSdk() registers the instance on window.schAccount and emits schAccount:ready event, but these should normally not be used directly (see "Using the Initialized SDK" section below).

    window.addEventListener('schAccount:ready', (event) => {
    const account = event.detail.instance;
    });

    Use getAccountSdk when another module needs to wait for the initialized Account

    import { getAccountSdk } from '@schibsted/account-sdk-browser';

    const account = await getAccountSdk();

    getAccountSdk() resolves immediately if the SDK has already been initialized. Otherwise it waits for schAccount:ready and rejects if initialization does not happen within 4000 ms.

    Tip

    Cross-package consumers should depend on the smallest interface they need, rather than on Account interface itself. Dependency injection provides the mechanism for passing the getAccountSdk implementation across the package boundary. Together with TypeScript’s structural typing system, this allows for flexible and decoupled code. This has two major benefits:

    1. Packages do not need to use the same Account SDK version, as long as the Account SDK implementation satisfies the interface required by the consuming package.
    2. The interface can be fulfilled by another provider, for example a different identity provider or a test double.

    It is possible for the consuming package to import getAccountSdk directly. However, this creates a direct dependency on the Account SDK and couples the package to that specific implementation. Dependency injection is therefore recommended for cross-package integrations in most cases, see example below.

    Dependency Injection Example Example:
    // my-module.ts

    // Declare the subset of the Account API that this module requires
    export interface AccountInterface {
    getUserId: () => Promise<number>; // Note: not compatible with the SDK v5 version which might also return a string
    }
    // Declare the Provider interface, compatible with the `getAccountSdk` function
    export type AccountProvider = () => Promise<AccountInterface>;

    class SomeModule {
    private accountProvider: AccountProvider;

    constructor(accountProvider: AccountProvider) {
    this.accountProvider = accountProvider;
    }

    async getUserId(): Promise<number> {
    const account = await this.accountProvider();
    return account.getUserId();
    }
    }
    // my-module.test-types.ts
    // Utilize some type checks to ensure AccountInterface is implemented by the latest Account SDK interface
    import type { Account as AccountV6 } from '@schibsted/account-sdk-browser-v6'; // dev dependency latest

    import type { Identity as IdentityV5 } from '@schibsted/account-sdk-browser-v5'; // dev dependency previous

    type AssertImplements<TRequired, TActual extends TRequired> = true;
    type _AssertLatestAccountSdkIsCompatible = AssertImplements<AccountInterface, AccountV6>;
    // NOTE: This check will make the compiler catch an error for us! 🎉
    // This will fail compilation, because the getUserId method in AccountInterface returns a number, while the SDK v5 version might return a string.
    type _AssertPreviousAccountSdkIsCompatible = AssertImplements<AccountInterface, AccountV5>;
    // my-app/main.ts
    import { getAccountSdk } from '@schibsted/account-sdk-browser';
    import { SomeModule } from '@schibsted/some-module';

    const someModule = new SomeModule(getAccountSdk);

    After initialization, use the Account instance to render user state and start login when needed:

    async function renderLoginState() {
    const container = document.getElementById('login-container');

    if (await account.isConnected()) {
    const user = await account.getUser();
    container.textContent = `Hello ${user.givenName || user.displayName || user.userId}`;
    return;
    }

    container.innerHTML = '<button type="button">Log in</button>';
    container.querySelector('button').addEventListener('click', () => {
    account.login({ state: createLoginState() });
    });
    }

    Use these methods to inspect the current Session Service state:

    • account.hasSession() returns the raw SDK-known session response
    • account.isLoggedIn() returns whether the browser has a Schibsted Account session
    • account.isConnected() returns whether the user is connected to your client
    • account.getUser() returns the connected user session data
    • account.getUserId() returns the realm-specific user id as a number
    • account.getExternalId() returns an identifier specific to the configured client and supplied external party; this method is deprecated and retained only for compatibility with existing integrations
    • account.getUserUuid() returns the globally unique user id
    • account.getUserSDRN() returns the user SDRN
    • account.getSpId() returns the Varnish sp_id value when present

    Some session methods can trigger a full-page Session Service refresh redirect, especially for Safari-based browsers. Use callbackBeforeRedirect if you need to persist client state before that happens.

    const account = createAccountSdk({
    clientId,
    redirectUri,
    sessionDomain,
    callbackBeforeRedirect: () => {
    saveCurrentUiState();
    },
    });

    Use account.login() to start the OAuth authorization flow:

    account.login({
    state: createLoginState(),
    scope: 'openid',
    preferPopup: false,
    });

    By default the SDK redirects the current window. Set preferPopup: true when login is triggered by a user gesture and you want to try a popup first. If the popup cannot be opened, the SDK falls back to the redirect flow.

    If you only need the URL, use account.loginUrl():

    const url = account.loginUrl({ state: createLoginState() });
    

    state is an opaque OpenID Connect value that is returned to your redirectUri with the authorization code. Use it to protect against CSRF and to restore application state.

    A common pattern is:

    1. Your backend creates a random token and stores it temporarily.
    2. Your frontend calls account.login({ state }) with that token, or with an encoded payload that contains it.
    3. Schibsted Account redirects back to your redirectUri with code and state.
    4. Your backend validates the returned state before exchanging code for tokens.

    Keep Access Tokens and Refresh Tokens on your backend. Do not send them to the browser.

    Use acrValues when you need to request a specific authentication method:

    account.login({
    state: createLoginState(),
    acrValues: 'otp-email',
    });

    Supported values include password, otp, sms, otp-email, eid, eid-no, eid-se, eid-fi, and eid-dk. Values other than otp-email can be combined as a space-separated string where supported by the account flow. Verify the resulting AMR claim in the ID token on your backend if you need to enforce a completed authentication method.

    Use account.logout() to remove the brand Session Service session and redirect the browser:

    account.logout('https://awesomenews.site/logged-out');
    

    Use account.logoutUrl() when you need the logout URL without navigating immediately:

    const logoutUrl = account.logoutUrl('https://awesomenews.site/logged-out');
    

    Use account.accountUrl() to send the user to their Schibsted Account profile page:

    window.location.href = account.accountUrl('https://awesomenews.site/account-return');
    

    Use account.hasAccess() to check access to Schibsted Account product ids or Zuora feature ids:

    const access = await account.hasAccess();

    if (access?.entitled) {
    showPremiumContent();
    } else {
    showPaywall();
    }

    Pass product ids to override the configured defaults for a specific check:

    const access = await account.hasAccess(['other-product-id']);
    

    hasAccess() requires sessionDomain. Results are cached according to the Session Service TTL. If no product ids are passed to the method, defaultProductIds must be configured. Use account.clearCachedAccessResult(productIds, userId) when you need to invalidate that cache.

    Before using the simplified login widget, make sure your site has no site-specific terms and conditions in the Schibsted Account login flow.

    The widget is shown only when the SDK can read enough user context from the global account session. Your application decides when and how often to show it.

    if (!(await account.isConnected()) && shouldShowSimplifiedLogin()) {
    const opened = await account.showSimplifiedLoginWidget(
    { state: createLoginState },
    { locale: 'nb' },
    );

    if (opened) {
    rememberSimplifiedLoginWasShown();
    }
    }

    state may be a string or a function that returns a string or Promise<string>. The function is called only if the user continues from the widget.

    The SDK exposes fluent Account#on and Account#off aliases for browser-native event listeners. The loginRequested, logoutRequested, and sessionUpdated events are CustomEvent instances and provide their data in event.detail:

    import type { AccountEventListener } from '@schibsted/account-sdk-browser';

    const handleLoginRequested: AccountEventListener<'loginRequested'> = (event) => {
    console.log('Login started', event.detail.url, event.detail.method);
    };

    account.on('loginRequested', handleLoginRequested);
    account.off('loginRequested', handleLoginRequested);

    account.on('sessionUpdated', (event) => {
    console.log('Session data updated', event.detail);
    });

    Available Account events:

    • loginRequested — a CustomEvent whose detail contains url and the method (popup or default)
    • logoutRequested — a CustomEvent whose detail contains the logout url
    • sessionUpdated — a CustomEvent emitted when hasSession() detects a change in the stored session state; detail contains the new session response
    • simplifiedLoginOpened — an Event emitted when the widget is displayed
    • simplifiedLoginCancelled — an Event emitted when the widget is closed

    The SDK also emits schAccount:ready on window when an Account instance is created.

    Validation, network, and service failures from API calls reject or throw SDKError. Convenience methods such as isLoggedIn(), isConnected(), and getSpId() return fallback values instead of throwing when the session lookup fails.

    import { SDKError } from '@schibsted/account-sdk-browser';

    try {
    await account.getUser();
    } catch (error) {
    if (error instanceof SDKError) {
    console.error(error.toString());
    }
    }

    For setup requirements, integration flow, and project scripts, see the project README.