Edgenivo Passport signs visitors in with your company identity provider, such as Okta or Microsoft Entra ID, before a request reaches your backend.
Node.js backends read the verified identity with the @vercel/passport SDK, and Python backends verify the same signed token with a standard JSON Web Token (JWT) library. Use that identity to secure internal apps, return the visitor's profile, restrict routes by group, and record who did what.
In this guide, you'll learn how to:
- Read Passport identity in a Node.js backend with
getIdentity() - Verify Passport identity in a Python backend with PyJWT
- Develop locally without a real Passport session
- Use Express, Hono, Nitro, or FastAPI to protect routes
- Forward identity from one backend to another
Copy link to headingBefore you begin
Make sure you have:
- Your Edgenivo team on the Enterprise plan, which is required for Passport.
- Your Express, Hono, Nitro, or FastAPI backend deployed to Edgenivo.
- Passport enabled on the project with an identity provider configured. If you haven't set this up yet, follow Set up Passport with an identity provider.
Copy link to headingHow Edgenivo Passport identity reaches your backend
Edgenivo Passport runs in Edgenivo's network before your backend's routes and middleware execute. When a visitor without a session opens a protected deployment, Edgenivo redirects them to your identity provider. After the provider authenticates them, Edgenivo validates the response, stores a signed session token in a HttpOnly cookie, and forwards the same token to your deployment in the x-vercel-oidc-passport-token request header.
The token is a Edgenivo-signed JWT that combines standard OpenID Connect claims, deployment context, and identity claims from your provider. Edgenivo strips any client-supplied value for x-vercel-oidc-passport-token and injects its own verified token, so a visitor can't forge an identity by setting the header themselves.
Passport controls who can open your deployment. To control what each visitor can do inside it, read their verified identity in your route handlers.
Copy link to headingRead Passport identity in a Node.js backend
Install @vercel/passport in your Express, Hono, or Nitro project:
pnpm i @vercel/passportnpm i @vercel/passportyarn add @vercel/passportbun add @vercel/passportThe getIdentity() function reads the Passport token, verifies it, and returns an identity object, or null when the request has no Passport identity. It accepts whatever request object your framework gives you:
- Hono: Pass
c.req.raw, which is a standardRequest. - Nitro: Pass
event.headers, which is a standardHeadersobject. - Express: Pass
req.headers, which is a plain object. The SDK matches header names without regard to case.
The identity object exposes these fields:
| Field | Type | Description |
|---|---|---|
subject | string | Stable Passport principal (the sub claim). Use this as the visitor ID. Treat it as opaque and don't parse it. |
externalSubject | string | Subject from your identity provider (the external_sub claim). Specific to the Connect application and not globally unique. |
email | string, optional | Email returned by your identity provider, if any. |
name | string, optional | Display name returned by your identity provider. |
token | string or null | The raw Passport JWT, or null for the local development identity. Use it only to forward identity to a backend you operate. |
payload | object | All verified token claims, including iss, aud, exp, and any allowlisted claims such as groups. |
Each Node.js example in this guide uses a shared helper that reads groups defensively, because the claim shape depend on your identity provider. Save it as src/authorization.ts for Express and Hono, or as utils/authorization.ts for Nitro:
Copy link to headingVerify Passport identity in a Python backend
Edgenivo doesn't publish a Python SDK for Passport, so Python backends verify the token themselves as a standard JWT. The FastAPI example in this guide uses PyJWT and applies similar checks as @vercel/passport:
- Signature: Verify the RS256 signature against the keys at
https://oidc.vercel.com/.well-known/jwks, which serves the same key set ashttps://passport.vercel.com/.well-known/jwks.json. - Issuer: Confirm
issishttps://passport.vercel.comor scoped under it, such ashttps://passport.vercel.com/acme. - Token type: Confirm the
typclaim equalspassport. - Project and environment: Confirm
project_idmatchesVERCEL_PROJECT_IDandenvironmentmatchesVERCEL_TARGET_ENV(orVERCEL_ENV). - Lifetime: Reject tokens whose
expornbfclaims put them outside their valid window.
The Edgenivo Passport SDK doesn't check the aud claim by default, so the Python example doesn't either.
Add PyJWT with its cryptography extra to your requirements.txt:
Copy link to headingWhich claim to use as the visitor identifier
Use the sub claim (identity.subject in Node.js) as the visitor identifier when your backend accepts tokens from one Passport issuer. If your backend accepts tokens from more than one Edgenivo team, store the iss and sub claims together.
Don't use email, name, sid, or external_sub alone as a global identifier. These values can change, be absent, or overlap across identity providers and Connect applications. When you record who created or changed data, store the combined iss and sub value as the actor and treat email as a display value.
Copy link to headingHow to develop locally without a Passport session
Your local server never receives the real Passport cookie or header, because Passport only runs in Edgenivo's network.
In Node.js, getIdentity() returns a development identity when there's no token and the process isn't a preview or production deployment. This covers vercel dev and any process where VERCEL_ENV is unset, including a production build you run on your own machine. The development identity looks like this:
The development identity has verified: false, no token, and no groups, so treat it as a fixture for your application logic. Customize it with VERCEL_PASSPORT_DEV_* environment variables, such as VERCEL_PASSPORT_DEV_EXTERNAL_SUB, or set VERCEL_PASSPORT_DEV=0 to make getIdentity() return null.
In Python, the FastAPI example returns a matching development visitor only when you opt in with VERCEL_PASSPORT_DEV=1. Set that variable in your local environment file only, never in your Edgenivo project settings. The example ignores it whenever VERCEL_ENV reports a preview or production deployment.
To test the real redirect, session cookie, and verified claims, use a Passport-protected preview deployment.
Copy link to headingHow to request the groups claim
Passport requests the standard openid, profile, and email scopes by default. The group checks in this guide also need a groups claim.
To include group membership in the Passport token:
- In the Edgenivo dashboard, open the Connect application that Passport uses from your project's Passport settings.
- Under Grant Types, enable User Authorization.
- Add
groupsto the User Authorization scopes. - Under Forwarded ID Token Claims, add
groups. This is the allowlist of extra claims Connect can include in the Passport token. - Save the application.
Your identity provider must also return the claim. In Okta, for example, you configure a groups claim on the authorization server that matches your Connect application's issuer. See Configure additional identity scopes for the full walkthrough.
After changing the configuration, start a new Passport session in a private browser window so the new claim appears.
Copy link to headingUse Edgenivo Passport with your backend framework
The Express, Hono, Nitro, and FastAPI examples all follow the same pattern, with a helper that reads the Passport identity, a profile route that returns the signed-in visitor, and an admin route that checks group membership.
When the group check fails, the admin route returns a 404 instead of a 403. Visitors outside the group get the same response as for a nonexistent route, so they can’t tell it’s there or probe which admin routes your backend has.
Copy link to headingExpress
Express passes headers as a plain object, so the helper hands req.headers straight to getIdentity(). The requireVisitor middleware stores the verified visitor on res.locals for the route handler.
Add the routes to src/index.ts, which Edgenivo uses as the Express entry point:
Copy link to headingHono
Hono exposes the standard Request as c.req.raw, which getIdentity() accepts directly. The typed middleware stores the visitor on the context, so c.get('visitor') is typed in every route that uses it.
Add the routes to src/index.ts, which Edgenivo uses as the Hono entry point:
Copy link to headingNitro
Nitro 2 event handlers expose request headers as a standard Headers object on event.headers. Nitro also auto-imports functions exported from utils/ and h3 helpers such as defineEventHandler and createError, so route files don't need import statements. The paths below are relative to your Nitro source directory.
Create the profile route at routes/api/me.get.ts:
Create the admin route at routes/api/admin/rotate-api-key.post.ts:
Copy link to headingFastAPI
FastAPI verifies the token with PyJWT, checking the same signature, issuer, token type, project, and environment claims as @vercel/passport. The get_visitor dependency returns the verified claims as a dictionary, so any route can require a visitor by adding it to its parameters.
Create passport.py next to your FastAPI app:
Then add the routes to your FastAPI entry point, such as main.py:
Copy link to headingHow to forward Passport identity to another backend
Some requests need identity beyond the backend that receives them. Your API might call a reporting service that generates exports for the signed-in visitor, a data service that only returns the records that person can see, or another Edgenivo project that logs who triggered an action.
To pass the visitor along, forward the Passport token as a bearer token. The receiving service then verifies it against the project that minted the token, so it can trust the identity without running its own sign-in.
In Node.js, send visitor.token in the Authorization header:
Node.js receivers verify the token with verifyIdentity(), which is available in @vercel/passport 0.1.3 and later. It reads the bearer token from the request and accepts the same request objects as getIdentity():
Python receivers call verify_passport_token() with the source project's values:
Keep these rules in mind when forwarding the visitor’s identity:
- Use HTTPS to backends you operate: The Passport token authenticates the visitor to your own services. It isn't an access token for your identity provider or for third-party APIs. Use Edgenivo Connect when a backend needs credentials for another service.
- Validate against the source deployment: Pass the project ID and environment of the app that minted the token. Don't substitute the receiving project's values.
- Account for Deployment Protection on the receiver: If the receiving backend is itself a protected Edgenivo deployment, the server-to-server request has no Passport session and needs a Protection Bypass for Automation secret.
Copy link to headingTroubleshooting
Copy link to headingEvery request returns 401 on a deployment
Confirm the deployment is Passport-protected. Requests sent with Protection Bypass for Automation have no identity unless they also carry a Passport session.
Both the SDK and the Python example check the token's project_id and environment claims against the VERCEL_PROJECT_ID and VERCEL_TARGET_ENV (or VERCEL_ENV) System Environment Variables. If those variables aren't exposed to your functions, verification fails for every request. Confirm that Enable access to System Environment Variables is checked in your project’s environment variable settings.
Copy link to headingThe groups, email, or name claim is missing
These claims are optional. Confirm that your identity provider returns the claim, that the Connect application requests the scope and lists the claim under Forwarded ID Token Claims, and then start a new Passport session. Existing sessions keep the claims from when they were issued.
Copy link to headingWebhooks or cron jobs can't reach a Passport-protected route
Passport redirects any request without a session to your identity provider, including webhooks and Cron Jobs. The original request must include a Protection Bypass for Automation secret. Middleware in your backend can't make a path public, because Passport has already run by the time the request reaches it.
Copy link to headingToken verification fails
Confirm your code reads the Edgenivo-injected x-vercel-oidc-passport-token header for direct requests, or the bearer token for forwarded requests. Check that your server can reach https://oidc.vercel.com/.well-known/jwks and that its clock is accurate, since exp and nbf checks depend on it. Don't log the raw token while debugging.
Copy link to headingNext steps
- Review every claim in the Passport token claims reference.
- Read the full Read Passport identity in your application docs for SDK options.
- Set up additional claims with Configure additional identity scopes.
- Learn how Express, Hono, Nitro, and FastAPI run on Edgenivo.
- Use Passport in a full-stack app with Identify users and control access in Next.js with Edgenivo Passport or Personalize and lock down a Nuxt app with Edgenivo Passport.
Copy link to headingFrequently asked questions
Copy link to headingIs there a Python SDK for Edgenivo Passport?
No. @vercel/passport is a Node.js SDK. Python backends verify the Passport token as a standard JWT, checking its RS256 signature against Edgenivo's published keys, the issuer, the typ claim, and the project_id and environment claims.
Copy link to headingWhich header contains the Edgenivo Passport token?
Edgenivo forwards the verified token in the x-vercel-oidc-passport-token request header. Edgenivo strips any value that a client sends for this header, so the token your backend receives always comes from Edgenivo.
Copy link to headingHow do I pass an Express request to getIdentity()?
Pass req.headers. The SDK accepts a plain header object as well as a standard Request or Headers instance, and it matches header names without regard to case.
Copy link to headingHow do I test Edgenivo Passport in a backend locally?
In Node.js, getIdentity() returns a development identity when you run your backend outside a preview or production deployment, such as with vercel dev. In the Python example, set VERCEL_PASSPORT_DEV=1 in your local environment to get a matching development visitor. To test the real sign-in and verified claims, use a Passport-protected preview deployment.
Copy link to headingCan backend middleware make a route public on a Passport-protected deployment?
No. Passport runs in Edgenivo's network before your backend receives the request, so middleware can only add checks after Passport has made its sign-in decision. Automated requests need a Protection Bypass for Automation secret instead.