Skip to main content

Overview

FastSkill HTTP API uses JWT (JSON Web Tokens) for authentication and role-based access control (RBAC). Authentication is required for most operations, with different roles having different permission levels.
JWT tokens are generated by the /auth/token endpoint and must be included in the Authorization header as Bearer <token>.

Authentication Flow

1. Get Token

Authenticate with the registry to receive a JWT token. Endpoint: POST /auth/token Request Body:
{
  "role": "manager",
  "username": "optional-username"
}
Fields:
FieldTypeRequiredDescription
rolestringYesUser role (user, manager, or admin)
usernamestringNoOptional username for tracking purposes
Example:
# Generate token as manager
curl -X POST http://localhost:8080/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "role": "manager",
    "username": "dev-user"
  }'

# Generate token as admin
curl -X POST http://localhost:8080/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "role": "admin"
  }'
Response:
{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhY21lL2FsaWNlIiwicm9sZSI6Im1hbmFnZXIiLCJleHAiOjE3MDk4MzAwMDAsImlhdCI6MTcwNzI0MjQwMH0.signature",
    "expires_at": "2025-02-15T10:30:00Z",
    "role": "manager"
  }
}

2. Use Token

Include the JWT token in the Authorization header for all subsequent requests.
# Use token in request
curl -X GET http://localhost:8080/api/skills \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

# Set token as environment variable for convenience
export TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
curl -X GET http://localhost:8080/api/skills \
  -H "Authorization: Bearer $TOKEN"

3. Verify Token (Optional)

Verify that a token is valid and retrieve user information. Endpoint: GET /auth/verify Headers:
  • Authorization: Bearer <token>
Example:
curl -X GET http://localhost:8080/auth/verify \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response (valid token):
{
  "success": true,
  "data": {
    "valid": true,
    "user": "acme/alice",
    "role": "manager",
    "scope": "acme",
    "expires_at": "2025-02-15T10:30:00Z"
  }
}
Response (invalid token):
{
  "success": false,
  "error": {
    "code": "INVALID_TOKEN",
    "message": "Invalid or expired token"
  }
}

Role-Based Access Control (RBAC)

FastSkill implements a three-tier role hierarchy:

Role Hierarchy

admin
  └─ manager
       └─ user
  • admin: Full access to all operations including user management and registry configuration
  • manager: Can publish, update, and delete skills; view registry metrics
  • user: Read-only access to skills and search functionality

Permission Matrix

Operationusermanageradmin
Read Operations
List skills
Get skill details
Search skills
Get skill versions
Write Operations
Create skills
Update skills
Delete skills
Create skill versions
Delete skill versions
Enable/disable skills
Registry Operations
Publish to registry
Update manifest
View registry metrics
Admin Operations
Manage users
Configure registry
View all registry data

Scope-Based Authorization

