Skip to content
Back to skills

Jwt Embed Issuance

ASecurity

Canonical 2026 JWT-embed flow for dashboard embedding — required claims (`sub`, `tenant_id`, `iat`, `exp`, `iss`, `aud`, `nonce`), tool-specific verification (Superset guest tokens, Metabase JWT URLs, Cube Authorization Bearer, Power BI MSAL-via-AAD), 5-15 min expiration policy, cross-boundary denial test contract. Invoked by `ravenclaude-core/security-reviewer` for any embed-auth review.

  • 7 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 23, 2026
ai-agentsrustsqlreactnodeazureapisecurity

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 23, 2026

npx -y skills add mcorbett51090/RavenClaude --skill jwt-embed-issuance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Jwt Embed Issuance?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Jwt Embed Issuance
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mcorbett51090-jwt-embed-issuance/badge)](https://www.skillsdirectory.com/skills/mcorbett51090-jwt-embed-issuance)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: jwt-embed-issuance
description: Canonical 2026 JWT-embed flow for dashboard embedding — required claims (`sub`, `tenant_id`, `iat`, `exp`, `iss`, `aud`, `nonce`), tool-specific verification (Superset guest tokens, Metabase JWT URLs, Cube Authorization Bearer, Power BI MSAL-via-AAD), 5-15 min expiration policy, cross-boundary denial test contract. Invoked by `ravenclaude-core/security-reviewer` for any embed-auth review.
invoked_by: ravenclaude-core/security-reviewer
---

# Skill: jwt-embed-issuance

> **Invoked by:** `ravenclaude-core/security-reviewer` (mandatory for any embed-auth change). Generated by `dashboard-builder` when scaffolding a new embed.
>
> **When to invoke:** any change touching JWT issuance, embed authentication, tenant-claim injection, key rotation. Mandatory pre-merge review.
>
> **Output:** verified JWT-issuance code + tenant-claim shape + expiration policy + rotation plan + cross-boundary denial test pass.

## The canonical 2026 flow

```
[End user] -- logs in --> [Host application (client's web app)]
                          |
                          | Authenticated session
                          v
                          [App's JWT issuer endpoint]
                          |
                          | Issues short-lived JWT (5-15 min)
                          | Claims: user_id, tenant_id, allowed_dashboards[], optionally explicit RLS rules
                          | Signature: HS256 (shared secret) or RS256 (asymmetric, preferred for cross-system trust)
                          v
                          [Front-end React app]
                          |
                          | Passes JWT to embed (iframe param, SDK call, Cube REST/SQL header)
                          v
                          [Embed verifies signature, extracts claims, enforces RLS at query time]
                          |
                          | Query runs scoped to tenant_id
                          v
                          [Data returned, scoped to tenant_id]
```

## Required claims

Every embed JWT must carry:

| Claim | Purpose | Format |
|---|---|---|
| `sub` | Subject (user identifier) | string (user ID from host app) |
| `tenant_id` | The tenant boundary the viewer can see | string or integer |
| `iat` | Issued-at timestamp | unix seconds |
| `exp` | Expiration timestamp | unix seconds (5-15 min from `iat`) |
| `iss` | Issuer (host app identifier) | string URL or app name |
| `aud` | Audience (the embed tool) | string ("metabase" / "superset" / "cube") |

Optional but recommended:
- `allowed_dashboards` — array of dashboard IDs the viewer can access (defense in depth)
- `roles` — for Power BI Embedded, the DAX role(s) to apply
- `nonce` — replay protection

## Tool-specific verification patterns

### Apache Superset
- Issuer side: HS256 with `GUEST_TOKEN_JWT_SECRET` matching Superset's config
- Embed side: front-end SDK calls Superset's `/api/v1/security/guest_token`; Superset validates + applies the `rls` claim filters server-side
- See [Superset embedding docs](https://superset.apache.org/user-docs/using-superset/embedding/)

### Metabase Interactive Embedding (Pro+ only)
- Issuer side: JWT-signed with `embedding-secret-key` from Metabase admin
- Embed side: Metabase SDK or iframe URL with JWT parameter
- Tenant scoping via `user.email` claim + Metabase Sandbox (Pro+ feature)

### Cube
- Issuer side: HS256 with `CUBEJS_API_SECRET`
- Embed side: every Cube query carries the JWT in `Authorization: Bearer <jwt>` header
- Tenant scoping via `securityContext` rules in the Cube schema that reference `securityContext.tenant_id`

### Power BI Embedded (App-Owns-Data)
- Not strictly JWT-flow; uses MSAL acquisition of a Power BI access token via service principal
- Tenant scoping via DAX roles (`EffectiveIdentity` parameter)
- The "JWT" here is the Azure AD token, not an app-issued one

## Expiration policy

- **Default: 5-15 minute expiration.** Stateless revocation otherwise requires a revocation list (operational overhead).
- **Refresh pattern:** front-end requests a new token from the host app's JWT-issuer endpoint when the current one is near expiry (typically at ~80% of lifetime).
- **Long-lived JWTs (>30 min)** are an anti-pattern. The hook ([`../../hooks/flag-data-platform-smells.sh`](../../hooks/flag-data-platform-smells.sh)) flags these in JWT-issuance code.

## Key rotation

- **Asymmetric (RS256 + JWKS):** rotate the signing key without coordinating with the embed-verifier; verifier fetches public keys from a JWKS endpoint
- **Symmetric (HS256 + shared secret):** rotation requires coordination — both sides must update at the same time. For SMB consulting engagements, this is usually acceptable; for productized SaaS scaling to many clients, prefer RS256.

## Cross-boundary denial test

Every JWT-issuance code path ships with a denial test that:
1. Creates two test tenants with disjoint data
2. Issues a JWT for tenant A
3. Attempts to query tenant B's data via the embed
4. **Test passes only when zero rows return**

The test ships in the engagement's CI; failure means RLS or `securityContext` is misconfigured.

## Required code-template companions

The skill ships with [`../../templates/jwt-issuer.ts`](../../templates/jwt-issuer.ts) — a Node/TS scaffold that demonstrates:
- Short-lived token issuance with `tenant_id` claim
- Signing-key management via env var (never inline)
- Rotation hook (signing key version in token header)
- Audience claim per embed tool
- Smoke test that the verification round-trip works

## Anti-patterns this skill flags

- JWT with `exp` > 30 minutes
- Signing key hard-coded in source (use env var; the hook catches this)
- Tenant ID derived from the URL or query parameter (must come from the JWT claim, signed by the host app)
- JWT issued without `iss` and `aud` claims (replay across systems possible)
- Front-end React code that *constructs* the JWT client-side (must come from server-issued endpoint)
- Long-lived "API tokens" reused across viewers (no per-viewer scope)
- HS256 + shared secret for a productized SaaS with many tenants (rotate-without-coordination is too valuable; switch to RS256 + JWKS)
- A JWT-issuer endpoint without rate-limiting on the host app (token-issuance becomes a DoS vector)
- Cross-boundary denial test not run / not present
- Power BI Embedded mistakenly trying to use app-issued JWTs (Power BI uses Azure AD tokens via MSAL)

## References

- Template: [`../../templates/jwt-issuer.ts`](../../templates/jwt-issuer.ts) (runnable; security-critical)
- Skill: [`../rls-policy-authoring/SKILL.md`](../rls-policy-authoring/SKILL.md) — the DB-side or semantic-layer-side enforcement that the JWT's `tenant_id` drives
- Skill: [`../embed-csp-and-iframe-sandboxing/SKILL.md`](../embed-csp-and-iframe-sandboxing/SKILL.md) — the iframe/CSP boundary that contains the embed
- Knowledge: [`../../knowledge/multi-tenant-rls-patterns.md`](../../knowledge/multi-tenant-rls-patterns.md) — where enforcement happens for each stack
- Upstream review rubric: [`../../../ravenclaude-core/agents/security-reviewer.md`](../../../ravenclaude-core/agents/security-reviewer.md) §4 (Sessions & tokens)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…