Skip to main content
An app that runs on your own domain signs merchants in itself, against the platform identity provider. It receives an access token, an ID token, and a refresh token, and uses the access token to call the JTL Cloud API. This is the browser sign-in flow described in Architecture Overview. Apps that render inside the Hub or Cloud ERP receive the same token through AppBridge instead. See App Token Authentication for that flow. An app with no frontend or fullstack authenticates as itself with a service account. See Service Account Authentication for that flow.

Adding Sign-In to an Existing App

To get started, install the JTL’s auth library:

Create the Manifest and Register your App

You must register your app with JTL before it can access JTL resources. Create an app.json file in your frontend project root:
The publicClient object tells JTL this is a browser-based authentication flow, where the user signs in through the JTL identity provider and is redirected back to your app after authentication. redirectUris defines the allowed URLs where JTL can send the user after signing in. postLogoutRedirectUris defines where the user can be redirected after signing out. See App Manifest: Authentication for the full field list. Next, create a .env file in your frontend project root:
The registration command reads your app.json and .env configuration and uses them to register the app with JTL. Run:
You can also use the registration wizard on Partner Portal to register your app.

Configure the Environment

Once your app is registered, copy the public client ID in the Partner Portal and add it to your environment variables:
.env
If you’re using the CLI to register the app, it will automatically generate a .env file and populate it with the required values.

Wrap your App

The provider holds the session and makes it available to the rest of your component tree. Mount it above your router, passing the values from your environment.
main.tsx
Until your app is registered and clientId has a value, the provider renders its children unchanged.

Protect a Route

RequireJtlAuth renders its children only when a merchant is signed in. Anyone reaching a protected route without a session is sent to the identity provider and returned to the same route afterwards.
App.tsx
Above the gated content, RequireJtlAuth renders a top bar showing the signed-in merchant and a sign-out control. To place that bar yourself, use the JtlAuthBar component directly. Gate the routes that run on your own domain. Routes that render inside the Hub or Cloud ERP receive identity through AppBridge and are not gated this way.

Read the Signed-In User

useJtlAuth returns the current session. The profile object carries the ID token claims, and access_token is the credential you send to the JTL Cloud API.
Inside RequireJtlAuth a session always exists, so the null check guards only the moment before the provider resolves.

Fetch the User Profile

The ID token carries basic identity claims. The provider’s userinfo endpoint returns the full set, including the tenant the merchant belongs to.
The response contains the standard OpenID Connect claims alongside JTL-specific ones:

Call the JTL-Wawi API

API calls carry the access token and the tenant the merchant belongs to. Read the tenant from urn:jtl:tenant_id in the userinfo response.
The library refreshes the access token as it approaches expiry, so read user.access_token at call time rather than holding a copy in component state. Optionally, you can use the getTokenInformation function to get the token information.
The organizationId in the token information is the same as the tenantId.

Sign Out

RequireJtlAuth includes a sign-out control in its top bar. To sign out from elsewhere in your app, call signoutRedirect from the session hook.
Sign-out clears the local session and redirects to the identity provider, which returns the merchant to one of the URLs declared in postLogoutRedirectUris. Without an entry, sign-out ends on the identity provider rather than back in your app.

Common Issues

Common failures and what causes them.
The identity provider matches redirect URIs exactly, so the URL your app sends must be present in your manifest character for character. A trailing slash, a different port, or http where the manifest declares https all count as a mismatch. Check the value in your manifest against the URL in the browser address bar when the error appears, and register a new app version if they differ.
This happens when the session cannot be stored, usually because the app is served over http on a host other than localhost, or because browser storage is blocked. Serve the app over https in any environment other than local development, and confirm that third-party storage restrictions are not applying to your domain.
The access token has expired or was rejected. Read user.access_token at the point of the call rather than capturing it once, so the value reflects the most recent refresh. If the token is current and the request still fails, confirm that VITE_JTL_ISSUER points at the same environment your app is registered in.
The client ID is missing, so the library has nothing to authenticate against. Confirm that VITE_JTL_CLIENT_ID is set and that the provider receives it. A freshly scaffolded app has no client ID until it is registered, so run npm run register first.
Tailwind has not scanned the library’s dist directory, so its utility classes were never generated. Add the @source directive for @jtl-software/cloud-apps-auth/dist to your CSS alongside the one for Platform UI, then restart the dev server.
The X-Tenant-ID header is missing or does not match a tenant the merchant belongs to. Read the value from urn:jtl:tenant_id in the userinfo response.

What’s Next?

Using Platform APIs

Query the JTL Cloud and JTL-Wawi APIs with the right headers and scoping.

App Manifest

Declare the public client, redirect URIs, and capabilities your app needs.

Architecture Overview

Compare the integration types and authentication flows available to your app.

Platform UI

Build your interface with the JTL component library.