> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cognite.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Cognite Data Fusion (CDF) is an industrial DataOps platform. Prefer English docs unless the user asks for another locale.
> Follow /_llms/ index links recursively until you reach page URLs ending in .md. Fetch those Markdown twins instead of HTML.
> For REST APIs, default to calendar version 20230101 (stable). Use 20230101-beta or 20230101-alpha only if the user is on a preview API. Cite paths under /api-reference/concepts/<version>/ and the Cdf-Version header. Prefer the Python SDK (cognite-sdk) unless the user is in JavaScript/TypeScript.
> Start here by task: platform overview /cdf/index.md; data modeling /cdf/dm/index.md; data integration /cdf/integration/index.md; access /cdf/access/index.md; CDF Toolkit /cdf/deploy/cdf_toolkit/index.md; REST quickstart /dev/quickstart.md; Python SDK /dev/sdks/python/index.md; Atlas AI /cdf/atlas_ai/concepts/index.md; Flows apps /cdf/flows/index.md; MCP and IDE setup /dev/guides/ide_ai_integration.md.
> For implementation work, also load /skill.md.

# Signing in with an additional Microsoft Entra ID account

> Step-by-step guide to using the App Hosting OAuth relay for an additional Microsoft Entra ID sign-in from within a Flows custom app.

Use the App Hosting OAuth relay when your Flows custom app needs an additional sign-in from within the app. For example, pull data from a source authenticated by a different Microsoft Entra ID tenant than Cognite Data Fusion (CDF). [`connectToHostApp`](/cdf/flows/reference/api/auth) returns CDF credentials only, so the user must sign in with a different Microsoft Entra ID account, typically in that other tenant. You can run the flow more than once for multiple additional accounts.

You implement the popup in your app code and decide when it runs. App Hosting serves the relay as a static, unauthenticated page at `/apphosting/oauth-relay` on the app's origin; the path stays the same across versions. The authorization code stays in the browser: it isn't sent to CDF or App Hosting, isn't persisted beyond the browser cache, and may be accessible across sessions.

## Prerequisites

