# Embedded Authentication

When you embed Prismatic, your users don't need a second set of credentials. They sign in to your app, and your backend gives them a short-lived token that lets them use Prismatic. The embedded SDK (`@prismatic-io/embedded`), the headless SDK (Solis), and the [MCP Flow Server](https://prismatic.io/docs/ai/model-context-protocol.md) all accept the same token.

The token is a JSON Web Token (JWT) that your backend signs with a private key registered with Prismatic. Its claims identify the user and the customer they belong to.

<!-- -->

For a guided walkthrough with a video, see [Get Started with Embedded Auth](https://prismatic.io/docs/get-started/embedded-marketplace/authenticate-embedded-users.md).

Use agent skills to set up embedding

The [Prismatic agent skills](https://prismatic.io/docs/custom-connectors/get-started/ai-assisted-development.md) can walk you through signing key setup, JWT backend generation, and frontend authentication interactively. The `prismatic-embedded` skill supports JWT generation in a variety of backends. Ask your coding assistant to use the `prismatic-embedded` skill to get started.

## JWT signing keys[​](#jwt-signing-keys "Direct link to JWT signing keys")

Before you can generate a JWT, you'll need a valid **signing key** from Prismatic. In Prismatic, click your organization name at the bottom of the left-hand sidebar, then open the **Embedded** tab. Click the **+ Add signing Key** button.

note

You must be an [owner or admin](https://prismatic.io/docs/configure-prismatic/organization-users.md#organization-team-member-roles) to create a signing key.

You will be presented with a private signing key. Store this key somewhere safe - it's the key you'll use to sign JWTs for users in your application.

![Get signing key in Prismatic app](/docs/assets/images/example-private-key-0ea08c3d0624b47f74bc6b548ddacf23.png)

Private keys are not stored in Prismatic

Prismatic does not store the private signing key that is generated. Instead, we only save the last 8 characters so you can easily match up a private key you have with one in our system. We store the corresponding public key to verify signatures of JWTs you send. Save the private key that you generate somewhere safe. If it's ever compromised or you lose it, you can deactivate old keys and generate a new one.

### Importing your own private signing key[​](#importing-your-own-private-signing-key "Direct link to Importing your own private signing key")

You can also import your own private signing key for embedded authentication. The OpenSSL CLI tool is most commonly used for generating public/private key pairs yourself:

```bash
# Generate a private key with 4096 bit encryption
openssl genrsa -out my-private-key.pem 4096

# Generate the corresponding public key
openssl rsa -in my-private-key.pem -pubout > my-public-key.pub

```

This will generate two files - a private key called `my-private-key.pem` and a public key called `my-public-key.pub`. Your public key will look like this:

```text
-----BEGIN PUBLIC KEY-----
EXAMPLE
-----END PUBLIC KEY-----

```

Import the public key using the [Prism CLI](https://prismatic.io/docs/cli/prism.md#organization-signing-keys-import):

```bash
prism organization:signing-keys:import -p my-public-key.pub

```

## Create and sign a JWT[​](#create-and-sign-a-jwt "Direct link to Create and sign a JWT")

Now that you have a signing key, you can create and sign a JSON web token (JWT). Your backend API (not your frontend) should generate a JWT for your users. Your frontend client should request this JWT from your backend API.

Do your JWT generation on the backend

Generate JWT tokens on your backend. If you bake JWT generation (including the signing key) into your frontend, you create a security problem - someone with the signing key could sign their own JWT and pretend to be any user.

Most programming languages offer JWT libraries for generating tokens - see [jwt.io](https://jwt.io/).

The JWT that you generate for a user should have the following required claims (properties):

| Field          | Description                                                                                                                            |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `sub`          | Unique user ID, typically a UUID                                                                                                       |
| `organization` | Your organization ID, found on the **Embedded** tab in Prismatic                                                                       |
| `customer`     | The [external ID](https://prismatic.io/docs/customers/managing-customers.md#customer-external-ids) of the customer the user belongs to |
| `iat`          | Signing time as a Unix timestamp. It's a good idea to use (current time) minus a small buffer to account for clock skew.               |
| `exp`          | Token expiration time as a Unix timestamp                                                                                              |

You can also include these optional JWT claims:

| Field                        | Description                                                                                                                                                                                                                     |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `external_id`                | External ID of the user in Prismatic; typically matches `sub`                                                                                                                                                                   |
| `name`                       | The user's name                                                                                                                                                                                                                 |
| `customer_name`              | If a customer with the given `customer` external ID doesn't exist, creates one with this name. Ignored if the customer already exists.                                                                                          |
| `concurrent_execution_limit` | Maximum concurrent executions for this customer (integer). See [per-customer execution concurrency](https://prismatic.io/docs/customers/managing-customers.md#per-customer-execution-concurrency).                              |
| `role`                       | Only for [user level configuration](https://prismatic.io/docs/integrations/config-wizard/user-level-configuration.md) (ULC). `"admin"` can deploy ULC instances; `"user"` can supply user configuration. Defaults to `"admin"`. |

For example, a JWT payload might look like this:

Example JWT Payload

```json
{
  "sub": "2E52B7CB-071B-4EA2-8E9D-F64910EBDBB1",
  "external_id": "2E52B7CB-071B-4EA2-8E9D-F64910EBDBB1",
  "name": "Phil Embedmonson",
  "organization": "T3JnYW5pemF0aW9uOmU5ZGVhZDU5LWU3YzktNDNkMi1hNjhhLWFhMjcyMzEyMTAxNw==",
  "customer": "abc-123",
  "customer_name": "Hooli",
  "concurrent_execution_limit": 25,
  "role": "admin",
  "iat": 1631676917,
  "exp": 1631680517
}

```

The JWT's **header** should indicate that it's signed with RSA SHA-256, and should read:

```json
{
  "alg": "RS256",
  "typ": "JWT"
}

```

Use unique identifiers as JWT subjects

The `sub` (subject) within the JWT identifies the user who is logged in to your system. The `sub` value can be any unique identifier - usually a UUID.

A customer user with that identifier will be created in Prismatic if it doesn't already exist, and will be granted permissions to configure and deploy instances to the customer they're assigned to (you assign the user to a customer in the examples below).

### Example JWT signing code snippets[​](#example-jwt-signing-code-snippets "Direct link to Example JWT signing code snippets")

Here are code snippets for JavaScript, Python, and .NET that create a valid JWT to authenticate a user in Prismatic:

* JavaScript
* Python
* .NET (C#)

```javascript
import jsonwebtoken from "jsonwebtoken";

/*
 This is for illustrative purposes only;
 Obviously don't hard-code a signing key in your code.
*/
const signingKey = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDP3+OrT0IXqCu4
EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEE
c5R7QVzxgmGRXjPZGPf5huA1
-----END PRIVATE KEY-----`;

const currentTime = Math.floor(Date.now() / 1000);

const token = jsonwebtoken.sign(
  {
    sub: "2E52B7CB-071B-4EA2-8E9D-F64910EBDBB1", // Some unique identifier for the user
    external_id: "2E52B7CB-071B-4EA2-8E9D-F64910EBDBB1", // Generally matches sub
    name: "Phil Embedmonson", // Optional
    organization:
      "T3JnYW5pemF0aW9uOmU5ZGVhZDU5LWU3YzktNDNkMi1hNjhhLWFhMjcyMzEyMTAxNw==",
    customer: "abc-123", // This is an external ID of a customer
    customer_name: "Hooli", // The optional name to use if we need to create a new customer record
    iat: currentTime - 60, // Issued 1 minute ago to account for clock skew
    exp: currentTime + 60 * 60, // 1 hour from now
  },
  signingKey, // Store this somewhere safe
  { algorithm: "RS256" },
);

```

```python
import jwt
import math
from time import time

# This is for illustrative purposes only;
# Obviously don't hard-code a signing key in your code.
signing_key = '''-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDP3+OrT0IXqCu4
EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEE
c5R7QVzxgmGRXjPZGPf5huA1
-----END PRIVATE KEY-----'''

current_time = math.floor(time())

token = jwt.encode(
  {
    "sub": "2E52B7CB-071B-4EA2-8E9D-F64910EBDBB1", # Some unique identifier for the user
    "external_id": "2E52B7CB-071B-4EA2-8E9D-F64910EBDBB1", # Generally matches sub
    "name": "Phil Embedmonson", # Optional
    "organization": "T3JnYW5pemF0aW9uOmU5ZGVhZDU5LWU3YzktNDNkMi1hNjhhLWFhMjcyMzEyMTAxNw==",
    "customer": "abc-123", # This is an external ID of a customer
    "customer_name": "Hooli", # The optional name to use if we need to create a new customer record
    "iat": current_time,
    "exp": current_time + 60 * 60, # 1 hour from now
  },
  signing_key,
  algorithm="RS256")

```

```csharp
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

/*
 This is for illustrative purposes only;
 Obviously don't hard-code a signing key in your code.
*/
var pem = @"-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDP3+OrT0IXqCu4
EXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEEXAMPLEE
c5R7QVzxgmGRXjPZGPf5huA1
-----END PRIVATE KEY-----";

Task<string> GetToken() {
  using var rsa = RSA.Create();
  rsa.ImportFromPem(pem);

  var descriptor = new SecurityTokenDescriptor();
  descriptor.SigningCredentials = new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256)
  {
    CryptoProviderFactory = new CryptoProviderFactory { CacheSignatureProviders = false }
  };

  var claims = new List<Claim> {
    new Claim("sub", "2E52B7CB-071B-4EA2-8E9D-F64910EBDBB1"), // Some unique identifier for the user
    new Claim("external_id", "2E52B7CB-071B-4EA2-8E9D-F64910EBDBB1"), // Generally matches sub
    new Claim("name", "Phil Embedmonson"), // Optional
    new Claim("organization", "T3JnYW5pemF0aW9uOmU5ZGVhZDU5LWU3YzktNDNkMi1hNjhhLWFhMjcyMzEyMTAxNw=="),
    new Claim("customer", "abc-123"), // This is an external ID of a customer
    new Claim("customer_name", "Hooli") // The optional name to use if we need to create a new customer record
  };

  descriptor.Subject = new ClaimsIdentity(claims);
  descriptor.IssuedAt = DateTime.UtcNow;
  descriptor.Expires = DateTime.UtcNow.AddHours(1); // Expire 1 hour from now

  var token = new JwtSecurityTokenHandler().CreateEncodedJwt(descriptor);
  return Task.FromResult(token);
}

var token = await GetToken();

```

Expose the token through an authenticated route on your backend, such as `/api/prismatic-auth`. Authenticate your own user first, then sign a token for that user. The example apps return the token with its lifetime and your Prismatic URL, so the frontend knows when to refresh it:

Example response from your backend

```json
{
  "token": "eyJhb...",
  "expiresIn": 3600,
  "prismaticUrl": "https://app.prismatic.io"
}

```

For complete Node.js implementations, see the [React example's signing middleware](https://github.com/prismatic-io/embedded/blob/main/examples/example-embedded-app/server/prismatic-auth.ts) and the [Angular example's `server.ts`](https://github.com/prismatic-io/embedded/blob/main/examples/angular-app/server.ts).

## Use the JWT to authenticate the user[​](#use-the-jwt-to-authenticate-the-user "Direct link to Use the JWT to authenticate the user")

Once your frontend has a signed JWT from your backend, pass it to the embedded SDK with `prismatic.authenticate()`:

```javascript
// Some function that fetches the JWT from your API:
const token = await getJwtToken();

prismatic.init({ prismaticUrl: "https://app.prismatic.io" });

try {
  await prismatic.authenticate({ token });
} catch (error) {
  console.error(`Authentication failed with error ${error}`);
}

```

If your customer or organization ID in your JWT are incorrect, if your JWT is not signed correctly, or if the JWT is expired, `prismatic.authenticate()` will throw an error.

Call `prismatic.init()` once per page load, before the first `authenticate()`. `init` resets the SDK's state, including the token from `authenticate`, so calling it again signs the user out of embedded screens.

### What does `prismatic.authenticate()` do?[​](#what-does-prismaticauthenticate-do "Direct link to what-does-prismaticauthenticate-do")

When you call `prismatic.authenticate({ token })`, your client will make an authenticate call to Prismatic with the JWT you provide. The request is equivalent to this curl command:

```bash
curl -X POST 'https://app.prismatic.io/embedded/authenticate' \
  --header 'authorization: Bearer eyJhb...' \

```

The request returns an HTTP 200 if the token is valid, and an error if it's not. If the user or customer you specified in the JWT doesn't already exist in Prismatic, they will be created automatically.

## Refreshing an embedded JWT[​](#refreshing-an-embedded-jwt "Direct link to Refreshing an embedded JWT")

If a customer user's JWT expires, the customer user will see a 404 in their embedded iframe. To keep the user signed in, have your frontend fetch a new token before the current one expires, and call `prismatic.authenticate({ token })` with it. Existing iframes and the embedded client are updated to use the new token, so the user doesn't lose their place.

Refresh with `authenticate` only. Calling `init` or `showMarketplace` again on refresh resets the SDK or reloads the embedded screen.

## Authenticating in your frontend framework[​](#authenticating-in-your-frontend-framework "Direct link to Authenticating in your frontend framework")

Fetch the token, initialize the SDK, and schedule refreshes in one app-wide place, such as a React context provider, an Angular service, a Vue composable, or a Svelte module. Every page then shares one token, and the SDK is initialized once.

Expose two values to the rest of your app:

* **The token**, for code that needs it directly, such as Solis or your own requests to Prismatic's API.
* **A "ready" flag that changes once**, after the first `authenticate` succeeds. Pages embed screens when it's true. Because it doesn't change on refresh, a refresh doesn't make pages embed their screens again.

- React
- Angular
- Vue
- Svelte

prismatic-auth.tsx

```tsx
import prismatic from "@prismatic-io/embedded";
import { createContext, useContext, useEffect, useState } from "react";

const REFRESH_MARGIN_SECONDS = 30;

interface PrismaticAuth {
  /** True after the first `authenticate` succeeds. */
  ready: boolean;
  token: string | null;
  prismaticUrl: string;
  error: Error | null;
}

const PrismaticAuthContext = createContext<PrismaticAuth | null>(null);
let initialized = false;

export function PrismaticAuthProvider({
  children,
}: {
  children: React.ReactNode;
}) {
  const [auth, setAuth] = useState<PrismaticAuth>({
    ready: false,
    token: null,
    prismaticUrl: "",
    error: null,
  });

  useEffect(() => {
    let cancelled = false;
    let timer: ReturnType<typeof setTimeout> | undefined;

    const connect = async () => {
      try {
        const response = await fetch("/api/prismatic-auth");
        const body = await response.json();
        if (!response.ok) throw new Error(body.error);

        // `init` resets the SDK, so call it once. Refreshes only re-authenticate.
        if (!initialized) {
          prismatic.init({ prismaticUrl: body.prismaticUrl });
          initialized = true;
        }
        await prismatic.authenticate({ token: body.token });
        if (cancelled) return;

        setAuth({
          ready: true,
          token: body.token,
          prismaticUrl: body.prismaticUrl,
          error: null,
        });
        timer = setTimeout(
          connect,
          (body.expiresIn - REFRESH_MARGIN_SECONDS) * 1000,
        );
      } catch (error) {
        if (!cancelled) {
          setAuth((auth) => ({
            ...auth,
            error: error instanceof Error ? error : new Error(String(error)),
          }));
        }
      }
    };

    connect();
    return () => {
      cancelled = true;
      clearTimeout(timer);
    };
  }, []);

  return (
    <PrismaticAuthContext.Provider value={auth}>
      {children}
    </PrismaticAuthContext.Provider>
  );
}

export function usePrismaticAuth() {
  const auth = useContext(PrismaticAuthContext);
  if (!auth)
    throw new Error(
      "usePrismaticAuth must be used inside PrismaticAuthProvider.",
    );
  return auth;
}

```

The React example app has a fuller version that also reads the signed-in user: [`use-prismatic-auth.tsx`](https://github.com/prismatic-io/embedded/blob/main/examples/example-embedded-app/frontend/hooks/use-prismatic-auth.tsx).

src/app/prismatic-auth.ts

```ts
import { computed, Injectable, signal } from "@angular/core";
import prismatic from "@prismatic-io/embedded";

/** Refresh this many seconds before the token expires. */
const REFRESH_MARGIN_SECONDS = 30;

@Injectable({ providedIn: "root" })
export class PrismaticAuth {
  readonly token = signal<string | null>(null);
  readonly prismaticUrl = signal("");
  readonly error = signal<string | null>(null);
  /** Changes once, unlike `token`, so it doesn't re-render embeds on refresh. */
  readonly ready = computed(() => this.token() !== null);

  constructor() {
    this.connect();
  }

  private async connect() {
    try {
      const response = await fetch("/api/prismatic-auth");
      const body = await response.json();
      if (!response.ok) throw new Error(body.error);

      // `init` resets the SDK, so call it once. Refreshes only re-authenticate.
      if (!this.ready()) prismatic.init({ prismaticUrl: body.prismaticUrl });
      await prismatic.authenticate({ token: body.token });

      this.prismaticUrl.set(body.prismaticUrl);
      this.token.set(body.token);
      setTimeout(
        () => this.connect(),
        (body.expiresIn - REFRESH_MARGIN_SECONDS) * 1000,
      );
    } catch (error) {
      this.error.set(error instanceof Error ? error.message : String(error));
    }
  }
}

```

`providedIn: "root"` makes the service a singleton, so the app fetches one token no matter how many components inject it. This service comes from the [Angular example app](https://github.com/prismatic-io/embedded/blob/main/examples/angular-app/src/app/prismatic-auth.ts).

src/prismatic-auth.ts

```ts
import { computed, ref } from "vue";
import prismatic from "@prismatic-io/embedded";

const REFRESH_MARGIN_SECONDS = 30;

// Module-level state, so every component shares one token.
const token = ref<string | null>(null);
const prismaticUrl = ref("");
const error = ref<string | null>(null);
const ready = computed(() => token.value !== null);
let started = false;

async function connect() {
  try {
    const response = await fetch("/api/prismatic-auth");
    const body = await response.json();
    if (!response.ok) throw new Error(body.error);

    // `init` resets the SDK, so call it once. Refreshes only re-authenticate.
    if (!ready.value) prismatic.init({ prismaticUrl: body.prismaticUrl });
    await prismatic.authenticate({ token: body.token });

    prismaticUrl.value = body.prismaticUrl;
    token.value = body.token;
    setTimeout(connect, (body.expiresIn - REFRESH_MARGIN_SECONDS) * 1000);
  } catch (e) {
    error.value = e instanceof Error ? e.message : String(e);
  }
}

export function usePrismaticAuth() {
  if (!started) {
    started = true;
    connect();
  }
  return { token, prismaticUrl, error, ready };
}

```

src/lib/prismatic-auth.svelte.ts

```ts
import prismatic from "@prismatic-io/embedded";

const REFRESH_MARGIN_SECONDS = 30;

class PrismaticAuth {
  token = $state<string | null>(null);
  prismaticUrl = $state("");
  error = $state<string | null>(null);
  /** Changes once, unlike `token`, so it doesn't re-render embeds on refresh. */
  ready = $derived(this.token !== null);
  #started = false;

  /** Call from the browser, such as in your root layout's `onMount`. */
  start() {
    if (this.#started) return;
    this.#started = true;
    this.#connect();
  }

  async #connect() {
    try {
      const response = await fetch("/api/prismatic-auth");
      const body = await response.json();
      if (!response.ok) throw new Error(body.error);

      // `init` resets the SDK, so call it once. Refreshes only re-authenticate.
      if (!this.ready) prismatic.init({ prismaticUrl: body.prismaticUrl });
      await prismatic.authenticate({ token: body.token });

      this.prismaticUrl = body.prismaticUrl;
      this.token = body.token;
      setTimeout(
        () => this.#connect(),
        (body.expiresIn - REFRESH_MARGIN_SECONDS) * 1000,
      );
    } catch (e) {
      this.error = e instanceof Error ? e.message : String(e);
    }
  }
}

export const auth = new PrismaticAuth();

```

Both SDKs use the DOM, so they run only in the browser. If you use server-side rendering, such as Next.js, Angular SSR, Nuxt, or SvelteKit, start authentication from code that runs in the browser.

To embed a screen once the user is authenticated, see [Embedding Marketplace](https://prismatic.io/docs/embed/marketplace.md#embedding-in-angular-vue-and-svelte).

## Authenticating the headless SDK[​](#authenticating-the-headless-sdk "Direct link to Authenticating the headless SDK")

Solis, the [headless SDK](https://prismatic.io/docs/embed/headless.md), uses the same JWT. It doesn't use `prismatic.init()` or `prismatic.authenticate()`; you pass the token to Solis directly:

| Package                     | Pass the token                                     | Pass a refreshed token                                  |
| --------------------------- | -------------------------------------------------- | ------------------------------------------------------- |
| `@prismatic-io/solis-react` | The `auth={{ token }}` prop of `PrismaticProvider` | Update the same prop                                    |
| `@prismatic-io/solis-core`  | The `jwt` option of `createClient`                 | Call `client.authenticate(newToken)` on the same client |

Solis applies a refreshed token to the live session, so loaded data stays in place. If your app uses only Solis, keep the token fetching and refresh scheduling from the examples above, and drop the `prismatic.init` and `prismatic.authenticate` calls. For details, see [Installing the headless SDK](https://prismatic.io/docs/embed/headless/install.md#connect-to-prismatic).
