Skip to content

Authentication Examples

Last Updated: 2025-11-23

Complete examples for user authentication workflows including registration, login, token management, and logout.


Table of Contents


User Registration

Create a new user account and organization.

REST API

Endpoint: POST /api/v1/auth/register

curl Example

curl -X POST http://localhost:8080/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "username": "johndoe",
    "email": "john@example.com",
    "password": "SecureP@ssw0rd123",
    "organization_name": "ACME Corporation"
  }'

JavaScript/TypeScript Example

const response = await fetch('http://localhost:8080/api/v1/auth/register', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    username: 'johndoe',
    email: 'john@example.com',
    password: 'SecureP@ssw0rd123',
    organization_name: 'ACME Corporation'
  })
});

const data = await response.json();
console.log('Access Token:', data.access_token);
console.log('Refresh Token:', data.refresh_token);

Python Example

import requests

response = requests.post(
    'http://localhost:8080/api/v1/auth/register',
    json={
        'username': 'johndoe',
        'email': 'john@example.com',
        'password': 'SecureP@ssw0rd123',
        'organization_name': 'ACME Corporation'
    }
)

data = response.json()
access_token = data['access_token']
refresh_token = data['refresh_token']
print(f'Access Token: {access_token}')

Go Example

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

type RegisterRequest struct {
    Username         string `json:"username"`
    Email            string `json:"email"`
    Password         string `json:"password"`
    OrganizationName string `json:"organization_name"`
}

type RegisterResponse struct {
    User struct {
        ID             string `json:"id"`
        Username       string `json:"username"`
        Email          string `json:"email"`
        OrganizationID string `json:"organization_id"`
    } `json:"user"`
    AccessToken  string `json:"access_token"`
    RefreshToken string `json:"refresh_token"`
    ExpiresIn    int    `json:"expires_in"`
}

func main() {
    reqData := RegisterRequest{
        Username:         "johndoe",
        Email:            "john@example.com",
        Password:         "SecureP@ssw0rd123",
        OrganizationName: "ACME Corporation",
    }

    jsonData, _ := json.Marshal(reqData)
    resp, err := http.Post(
        "http://localhost:8080/api/v1/auth/register",
        "application/json",
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    var result RegisterResponse
    json.Unmarshal(body, &result)

    fmt.Printf("Access Token: %s\n", result.AccessToken)
    fmt.Printf("User ID: %s\n", result.User.ID)
}

Response

{
  "user": {
    "id": "user-123",
    "username": "johndoe",
    "email": "john@example.com",
    "organization_id": "org-456"
  },
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
  "expires_in": 900
}

User Login

Authenticate existing user and receive JWT tokens.

REST API

Endpoint: POST /api/v1/auth/login

curl Example

curl -X POST http://localhost:8080/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "johndoe",
    "password": "SecureP@ssw0rd123"
  }'

JavaScript/TypeScript Example

const response = await fetch('http://localhost:8080/api/v1/auth/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    username: 'johndoe',
    password: 'SecureP@ssw0rd123'
  })
});

const data = await response.json();

// Store tokens securely (not in localStorage!)
sessionStorage.setItem('access_token', data.access_token);
sessionStorage.setItem('refresh_token', data.refresh_token);

Python Example

import requests

response = requests.post(
    'http://localhost:8080/api/v1/auth/login',
    json={
        'username': 'johndoe',
        'password': 'SecureP@ssw0rd123'
    }
)

data = response.json()
access_token = data['access_token']
refresh_token = data['refresh_token']

# Use tokens for subsequent requests

Go Example

type LoginRequest struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

type LoginResponse struct {
    AccessToken  string `json:"access_token"`
    RefreshToken string `json:"refresh_token"`
    ExpiresIn    int    `json:"expires_in"`
}

func login(username, password string) (*LoginResponse, error) {
    reqData := LoginRequest{
        Username: username,
        Password: password,
    }

    jsonData, _ := json.Marshal(reqData)
    resp, err := http.Post(
        "http://localhost:8080/api/v1/auth/login",
        "application/json",
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var result LoginResponse
    json.NewDecoder(resp.Body).Decode(&result)
    return &result, nil
}

Response

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
  "expires_in": 900
}

Using Access Tokens

Include the access token in API requests.

REST API

Authorization Header: Authorization: Bearer <access_token>

curl Example

# List organizations
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  http://localhost:8080/api/v1/organizations

JavaScript/TypeScript Example

const accessToken = sessionStorage.getItem('access_token');

const response = await fetch('http://localhost:8080/api/v1/organizations', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});

const organizations = await response.json();

Python Example

import requests

access_token = "eyJhbGciOiJSUzI1NiIs..."

