If your organization has a SAML 2.0 identity provider (Okta, Entra ID, Google Workspace…), use SSO & SAML instead. Portal SSO is for organizations without an IdP.
How it works
- A learner is signed into your portal
- Your backend signs a JSON Web Token (JWT) containing the learner’s email
- Your page auto-submits that token to Juno with a hidden form (POST)
- Juno verifies the token, creates or updates the learner, and redirects them into Juno — signed in
role claim in the token is ignored. An existing Juno user is signed in as themselves, with whatever role they already have.
How learners are matched
Juno looks for an existing learner in this order:external_id: the learner already linked to that id. The token’semailis ignored and no one’s email is changed.email(case-insensitive): the learner with that email. If they aren’t linked to anexternal_idyet, they’re linked to the token’s id from now on. If they’re already linked to a differentexternal_id, the hand-off is rejected withidentity_conflict.- No match: a new Learner is created.
Setup
1
Ask Juno to turn on Portal SSO
Portal SSO is behind a feature flag. Ask your Juno customer success manager to enable it for your organization and to give you your tenant ID (the token
aud). Once enabled, a Portal SSO section appears under Admin → SSO settings — that’s the only step that needs Juno.2
Generate a signing secret (self-serve)
In the Portal SSO section, click Generate secret and copy the secret and its key id (
kid) — the secret is shown only once, so store it in your backend’s secret manager (never in browser code). Then flip Enable Portal SSO on. You manage secrets yourself here — generate, rotate, and delete.3
Sign tokens in your backend
Use a backend signing example below. Tokens must be signed server-side; the secret must never reach the browser.
4
Add the hand-off snippet to your portal
Use a frontend snippet below — React, plain JS, or server-rendered HTML; new tab or embedded iframe.
Token contract
Tokens are HS256-signed JWTs. Juno enforces every rule below and rejects the hand-off otherwise.
Set the
kid header to the key id you received. Any role claim is ignored — new users are created as Learners (an existing user keeps their current role; see the security note above). The attributes object is allowlisted to the five fields above; nothing else (roles, permissions, ids) is ever read from it.
Scaffold with an AI agent
In a hurry? Paste this into your coding assistant (Cursor, Claude, Copilot…) to scaffold the integration, then fill in the values from Admin → SSO settings → Portal SSO.Prompt
Backend: sign the token
Sign server-side with the secret from Juno. Generate a fresh token for every hand-off — tokens are single-use and expire within minutes.Frontend: hand off to Juno
Your frontend fetches the signed token from your backend, then POSTs it tohttps://api.the-juno.com/api/v1/sso/portal. Set the form’s target to _blank for a new tab, or to an <iframe name> to embed Juno in your page. The optional junoRedirectUrl deep-links the learner (a path like /unit/123, or an absolute URL on your Juno origin).
Error codes
Failed hand-offs redirect to the Juno app root withkind=error&code=…&message=…:
Secret rotation
Two secrets can be active at once, each with its ownkid — so you rotate with zero downtime, all self-serve in the Portal SSO section:
- Click Generate secret to add a second secret (you’ll now have two active)
- Switch your backend to the new secret and
kid - Delete the old secret