When a user authenticates, their token includes a scope claim that determines which skills they can publish:
{
  "sub": "acme/alice",
  "scope": "acme",
  "role": "manager"
}
Scope Examples:
User (sub)ScopeCan Publish To
acme/aliceacmeacme/* skills
personalpersonalpersonal/* skills
company/dev-team/bobcompanycompany/* skills
Publishing with Scopes: When publishing skills, the server automatically prepends the scope to the skill ID:
# User: acme/alice (scope: acme)
# Publish skill with name: web-scraper
# Result: skill published as "acme/web-scraper"

API Key Authentication

For service-to-service integration, FastSkill supports API key authentication. Header: x-api-key: <api-key>
curl -X GET http://localhost:8080/api/skills \
  -H "x-api-key: your-api-key-here"
API keys are typically generated through the registry admin interface or configuration. API keys may have different permissions than JWT tokens.

API Key vs JWT Token

FeatureAPI KeyJWT Token
AuthenticationHeader x-api-keyHeader Authorization: Bearer
ExpirationNever expires (unless revoked)Typically expires in 24 hours
User ContextNo user contextIncludes user, role, and scope
RevocationCan be revoked in admin panelCan be revoked by logout
Use CaseService accounts, CI/CDUser sessions, interactive use

Token Management

Token Expiration

JWT tokens have a configurable expiration time (default: 24 hours). After expiration:
# Token expired response
{
  "success": false,
  "error": {
    "code": "INVALID_TOKEN",
    "message": "Token has expired"
  }
}
Solution: Generate a new token:
curl -X POST http://localhost:8080/auth/token \
  -H "Content-Type: application/json" \
  -d '{"role": "manager"}'

Token Refresh

FastSkill CLI automatically manages token refresh when needed:
# CLI automatically checks token expiration
fastskill auth whoami

# If expired, re-authenticate
fastskill auth login --role manager

Token Storage

Tokens are stored locally for reuse:
  • CLI: Stored in ~/.fastskill/auth.toml
  • HTTP Clients: Store in application configuration or environment variables
Example storage format:
[[registries]]
registry_url = "https://registry.example.com"
username = "dev-user"
role = "manager"
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
expires_at = "2025-02-15T10:30:00Z"
last_refresh = "2025-01-15T10:30:00Z"
Never commit auth.toml or tokens to version control. Rotate tokens regularly for security.

Using Authentication in Different Scenarios

Interactive CLI

# Login once
fastskill auth login --role manager

# Token stored automatically
# All subsequent commands use the token
fastskill list
fastskill search "query"
fastskill publish --artifacts ./artifacts/pptx-1.2.3.zip

Shell Scripts

#!/bin/bash

# 1. Get token
TOKEN=$(curl -s -X POST http://localhost:8080/auth/token \
  -H "Content-Type: application/json" \
  -d '{"role": "manager"}' | jq -r '.data.token')

# 2. Use token in requests
curl -X GET http://localhost:8080/api/skills \
  -H "Authorization: Bearer $TOKEN"

# 3. Create skill
curl -X POST http://localhost:8080/api/code/v1/skills \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "my-skill",
    "name": "My Skill",
    "description": "Description",
    "version": "1.0.0"
  }'

Web Applications (JavaScript)

// Fetch token
const response = await fetch('http://localhost:8080/auth/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    role: 'manager',
    username: 'web-user'
  })
});

const { data } = await response.json();
const token = data.token;

// Store token (e.g., localStorage)
localStorage.setItem('fastskill_token', token);

// Use token in requests
const skillsResponse = await fetch('http://localhost:8080/api/skills', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
});

Python Requests

import requests

# Get token
response = requests.post(
    'http://localhost:8080/auth/token',
    json={'role': 'manager', 'username': 'python-user'}
)
data = response.json()
token = data['data']['token']

# Use token in requests
headers = {'Authorization': f'Bearer {token}'}
skills_response = requests.get(
    'http://localhost:8080/api/skills',
    headers=headers
)
skills = skills_response.json()

Error Handling

Common Authentication Errors

401 Unauthorized

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or invalid authentication token"
  }
}
Causes:
  • No Authorization header provided
  • Invalid token format
  • Expired token
Solutions:
  • Check Authorization header format
  • Generate a new token
  • Verify token isn’t truncated

403 Forbidden

{
  "success": false,
  "error": {
    "code": "FORBIDDEN",
    "message": "Insufficient permissions for this operation"
  }
}
Causes:
  • User role insufficient for operation
  • User doesn’t have access to requested scope
Solutions:
  • Authenticate with higher role (manager or admin)
  • Verify scope matches target skill
  • Contact registry administrator

409 Conflict (Scope Issues)

{
  "success": false,
  "error": {
    "code": "CONFLICT",
    "message": "Skill ID already exists in this scope"
  }
}
Causes:
  • Attempting to publish skill that already exists in scope
  • Another user in same scope has same skill name
Solutions:
  • Choose a different skill name
  • Coordinate with team members
  • Update existing skill instead of creating new one

Security Best Practices

1

Use HTTPS

Always use HTTPS for authentication and API requests:
# Good
curl https://registry.example.com/auth/token

# Bad - insecure
curl http://registry.example.com/auth/token
2

Protect Tokens

Never expose tokens in:
  • URL parameters
  • Client-side JavaScript (except in secure sessionStorage)
  • Version control
  • Logs or error messages
  • Public documentation
3

Rotate Tokens

Regularly rotate JWT tokens:
  • Logout and re-authenticate periodically
  • Use short expiration times for high-risk environments
  • Revoke compromised tokens immediately
4

Use Appropriate Roles

Assign minimal required role:
  • Use user for read-only applications
  • Use manager for publishing workflows
  • Use admin only for administration
5

Store Tokens Securely

  • Use secure storage mechanisms (keychain, secret managers)
  • Restrict file permissions on token storage files
  • Never hardcode tokens in source code
  • Use environment variables for CI/CD

CI/CD Integration

GitHub Actions

name: Publish Skill
on:
  push:
    branches: [main]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Authenticate
        id: auth
        run: |
          TOKEN=$(curl -s -X POST ${{ secrets.REGISTRY_URL }}/auth/token \
            -H "Content-Type: application/json" \
            -d '{"role": "manager"}' | jq -r '.data.token')
          echo "token=$TOKEN" >> $GITHUB_OUTPUT
        env:
          REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
      
      - name: Publish skill
        run: |
          curl -X POST ${{ secrets.REGISTRY_URL }}/api/code/v1/skills \
            -H "Authorization: Bearer ${{ steps.auth.outputs.token }}" \
            -H "Content-Type: application/json" \
            -d @skill.json

GitLab CI

publish:
  script:
    - |
      # Get token
      TOKEN=$(curl -s -X POST $REGISTRY_URL/auth/token \
        -H "Content-Type: application/json" \
        -d '{"role": "manager"}' | jq -r '.data.token')
      
      # Publish skill
      curl -X POST $REGISTRY_URL/api/code/v1/skills \
        -H "Authorization: Bearer $TOKEN" \
        -H "Content-Type: application/json" \
        -d @skill.json
  variables:
    REGISTRY_URL: https://registry.example.com

Troubleshooting

Check server time: Ensure server and client clocks are synchronized. JWT expiration uses server time.
# Check server time
curl http://localhost:8080/api/status
Verify header format: Ensure Authorization: Bearer <token> format (with space after “Bearer”).
# Correct
curl -H "Authorization: Bearer $TOKEN"

# Incorrect (missing space)
curl -H "Authorization:Bearer $TOKEN"
Check role: Verify your role has permissions for the operation.
# Verify your role
curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8080/auth/verify

# Re-authenticate with higher role if needed
fastskill auth login --role admin

See Also