Optare v1.0 is now available. Get started →
reference
Token Verification

Token Verification

Verify Optare ID tokens on your backend using JWKS (JSON Web Key Set).

JWKS Endpoint

Optare publishes public keys at:

https://id.optare.one/.well-known/jwks.json

Using jose Library (Recommended)

The jose library provides secure JWT verification with automatic key rotation support.

Installation

npm install jose

Basic Verification

import { createRemoteJWKSet, jwtVerify } from 'jose';
 
// Create JWKS client (cache this in production)
const JWKS = createRemoteJWKSet(
  new URL('https://id.optare.one/.well-known/jwks.json')
);
 
// Verify a token
async function verifyToken(token: string) {
  try {
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: 'https://id.optare.one',
      audience: 'your-client-id', // Optional: your OAuth client ID
    });
    
    return {
      valid: true,
      userId: payload.sub,
      email: payload.email,
      organizationId: payload.org_id,
    };
  } catch (error) {
    return { valid: false, error: error.message };
  }
}

Next.js API Route Example

// app/api/protected/route.ts
import { createRemoteJWKSet, jwtVerify } from 'jose';
import { NextRequest, NextResponse } from 'next/server';
 
const JWKS = createRemoteJWKSet(
  new URL('https://id.optare.one/.well-known/jwks.json')
);
 
export async function GET(request: NextRequest) {
  const authHeader = request.headers.get('authorization');
  
  if (!authHeader?.startsWith('Bearer ')) {
    return NextResponse.json({ error: 'Missing token' }, { status: 401 });
  }
  
  const token = authHeader.slice(7);
  
  try {
    const { payload } = await jwtVerify(token, JWKS, {
      issuer: 'https://id.optare.one',
    });
    
    // Token is valid - proceed with request
    return NextResponse.json({
      message: 'Authenticated',
      user: {
        id: payload.sub,
        email: payload.email,
      }
    });
  } catch (error) {
    return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
  }
}

Express Middleware

import { createRemoteJWKSet, jwtVerify } from 'jose';
import { Request, Response, NextFunction } from 'express';
 
const JWKS = createRemoteJWKSet(
  new URL('https://id.optare.one/.well-known/jwks.json')
);
 
export async function requireAuth(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing token' });
  }
  
  try {
    const { payload } = await jwtVerify(authHeader.slice(7), JWKS, {
      issuer: 'https://id.optare.one',
    });
    
    req.user = {
      id: payload.sub as string,
      email: payload.email as string,
      organizationId: payload.org_id as string,
    };
    
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
}
 
// Usage
app.get('/api/profile', requireAuth, (req, res) => {
  res.json({ user: req.user });
});

Token Claims

Optare access tokens include these standard claims:

ClaimDescription
subUser ID
emailUser's email address
nameUser's display name
org_idOrganization ID
issIssuer (https://id.optare.one)
audAudience (your client ID)
expExpiration timestamp
iatIssued at timestamp

OIDC Discovery

For automatic configuration, use the OIDC discovery endpoint:

https://id.optare.one/.well-known/openid-configuration

This returns all endpoints and supported features:

{
  "issuer": "https://id.optare.one",
  "authorization_endpoint": "https://id.optare.one/oauth/authorize",
  "token_endpoint": "https://id.optare.one/oauth/token",
  "userinfo_endpoint": "https://id.optare.one/oauth/userinfo",
  "jwks_uri": "https://id.optare.one/.well-known/jwks.json",
  "scopes_supported": ["openid", "email", "profile", "offline_access"]
}

Security Best Practices

  1. Cache JWKS: createRemoteJWKSet caches keys automatically with proper TTL
  2. Validate issuer: Always verify iss claim matches https://id.optare.one
  3. Check expiration: The library automatically rejects expired tokens
  4. Use HTTPS: Never transmit tokens over unencrypted connections

Next Steps