Skip to main content

Overview

User tokens are JWT (JSON Web Tokens) obtained through Auth0 OAuth 2.0 authentication. They are used for user-facing applications where individual users need to authenticate and access resources based on their permissions.

How It Works

User authentication follows the OAuth 2.0 authorization code flow:
1

Initiate Login

Your application redirects users to the Auth0 login endpoint:
GET /api/v1/auth/login?redirect_uri=http://localhost:3000/dashboard
2

User Authenticates

Auth0 presents a login page where users authenticate with their credentials or social login providers.
3

Authorization Code

After successful authentication, Auth0 redirects back with an authorization code:
GET /api/v1/auth/callback?code=AUTH_CODE&state=STATE_TOKEN
4

Token Exchange

The server exchanges the code for access and ID tokens containing user information.
5

Use Token

Your application receives a JWT token to authenticate API requests.

Configuration

Basic Setup

User token authentication is the default when creating a client:
import (
    "context"
    "os"
    "github.com/garnet-org/api/client"
)

func main() {
    token := os.Getenv("GARNET_USER_TOKEN")
    client := client.New("https://api.garnet.ai", token)
    
    // Make authenticated requests
    var user UserInfo
    err := client.Get(context.Background(), &user, "/api/v1/me")
    if err != nil {
        panic(err)
    }
}
When a token is provided to client.New(), it automatically sets TokenType to TokenTypeUser.

Explicit User Token Configuration

You can explicitly configure a client to use user tokens:
client/client.go
// WithUserToken configures the client to use a user token for authentication.
func (c *Client) WithUserToken(token string) *Client {
	client := c.Clone()
	client.AuthToken = token
	client.TokenType = TokenTypeUser
	return client
}
// Switch an existing client to user token
userClient := client.WithUserToken("eyJhbGciOiJIUzI1NiIs...")

HTTP Header Format

User tokens are sent in the Authorization header with Bearer authentication:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
The SDK automatically handles the “Bearer ” prefix:
client/client.go
switch c.TokenType {
case TokenTypeUser:
    // Check if it's already a bearer token
    if strings.HasPrefix(c.AuthToken, "Bearer ") {
        req.Header.Set("Authorization", c.AuthToken)
    } else {
        req.Header.Set("Authorization", "Bearer "+c.AuthToken)
    }
}
Don’t manually add the “Bearer ” prefix. The SDK handles this automatically.

Permissions and RBAC

User tokens include role-based access control (RBAC) permissions. The API enforces granular permissions:
PermissionDescription
createCreate new resources
readView existing resources
updateModify resources
deleteRemove resources
listList multiple resources

Example: Creating Project Tokens

Users can only create project tokens with permissions they themselves possess:
import "github.com/garnet-org/api/types"

// User with 'create' and 'read' permissions on tokens
createReq := types.CreateToken{
    Name: "CI/CD Token",
    Permissions: []types.Permission{
        types.PermissionTokenRead,
        types.PermissionTokenList,
    },
}

var tokenResp types.TokenCreated
err := client.Post(ctx, &tokenResp, "/api/v1/tokens", createReq)
Attempting to create a token with more permissions than you have results in a 403 Forbidden error.

Supported Endpoints

User tokens can access most API endpoints:
  • User Info: GET /api/v1/me
  • Agents: GET /api/v1/agents, POST /api/v1/agents, GET /api/v1/agents/{id}
  • Issues: GET /api/v1/issues, POST /api/v1/issues, PATCH /api/v1/issues/{id}
  • Events: GET /api/v1/events
  • Tokens: POST /api/v1/tokens, GET /api/v1/tokens, PATCH /api/v1/tokens/{id}
  • Network Policies: All network policy endpoints
  • Project Settings: All project settings endpoints

Token Lifecycle

Token Expiration

JWT tokens have an expiration time (exp claim). When a token expires, API requests return 401 Unauthorized:
err := client.Get(ctx, &result, "/api/v1/agents")
if err != nil && strings.Contains(err.Error(), "401") {
    // Token expired or invalid
    // Redirect user to login
}

Logout

To log out a user, redirect them to the logout endpoint:
GET /api/v1/auth/logout?returnTo=http://localhost:3000
This clears the Auth0 session and redirects to the specified URL.

Security Best Practices

Critical Security Guidelines
1

Never Hardcode Tokens

// ❌ BAD: Hardcoded token
client := client.New("https://api.garnet.ai", "eyJhbGciOiJIUzI1NiIs...")

// ✅ GOOD: Environment variable
token := os.Getenv("GARNET_USER_TOKEN")
client := client.New("https://api.garnet.ai", token)
2

Validate Token Claims

Verify the token’s exp, iss, and aud claims before trusting it.
3

Use HTTPS Only

Always use HTTPS endpoints to prevent token interception:
// ✅ GOOD: HTTPS
client := client.New("https://api.garnet.ai", token)

// ❌ BAD: HTTP (insecure)
client := client.New("http://api.garnet.ai", token)
4

Implement Token Refresh

Handle token expiration gracefully by implementing refresh logic or redirecting to login.
5

Secure Storage

Store tokens securely on the client side (e.g., httpOnly cookies, secure storage).

Error Handling

Common Authentication Errors

err := client.Get(ctx, &result, "/api/v1/agents")
if err != nil {
    if strings.Contains(err.Error(), "401") {
        // Token is invalid, expired, or missing
        return fmt.Errorf("authentication failed: %w", err)
    }
    if strings.Contains(err.Error(), "403") {
        // Token is valid but lacks required permissions
        return fmt.Errorf("permission denied: %w", err)
    }
}

Authorization Errors

The API returns resource-specific authorization errors:
  • UnauthorizedAgent: No access to agent resources
  • UnauthorizedIssue: No access to issue resources
  • UnauthorizedEvents: No access to event resources
  • UnauthorizedTokenAccess: No access to token resources
  • UnauthorizedNetworkPolicy: No access to network policy resources

Complete Example

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    
    "github.com/garnet-org/api/client"
    "github.com/garnet-org/api/types"
)

func main() {
    // Get token from environment
    token := os.Getenv("GARNET_USER_TOKEN")
    if token == "" {
        log.Fatal("GARNET_USER_TOKEN not set")
    }
    
    // Create authenticated client
    c := client.New("https://api.garnet.ai", token)
    
    // Get current user info
    var user types.CurrentUserInfo
    if err := c.Get(context.Background(), &user, "/api/v1/me"); err != nil {
        log.Fatalf("Failed to get user info: %v", err)
    }
    
    fmt.Printf("Authenticated as: %s\n", user.Email)
    fmt.Printf("Organization: %s\n", user.OrganizationName)
    fmt.Printf("Project: %s\n", user.ProjectName)
}

Next Steps

Agent Tokens

Learn about agent authentication

Project Tokens

Set up programmatic access

Build docs developers (and LLMs) love