Enterprise SSO for IT admins
A page to forward to a customer company's IT admin - verify its domains, connect its identity provider through WorkOS, and run the company's sign-in rules.
This page is for the IT admin of a company whose employees will sign in to an app with single sign-on. The app's team sent you here, usually with an email from WorkOS that holds a setup link.
Your app's team sets the app up for single sign-on with the setup guide; this page is what is left for you.
It covers verifying the company's domains, connecting your identity provider through the WorkOS setup portal, naming who WorkOS alerts, assigning the app's roles to the company's groups, requiring passkeys for people who do not use single sign-on, confirming it's you before a settings change, keeping the company's network to its own accounts, ending a session, letting the identity provider approve AI agents, and reading or streaming the company's activity.
What you are setting up
A connection between your identity provider and the app, through WorkOS, a single sign-on service the app's team uses. Once it is active, your employees choose Continue with SSO on the app's login page, enter their work email, and sign in with your identity provider. You do not need an account in the app.
Before you start
- The setup link from WorkOS's email, one the app's team opens with you on a call, or one a workspace admin at your company opens from the app's own settings (Single sign-on, Set up SSO). It expires, so ask for a new one if it has. A link opened from the app brings you back to the app when you finish.
- Admin access to your identity provider: Okta, Microsoft Entra ID, Google Workspace, OneLogin, JumpCloud, or any SAML 2.0 or OpenID Connect provider.
- Your company's email domains verified in WorkOS. You do that yourself in the same setup portal (below), and single sign-on works only for people on a verified domain.
Verify your company's domains
WorkOS checks that your company owns each email domain its people sign in with, and the app trusts WorkOS's answer. The app's team has usually added your domain already; you prove it is yours.
- Open the setup link on the domain step: the one in WorkOS's email when the app's team ticked Domain verification, one they open with you, or Verify in WorkOS on the app's Single sign-on screen if your workspace admins have it.
- The portal takes your domain, works out who hosts your DNS, and shows the TXT record to publish there: the exact host and value to copy, with steps for your provider.
- Publish the record at your DNS provider. WorkOS checks for it, and a new record can take a few minutes to be visible; the portal marks the domain verified once WorkOS sees it.
Nothing else to do: the app reads WorkOS's answer on its own, and a domain you add and verify in the portal appears in the app too. Each subdomain is a domain of its own, and consumer mail domains such as gmail.com cannot be verified. If WorkOS stops verifying a domain, or you delete it in the portal, people on it can no longer sign in by SSO until it is verified again.
SAML
- Open the setup link. Choose your identity provider; the portal shows the steps for it, with screenshots.
- In your identity provider, create a SAML application with the values the portal gives you (the ACS URL and the entity ID).
- Map these attributes: email, first name and last name. The email must be on your company's verified domain, or sign-in is refused.
- Paste the identity provider's metadata URL (or upload its metadata file) back into the portal.
- Assign the application to the people or groups who should have access.
OpenID Connect
- Open the setup link and choose OpenID Connect.
- In your identity provider, create an OIDC web application with the redirect URI the portal gives you.
- Paste the client ID, client secret and discovery endpoint into the portal.
- Make sure the
emailandprofilescopes are granted, so the email and names are sent.
After setup
The portal shows the connection as active once your identity provider answers. Tell the app's team; they turn sign-in on. The first time each person signs in, WorkOS may show a consent screen asking them to share their profile with the app: that is expected.
Signing in from your identity provider's dashboard works for SAML connections once the app's team has turned it on: add the app's tile in your identity provider as you would for any SAML app, and a click signs the person in. Otherwise employees start from the app's login page.
If the app's team requires single sign-on for your company, everyone with an email on your verified domains is refused at every other sign-in method and asked to use Continue with SSO. That includes people who made an account before your company was set up: their first SSO sign-in links it.
The same portal is where, after setup, you rotate the SCIM bearer token, change the directory's attribute mapping and the groups it syncs, test a sign-in, sync a Google Workspace directory by hand, and keep your company's IT contacts list: the app has no screens of its own for these.
A second identity provider
Your company can sign its people in through more than one identity provider, for example after buying a company whose people use Microsoft Entra ID while yours use Okta. Ask the app's team: they add the second one to your company in WorkOS, and you or they finish its setup there. Until a domain is sent to it, it signs in nobody.
Which identity provider signs in each of your verified email domains is set in the app by your company's admins whose role manages Setup (they confirm it's them first), or by the app's team. A domain signed in by two shows your people a choice on the login page, so a move from one to the other can happen person by person. Each identity provider's certificate and state are its own, and a domain none of your active identity providers signs in is named on the Single sign-on screen.
The setup link the app gives you opens the WorkOS setup portal on the connection WorkOS's portal manages for your company; which one that is, with two, has not been checked yet against WorkOS itself.
Provisioning (SCIM)
If the app's team turned on provisioning for your company, you can manage who has an account in the app from your directory instead of one sign-in at a time. The setup link (from WorkOS's email with Provisioning ticked, or Set up provisioning in the app) opens the setup portal on the provisioning step.
- Choose your directory: Okta, Microsoft Entra ID, Google Workspace, an HR system, or a generic SCIM 2.0 provider.
- The portal shows a SCIM endpoint and a bearer token. In your directory, create a SCIM provisioning application (or turn provisioning on in the SSO application you already made) and paste both in. You can create a second token before revoking the first, so rotating it never stops provisioning.
- Assign the people who should have accounts, by group or one by one. Push the groups too: the app's team can give a role to everyone in a group, and take it back when someone leaves it.
- The portal shows the directory as linked once the first sync succeeds.
What each change does in the app:
| In your directory | In the app |
|---|---|
| A person is assigned | They get an account (on your verified domain only), join the workspaces the app's team chose, and sign in by SSO |
| A person is moved between groups | Their role follows the groups the app's team mapped, and the roles you assigned those groups in the portal |
| A person is deactivated or suspended | They are blocked and signed out everywhere; their account stays |
| A person is deleted or unassigned | The same |
| A person is reactivated or reassigned | They are unblocked |
| The directory is disconnected or deleted | Its people are marked inactive and lose the access the directory gave them, but are not blocked |
Nobody receives an email from the app about any of this. Changes reach the app within five minutes, or within seconds if the app's team set up a webhook.
A Google Workspace directory is the exception, since Google does not push its changes: WorkOS reads it about every 30 minutes. To have WorkOS read it now, choose Sync manually on the directory's page in the setup portal. WorkOS allows one such read every five minutes per directory, and counts the app's team's Sync now among them. Once WorkOS has read it, the changes reach the app as above.
Renewing the SAML certificate
Your identity provider signs every SAML sign-in with a certificate, and the connection trusts that one certificate. When it expires, nobody at your company can sign in to the app by SSO until the new one is uploaded. WorkOS warns ahead of the date, and the app shows the warning to your workspace admins on its Single sign-on screen.
- In your identity provider, create the new signing certificate for the app's SAML application (most providers let two be active while you switch).
- Open the setup portal on the certificate: Renew the certificate on the app's Single sign-on screen, or a link from the app's team. It lasts five minutes.
- Upload the new certificate, or the new metadata, in the portal.
- Make the new certificate the active one in your identity provider.
The warning goes once WorkOS reports the certificate renewed.
Your company's IT and security contacts
WorkOS keeps a list of your company's IT and security contacts: the email addresses it writes to about your connection, up to 20. Each one receives:
- a setup link, when the app's team invites that address;
- a warning before your SAML certificate expires, with a link to renew it;
- an alert when your connection has errors.
The emails come from WorkOS, from a WorkOS address unless the app's team has WorkOS send them from its own domain. Keep the list current: a contact who left takes the next certificate warning with them.
Change the list in the setup portal. WorkOS documents that "after setup, users can view and manage their IT contacts list from the Admin Portal post-configuration page" (WorkOS changelog): the setup portal's page for your connection once it is set up. To open it:
- On the app's Single sign-on screen, choose Change in WorkOS in its IT and security contacts section, if your app shows it. It appears once your single sign-on is set up: before that, the setup portal opens the setup itself. It needs a company role that manages Setup, and the link lasts five minutes, as Set up SSO's does. Like every setup link, it first asks you to confirm it's you unless you did in the last 10 minutes; back on the screen, choose Change in WorkOS again.
- Or ask the app's team to open it with you, or to change the list from their WorkOS dashboard.
When the app's team invites you, or someone else, by email, the invited address joins the list too.
See the list in the app. The Single sign-on screen shows the list, read only, with when each address was added, to your admins whose company role views Company roles. Your tools read it from the company API with admins:read. A change made in the portal shows in the app within a minute, or at once with Refresh. WorkOS does not report those changes, so they are not in your company's activity; the app's team's invitations are.
Roles from your identity provider
The app can give people their role from your identity provider, at each sign-in, without provisioning:
- In the setup portal's SSO connection, or your identity provider's application, assign roles to groups (WorkOS calls this role assignment), or send the group names as an attribute.
- Tell the app's team which role or group should give which role in the app. They map it once; it applies from each person's next sign-in.
A person's role follows what your identity provider sends: remove them from the group, and their next sign-in takes the role back. Every change is in the activity.
If you deactivate or delete the connection, nobody at your company can sign in to the app by SSO until it is active again, and the app's team is told at once. With single sign-on required, only your break-glass people can sign in in the meantime.
Assigning roles in the Admin Portal
If the app's team has turned it on for your company, you can decide who holds the app's roles yourself, from your own groups, without asking anyone to set up a mapping. The app's company roles and groups are in WorkOS as roles, and the setup portal has a step where you assign each of your groups a role.
- Open the portal with Assign roles in WorkOS: on the app's Single sign-on screen, if your company role manages its setup, or from the app's team. It opens your directory's setup when your company has one connected, and your single sign-on's otherwise, since that is where WorkOS keeps the role step.
- Find the role assignment step. If it is not there, ask the app's team: they choose whether WorkOS shows it.
- For each group, choose the role it gives. The app's own roles carry the names they have in the app, and slugs that start with
org-bb-:org-bb-auditoris the Auditor company role, andorg-bb-group-design-editorsis the company group Design editors, which gives whatever workspaces and role that group gives in the app. - Save. Coming back to the app reads your directory and your roles again, and the Single sign-on screen says whether the roles are in sync.
What a role gives is decided in the app, not in WorkOS: renaming or changing a role there changes what your assignment means, never which group you assigned. Some things to know:
- Assigning roles works for SCIM and Google Workspace directories, and for SAML and OIDC connections other than Google OIDC. For single sign-on, send your groups as the SAML
groupsattribute, and define the groups in the portal. - Unless the app's team has turned on multiple roles in WorkOS, someone in two assigned groups gets only the one higher in the order. Ask them, or make one group for people who need both.
- A role someone was given by hand in the app stays, whatever you assign. Your assignments add roles; they never remove one the app gave directly.
- When the app's team or your admins want to stop showing a role, WorkOS refuses while your groups are still assigned to it, and the app tells them to ask you. Remove the groups from that role in the portal, then tell them.
- A change to someone's groups reaches the app within minutes through your directory, or at their next sign-in through single sign-on. A change to which role a group is assigned reaches your directory's people when you come back to the app from the portal, when someone chooses Refresh from WorkOS, or at the next daily read, so it can take a day; people whose roles come from single sign-on get it at their next sign-in. To take someone's access away at once, deprovision them in your directory rather than changing their group's role.
Your company's access policy
Your workspace admins set it on the app's Single sign-on screen, under Access policy, unless the app's team manages it for you:
- Session length: sign people out after a stretch without activity, or after a fixed time however active.
- Only allow these addresses: your office and VPN ranges, IPv4 or IPv6. Outside them nobody can sign in, and requests from outside are refused until the person is back on the network. The screen offers to add the address you are saving from, and refuses a list that leaves you out.
- Break-glass people: up to five people who can still sign in another way (a passkey, if you choose Only with a passkey) while single sign-on is required. Keep them for when your identity provider is down; every such sign-in is in the activity. They follow your sign-in methods too.
- Sign-in methods without single sign-on: the ways your people may sign in when they do not use single sign-on, down to passkeys only. See requiring passkeys.
- Sign everyone out: ends every session at once, for example after a security incident. People can sign in again straight away; to keep someone out, deprovision them in your directory. To end one person's session on one device, see ending one session.
- Someone on a verified domain signs in: whether people at your company who sign in another way than SSO, with a verified email on your domains, join the company automatically, ask to join first, or stay outside it. The app's team can also bring in the accounts made before your company was set up.
- People outside the company in its workspaces: contractors and others not on your domains. Allow them, limit them to editor or viewer and your address list, or block them. A workspace's owner is never refused.
- The app's team signing in as one of your people: Allowed, and recorded or Not allowed. See the app's team signing in as your people.
- Keep the company's network to its own accounts: your company's ID and the header your proxy adds. Nothing here turns it on; your proxy does. See below.
- Stricter rules for one workspace: hold one of your company's workspaces, such as finance, to shorter sessions, fewer addresses or fewer ways in, never looser than the rest of your policy. See below.
Saving any of these asks you to confirm it's you unless you proved it is you in the last 10 minutes in a way your company counts.
Stricter rules for one workspace
Your company's policy applies in every workspace it binds. One of them can be held to stricter rules: a finance workspace with shorter sessions, only your office network, passkeys only or single sign-on required, while the help desk keeps your company's policy. A workspace's rules can only be stricter than your company's, never looser, and your company's policy still applies first.
- On the app's Single sign-on screen, under Access policy, find Stricter rules for one workspace. Changing them needs a company role that manages the access policy, while your company manages its own policy, and you confirm it's you first. Removing them asks too.
- Choose the workspace (any of your company's bound workspaces, from any of them), then the rules: a shorter idle limit, confirming it's them more often, fewer addresses (each inside your company's list), fewer ways in, single sign-on required, fewer agents, people outside your company held tighter, or the app's team not signing in as your people there. Each rule shows your company's value beside it; a looser one is refused and says which.
- Save. The screen says how many open sessions the rules will meet at their next request there. Nobody is signed out.
The screen will not let you lock yourself out of the workspace you are saving from: if your own session would be refused there by the new rules (your address, or the way you signed in), it stops you with the reason. Save from another of your workspaces, or ask the app's team, if that is what you mean to do.
What your people see. In that workspace only, a request that does not meet its rules is refused with a sentence that names your company and the workspace, and the rest of the app keeps working:
- "Globex asks you to sign in with single sign-on to use Globex finance. Confirm it's you to continue." Confirm it's you opens the app's sign-in page ("Confirm it's you to continue in Globex finance") with your identity provider, asked to sign them in afresh, or their passkey. They come back to the same page, still signed in, and the confirmation counts in that workspace.
- "Globex asks you to confirm it's you every 8 hours in Globex finance." and "Globex asks you to confirm it's you after 15 minutes away from Globex finance." work the same way. Being away from that workspace is what counts, not from the app.
- Someone who cannot confirm (no passkey, and single sign-on not working) reads "Sign in again" instead, and signs in again a way the workspace allows. The sign-in page says "Your company asks you to sign in again to keep using one of its workspaces."
- An address outside the workspace's list, an agent it does not allow, or a session the app's team opened as them cannot be fixed by confirming: the sentence says what to do instead ("Globex allows Globex finance only from its own network. Connect to it (or its VPN) to continue there.").
Someone who is not a member of the workspace never learns its rules. Its notifications are left out of a refused person's inbox, and its live updates are not sent to them, until they are back in.
What it does not cover. Your company API keys reach your company's settings, never a workspace's data, so they keep your company's own address list. The app's team's console is never held. If the app calls the service from its own servers with your people's sessions, those servers' addresses must be on the workspace's list too: ask the app's team.
Your activity records Stricter workspace rules saved and Stricter workspace rules removed (with "removed with its binding" when the app's team unbinds the workspace, which removes its rules too), and Refused in a workspace, once an hour per session, workspace and reason, with who, where and why. With company audit logs in WorkOS on, they arrive as company.workspace_policy_updated, company.workspace_policy_removed and company.workspace_access_refused.
Require passkeys for people who do not use single sign-on
Single sign-on is the strongest way in, and the app's team can require it for your company. Until your connection is set up, or for people your identity provider does not cover, you choose which other ways your people may use. Passkey alone is the phishing-resistant choice: a passkey only signs in to the real site, so it cannot be typed into a fake sign-in page.
- On the app's Single sign-on screen, under Access policy, find Sign-in methods without single sign-on. Changing it needs a company role that manages the access policy, while your company manages its own policy.
- Tick the ways your people may use: Password, Magic link, Email code, Phone code, Passkey, Google, Microsoft, GitHub, LinkedIn, Apple, Custom OpenID Connect, Ethereum wallet or Solana wallet. A way the app does not offer is shown disabled; ask the app's team to turn it on. Single sign-on is always allowed, and clearing the list allows every way the app offers.
- Save. The card says how many of your people already have a passkey: with Passkey alone, "31 of 40 people have a passkey; the others set one up at their next sign-in, unless they use single sign-on.", and with Passkey among other ways, "31 of 40 people have a passkey; the others set one up if they next sign in a way the list does not allow."
The screen will not let you lock yourself out. If you signed in a way the new list leaves out, it stops you, in one of two ways: when the list includes Passkey and you have none, "Add a passkey first, or keep Password on the list."; when the list leaves Passkey out, "Sign in a way it allows first, or keep Password on the list."
What your people see
- A way you do not allow. After their password or sign-in link is accepted, they read "Your company asks its people to sign in with a passkey or Google.", naming the ways you chose, and the sign-in page shows only those. Someone who tries a social sign-in you do not allow reads "Your company does not allow that way to sign in here. Choose another way, or ask your IT team." A wrong password still reads as a wrong password, so nobody learns your rule without one. The sign-up form, where anyone can type any address, gives only the second sentence, names no ways, and leaves nothing in your activity.
- When your rules cannot be checked. If the app cannot read your settings at that moment, it does not let people in another way: they read "We could not check your company's sign-in rules just now. Try again in a moment." and try again.
- Their first passkey. Someone with no passkey who signs in their usual way goes straight to Add a passkey to continue. They add one on their device, which checks their fingerprint, face or device PIN, and are signed in with it at once. This happens once, while they have no passkey; the app's new-device email covers that sign-in, and your activity records Passkey set up with the way they signed in.
- More passkeys, for a second laptop or a phone, are added on the app's sign-in page. Once one of your people has a passkey, adding another needs a passkey sign-in there in the last 10 minutes, list or none: a password alone cannot add one, so a stolen password cannot add a passkey to an account that already has one. While your list includes Passkey, every passkey added must also check it is them.
- Removing a passkey needs a passkey that checked it is them in the last 10 minutes: a passkey sign-in, or confirming with a passkey. Two removals at the same moment never take the last one.
- The last passkey cannot be removed while your list includes Passkey: "Your company asks you to sign in with a passkey. Add another before you remove this one."
- People already signed in stay signed in. The card counts the sessions that began a way the list no longer allows, and Sign them out ends them; those people sign in again a way you allow.
Why the passkey must check it is them
A passkey on a phone or laptop asks for a fingerprint, a face or the device's PIN before it signs in, so it proves both the device and the person holding it. A security key with no PIN proves only the device. While you have a list, the app accepts only a passkey that checked the person, and refuses the other with "Your company asks for a passkey that checks it is you, with your fingerprint, face or device PIN. Use one that does." Set a PIN on the security key, or use the device's own passkey.
A lost passkey
When someone loses the only device with their passkey, someone whose company role manages People chooses Reset passkeys for that person, under Sessions, after confirming it's them. The person's passkeys for the app are removed and they are signed out everywhere, with "Your company signed you out. Sign in again."; their next sign-in sets up a new passkey, as the first time did. Any sign-in still in progress from before stops working too, so someone who took their device cannot finish one, and the person gets an email saying your company reset their passkeys, with no link in it. It is in your activity as Passkeys reset, and your tools can do it with the company API, with a key that has the Reset passkeys scope.
Before you choose Passkey alone
- People on a browser or device without passkeys, such as an old browser or a locked-down shared device, can then sign in only with single sign-on.
- A passkey belongs to the web address it was made on. If the app's team moves their sign-in page to a new address, your people set up a new passkey at their next sign-in.
- Your break-glass people follow the list too, as Only with a passkey already narrows them.
- Every refusal is in your activity as Sign-in method refused, with the person, the way they tried and the ways you allow.
Confirming it's you before a settings change
A session can stay open for weeks, on a laptop someone walks away from. So before a change to your company's security settings, the app asks whoever is making it to confirm it is them, unless they proved it in the last 10 minutes in a way that counts (below). Signing in with a password, a sign-in link, a social account, or an ordinary single sign-on is never enough on its own, however recent: only a passkey that checked it was them, or a sign-in your identity provider was asked to make afresh. It is on for every company, and there is nothing to set up.
It asks before a change to the access policy, signing everyone out, opening a setup link (other than the audit log viewer), naming or changing company admins and roles, a group that gives a company role, making an API key or service account, changing log streams, resetting someone's passkeys, and trusting your identity provider to approve agents or turning it back on. Reading, revoking a key, disconnecting an app, signing one person out, ending one session, and turning off or removing your identity provider for agents never wait for it: taking access away should be quick.
- The screen shows Confirm it's you, and how: "Confirm it's you to change Globex's settings: sign in again at your company's identity provider." (or "use your passkey", or either).
- The app's sign-in page opens with your email filled in and only the ways that count. With single sign-on, your identity provider asks you to sign in again. With a passkey, your device checks it is you.
- You come back to the same page, still signed in, with your change waiting: "You confirmed it's you. Save again to apply it." Save within 10 minutes. Making changes does not extend the 10 minutes. The confirmation only counts in the browser you started it from, and for the company whose screen asked for it.
Which way counts. While your connection works, your people confirm with single sign-on, or with their passkey too when single sign-on is not required. A break-glass person who signed in without it, someone managing your company who is not one of its people (before you name a Company admin), and anyone while single sign-on is not set up or not working, confirm with their passkey. So when your identity provider is down, your break-glass people can still make changes.
Your sign-in methods do not change how someone confirms: they say how your people sign in, not how your admins confirm it's them, so an admin still confirms with their passkey when your list leaves Passkey out. Someone who has no passkey for the app yet sets one up on the same page first: the page says so and offers the ways they usually sign in (your list, or every way the app offers), with their email fixed. They sign in that way, add a passkey their device verifies, which your activity records as Passkey set up, and confirm with it. This works only while they have no passkey for the app's sign-in page; someone who has one on another device confirms with it, and a lost one is reset first.
Your identity provider. The app asks WorkOS to have your identity provider sign the person in afresh rather than reuse a session it already has open. WorkOS passes that on, as ForceAuthn="true" in a SAML request. Microsoft Entra ID documents that it honours it. Other identity providers may not: WorkOS notes that "providers that don't support it are unaffected", and such a provider signs the person straight back in from the session it already has. Nothing comes back to say which happened, so your activity records that the app asked for a fresh sign-in, never that one took place.
What never asks. Your company's API keys: a key has no person to sign in again, so making one in the app asks instead. The app's team's console. And a session the app's team opened as one of your people, or an agent or app your people connected, cannot confirm at all, so none of them can change these settings. An agent your identity provider approved is refused any change before it is asked anything.
Each confirmation is in your activity as Confirmed it's them, with how, and the change it allowed reads "after confirming".
Keep your network to your company's accounts
Your network's proxy can tell the app which companies' accounts may be used on it. On that network only your company's accounts get in: a personal account, or one that belongs to another company, is refused at sign-in and on every request. Off that network nothing changes. There is nothing to switch on in the app: the proxy is the switch. If you already set tenant restrictions for other SaaS apps, this is the same idea, with its own header.
-
Find your company's ID. On the app's Single sign-on screen, under Access policy, the card Keep the company's network to its own accounts shows it with a copy button, the header line to paste and the app's API host. It needs a company role that views the access policy. The app's team can read it to you from their console, and your tools get it from the company API (
GET /answerscompany.enterpriseId). -
Add the header to every request your proxy sends to the app's API host:
allowed-company-ids: 6650f1a2b3c4d5e6f7a8b9c0To allow more than one company, for example two of yours, or companies in other apps that share the app's API host, list them on the one line, separated by commas: up to 50.
-
Replace the header, do not add it. Set the proxy to overwrite
allowed-company-idson every request, not to add it only when it is missing. A request that carries the header twice is refused with "This network sent the allowed-company-ids header twice. Ask your IT team to make their proxy replace the header rather than add a copy." That rule is what stops someone on your network sending a list of their own beside yours. A proxy that appends to a value someone already sent, on the same line, cannot be told apart from yours, so replace. -
Turn TLS inspection on for the app's API host. TLS inspection is required: a proxy can only add a header to traffic it decrypts.
Any HTTPS proxy that can add a request header works. Zscaler, Netskope, Cloudflare Zero Trust, Palo Alto Prisma Access and Cato Networks are examples; follow your proxy's own steps for adding a request header to one host.
Check this network
Check this network, on the same card, reads what reached the app from wherever you are and says which of these it found:
| It says | What to do |
|---|---|
| "This network names your company. Only your company's accounts can use the app here." | Nothing: it works |
| "This network names other companies in this app, not yours. Your company's people are refused here." | The ID in the header is not your company's. Copy it again from the card |
| "This network names other companies only, so it does not apply to this app." | The header names companies in other apps only. Add your company's ID |
| "The allowed-company-ids header reached the app from this network but names no company ID, so it does not apply to this app." | Check the value: a company ID is 24 characters, the digits 0 to 9 and the letters a to f |
| "No allowed-company-ids header reached the app from this network." | The proxy does not add it on this host, or TLS inspection is off for the host |
| "This network sends the header twice. Ask your IT team to make their proxy replace it." | Set the proxy to replace the header (step 3) |
Anything in the header that is not a company ID is skipped rather than refused, and the card says how many entries it skipped. A header that names none of the app's companies is ignored by the app, so a mistake in it leaves your network open rather than locking your people out.
What it covers
- Every way your people sign in: password, magic link, email code, phone code, passkey, wallet, social sign-in and single sign-on, and a device such as a TV or a command line signed in from another screen. Single sign-on is refused before your identity provider is asked, so nobody is sent to sign in only to be turned away, and a first sign-in from your network creates no account for someone who is not yours.
- Every request the app makes with a session, and its live connections. Someone signed in with a personal account is refused as soon as they join your network, and the same session works again once they leave it.
- Apps and agents your people connect: on your network, one acting for an account that is not your company's gets no token and cannot refresh the one it has. Off the network it works again.
- The company API: a key of another company is refused on your network.
- Your break-glass people, and sessions the app's team opens as one of your people, belong to your company, so they get in.
- Someone on one of your verified domains whose account is not linked to your company yet is refused, unless your access policy has people on your domains join automatically. Signing in with single sign-on links them.
It does not cover:
- The console of the app's team. It is their tool, not an account of one of your people.
- Calls the app makes from its own servers, which leave from the app's network, not yours. The app's team can pass your header on from their servers when a call is made for a request that came from your network.
- Other apps. The app may share its API host with other apps, and the header applies only in an app whose companies it names: everywhere else it is ignored. To block an app outright, block its domain at your proxy.
What a refused person sees, and what is recorded
At sign-in they read "This network only allows your company's own accounts in this app. Sign in with your work account, or use another network." Already signed in, the app tells them "Your company only allows its own accounts on this network. Sign in with your work account, or use another network." Their session is not ended: it works again off your network.
Each refusal is in your company's activity as Refused on the company network, under the access policy filter, with:
- the refused account's full email address;
- whether it was a personal account or another company's, never which company;
- where it was refused: at sign-in, at single sign-on, on a request, on a live connection, when the app opened a session, when a connected app or agent got a token, or on the company API;
- the address and browser it came from: your network's own egress address, except when the app opened a session, which the app's own server asked for.
A refused sign-in is recorded every time; a refused request once an hour per session, and a connected app's or agent's token once an hour per person. When the header names more than one of the app's companies, each one's activity records it. Insights count sign-ins and requests refused on the company network, and with company audit logs in WorkOS on, each refusal reaches WorkOS as company.tenant_restricted.
Ending one session, for a lost laptop
Sign out ends every session a person has, on every device, and every app they connected. When one laptop is lost or stolen, end that one session instead: the person stays signed in everywhere else.
- On the Single sign-on screen, find the person in the people list and choose Sessions.
- Each session shows its device, browser and system, where it was used from, when it began, when it was last active, when it ends, and how it began: password, magic link, email code, phone code, passkey, social sign-in, wallet, single sign-on, or opened by the app's team (a session someone on that team opened as one of your people, from their console or with a key).
- Choose End on the lost device's session. That session ends at once, and the device is told at its next request that your company ended it. The person's other sessions, the apps they connected and your company's API keys stay as they are.
Listing a person's sessions needs a company role that views People; ending one needs a role that manages People. Only your company's own people are listed. Apps and agents a person connected are not sessions here: end those under Connected apps. Each ending is in the activity as a sign-out of one session, with the device, and your tools can do the same with the company API.
The app's team signing in as your people
The app's team can open a session as one of your people from their own tools, for example to see what that person sees while they answer a support request. Your company decides whether that may happen, and every time it does, or is refused, it is in your activity. The setting is The app's team signing in as one of your people, under Access policy:
- Allowed, and recorded (the default): each session is in the activity as Session opened by the app's team, with the person it was opened as, the name of the key the app's team opened it with, and the note they gave, when they gave one, after a colon: "Support tool: Ticket 4812". The note is also in the entry's details (
appTeamReason) in the CSV download, the company API and your log streams. - Not allowed: the app's team cannot sign in as your people, and each attempt is in the activity as App's team session refused. Choosing it also ends at once every session the app's team already has open as one of your people.
There is no setting that allows it without a record. Whoever may change your access policy may change this one: your admins whose company role manages the access policy, while your company manages its own policy, and the app's team from their console. The app's team cannot change it, or anything else of your company's settings, from a session they opened as one of your people, even an admin: such a session never counts as someone confirming it's them, so the screen refuses with "This session was not opened by you signing in, so it cannot confirm it's you. Sign in to the app yourself to make this change." Every change is in the activity.
You see "the app's team" and the name of their key, never which member of their team, and never their server's address. While they are in such a session, what they do is recorded as the person's act by the app's team: a role change names Priya and adds "by the app's team: Support tool". The session is in the person's own list of sessions as "Opened by the app's team", and in yours under Sessions, where you can end it like any other.
What your people see when they are signed out
When your access policy signs someone out, the app is told why. The app's hosted sign-in page says it when they come back to sign in, and the app can show it too:
| Why | They read |
|---|---|
| The session reached its maximum length | "Your company signs you out after 8 hours. Sign in again." |
| No activity for longer than the idle time | "Your company signs you out after 30 minutes without activity. Sign in again." |
| You signed them out, or everyone | "Your company signed you out. Sign in again." |
| You ended that one session | "Your company ended this session. Sign in again." |
The lengths are the ones your policy sets. Someone who signed themselves out, or whose session ended for any other reason, is not told your company did it.
Agents through your identity provider
If the app's team has turned it on, your identity provider decides which AI agents act for your people in the app. You approve an agent for your people once, in your identity provider, and it gets a short-lived token for any of them with no consent screen. Its access ends when your identity provider stops approving it, when someone leaves your directory, or when you turn it off here. It is built for Okta, with its Cross App Access.
Before you start:
- Okta with Cross App Access turned on for your Okta org. Okta Support turns it on (Okta Developer Support for an Integrator Free Plan org), and setting it up needs an Okta super admin. See Okta's Cross App Access guide.
- Single sign-on set up for your company, so the app knows who your people are.
- A company role that manages Setup, the same that may open the single sign-on setup links. The card is Agents approved by your identity provider on the app's Single sign-on screen. If it says "Your app's team has not turned on agents through your identity provider.", or the screen has no such card, ask the app's team: they turn it on, and can set it up for you from their console.
1. Tell the app which identity provider
Paste your identity provider's issuer and save; saving asks you to confirm it's you, as turning it back on later does. For Okta it is your Okta org's address, such as https://globex.okta.com: exactly what https://globex.okta.com/.well-known/openid-configuration names as its issuer, with nothing added. Leave Tenant empty for Okta; it is only for an identity provider several companies share. Leave Keys URL empty unless your signing keys are not where the discovery document says.
The app reads that discovery document, checks it names exactly what you pasted, reads your signing keys, and says whether your identity provider says it issues agent grants. If saving is refused, the message says why:
| It says | What to do |
|---|---|
| "The issuer must be an https address with no user name, query or fragment, such as https://acme.okta.com." | Paste the address alone |
| "That issuer signs for everyone who has an account with that provider, not for your company alone, ..." | Use your company's own issuer, such as your Okta org's address |
| "Your identity provider's discovery document names a different issuer (...). Paste exactly that." | Paste the issuer it names, character for character. It is named only when it is on the address you typed |
| "Your identity provider's discovery document could not be read from that issuer. Check that it is your identity provider's public issuer, and try again." | Check the address; the app reads it from the internet, never from a private network |
| "Your identity provider's keys could not be read. Check the keys URL, or leave it empty to use the one its discovery document names." | Empty Keys URL, or fix it |
| "Your identity provider's key set has no signing key this app accepts." | The app takes RSA keys of 2,048 bits or more, and EC keys on P-256 or P-384 |
| "Another company in this app already trusts this identity provider for agents." | Ask the app's team. The app never says which company |
| "This app does not take agents through a company's identity provider yet. Ask the app's team." | The app's team turns it on |
2. Copy what to enter in Okta
Once it is saved, the card shows What to enter in Okta:
- the app's issuer, which Okta calls Issuer URL. Okta does not let it change without deleting and resetting the connection, so copy it exactly;
- the app's token endpoint;
- each agent your company allows, with its client ID in the app. The list follows your Connected apps policy: with Approved apps only, approve the agent there first. An agent that identifies itself by a metadata document, as Claude does, holds no secret, so it is taken only with Approved apps only and its client ID listed, the address of that document, which the agent's own documentation gives; it appears here once you have.
3. Turn on Cross App Access in Okta
In Okta's words, from Okta's guide to managing Cross App Access, which has the current screens:
- In the Okta Admin Console, open the application your company signs in to the app with: the SAML or OIDC application you made when you set up single sign-on.
- Under Machine Assignments, Callers, Cross App Access (XAA), choose Enable. Set Issuer URL to the app's issuer from the card, leave Audience/tenant ID empty, and add the scopes agents may ask for, as the app's team names them.
- On each AI agent, under Resource connections, connect it to that application, with the agent's client ID in the app (from the card), the resource, which is the app's MCP server address the app's team gives you, and the scopes it may use.
- Decide in Okta who may use each agent. When Okta stops approving someone, it stops issuing agent grants for them.
4. The first sign-in proves it
Once saved, the card shows Pending: no agent gets a token through it yet. Anyone who manages Setup can paste an issuer, so the app waits for proof that this identity provider is really your company's. The first time one of your people uses an agent through it, and the app knows them by their single sign-on (they have signed in to the app by SSO before), your identity provider has vouched for your own person: the card turns On, that agent gets its token, and your activity says the identity provider was proven. A person the app knows only by your directory or by their email does not prove it. The app's team can also approve it for you from their console.
While it is pending, nothing about it is shared: if another company has pasted the same issuer, it learns nothing from your people's agents, and once yours is proven, its claim is removed. If the app says Another company in this app already trusts this identity provider for agents, that company's is already proven or approved: ask the app's team. If you typed a Keys URL by hand, the first sign-in cannot prove it, and only the app's team can approve it.
5. Check it
Check reads your discovery document and signing keys again: after you rotate keys, or when the card warns that they could not be read. The card shows how many keys were found and when, when an agent last got a token, and who added the identity provider. While a check fails, the keys found last stay in use, and the app reads your keys again by itself when a grant is signed with a key it has not seen, at most once a minute.
Token lifetime is 5, 15, 30 or 60 minutes, never longer than the app's team allows. It is how long an agent keeps working after Okta stops approving it. Each token spends one of Okta's agent grants, and Okta limits how many it issues per person and app each month as part of SSO (see Okta's guide above), so a shorter lifetime spends more.
Turning it off
- Turn off ends at once every agent session your identity provider approved, and refuses its grants until you choose Turn on. One that was still pending is pending again when turned back on.
- An agent never changes these settings: inside a session an agent got through your identity provider, every change to your company's settings is refused, whatever the person it acts for may do.
- Remove stops trusting it altogether, and also ends every agent session it approved. Saving a different issuer does the same for the old one.
- Your tools can turn it off with a company API key that has
apps:write(the company API). A key can never turn it on or change the issuer.
Who an agent can act for
Only your own people, found from what your identity provider's grant says: the person's single sign-on link, made the first time they signed in to the app by SSO; their directory record, if your directory provisions them; or an email, in plain ASCII, on one of your verified domains belonging to someone who is already one of your people and has verified that address. Nobody is ever created. Someone who has never signed in by SSO, been provisioned or joined the company is refused as Not one of the company's people, and the activity keeps your identity provider's id for them (sub) so you can find who it was.
When someone leaves, or you change your mind
| You | What happens |
|---|---|
| Stop approving someone, or an agent, in Okta | Okta issues no new grant. A token already issued works until it expires: at most the token lifetime |
| Deprovision someone in your directory | At once: they are blocked, every session ends, agents' included, and every grant for them is refused |
| Sign someone, or everyone, out | Their sessions end at once, agents' included. Okta may approve again: to keep someone out, stop approving them in Okta or deprovision them |
| Stop allowing an agent under Connected apps | Its live sessions among your people end at once, and its grants are refused |
| Disconnect an agent under Connected apps | Its access for those people ends at once. Okta may approve it again: to keep it out, stop allowing it in your policy or in Okta |
| Turn off or remove the identity provider here | Every agent session it approved ends at once |
An agent's session never outlives your access policy's maximum session length.
In the activity
- Identity provider trusted for agents, Agents through the identity provider changed (proven by single sign-on, approved by the app's team, turned on or off, its token lifetime or keys URL) and Identity provider no longer trusted for agents (also when another company proved the same identity provider first), under setup changes.
- Agent approved by the identity provider: an agent got a token for one of your people, once a day per identity provider, person, agent and way they were found (every time when found by email), with how the person was found and the grant's id (
assertionId, itsjti). - Agent refused, once an hour per reason (and per person and agent once the grant's signature holds), under connected apps:
| Reason | What to do |
|---|---|
| Identity provider turned off for agents | Turn it on here. If it is on, your company or its single sign-on is turned off in the app: ask the app's team |
| Signature not valid | Choose Check. A stream of these is worth a look: someone is sending grants your identity provider did not sign |
| Meant for another app | Okta's Issuer URL for this application is not the app's issuer: copy it again from the card |
| Not an agent grant | The agent sent another kind of token, such as an ID token |
| Expired | The grant expired before it was used, or was issued to live longer than an hour |
| Issued to another agent | The agent's resource connection in Okta names another client ID: copy it from the card |
| Already used | Each grant is used once. The agent asks Okta for a new one |
| Asks for more than the app supports | The grant asks for proof of possession or detailed authorization, which the app does not support yet |
| Not one of the company's people | They sign in to the app by SSO once, or your directory provisions them |
| Person blocked or deprovisioned | As intended |
| App not allowed | Allow it under Connected apps, or leave it refused |
| Agent with no secret not approved by name | An agent that identifies itself by a metadata document, as Claude does, needs Approved apps only with its client ID listed; allowing any app is not enough |
| Not allowed on the company network | Your network's header names another company (check this network) |
Insights count the agents approved, the people they acted for and the refusals by reason. With company audit logs in WorkOS on, these entries reach WorkOS as company.agent_issuer_added, company.agent_issuer_updated, company.agent_issuer_removed, company.agent_access_granted and company.agent_access_refused.
Workload identity for CI and cloud
Your CI jobs and cloud workloads can call the app's company API without a stored API key, once the app's team has turned workload identity on. A workload sends the short-lived token its platform already gives it (GitHub Actions, GitLab, Google Cloud, AWS, Microsoft Entra or another OpenID Connect issuer) and gets a company API token of at most an hour, acting as one of your service accounts.
- Under API keys, add a service account for the workload, or use one you have.
- Under it, Add workload credential: choose where it runs, and give the numbers that name your repository or account (GitHub's organization and repository IDs, GitLab's group and project IDs, Google's service account ID, the AWS role ARN, the Entra tenant and object IDs), a branch or environment where the platform has one, its scopes and how long its tokens live. You need API keys at Can manage, and you can give only scopes your own role reaches. The app asks you to confirm it's you first.
- Open Setup on the credential, and copy its reference and the step into your workflow or command.
Pin a protected branch or an environment with required reviewers: a pull request from a fork runs without one. Names are never matched, only numbers, so a renamed or recreated organization of the same name does not match.
Turning a credential off or deleting it stops its live tokens at once, and so does deleting its service account. The card shows when it was last used and its last refusal, with the claim that did not match. In your activity, Workload credential added, changed and removed, Workload signed in (with the repository, branch and run) and Workload refused (with why) are under API keys. See workload identity for each platform's setup.
Viewing and exporting your company's activity
The app keeps your company's activity: sign-ins, refusals, provisioning and every settings change. Your company admins read it on the app's Single sign-on screen, under Activity, and download it with Download CSV, as far back as the app keeps it.
If the app's team has turned on company audit logs, your company's activity is in WorkOS too, and you can work with it there:
- On the Single sign-on screen, choose Open the audit log viewer (it needs a company role that views the activity), or ask the app's team to open it with you. The link lasts five minutes.
- The WorkOS Admin Portal shows the events, newest first, and filters them. Actions are named
company.and what happened:company.sign_in,company.sign_in_refused,company.member_role_changed. - Export from the portal, or with Export from WorkOS on the Single sign-on screen. One export covers up to 31 days at a time, from the last three months or as long as WorkOS keeps your activity, if less, and its download link works for ten minutes; ask again for a fresh link.
For anything older than WorkOS exports, or more than 31 days in one file, use Download CSV. How long WorkOS keeps your activity (30 days to ten years) is set by the app's team, and the screen shows it.
Sending the activity to your SIEM
Your company's activity can reach your SIEM as it happens. How depends on whether the app's team has turned on company audit logs.
With company audit logs on, WorkOS sends it. WorkOS streams to Datadog, Splunk, AWS S3, Google Cloud Storage, Microsoft Sentinel, Snowflake or any HTTPS endpoint (JSON or NDJSON, with headers you set). The app's team starts the setup, since each stream is billed to their WorkOS account: they set it up with you on a call, or send WorkOS's email inviting you to set up log streams. In the portal, choose the destination and enter your SIEM's credentials; they stay in WorkOS, and the app's team never sees them.
- WorkOS sends from a fixed set of IP addresses, listed on WorkOS's log streams page, which also documents what each destination receives. Allow those addresses if your intake is behind a firewall.
- Requests are not signed by the app: on an HTTPS endpoint, authenticate them with the headers you set in the portal.
- The Single sign-on screen shows the stream's destination, state and last delivery, read only. A stream in error or invalid stays that way until whoever set it up fixes it in WorkOS: the app's team, from their console, with you when your SIEM's credentials changed (they send you a new link). WorkOS then sends the events it still keeps from the gap.
Otherwise, the app sends it. The Single sign-on screen has a Log streams section:
- HTTPS endpoint: any collector that takes a JSON POST. Each request is signed: verify
x-buildbase-signature, which issha256=and the HMAC-SHA256 of<x-buildbase-timestamp>.<body>under the secret shown once when you add the stream. You can also send anAuthorizationheader. - Datadog: pick your site (the part of your Datadog URL, such as
us1oreu1) and paste an API key. Records arrive withsource:buildbase. - Splunk: paste your HTTP Event Collector URL and token, and an index if you want one. Events arrive with
sourcetype=buildbase:enterprise:audit.
Send a test event checks it at once. Events can arrive twice; each carries an id to drop the repeat. If your receiver is down, nothing is lost: the stream retries for as long as it takes and catches up in order, and the screen says it is failing until then. If the app's team keeps these settings themselves, the section is read-only: ask them.
Moving from the app's stream to WorkOS's. Once company audit logs are on, a new stream is WorkOS's, and the app's streams you already have keep sending until you delete them. Run both for a while if you like: drop a WorkOS event whose metadata.entry_id matches the id of an event the app's stream already sent. Once WorkOS's stream is active, delete the app's.
The same screen has Insights: how many of your people signed in, how, and who was refused and why, over the last 7, 30 or 90 days.
If something goes wrong
- "Single sign-on is not set up for that email address": the person's email domain is not verified for your company. Check it in the setup portal's domain step, or ask the app's team.
- "The email on your company account is not on a domain this app accepts": your identity provider sent an email on a different domain. Check the email attribute mapping.
- "Your company's sign-in service could not finish signing you in": check that the application is assigned to the person, and that the certificate in the portal is current. An expired certificate does this to everyone at once; see Renewing the SAML certificate.
- "Your company only allows sign-in from its own network": the person is outside your access policy's addresses. Connect them to the office network or VPN, or add the range under Access policy.
- "This network only allows your company's own accounts in this app", or "Your company only allows its own accounts on this network": your proxy names your company, and the person used a personal account or another company's. They sign in with their work account. If they did, choose Check this network: the header may name another company (check this network).
- "This network sent the allowed-company-ids header twice": your proxy adds the header instead of replacing it. Set it to replace the header on every request.
- "Your company signs you out after ..." or "Your company signed you out": your access policy ended the session, as set. They sign in again. Change the lengths under Access policy if they are wrong.
- "Your company's single sign-on certificate has expired": WorkOS reported your SAML certificate expired, so no sign-in through it can finish. See Renewing the SAML certificate. If nobody saw WorkOS's warnings, check who is on your IT and security contacts.
- "Your company requires single sign-on": the person used a password or another method; they should use Continue with SSO instead.
- "Your company asks its people to sign in with ..." or "Your company does not allow that way to sign in here": your sign-in methods leave out the way they used. They use one you allow, or you change the list under Access policy.
- "Your company asks for a passkey that checks it is you": their passkey or security key did not check their fingerprint, face or PIN. They set a PIN on the security key, or use their device's own passkey.
- "Add a passkey to continue": expected, once, for someone with no passkey while your list asks for one.
- "Your company asks you to sign in with a passkey. Add another before you remove this one.": they tried to remove their last passkey. They add another first; a lost one is reset by your admins (a lost passkey).
- "Confirm it's you to change ...": expected before a settings change (confirming it's you).
- "You signed in as someone else": while confirming, the person signed in to another account, at your identity provider or with another passkey. They sign in with the account they are confirming.
- "This session was not opened by you signing in": the change was tried from a session the app's team opened, or from an agent. Someone at your company signs in themselves to make it.
- Your identity provider did not ask for the password again while confirming: it does not support a forced sign-in, so it reused the session it had (your identity provider). Signing out of the identity provider first makes it ask.
- "This workspace belongs to Acme, which keeps it to its own people" (with your company's name): the person is not on your verified domains, and your access policy blocks people outside the company. If they are one of yours, check their email is on a verified domain; if not, change People outside the company in its workspaces.
- Someone the directory assigned has no account in the app: their email is not on your verified domain, or the app is at its plan's user limit. The app's team sees the reason on your company's page in their console.