* A [Flows custom app](/cdf/flows/guides/getting-started) that runs inside CDF.
* A Microsoft Entra ID app registration for the other tenant or service, with the [single-page application](#register-the-redirect-uri) platform.
* CDF credentials from [`connectToHostApp`](/cdf/flows/reference/api/auth). Use the relay only for the additional Microsoft Entra ID sign-in, not for CDF.

## How the relay works

1. Your app opens a popup at the Microsoft Entra ID authorize URL with `response_mode=fragment`, so Microsoft Entra ID returns the authorization code in the URL fragment (`#code=...`).

2. Include `prompt=select_account` on that authorization request so Microsoft Entra ID shows the account picker. Without it, Microsoft Entra ID may reuse the CDF account, and the app won't get a token for the other tenant.

3. After sign-in, Microsoft Entra ID redirects the popup to `{app-origin}/apphosting/oauth-relay#code=...&state=...`.

4. The relay posts the fragment to `window.opener` and does not store it:

   ```js theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
   { type: "APPHOSTING_OAUTH_RESPONSE", fragment: window.location.hash }
   ```

   The relay uses `postMessage` to send the fragment to the window that opened the popup (`window.opener`). The `postMessage` target origin is the relay's own origin. If there is no opener, the page does nothing.

5. Your app checks `event.origin`, reads `code` from the fragment, and exchanges it for tokens with PKCE (Proof Key for Code Exchange). You store those tokens and attach them to calls to the other service. See [Store the token and call the other service](#store-the-token-and-call-the-other-service).

<Warning>
  The relay reads `window.location.hash`. If you omit `response_mode=fragment`, the code lands in the query string and the app receives an empty fragment.
</Warning>

## Find the deployed app origin

You need the app origin to register the redirect URI in Microsoft Entra ID. The origin stays the same for every version of the app in a project. For example, the origin is `https://9f446ebf4f46.apps.api.cogniteappsdata.com`. You can copy it from the browser or compute it from the app's `externalId` and `project`.

<Tabs>
  <Tab title="Copy from the browser">
    Open the deployed app in CDF and copy the origin from the address bar. Keep `https://` and the host, and drop everything after the first `/` that follows the host.
  </Tab>

  <Tab title="Compute the hash">
    Assemble `https://<app-hash>.apps.<cluster>.cogniteappsdata.com` from `app.json`. `{cluster}` is the `baseUrl` subdomain (`api` for `https://api.cognitedata.com`). The command prints `{app-hash}`: the first 12 characters of the SHA-256 hash of `externalId`, a null byte, and `project`. Replace `my-app` and `my-project` with your `externalId` and `project`.

    <Tabs>
      <Tab title="Bash">
        ```bash theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
        printf 'my-app\0my-project' | shasum -a 256 | cut -c1-12
        ```
      </Tab>

      <Tab title="PowerShell">
        ```powershell theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
        $sha = [System.Security.Cryptography.SHA256]::Create()
        $bytes = [System.Text.Encoding]::UTF8.GetBytes("my-app" + [char]0 + "my-project")
        (-join ($sha.ComputeHash($bytes) | ForEach-Object { $_.ToString("x2") })).Substring(0, 12)
        ```
      </Tab>
    </Tabs>
  </Tab>
</Tabs>

Use that origin when registering the redirect URI. The redirect URI is the origin plus `/apphosting/oauth-relay`.

## Register the redirect URI

In the Azure portal, add the redirect URIs to the **Single-page application** platform. Don't use the **Web** platform because it doesn't return the CORS headers required to exchange the code from the iframe.

<Steps>
  <Step title="Open Authentication">
    Go to **Microsoft Entra ID** → **App registrations** → your registration → **Authentication**.
  </Step>

  <Step title="Select a platform to add a redirect URI">
    Under the **Redirect URI configuration** tab, select **+ Add a platform** or **+ Add Redirect URI**. In the right side panel, select **Single-page application**.
  </Step>

  <Step title="Add a URI for each origin">
    Add the redirect URI for local and one for the deployed app. The path is always `/apphosting/oauth-relay`.

    | Environment | Redirect URI                                                                   |
    | ----------- | ------------------------------------------------------------------------------ |
    | Local       | `https://localhost:3001/apphosting/oauth-relay`                                |
    | Deployed    | `https://<app-hash>.apps.<cluster>.cogniteappsdata.com/apphosting/oauth-relay` |

    Don't register an app asset such as `callback.html` on the deployed origin. App assets require the CDF session cookie, so the popup receives a 401 response and the opener can't read it.
  </Step>

  <Step title="Update application authentication">
    After you add the URI, select **Confirm** to update authentication and save the changes.
  </Step>
</Steps>

## Serve the relay locally

App Hosting serves `/apphosting/oauth-relay` in production. For local development, serve the same path from the Vite dev server:

```ts wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
import type { PluginOption } from "vite";

function oauthRelayDevPlugin(): PluginOption {
  const relayHtml = `<!DOCTYPE html>
<html lang="en">
  <head><meta charset="UTF-8" /><title>OAuth relay</title></head>
  <body>
    <p>Completing sign-in…</p>
    <script>
      if (window.opener) {
        window.opener.postMessage(
          { type: "APPHOSTING_OAUTH_RESPONSE", fragment: window.location.hash },
          window.location.origin
        );
      } else {
        document.body.textContent = "This page only works as an OAuth popup redirect.";
      }
    </script>
  </body>
</html>`;
  return {
    name: "oauth-relay-dev",
    configureServer(server) {
      server.middlewares.use("/apphosting/oauth-relay", (_req, res) => {
        res.setHeader("Content-Type", "text/html");
        res.end(relayHtml);
      });
    },
  };
}
```

Add `oauthRelayDevPlugin()` to the `plugins` array in `vite.config.ts`.

## Handle the relay response

The relay posts the fragment to your app with `postMessage`. Handle the `message` event and read the authorization `code` from the fragment. Then POST the code and the PKCE verifier to the Microsoft Entra ID token endpoint to get tokens.

```ts wrap theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
window.addEventListener("message", (event) => {
  if (event.origin !== window.location.origin) return;
  if (event.data?.type !== "APPHOSTING_OAUTH_RESPONSE") return;

  const params = new URLSearchParams(
    String(event.data.fragment).replace(/^#/, "")
  );
  const code = params.get("code");
  // Next: POST code + PKCE verifier to the Microsoft Entra ID token endpoint.
});
```

Allow the identity provider in `manifest.json`. If you call Microsoft Graph (Microsoft's API for user and organization data, for example the `/me` endpoint), also allow `https://graph.microsoft.com`. See [Content Security Policy](/cdf/flows/reference/api/csp).

```json theme={"languages":{"custom":["/_languages/kuiper.json","../_languages/kuiper.json"]}}
{
  "manifestVersion": 1,
  "permissions": {
    "network": [
      {
        "sources": [
          "https://login.microsoftonline.com",
          "https://graph.microsoft.com"
        ],
        "directives": ["connect-src"]
      }
    ]
  }
}
```

## Store the token and call the other service

The relay doesn't store the authorization code or the tokens. After you exchange the code, keep the access token (and refresh token, if returned) in your app, for example, in memory or in `localStorage`. Attach the access token to the requests you send to the other service.

## Refresh the access token

If the token response includes a `refresh_token`, redeem it before the access token expires. Microsoft Entra ID single-page application (SPA) registrations rotate refresh tokens, so replace the stored refresh token after each successful response.

## Further reading

* [Flows Auth API](/cdf/flows/reference/api/auth): Get a CDF token with `connectToHostApp`.
* [Content Security Policy](/cdf/flows/reference/api/csp): Allow `login.microsoftonline.com` in `manifest.json`.
* [Run your app locally](/cdf/flows/guides/running-locally): Use the development server and CDF iframe.
