Pub/sub Tokens
Verify pub/sub JWTs and sync verification keys in Rust
Pub/sub tokens
Verify short-lived JWTs for path-scoped publish (put) and subscribe (get). Claim rules and minting: Scout DB, Scout Core.
Flow
- A client mints a JWT, often on some application interface like a web app.
- The client connects to your service with the token.
- Your service verifies the token and then checks each path with
can_publish/can_subscribe.
Your service can poll scout for new public keys. This allows for programmatic rotation across your entire fleet of devices. Example included at the bottom of this doc.
Environment
export SCOUT_PUBSUB_JWT_KEY_DIR=/path/to/public-keys # local dir or https:// base URLDatabaseConfig env vars are required when syncing keys from the database.
Config
PubsubTokenConfig::from_env()
- Requires:
SCOUT_PUBSUB_JWT_KEY_DIR. - Behavior: Loads
{kid}.jwkfrom a local dir on startup; remote URLs fetch keys on demand bykid. - Returns:
Result<PubsubTokenConfig>
PubsubTokenConfig::from_local_key_dir(path)
- path: Directory of
{kid}.jwkfiles (no DB sync). - Returns:
Result<PubsubTokenConfig>
PubsubTokenConfig::from_public_jwks(jwks: &[&str])
- jwks: Public JWK JSON strings, each with
kid. For tests and tooling. - Returns:
Result<PubsubTokenConfig>
sync_public_keys_from_database(&self, db: &DatabaseConfig)
- Calls:
get_pubsub_jwt_public_keysvia PostgREST. - Behavior: Updates in-memory cache; if key dir is local, writes
{kid}.jwkand removes stale files. - Returns:
Result<()>
refresh_key_dir(&self)
- Behavior: Reloads
{kid}.jwkfrom a local key dir only. - Returns:
Result<()>
Mint
mint_pubsub_token(db: &DatabaseConfig)
- Auth: Device
api_keyondb(SCOUT_DEVICE_API_KEY) plusSUPABASE_PUBLIC_API_KEY. - Calls: Edge function
mint-pubsub-token. - Returns:
Result<PubsubTokenMint, MintPubsubTokenError>—token,put,get,iat,exp.
mint_service_pubsub_token(db: &DatabaseConfig, herd_ids: &[i64])
- Auth:
service_role_keyondb(SUPABASE_SERVICE_ROLE_KEY). For trusted backends like a relay, not devices. - Calls: Edge function
mint-pubsub-tokenwith a{ herd_ids }body; edge resolvesget_service_pubsub_token_claims. - Returns:
Result<PubsubTokenMint, MintPubsubTokenError>— publish-only (put= herd ids,get=[]).
Verify
verify_pubsub_token(config, token)
- token: JWT string from the client.
- Returns:
Result<PubsubTokenClaims, PubsubTokenError>—InvalidorExpired. - Claims:
root,publish(put),subscribe(get),issued_at,expires_at.
can_publish(claims, path) / can_subscribe(claims, path)
- path: Channel path (e.g.
{herd_id}/live). Prefix match againstroot+ claim patterns; empty patterns deny.
Key helpers
fetch_pubsub_jwt_public_keys(db)
- Returns:
Result<Vec<PubsubJwkPublicKey>>(kid+jwk).
sync_pubsub_public_keys_to_dir(keys, dir)
- Behavior: Writes
{kid}.jwk; deletes local*.jwknot inkeys. - Returns:
Result<()>
Example
use scout_rs::db_client::DatabaseConfig;
use scout_rs::{
can_publish, can_subscribe, mint_pubsub_token, verify_pubsub_token, PubsubTokenConfig,
};
let db = DatabaseConfig::from_env()?;
let mint = mint_pubsub_token(&db)?;
let config = PubsubTokenConfig::from_env()?;
config.sync_public_keys_from_database(&db)?;
let claims = verify_pubsub_token(&config, &mint.token)?;
if can_subscribe(&claims, "hey/live") {
// allow subscribe
}
if can_publish(&claims, "hey/live") {
// allow publish
}