Authentication

JWT Tokens

Understanding JWT token structure, claims, and validation

All Synoveo API authentication uses RS256-signed JWT tokens. This page explains the token structure and how to validate tokens.

Token Types

TypeScope ClaimPurposeDefault TTL
Access Tokenuser or serviceAPI requests90 days (service) / 7 days (user)
Refresh TokenserviceGet new access tokens30 days

Token Structure

Access Token (Service Scope)

Issued when exchanging API key credentials:

{
  "alg": "RS256",
  "typ": "JWT"
}

Payload:

{
  "scope": "service",
  "plan": "pro",
  "permissions": ["business.read", "business.write"],
  "uid": 570,
  "sub": "syncid_570_1703030400000_my_app",
  "iss": "https://api.synoveo.com",
  "aud": "https://api.synoveo.com",
  "iat": 1703030400,
  "exp": 1710806400
}
ClaimTypeDescription
scopestring"service" for API keys, "user" for dashboard
planstringUser's subscription plan (lite, solo, pro, business)
permissionsarrayGranted permissions (business.read, business.write)
uidnumberNumeric user ID (owner of the API key)
substringSubject - the client_id for service tokens
issstringIssuer URL
audstringAudience URL
iatnumberIssued at (Unix timestamp)
expnumberExpiration (Unix timestamp)

Access Token (User Scope)

Issued after Google OAuth authentication:

{
  "scope": "user",
  "plan": "pro",
  "permissions": [],
  "uid": 570,
  "sub": "570",
  "iss": "https://api.synoveo.com",
  "aud": "https://api.synoveo.com",
  "iat": 1703030400,
  "exp": 1703635200
}

Note: User-scoped tokens have empty permissions array but are granted full access.

Refresh Token

{
  "typ": "refresh",
  "scope": "service",
  "permissions": ["business.read", "business.write"],
  "uid": 570,
  "sub": "syncid_570_1703030400000_my_app",
  "iss": "https://api.synoveo.com",
  "aud": "https://api.synoveo.com",
  "iat": 1703030400,
  "exp": 1705622400
}

The typ: "refresh" claim distinguishes refresh tokens from access tokens.


Token Signing

Tokens are signed using RS256 (RSA Signature with SHA-256):

  • Algorithm: RS256
  • Key Type: RSA 2048-bit or higher
  • Private Key: Used for signing (server-side only)
  • Public Key: Used for verification (can be shared)

Obtaining the Public Key

For token verification in your application, contact support for the public key or use the JWKS endpoint (if available).


Token Validation

When validating tokens, verify:

  1. Signature - RS256 signature matches using public key
  2. Issuer - iss claim equals https://api.synoveo.com
  3. Audience - aud claim equals https://api.synoveo.com
  4. Expiration - exp claim is in the future
  5. Scope - scope claim matches expected value

Example Validation (Node.js)

import jwt from 'jsonwebtoken'

const publicKey = process.env.JWT_PUBLIC_KEY

function validateToken(token) {
  try {
    const decoded = jwt.verify(token, publicKey, {
      algorithms: ['RS256'],
      issuer: 'https://api.synoveo.com',
      audience: 'https://api.synoveo.com'
    })
    return { valid: true, payload: decoded }
  } catch (error) {
    return { valid: false, error: error.message }
  }
}

Using Tokens

Include the access token in the Authorization header:

GET /api/v1/google-business/locations HTTP/1.1
Host: api.synoveo.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

Token Refresh Flow

When access tokens expire, use the refresh token to obtain a new one:

POST /api/v1/auth/token HTTP/1.1
Host: api.synoveo.com
Content-Type: application/json

{
  "grant_type": "refresh_token",
  "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response:

{
  "status": "ok",
  "data": {
    "token_type": "Bearer",
    "scope": "service",
    "plan": "pro",
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "expires_in": 7776000,
    "permissions": ["business.read", "business.write"],
    "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Scope Differences

Service Scope (scope: "service")

  • Issued to API keys
  • Permissions explicitly listed in token
  • Access restricted to assigned location
  • Plan inherited from key owner

User Scope (scope: "user")

  • Issued after Google OAuth
  • Full access to all user resources
  • Permissions array is empty (full access implied)
  • Can access all locations owned by user

Common Errors

ErrorCauseSolution
AUTH_INVALID_TOKENMalformed or invalid signatureCheck token format and signature
AUTH_TOKEN_EXPIREDToken past expirationUse refresh token to get new access token
AUTH_MISSING_TOKENNo Authorization headerInclude Authorization: Bearer <token>
AUTH_INSUFFICIENT_PERMISSIONSMissing required permissionRequest additional permissions

SDK Usage

The @synoveo/sdk handles token management automatically:

import { SynoveoClient } from '@synoveo/sdk'

const client = new SynoveoClient({
  clientId: 'syncid_570_...',
  clientSecret: 'your_secret',
  // Tokens are managed automatically
  hooks: {
    onTokenRefresh: (token) => {
      console.log('Token refreshed, expires:', token.expiresAt)
    }
  }
})

See SDK Documentation for complete token management features.

On this page