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
- User Login
- Using Access Tokens
- Refreshing Tokens
- API Key Authentication
- Logout
- Change Password
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¶
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¶
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):
Token Expired (401 Unauthorized):
Rate Limit Exceeded (429 Too Many Requests):
Response Headers:
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¶
- Authentication Guide
- REST API Reference
- gRPC API Reference
- See
SECURITY.mdin the project root for security best practices