Access Requires an Installation
Signing in with a JTL ID proves who the user is; it does not by itself grant access to your app. Access follows the installation: a user can access your app’s data only for an organization where your app is installed. When a user signs in, they select the organization they are acting for. That organization is included in the token’surn:jtl:tenant_id.
If the organization has installed your app, the token includes the scopes granted to your app in urn:jtl:api_scopes. If it has not, the token does not include your app’s scopes, and API calls against that organization return 403 Forbidden.
A merchant, or one of their staff, must therefore install your app for their organization from the App Store before anyone in that organization can use it. See Scopes & Permissions for how installation grants and bounds your scopes.
Adding Sign-In to your 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 anapp.json file in your frontend project root:
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:
app.json and .env configuration and uses them to register the app with JTL.
Run:
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
.env file 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
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
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.
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.Call the JTL-Wawi API
API calls carry the access token and the tenant the merchant belongs to. Read the tenant fromurn:jtl:tenant_id in the userinfo response.
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.
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.
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.Sign-in fails with a redirect URI error
Sign-in fails with a redirect URI error
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.The merchant returns to the sign-in page repeatedly
The merchant returns to the sign-in page repeatedly
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 userinfo request returns 401
The userinfo request returns 401
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.A protected route shows a configuration hint instead of signing in
A protected route shows a configuration hint instead of signing 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.The sign-in top bar renders without styling
The sign-in top bar renders without styling
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.API calls return a tenant error
API calls return a tenant error
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.The merchant signs in but every API call returns 403
The merchant signs in but every API call returns 403
Sign-in succeeds, but the organization the merchant selected has not
installed your app, so the token carries none of your scopes. Check
urn:jtl:api_scopes in the token: if it is empty or missing your scopes, ask
the merchant to install your app for that organization from the App Store, or
to switch to an organization that already has it. See
Access Requires an Installation.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.