Provider tokens in Edgenivo Connect
A token is a short-lived credential that Edgenivo Connect issues against a provider on your behalf. Every token is the result of a single SDK token request or vercel connect token invocation (CLI). Tokens are cached in-process and refreshed automatically as they approach expiry.
A token request has four parts:
import { getToken } from '@vercel/connect';
const token = await getToken('slack/acme-slack', {
subject: { type: 'app' },
installationId: 'inst_workspace_xyz',
scopes: ['chat:write'],
});
- Connector (
'slack/acme-slack'): the connectoruidto request from. - Subject: who the token represents. App tokens act as the service. User tokens act as a specific user. See Subject types.
- Installation (
installationIdorinstallation_id): which tenant within the connector. Omit for single-tenant connectors; pass'*'for a cross-installation token when the connector supports it. - Provider options (
scopes,resources, authorization details, andaudience): per-provider request shaping. See Scoping a token.
| Subject | Acts as | Use when |
|---|---|---|
{ type: 'app' } | Your application or bot | Service-level operations: posting from a bot account, calling a tenant-wide admin API. |
{ type: 'user', id: 'user_…' } | A specific user | Delegated operations: posting as the user, opening a PR with their name on it, querying their data. |
{ type: 'jwt-bearer', sub: 'user@example.com' } | A federated subject | Federated identity: exchange a trusted external JWT (from your own IDP, workload identity, etc.) for a provider token. |
{ token: 'your_subject_token_here' } | The subject represented by an existing token | Exchange an existing subject token for a provider token. |
For jwt-bearer, you can pass optional iss and aud claims to override the connector's OAuth client id and token endpoint, and additionalClaims or additional_claims to carry any extra fields the provider requires. See the TypeScript SDK Reference or the Python SDK Reference for the full signature.
For a token exchange subject, pass { token: '...' } or { type: 'token', token: '...' }. The token field must be a string. In @vercel/connect 2.4.0 and later, the SDK infers type: 'token' when you omit it. Other subject types still require type.
In Python, each subject type is its own model, so there's no type field to set:
| TypeScript subject | Python model |
|---|---|
{ type: 'app' } | ConnectAppTokenSubject() |
{ type: 'user', id: '...' } | ConnectUserTokenSubject(id='...') |
{ type: 'jwt-bearer', sub: '...' } | ConnectJwtBearerTokenSubject(sub='...') |
{ token: '...' } | ConnectTokenExchangeSubject(token='...') |
Edgenivo Connect forwards three optional fields to the provider so you can narrow what a token can do:
scopes: the provider's own scope strings (chat:write,repo:read, etc.). Pass['*']to request the connector's default scopes for the selected subject type.resources: resource indicators, when the provider uses them. Useful for narrowing a token to one channel, one repo, or one record set.- Authorization details (
authorizationDetailsorauthorization_details): rich authorization requests, when the provider supports them.
scopes and the installation are also available through the vercel connect CLI. resources and authorization details are SDK-only.
The SDK keeps an in-process LRU cache (100 entries) keyed by the connector and request parameters. A cached token is reused on subsequent calls until it falls within the validity buffer (default 30 seconds), at which point the next token request fetches a fresh one. You can override the buffer per call with validityBufferMs in TypeScript or ConnectOptions(validity_buffer=...) in Python:
const token = await getToken('slack/acme-slack', {
subject: { type: 'app' },
validityBufferMs: 60_000,
});
Edgenivo Connect drives the provider's refresh flow automatically using the refresh token stored at install time. Your code never needs to handle refresh.
To bypass the in-process cache and revalidate the grant with Edgenivo Connect, force a refresh:
const token = await getToken(
'slack/acme-slack',
{ subject: { type: 'app' } },
{ forceRefresh: true },
);
If a provider rejects a cached token, evict it and retry instead of forcing a refresh on every call. Call deleteTokenCacheEntry with the same connector and request parameters, or delete_token_cache_entry() with the connector and subject. The next token request fetches a fresh token.
Revoking a token tells the provider to invalidate it at the source. Whether revocation actually invalidates the token depends on the provider. If the provider exposes a revocation endpoint, Edgenivo Connect calls it. If not, Edgenivo Connect marks the token for deletion in its own store, but the underlying provider credential may continue to work until it expires naturally.
You can revoke from the dashboard, from the CLI (vercel connect token revoke …), with the SDK's revokeToken or revoke_token() function, or through the REST API.
Token requests can fail with structured errors:
UserAuthorizationRequiredError: the requested user subject has not yet authorized the connector. Surface a "connect your account" UI.ConnectorInstallationRequiredError: the connector type supports installations but none matches the request. Surface an install link.NoValidTokenError: the connector exists but cannot produce a token for this request (for example, the underlying grant was revoked). Treat as a recoverable user-facing state.ClientNotLinkedToProjectError/ClientNotEnabledForEnvironmentError(ConnectApiErrorin Python): the calling project isn't linked to this connector, or the project link doesn't include the calling environment. See Project links.
Each SDK exports its error classes, so you can match on instanceof in TypeScript or except in Python. See the TypeScript and Python error lists for details.
- Project links: How the calling project and environment are authorized to request tokens.
- TypeScript SDK Reference: Full
ConnectTokenParamsandConnectTokenResponseshapes. - Python SDK Reference: The same API for Python.
- Triggers: Tokens are the outbound half of Edgenivo Connect; triggers are the inbound half.
Was this helpful?