AgentSkillsCN

Authentication & Authorization

当处理 WebSpec OAuth 流程、JWT 令牌结构、会话管理、钥匙串集成、设备绑定或注册流程时,应使用此技能。触发短语包括“WebSpec 认证”、“令牌结构”、“OAuth 流程”、“JWT 声明”、“钥匙串集成”、“会话令牌”、“设备绑定”。

SKILL.md
--- frontmatter
name: Authentication & Authorization
description: This skill should be used when working with WebSpec OAuth flows, JWT token structure, session management, keychain integration, device binding, or onboarding flows. Trigger phrases include "webspec auth", "token structure", "oauth flow", "jwt claims", "keychain integration", "session token", "device binding".
version: 1.0.0

WebSpec Authentication & Authorization

Domain Note: This skill uses gimme.tools as the default WebSpec domain. For self-hosted or enterprise deployments, substitute your configured domain.

Overview

WebSpec implements a three-layer authorization model with OAuth 2.0 + PKCE for service connections. The system integrates with device keychains for zero-friction discovery and supports progressive authorization.

Three Authorization Layers

code
┌─────────────────────────────────────────────────────────────────┐
│                    PLATFORM AUTHORIZATION                        │
│   User has a gimme.tools account and active platform session     │
│   Token: Platform session token (days)                           │
├─────────────────────────────────────────────────────────────────┤
│                    SESSION AUTHORIZATION                         │
│   User has connected a specific service (e.g., Slack)            │
│   Token: Service connection token (weeks, refreshable)           │
├─────────────────────────────────────────────────────────────────┤
│                   INVOCATION AUTHORIZATION                       │
│   User has granted permission for specific action                │
│   Token: Short-lived invocation token (minutes)                  │
└─────────────────────────────────────────────────────────────────┘

Layer Details

LayerScopeToken LifetimeRefresh
Platformgimme.tools account7 daysYes
SessionService connection30 daysYes
InvocationSpecific action5 minutesNo

OAuth 2.0 with PKCE Flow

Authorization Request

code
GET https://auth.gimme.tools/oauth/authorize
  ?client_id=gimme_client
  &redirect_uri=https://app.gimme.tools/callback
  &response_type=code
  &scope=slack:channels:read slack:messages:write
  &state=random_state_value
  &code_challenge=base64url(sha256(code_verifier))
  &code_challenge_method=S256

Authorization Flow

code
┌────────────┐     ┌────────────────┐     ┌────────────────┐
│   Agent    │     │ auth.gimme.tools│     │  Service OAuth │
│            │     │                 │     │  (e.g., Slack) │
└─────┬──────┘     └────────┬───────┘     └────────┬───────┘
      │                      │                      │
      │ 1. Request auth      │                      │
      │─────────────────────>│                      │
      │                      │                      │
      │ 2. Redirect to       │                      │
      │    service OAuth     │                      │
      │<─────────────────────│                      │
      │                      │                      │
      │ 3. User authorizes   │                      │
      │─────────────────────────────────────────────>
      │                      │                      │
      │ 4. Callback with     │                      │
      │    auth code         │                      │
      │<─────────────────────────────────────────────
      │                      │                      │
      │ 5. Exchange code     │                      │
      │─────────────────────>│                      │
      │                      │                      │
      │                      │ 6. Fetch service     │
      │                      │    tokens            │
      │                      │─────────────────────>│
      │                      │                      │
      │                      │<─────────────────────│
      │                      │                      │
      │ 7. Return gimme      │                      │
      │    session token     │                      │
      │<─────────────────────│                      │
      │                      │                      │

Token Exchange

bash
POST https://auth.gimme.tools/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=auth_code_here
&redirect_uri=https://app.gimme.tools/callback
&client_id=gimme_client
&code_verifier=original_random_string

Token Response

json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 300,
  "refresh_token": "rt_abc123xyz",
  "scope": "GET:slack.gimme.tools/channels/*"
}

JWT Token Structure

Standard Claims

json
{
  "iss": "auth.gimme.tools",
  "sub": "user_12345",
  "aud": "slack.gimme.tools",
  "exp": 1704067200,
  "iat": 1704066900,
  "nbf": 1704066900,
  "jti": "tok_unique_id"
}

WebSpec-Specific Claims

json
{
  "iss": "auth.gimme.tools",
  "sub": "user_12345",
  "aud": "slack.gimme.tools",
  "exp": 1704067200,
  "scope": "GET:channels/*,POST:channels/*/messages",
  "session_id": "sess_abc123",
  "device_id": "dev_xyz789",
  "platform_session": "plat_session_ref",
  "service_connection": "conn_slack_12345"
}

Claim Definitions

ClaimTypeDescription
issstringToken issuer (always auth.gimme.tools)
substringUser identifier
audstringTarget subdomain (audience)
expnumberExpiration timestamp (Unix)
scopestringPermission scopes (comma-separated)
session_idstringCurrent session identifier
device_idstringBound device identifier
platform_sessionstringReference to platform session
service_connectionstringReference to service connection

Token Validation Algorithm

