This document describes the endpoint-level authorization system for Stellarbill-backend. All endpoints enforce authentication and authorization based on user roles and route requirements.
Tokens are signed with HS256 using the JWT_SECRET environment variable.
Claims Structure:
{
"user_id": "string",
"email": "string",
"role": "string",
"roles": ["string"],
"merchant_id": "string",
"exp": "timestamp",
"iat": "timestamp",
"nbf": "timestamp"
}- All protected endpoints require a valid JWT token in the
Authorizationheader - Format:
Authorization: Bearer <token> - Token signature must be valid (HS256 with configured JWT_SECRET)
- Token must not be expired
- Token must contain required claims (
user_id)
| Status | Error | Cause |
|---|---|---|
| 401 | missing authorization header |
No Authorization header provided |
| 401 | invalid authorization header format |
Header doesn't follow "Bearer " format |
| 401 | invalid or expired token |
Token signature invalid or expired |
| 401 | invalid token claims: missing user_id |
Token missing required user_id claim |
Three roles are defined:
- admin: Full access to all endpoints
- merchant: Access to merchant-specific endpoints
- customer: Limited access to customer-facing endpoints
| Method | Path | Public | Required Roles | Description |
|---|---|---|---|---|
| GET | /api/health |
Yes | - | Service health check |
| GET | /api/plans |
No | - (any authenticated) | List plans |
| GET | /api/subscriptions |
No | admin, merchant | List subscriptions |
| GET | /api/subscriptions/:id |
No | admin, merchant | Get subscription by ID |
| Status | Error | Cause |
|---|---|---|
| 403 | insufficient permissions |
User's roles don't match required roles |
Protected routes use the following middleware chain:
- corsMiddleware() - Handles CORS headers (global)
- AuthMiddleware - Validates JWT token (route group)
- AuthzMiddleware - Checks required roles (individual routes)
AuthMiddleware
authenticated := api.Group("")
authenticated.Use(auth.AuthMiddleware(cfg.JWTSecret))Validates JWT signature and required claims.
AuthzMiddleware
authenticated.GET("/subscriptions",
auth.AuthzMiddleware(auth.RoleAdmin, auth.RoleMerchant),
handlers.ListSubscriptions)Checks if user has any of the specified roles. If no roles are specified, any authenticated user is allowed.
Comprehensive endpoint tests verify:
-
Authentication Tests
- Missing token (401)
- Malformed header (401)
- Expired token (401)
- Invalid signature (401)
- Token without required claims (401)
- Valid token (200)
-
Authorization Tests
- Insufficient permissions (403)
- Authorized role access (200)
- Unauthorized role access (403)
-
Edge Cases
- Malformed JWT structure
- Token without user_id claim
- Token without roles
- Token with wrong signing algorithm
# Run all tests
go test ./...
# Run authorization tests with verbose output
go test -v ./internal/handlers/authorization_test.go -test.v
# Run specific test
go test -run TestListSubscriptionsAuthorization ./...Each endpoint is tested with:
- No token
- Expired token
- Invalid signature
- Valid admin token
- Valid merchant token
- Valid customer token (where applicable)
- Token without required claims
- JWT tokens should be stored securely (HttpOnly cookies or secure storage)
- Never expose tokens in logs or error messages
- Always use HTTPS in production
user_idclaim is required for all tokens- Role claim should be validated per endpoint
- Expired tokens are automatically rejected
Future implementations should add:
- Rate limiting per user
- Token refresh mechanisms
- API key authentication for machine-to-machine communication
- API Key Authentication - For service-to-service calls
- Role-Based Resource Filtering - Filter subscriptions by merchant_id
- Fine-Grained Permissions - More granular than role-based (create, read, update, delete)
- Token Refresh - Implement refresh token flow
- Audit Logging - Log all authentication/authorization events
- Multi-Tenancy - Proper merchant isolation using merchant_id
With valid token:
curl -H "Authorization: Bearer <token>" \
https://api.stellarbill.io/api/subscriptionsResponse (200 OK):
{
"subscriptions": [...]
}Invalid/missing token:
curl https://api.stellarbill.io/api/subscriptionsResponse (401 Unauthorized):
{
"error": "missing authorization header"
}Insufficient permissions:
# Customer token trying to access merchant-only endpoint
curl -H "Authorization: Bearer <customer_token>" \
https://api.stellarbill.io/api/subscriptionsResponse (403 Forbidden):
{
"error": "insufficient permissions"
}For questions about authorization or authentication, see the inline code documentation or contact the backend team.