OpenX AI App
Goal: Give the AI's Azure sign-in an app of its own, from inside OpenX, with nothing to do in a portal and nothing to run by hand.
The OpenX AI app is a multi-tenant Microsoft Entra app, created in a workspace's company directory. It is a registered app like any other in App Identity, under that workspace's company directory. Setting it up is a product flow: an AI proposes it, a person accepts it, and Deploy builds it. Any workspace with a company directory sets it up the same way, and the same three moves work the next time someone needs an app like this, in a new directory or a new environment.
Which app the AI signs in with. OpenX's AI sign-in to a person's Azure, the one that lists their subscriptions, uses one app for every workspace: the OpenX AI app on Fathym's root workspace (the workspace with no parent), under Fathym's own Entra directory. An OpenX AI app set up on any other workspace is created in that workspace's directory and recorded there. It belongs to that workspace, and OpenX's AI sign-in does not use it.
This page is for the workspace owner, or anyone who holds Workspace.Access.Manage there. It describes the
setup from the screens OpenX shows. If a step you need is not on a screen, the page says so plainly instead of
sending you somewhere else.
Before you start
- The workspace has a company directory in App Identity that is complete (Company Azure AD, with its tenant). A B2C directory cannot hold this app.
- You are the workspace owner or hold
Workspace.Access.Manage. Anyone else sees the proposal but no Accept or Reject. - For the certificate choice only: the workspace's own Key Vault, from its private landing zone, is ready.
The three moves
| Move | Who | Where | What it does |
|---|---|---|---|
| Propose | An AI (Azi, or an agent connected over MCP) | A proposal on the workspace | Asks for the app. Writes nothing |
| Accept | You | Environment -> App Identity -> Platform apps | Records the request on the directory |
| Deploy | You | Deploy | Creates the app in the directory and records what it made |
1. An AI proposes the app
The proposal is a kind of its own, AISignInApps. It can carry only these fields:
| Field | What it is |
|---|---|
ProviderLookup | The workspace's company directory, as named in App Identity |
DisplayName | The name people see in their own directory. Left out, it is OpenX AI |
Hosts | At least one OpenX origin the sign-in returns to, such as www.openindustrial.co or localhost:5410, with no path. OpenX adds /workspace/azure-ai/callback to each one itself |
Purpose | Why it is proposed, for the person who accepts. Never stored |
CredentialKind | federated (the default) or certificate. You can change it when you accept |
A proposal never carries a secret, an id, a scope or a credential: OpenX sets the audience, the permissions and the credential itself. It sits alone in its own bundle, and a directory holds one OpenX AI app. How an AI sends a proposal is on the proposals tool page.
An AI can only propose it once App Identity is configured, so a workspace with no directory yet answers
IDENTITY_NOT_CONFIGURED and an operator sets one up first.
Refusals when it is proposed
An AI gets one of these back at once and nothing is stored.
| Code | What it means |
|---|---|
SECRET_IN_PROPOSAL | A field looks like a secret. The app never carries one, so the whole field is refused and its value is never echoed |
APPLIED_FIELD | The field is one OpenX writes itself: the ids, credential, audience, scopes or reply URLs |
UNKNOWN_AI_SIGNIN_APP_FIELD | The field is not part of this proposal, so it would be stored and never read |
AI_SIGNIN_APP_HOSTS_REQUIRED | No OpenX origin was named |
AI_SIGNIN_APP_HOST_INVALID | A host has a path, or is not an origin. OpenX adds the path itself |
AI_SIGNIN_APP_NAME_BLANK | DisplayName is blank. Give it a name or leave it out |
AI_SIGNIN_APP_CREDENTIAL_KIND_INVALID | CredentialKind is not federated or certificate |
AI_SIGNIN_APP_LOOKUP_INVALID | The app's lookup is not lowercase letters, numbers and single hyphens, like openx-ai |
AI_SIGNIN_APP_DIRECTORY_NOT_FOUND | ProviderLookup names no App Identity in this workspace |
AI_SIGNIN_APP_DIRECTORY_UNSUPPORTED | The directory is B2C or another OpenID Connect directory. The app is created in a company Entra directory |
AI_SIGNIN_APP_EXISTS | This directory already holds the app, requested or created |
AI_SIGNIN_APP_BUNDLE_NOT_ALONE | Another proposal is in the same bundle. Move it to its own bundle |
UNKNOWN_ROOT_KEY | The proposal hung a key on the record that is not part of it |
2. You accept it in Platform apps
Open Environment -> App Identity on the workspace. After Access Cards there is a fifth tab, Platform apps, on every workspace. It says "OpenX's own Entra apps in this workspace's company directories, managed here like your directories' apps. The OpenX AI app is the one the AI's Azure sign-in uses."
With nothing waiting it reads "No platform app is requested yet. An AI proposes the OpenX AI app, and it appears here to accept."
A waiting proposal shows as Proposed: and the app's name, with its purpose, its Directory, and Returns to, the origins it named. If you can manage, you also see:
| Control | What it does |
|---|---|
| Use a certificate instead of OpenX's managed identity (no secret either way) | Unticked is the default, a federated credential with no secret. Ticked makes the credential a certificate in your Key Vault. Neither stores a secret |
| Accept | Records the request on the directory and answers "Accepted. The request is recorded on the directory." Creates nothing yet |
| Reject | Rejects the proposal |
When you accept, OpenX re-reads the proposal from the workspace itself, so nothing on the page can alter what is recorded. For a federated app it also fills in the managed identity the credential will trust, from the workspace's own deployment, so you never type it.
The app then appears in the tab as OpenX AI app with "Requested, not yet created", and the credential you chose. Nothing exists in the directory yet.
TIP
The line after "Requested, not yet created" still says the step that creates the app is not built. That sentence is out of date: the next Deploy is that step.
Accepting it through the generic proposal accept does not work. That door answers
AI_SIGNIN_APP_ACCEPT_IN_APP_IDENTITY and leaves the proposal in place: "The OpenX AI app is accepted in the App
Identity modal's Platform apps section. Open it there to accept or reject it. Nothing was accepted here."
Refusals when you accept
| Code | What it means |
|---|---|
access-manage-required | "Only a workspace owner or an access admin can manage connected apps." You are not the owner and do not hold Workspace.Access.Manage |
ai-signin-app-credential-kind-invalid | The credential is federated or certificate. Nothing was saved |
proposal-not-found, not-an-ai-signin-app-proposal, proposal-not-pending | The proposal is gone, is a different kind, or was already accepted or rejected. Reload |
ai-signin-app-directory-unknown | The proposal names no App Identity in this workspace |
commit-failed | "The request was not saved. Try again." |
AI_SIGNIN_APP_REQUEST_NOT_ALLOWED | A save, other than this tab, that asks for the app or changes the request was made by someone who is not the owner or a manage holder. "Only the workspace owner or someone with the 'Workspace.Access.Manage' right can ask OpenX to set up the OpenX AI app, or change that request." |
3. Deploy creates the app
On the next Deploy, or the next save in App Identity that runs the
directory's setup, OpenX acts on the recorded request, using the directory's own management app, read from the
workspace's own landing zone, as it does for job identities and sign-in apps. Deploying needs the
Workspace.Deploy right. OpenX does this, and nothing else:
| Step | What happens |
|---|---|
| The app | Created in your company directory under the name you gave. Anyone's Microsoft work or school account may sign in to it (multi-tenant) |
| Its permissions | Exactly two: Azure Service Management's user impersonation, and sign-in (openid) |
| Its reply URLs | One per origin you named, each ending /workspace/azure-ai/callback |
| Its service principal | Created if the directory has none |
| Its one credential | Federated (the default): a trust that lets Microsoft accept OpenX's own managed identity as the app, so there is no password and no file to keep. Certificate: the workspace's own Key Vault generates the certificate, its private key never leaves the vault, and only the public part is bound to the app |
| The record | The client id, object id, credential kind, tenant and active are written to the directory's record. For a certificate, the record holds a $secret: reference to it. Never a secret |
No client secret is ever written, and a person never edits a setting, runs a script or opens a portal for any of this. The client id is kept on that record, not in a server setting.
The Platform apps tab then reads "Created in the directory.", with the Client ID. The commit's message says which kind it made:
- "The OpenX AI app was created in the directory with a federated credential trusting the workspace runtime’s managed identity, and no secret."
- "The OpenX AI app was created in the directory with a Key Vault certificate whose private key never leaves the vault, and no secret."
Deploying again is safe
Every Deploy re-checks the app. If it already matches, nothing is written. If a reply URL, a permission or the credential has drifted, OpenX brings it back to the one setup above, and the message says it was "checked and brought back to its setup". The certificate is read, not made again.
An app that already holds the name is used only if the directory's management app created it and it holds no client secret. Otherwise OpenX refuses it by name and touches nothing.
OpenX's own AI sign-in reads the app from Fathym's platform workspace. Keep to one OpenX AI app there: if two are recorded, the sign-in reads it as not set up rather than guess which one.
Refusals when Deploy builds it
A refusal is a message, never a failed Deploy. It records nothing, and the next save or Deploy tries again.
| Code | What it means |
|---|---|
AI_SIGNIN_APP_DIRECTORY_UNSUPPORTED | "... it is created only in a company (Entra) directory named by its tenant." |
AI_SIGNIN_APP_CREDENTIAL_KIND_UNKNOWN | "... its credential kind must be 'federated' or 'certificate'." The Platform apps tab cannot record any other |
AI_SIGNIN_APP_HOSTS_REQUIRED | "... its request names no OpenX origin, so it would have no reply URL." |
AI_SIGNIN_APP_HOSTS_INVALID | A host in the request is not an OpenX origin. The message says which |
AI_SIGNIN_APP_ARM_SCOPE_UNKNOWN | "... Azure Service Management's user impersonation permission could not be read from its service principal in this directory." Nothing was written |
AI_SIGNIN_APP_IDENTITY_UNKNOWN | A federated request: "the managed identity its federated credential must trust is not named on the request, or is not a managed identity in this directory." OpenX records the identity at accept; when the deployment had none, the request records none, and OpenX will not guess. The identity is looked up in the app's own directory, so in a directory where OpenX's managed identity does not exist, such as a customer's own, this is the answer today: choose Use a certificate there |
AI_SIGNIN_APP_VAULT_NOT_READY | A certificate request, and the workspace's Key Vault could not be opened. The message names why |
AI_SIGNIN_APP_CERTIFICATE_UNREADABLE | "The OpenX AI app's certificate was not bound: Key Vault returned no public certificate." |
AI_SIGNIN_APP_UNTRUSTED | An app under the name exists but was not created by this directory's management app, or holds a client secret, so OpenX did not use it |
AI_SIGNIN_APP_MISSING | The app the record names is no longer in the directory under its name. OpenX created no other and changed no app, because a new app would carry a new client id |
DIRECTORY_NOT_REACHED | The directory's management app could not be reached, or the run stopped part-way. Nothing was recorded |
NOTE
OpenX has no screen yet to change a request after you accept it. A request that Deploy keeps refusing
(AI_SIGNIN_APP_IDENTITY_UNKNOWN, for one) stays recorded and Deploy keeps retrying it. Choosing Use a
certificate when you accept avoids that refusal, because a certificate needs no managed identity.
Customers' directories and admin approval
The app is multi-tenant, so people from other directories sign in to it. Publisher verification of the app is
not done yet, so until it is, the app ships with administrator approval required in a customer's directory. A
person there who signs in before their directory has approved it is stopped with Microsoft's AADSTS90094,
which says the grant requires admin permission.
That approval is given by the customer's own directory administrator, in the customer's directory. OpenX does not do it for them, and nothing on this page does. When publisher verification is done, the app links its verified publisher.
Repeat the setup
The setup is the same each time, and every step is a screen or a Deploy:
- An AI proposes the app for the directory and the origins you need.
- You accept it in Platform apps, with the federated default or the certificate choice.
- You Deploy.
That is how to set it up in a new directory, or in a new environment. Deploying again also repairs an app that has drifted, so no one has to check it by hand.
Two things are not built yet, and the page does not invent a way around them: there is no screen to change a request after you accept it (for example to add another origin), and an app that vanished from its directory is refused by name rather than created again, because a new app would carry a new client id.
The Cloud-connect app
NOTE
This part is coming. It is not built yet, and nothing on screen does it today.
The app behind the browser's Cloud connect still signs in with a client secret that each runtime reads from its own settings. It will move to the same record and the same Deploy, in two steps, each its own change:
- Adopt. The record names the existing Cloud-connect app. OpenX adds a federated or certificate credential to it, and every runtime reads the client id and the credential from the record. The old secret keeps working, so nothing breaks while this lands. OpenX adopts only an app its own management app owns.
- Remove the secret. Once every runtime signs in with the record's credential and a person accepts it in App Identity, OpenX removes the app's secret and the runtimes stop reading it.
Until both are built, the Cloud-connect app is unchanged.
Next steps
- Sign-in for the apps you deploy -> App Identity
- What Deploy builds -> Deploy
- How an AI sends a proposal -> Proposals over MCP
- The order things unlock in -> Environment