javascript
async function validateToken(token, request) {
  // 1. Decode and verify signature
  const payload = await verifyJWT(token, publicKey);
  if (!payload) {
    return { valid: false, error: 'Invalid signature' };
  }

  // 2. Check expiration
  const now = Math.floor(Date.now() / 1000);
  if (payload.exp < now) {
    return { valid: false, error: 'Token expired' };
  }

  // 3. Check not-before
  if (payload.nbf && payload.nbf > now) {
    return { valid: false, error: 'Token not yet valid' };
  }

  // 4. Verify audience matches request subdomain
  const requestHost = new URL(request.url).hostname;
  if (payload.aud !== requestHost) {
    return { valid: false, error: 'Audience mismatch' };
  }

  // 5. Verify scope covers request
  const requestScope = `${request.method}:${request.path}`;
  if (!scopeMatches(payload.scope, requestScope)) {
    return { valid: false, error: 'Insufficient scope' };
  }

  // 6. Verify device binding (for local.gimme.tools)
  if (requestHost === 'local.gimme.tools') {
    if (!await verifyDevice(payload.device_id)) {
      return { valid: false, error: 'Device not verified' };
    }
  }

  return { valid: true, payload };
}

Keychain Integration

Zero-Friction Discovery

WebSpec integrates with device keychains to enable passwordless connection:

code
┌────────────────────────────────────────────────────────────┐
│                    User's Device                            │
│                                                             │
│  ┌──────────────────┐    ┌──────────────────────────────┐  │
│  │ Password Manager │    │        gimme.tools           │  │
│  │  (1Password,     │    │                              │  │
│  │   Keychain, etc.)│    │  1. Query keychain for       │  │
│  │                  │    │     matching domains         │  │
│  │  - slack.com     │───>│                              │  │
│  │  - notion.so     │    │  2. Suggest connections      │  │
│  │  - github.com    │    │     without password entry   │  │
│  │                  │    │                              │  │
│  └──────────────────┘    │  3. Use biometric to         │  │
│                          │     authorize OAuth flow     │  │
│                          └──────────────────────────────┘  │
└────────────────────────────────────────────────────────────┘

Keychain Query Flow

javascript
async function discoverConnections(user) {
  // 1. Get user's keychain hints (domains they have credentials for)
  const keychainDomains = await queryKeychain(user.device_id);

  // 2. Match against registered services
  const matches = [];
  for (const domain of keychainDomains) {
    const service = await findServiceByDomain(domain);
    if (service) {
      matches.push({
        service: service.id,
        domain: domain,
        status: 'KEYCHAIN',
        connectAction: 'biometric'
      });
    }
  }

  return matches;
}

Connection Status

StatusDescriptionUX
CONNECTEDOAuth already authorizedDirect access
KEYCHAINCredentials in password manager"Connect with FaceID"
AVAILABLEService in registry"Sign up / Connect"
UNAVAILABLENot in registryCannot connect

Token Types & Lifetimes

Token TypeLifetimeStorageRefresh
Platform session7 daysHttpOnly cookieYes
Service connection30 daysSecure storageYes
Invocation token5 minutesMemory onlyNo
Refresh token90 daysSecure storageYes

Token Refresh Flow

bash
POST https://auth.gimme.tools/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=rt_abc123xyz
&client_id=gimme_client

Device Binding

Device Registration

json
{
  "device_id": "dev_xyz789",
  "device_name": "MacBook Pro",
  "registered_at": "2024-01-01T00:00:00Z",
  "last_used": "2024-01-15T10:30:00Z",
  "capabilities": ["local_bridge", "biometric"],
  "verified": true
}

Device Verification

For sensitive operations (especially local.gimme.tools):

javascript
async function verifyDevice(deviceId, challenge) {
  // 1. Generate challenge
  const challenge = generateSecureRandom();

  // 2. Sign with device key (Secure Enclave/TPM)
  const signature = await deviceSign(challenge);

  // 3. Verify signature
  return verifyDeviceSignature(deviceId, challenge, signature);
}

Onboarding Flow

Progressive Authorization

code
┌─────────────────────────────────────────────────────────────┐
│  Step 1: Platform Account                                    │
│  "Create your gimme.tools account"                           │
│  → Email/password or SSO                                     │
├─────────────────────────────────────────────────────────────┤
│  Step 2: Device Registration                                 │
│  "Register this device"                                      │
│  → Biometric enrollment                                      │
├─────────────────────────────────────────────────────────────┤
│  Step 3: Keychain Discovery                                  │
│  "We found services you might use"                           │
│  → Show KEYCHAIN matches                                     │
├─────────────────────────────────────────────────────────────┤
│  Step 4: First Connection                                    │
│  "Connect your first service"                                │
│  → OAuth flow with one-tap biometric                         │
└─────────────────────────────────────────────────────────────┘

Validation Checklist

When reviewing authentication and authorization:

  • OAuth 2.0 with PKCE is used (no implicit flow)
  • JWT signature is verified with correct public key
  • Token expiration is checked before use
  • Audience claim matches target subdomain
  • Scope is sufficient for requested action
  • Device binding verified for local operations
  • Refresh tokens stored securely
  • Session tokens use HttpOnly cookies
  • No tokens logged or exposed in URLs
  • Token refresh happens before expiration

Common Auth Issues

IssueRiskFix
Missing PKCEAuthorization code interceptionAdd code_challenge
Long-lived tokensToken theft impactUse short-lived + refresh
Scope too broadPrinciple of least privilegeRequest minimal scopes
Missing aud checkToken replay attacksValidate audience
Tokens in URLsLog exposureUse Authorization header
No device bindingUnauthorized device accessRequire device verification