response = requests.get(
    'http://localhost:8080/api/v1/organizations',
    headers={'Authorization': f'Bearer {access_token}'}
)

organizations = response.json()

Go Example

func listOrganizations(accessToken string) ([]Organization, error) {
    req, _ := http.NewRequest("GET",
        "http://localhost:8080/api/v1/organizations",
        nil)

    req.Header.Set("Authorization", "Bearer "+accessToken)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var orgs []Organization
    json.NewDecoder(resp.Body).Decode(&orgs)
    return orgs, nil
}

gRPC API

gRPC Metadata: Include token in request metadata

Go Example (ConnectRPC)

import (
    "connectrpc.com/connect"
    organizationv1 "github.com/j-pye/ocmonica/gen/ocmonica/v1"
    "github.com/j-pye/ocmonica/gen/ocmonica/v1/ocmonicav1connect"
)

func main() {
    client := ocmonicav1connect.NewOrganizationServiceClient(
        http.DefaultClient,
        "http://localhost:8080",
    )

    req := connect.NewRequest(&organizationv1.ListOrganizationsRequest{})

    // Add authorization header
    req.Header().Set("Authorization", "Bearer "+accessToken)

    resp, err := client.ListOrganizations(context.Background(), req)
    if err != nil {
        log.Fatal(err)
    }

    for _, org := range resp.Msg.Organizations {
        fmt.Printf("Organization: %s (%s)\n", org.Name, org.Id)
    }
}

Refreshing Tokens

Obtain new access token when the current one expires.

REST API

Endpoint: POST /api/v1/auth/refresh

curl Example

curl -X POST http://localhost:8080/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "eyJhbGciOiJSUzI1NiIs..."
  }'

JavaScript/TypeScript Example

async function refreshAccessToken() {
  const refreshToken = sessionStorage.getItem('refresh_token');

  const response = await fetch('http://localhost:8080/api/v1/auth/refresh', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      refresh_token: refreshToken
    })
  });

  if (response.ok) {
    const data = await response.json();

    // Update tokens (refresh tokens are rotated)
    sessionStorage.setItem('access_token', data.access_token);
    sessionStorage.setItem('refresh_token', data.refresh_token);

    return data.access_token;
  } else {
    // Refresh token invalid or expired - redirect to login
    window.location.href = '/login';
    return null;
  }
}

// Automatic token refresh before expiry
setInterval(() => {
  refreshAccessToken();
}, 14 * 60 * 1000); // Refresh every 14 minutes (tokens expire in 15)

Python Example

import requests

def refresh_token(refresh_token):
    response = requests.post(
        'http://localhost:8080/api/v1/auth/refresh',
        json={'refresh_token': refresh_token}
    )

    if response.ok:
        data = response.json()
        return data['access_token'], data['refresh_token']
    else:
        # Handle refresh failure - user needs to login again
        raise Exception('Refresh token invalid or expired')

# Usage
new_access_token, new_refresh_token = refresh_token(current_refresh_token)

Go Example

type RefreshRequest struct {
    RefreshToken string `json:"refresh_token"`
}

type RefreshResponse struct {
    AccessToken  string `json:"access_token"`
    RefreshToken string `json:"refresh_token"`
    ExpiresIn    int    `json:"expires_in"`
}

func refreshAccessToken(refreshToken string) (*RefreshResponse, error) {
    reqData := RefreshRequest{RefreshToken: refreshToken}
    jsonData, _ := json.Marshal(reqData)

    resp, err := http.Post(
        "http://localhost:8080/api/v1/auth/refresh",
        "application/json",
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("refresh failed: %s", resp.Status)
    }

    var result RefreshResponse
    json.NewDecoder(resp.Body).Decode(&result)
    return &result, nil
}

Response

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
  "expires_in": 900
}

Note: Refresh tokens are single-use and automatically rotated. Always store the new refresh token received.


API Key Authentication

Use API keys for service-to-service authentication (alternative to JWT).

REST API

Authorization Header: X-API-Key: <api_key>

curl Example

curl -H "X-API-Key: ${API_KEY}" \
  http://localhost:8080/api/v1/organizations

JavaScript/TypeScript Example

const apiKey = process.env.OCMONICA_API_KEY;

const response = await fetch('http://localhost:8080/api/v1/organizations', {
  headers: {
    'X-API-Key': apiKey
  }
});

const organizations = await response.json();

Python Example

import os
import requests

api_key = os.getenv('OCMONICA_API_KEY')

response = requests.get(
    'http://localhost:8080/api/v1/organizations',
    headers={'X-API-Key': api_key}
)

organizations = response.json()

Go Example

func listOrgsWithAPIKey(apiKey string) ([]Organization, error) {
    req, _ := http.NewRequest("GET",
        "http://localhost:8080/api/v1/organizations",
        nil)

    req.Header.Set("X-API-Key", apiKey)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var orgs []Organization
    json.NewDecoder(resp.Body).Decode(&orgs)
    return orgs, nil
}

Note: API keys are long-lived and should be rotated periodically. Store them securely (environment variables, secrets manager).


Logout

Invalidate refresh token and clear client-side tokens.

REST API

Endpoint: POST /api/v1/auth/logout

curl Example

curl -X POST http://localhost:8080/api/v1/auth/logout \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

JavaScript/TypeScript Example

async function logout() {
  const accessToken = sessionStorage.getItem('access_token');

  await fetch('http://localhost:8080/api/v1/auth/logout', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`
    }
  });

  // Clear tokens from client
  sessionStorage.removeItem('access_token');
  sessionStorage.removeItem('refresh_token');

  // Redirect to login page
  window.location.href = '/login';
}

Python Example

import requests

def logout(access_token):
    response = requests.post(
        'http://localhost:8080/api/v1/auth/logout',
        headers={'Authorization': f'Bearer {access_token}'}
    )

    # Clear tokens
    # (Implementation depends on your storage mechanism)
    return response.ok

logout(access_token)

Go Example

func logout(accessToken string) error {
    req, _ := http.NewRequest("POST",
        "http://localhost:8080/api/v1/auth/logout",
        nil)

    req.Header.Set("Authorization", "Bearer "+accessToken)

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return fmt.Errorf("logout failed: %s", resp.Status)
    }

    return nil
}

Change Password

Update user password (revokes all existing tokens).

REST API

Endpoint: POST /auth/change-password

curl Example

curl -X POST http://localhost:8080/auth/change-password \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "old_password": "OldP@ssw0rd123",
    "new_password": "NewSecureP@ssw0rd456"
  }'

JavaScript/TypeScript Example

async function changePassword(oldPassword: string, newPassword: string) {
  const accessToken = sessionStorage.getItem('access_token');

  const response = await fetch('http://localhost:8080/auth/change-password', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      old_password: oldPassword,
      new_password: newPassword
    })
  });

  if (response.ok) {
    // All tokens are revoked - user must login again
    sessionStorage.clear();
    window.location.href = '/login';
  } else {
    const error = await response.json();
    throw new Error(error.message);
  }
}

Python Example

import requests

def change_password(access_token, old_password, new_password):
    response = requests.post(
        'http://localhost:8080/auth/change-password',
        headers={'Authorization': f'Bearer {access_token}'},
        json={
            'old_password': old_password,
            'new_password': new_password
        }
    )

    if response.ok:
        # All tokens revoked - need to login again
        return True
    else:
        raise Exception(response.json().get('message', 'Password change failed'))

Go Example

type ChangePasswordRequest struct {
    OldPassword string `json:"old_password"`
    NewPassword string `json:"new_password"`
}

func changePassword(accessToken, oldPassword, newPassword string) error {
    reqData := ChangePasswordRequest{
        OldPassword: oldPassword,
        NewPassword: newPassword,
    }

    jsonData, _ := json.Marshal(reqData)
    req, _ := http.NewRequest("POST",
        "http://localhost:8080/auth/change-password",
        bytes.NewBuffer(jsonData))

    req.Header.Set("Authorization", "Bearer "+accessToken)
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return fmt.Errorf("password change failed: %s", resp.Status)
    }

    return nil
}

Important: After password change, all refresh tokens are revoked. User must login again to obtain new tokens.


Error Handling

Common Error Responses

Invalid Credentials (401 Unauthorized):

{
  "error": "invalid credentials"
}

Token Expired (401 Unauthorized):

{
  "error": "token expired"
}

Rate Limit Exceeded (429 Too Many Requests):

{
  "error": "rate limit exceeded"
}

Response Headers:

X-RateLimit-Limit: 5
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1699900000

Error Handling Example

async function loginWithErrorHandling(username: string, password: string) {
  try {
    const response = await fetch('http://localhost:8080/api/v1/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password })
    });

    if (!response.ok) {
      if (response.status === 401) {
        throw new Error('Invalid username or password');
      } else if (response.status === 429) {
        const resetTime = response.headers.get('X-RateLimit-Reset');
        throw new Error(`Rate limit exceeded. Try again at ${new Date(parseInt(resetTime) * 1000)}`);
      } else {
        throw new Error('Login failed');
      }
    }

    return await response.json();
  } catch (error) {
    console.error('Login error:', error);
    throw error;
  }
}

See Also