Skip to main content

User Svc

The User Svc is the authentication and authorization backbone of 1Backend, providing comprehensive identity management, role-based access control, multi-tenant isolation, and service-to-service authentication. It manages users, tokens, organizations, permissions, and provides the foundation for secure, scalable applications.

This page provides comprehensive usage examples and advanced patterns. For API details, see User Svc API documentation.

Quick Start

Basic Authentication

# Register a new user
oo register alice
Enter password: [hidden]

# Login with existing credentials
oo login alice
Enter password: [hidden]

# Check current authentication status
oo whoami

# View authentication token
oo token

# Switch between users (if multiple logged in)
oo use bob

Service Account Setup

# Register a service account
oo register payment-svc
Enter password: [secure-service-password]

# Login as service for automation
oo login payment-svc secure-service-password

# Verify service authentication
oo whoami

CLI Reference

Authentication Commands

oo register - Create New Accounts

Usage:

oo register [slug] [password]
oo register [--contact-id email] [--contact-platform email]

Examples:

# Interactive registration (secure - no terminal history)
oo register alice
Enter password: [hidden]

# Service account registration
oo register payment-service
oo register order-processor
oo register notification-service

# User with contact information
oo register john-doe --contact-id john@company.com --contact-platform email

# Direct registration (automation only - avoid for security)
oo register test-user test-password

oo login - Authenticate

Usage:

oo login [slug] [password]

Examples:

# Interactive login (recommended)
oo login alice
Enter password: [hidden]

# Service authentication in CI/CD
oo login payment-svc $SERVICE_PASSWORD

# Quick development login (avoid in production)
oo login dev-user dev-password

oo whoami - Identity Information

Usage:

oo whoami [--all]

Examples:

# Current user information
oo whoami

# All logged in users
oo whoami --all

# Example output:
# id: usr_abc123
# slug: alice
# roles:
# - user-svc:user
# - user-svc:org:org_xyz789:admin
# - payment-svc:processor

oo use - Switch User Context

Examples:

# Switch to different authenticated user
oo use payment-svc
oo use alice
oo use admin-user

# Verify switch
oo whoami

oo token - Access Tokens

# Get current authentication token
oo token

# Use in API calls
curl -H "Authorization: Bearer $(oo token)" \
https://api.1backend.com/user-svc/self

User Management Commands

oo user list - Browse Users

Usage:

oo user list [--userId id] [--contactId email] [--limit count]

Examples:

# List all users (admin required)
oo user list

# Find specific user
oo user list --userId usr_abc123

# Find by email
oo user list --contactId alice@company.com

# Paginated results
oo user list --limit 50

Permission Management

oo permit save - Grant Permissions

Usage:

oo permit save <permit-file.yaml>
oo permit save <permits-directory>

Single Permit Example:

# api-access-permit.yaml
id: "payment-api-access"
permissionId: "payment-svc:process"
slugs:
- "order-service"
- "subscription-service"
roles:
- "payment-svc:processor"

Multiple Permits Example:

# service-permissions.yaml
- id: "chat-read-permit"
permissionId: "chat-svc:message:read"
slugs: ["frontend-app", "mobile-app"]

- id: "chat-write-permit"
permissionId: "chat-svc:message:create"
roles: ["chat-svc:user"]

- id: "admin-chat-permit"
permissionId: "chat-svc:admin"
roles: ["user-svc:admin"]

Apply Permits:

# Save single permit
oo permit save api-access-permit.yaml

# Save multiple permits
oo permit save service-permissions.yaml

# Save from directory
oo permit save permissions/production/

oo permit list - View Permissions

# List all permits (admin required)
oo permit list

# Example output:
# PERMIT ID PERMISSION SLUGS ROLES
# payment-api-access payment-svc:process order-service payment-svc:processor
# chat-read-permit chat-svc:message:read frontend-app

Role Enrollment

oo enroll save - Assign Roles

Usage:

oo enroll save <role> --userId <id>
oo enroll save <role> --contactId <email>
oo enroll save <enroll-file.yaml>

Direct Enrollment:

# Enroll user to admin role
oo enroll save user-svc:admin --userId usr_abc123

# Enroll by email (future user)
oo enroll save payment-svc:processor --contactId alice@company.com

# Organization role
oo enroll save user-svc:org:org_xyz789:admin --userId usr_def456

File-Based Enrollment:

# team-enrollments.yaml
- id: "admin-enrollment-1"
role: "user-svc:admin"
contactId: "admin@company.com"

- id: "dev-team-enrollment"
role: "dev-team:developer"
contactId: "developer@company.com"

- id: "payment-processor-enrollment"
role: "payment-svc:processor"
userId: "usr_abc123"
# Apply enrollments
oo enroll save team-enrollments.yaml

oo enroll list - View Enrollments

Usage:

oo enroll list [--role role] [--userId id] [--contactId email]

Examples:

# List all enrollments
oo enroll list

# Filter by role
oo enroll list --role user-svc:admin

# Filter by user
oo enroll list --userId usr_abc123

# Filter by contact
oo enroll list --contactId alice@company.com

Service-to-Service Authentication

Service Registration & Authentication

1. Service Account Setup

# Register service account
oo register payment-processor
Enter password: [secure-service-password]

# Login as service
oo login payment-processor
Enter password: [secure-service-password]

# Verify service identity
oo whoami
# Output:
# id: usr_svc_001
# slug: payment-processor
# roles:
# - user-svc:user

2. Permission Configuration

# payment-service-permissions.yaml
- id: "payment-database-access"
permissionId: "data-svc:read"
slugs:
- "payment-processor"

- id: "payment-write-access"
permissionId: "data-svc:write"
slugs:
- "payment-processor"

- id: "notification-send"
permissionId: "email-svc:send"
slugs:
- "payment-processor"

- id: "secret-access"
permissionId: "secret-svc:secret:list"
slugs:
- "payment-processor"
# Apply service permissions
oo permit save payment-service-permissions.yaml

3. Service Integration Code

Go Service Implementation:

// payment-service/main.go
package main

import (
"context"
"log"
"os"

"github.com/1backend/1backend/clients/go"
"github.com/1backend/1backend/sdk/go/client"
"github.com/1backend/1backend/sdk/go/boot"
)

func main() {
// Service authentication
clientFactory := client.NewApiClientFactory(os.Getenv("ONEBACKEND_URL"))

// Register or login service account
token, err := boot.RegisterServiceAccount(
clientFactory.Client().UserSvcAPI,
"payment-processor",
"Payment Processing Service",
credentialStore, // Your credential storage
)
if err != nil {
log.Fatal("Failed to authenticate service:", err)
}

// Use authenticated client for service calls
authenticatedClient := clientFactory.Client(client.WithToken(token.Token))

// Example: Access secrets
secrets, _, err := authenticatedClient.SecretSvcAPI.ListSecrets(context.Background()).
Body(openapi.SecretSvcListSecretsRequest{
Keys: []string{"STRIPE_SECRET_KEY", "DATABASE_URL"},
}).Execute()
if err != nil {
log.Fatal("Failed to load secrets:", err)
}

// Example: Check permissions
hasPermission, _, err := authenticatedClient.UserSvcAPI.HasPermission(context.Background(), "data-svc:write").Execute()
if err != nil || !hasPermission.Authorized {
log.Fatal("Service lacks required permissions")
}

// Start service with authenticated context
startPaymentProcessor(authenticatedClient)
}

Node.js Service Implementation:

// notification-service/index.js
const { UserSvcApi, SecretSvcApi, Configuration } = require('@1backend/client');

async function authenticateService() {
const config = new Configuration({
basePath: process.env.ONEBACKEND_URL,
});

const userApi = new UserSvcApi(config);

// Service login
const loginResponse = await userApi.login({
slug: 'notification-service',
password: process.env.SERVICE_PASSWORD,
device: 'server'
});

// Update config with token
config.accessToken = loginResponse.token.token;

return config;
}

async function loadServiceSecrets(config) {
const secretApi = new SecretSvcApi(config);

const secrets = await secretApi.listSecrets({
keys: ['SENDGRID_API_KEY', 'NOTIFICATION_TEMPLATES']
});

return secrets.secrets.reduce((acc, secret) => {
acc[secret.key] = secret.value;
return acc;
}, {});
}

async function main() {
try {
const config = await authenticateService();
const secrets = await loadServiceSecrets(config);

// Start notification service with authenticated context
startNotificationService(config, secrets);
} catch (error) {
console.error('Service authentication failed:', error);
process.exit(1);
}
}

main();

CI/CD Service Setup

GitHub Actions Service Authentication

# .github/workflows/deploy-payment-service.yml
name: Deploy Payment Service
on:
push:
branches: [main]
paths: ['payment-service/**']

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Setup 1Backend CLI
run: |
curl -L https://releases.1backend.com/oo-linux -o oo
chmod +x oo

- name: Configure Environment
run: |
./oo env add production ${{ secrets.ONEBACKEND_URL }}
./oo env use production

- name: Service Authentication
run: |
./oo login payment-processor ${{ secrets.SERVICE_PASSWORD }}

- name: Deploy Service Permissions
run: |
./oo permit save deploy/permissions/
./oo enroll save deploy/enrollments/

- name: Verify Service Setup
run: |
./oo whoami
./oo permit list

Docker Service Startup

# payment-service/Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

COPY . .

# Service authentication in container
ENV ONEBACKEND_URL=${ONEBACKEND_URL}
ENV SERVICE_PASSWORD=${SERVICE_PASSWORD}

CMD ["node", "src/index.js"]
# Docker Compose with service authentication
version: '3.8'
services:
payment-service:
build: ./payment-service
environment:
- ONEBACKEND_URL=${ONEBACKEND_URL}
- SERVICE_PASSWORD=${PAYMENT_SERVICE_PASSWORD}
restart: unless-stopped

notification-service:
build: ./notification-service
environment:
- ONEBACKEND_URL=${ONEBACKEND_URL}
- SERVICE_PASSWORD=${NOTIFICATION_SERVICE_PASSWORD}
restart: unless-stopped

Organization Management

Creating Organizations

# Create organization (via API or SDK)
# Organizations are typically created programmatically

Create Organization via SDK:

// Create organization
org, _, err := client.UserSvcAPI.SaveOrganization(context.Background()).
Body(openapi.UserSvcSaveOrganizationRequest{
Organization: &openapi.UserSvcOrganizationInput{
Name: openapi.PtrString("Acme Corporation"),
Slug: openapi.PtrString("acme-corp"),
},
}).Execute()

Organization Role Management

Administrative Roles

# organization-admin-enrollments.yaml
- id: "acme-corp-ceo"
role: "user-svc:org:org_acme123:admin"
contactId: "ceo@acme.com"

- id: "acme-corp-cto"
role: "user-svc:org:org_acme123:admin"
contactId: "cto@acme.com"

Member Roles

# organization-member-enrollments.yaml
- id: "acme-developer-1"
role: "user-svc:org:org_acme123:user"
contactId: "developer1@acme.com"

- id: "acme-developer-2"
role: "user-svc:org:org_acme123:user"
contactId: "developer2@acme.com"

- id: "acme-designer"
role: "user-svc:org:org_acme123:user"
contactId: "designer@acme.com"

Apply Organization Enrollments

# Setup organization structure
oo enroll save organization-admin-enrollments.yaml
oo enroll save organization-member-enrollments.yaml

# Verify organization setup
oo enroll list --role user-svc:org:org_acme123:admin
oo enroll list --role user-svc:org:org_acme123:user

Organization-Scoped Permissions

# organization-scoped-permissions.yaml
- id: "acme-project-management"
permissionId: "project-svc:manage"
roles:
- "user-svc:org:org_acme123:admin"

- id: "acme-project-view"
permissionId: "project-svc:view"
roles:
- "user-svc:org:org_acme123:user"
- "user-svc:org:org_acme123:admin"

- id: "acme-billing-access"
permissionId: "billing-svc:access"
roles:
- "user-svc:org:org_acme123:admin"

Advanced Permission Patterns

Hierarchical Role System

Service-Specific Roles

# payment-service-roles.yaml
- id: "payment-admin-permissions"
permissionId: "payment-svc:admin"
roles:
- "payment-svc:admin"

- id: "payment-processor-permissions"
permissionId: "payment-svc:process"
roles:
- "payment-svc:processor"
- "payment-svc:admin" # Admins can also process

- id: "payment-viewer-permissions"
permissionId: "payment-svc:view"
roles:
- "payment-svc:viewer"
- "payment-svc:processor" # Processors can view
- "payment-svc:admin" # Admins can view

Cross-Service Access

# cross-service-permissions.yaml
- id: "api-gateway-user-access"
permissionId: "user-svc:user:view"
slugs:
- "api-gateway"

- id: "frontend-chat-access"
permissionId: "chat-svc:message:create"
slugs:
- "frontend-app"
- "mobile-app"

- id: "admin-panel-access"
permissionId: "admin-svc:access"
roles:
- "user-svc:admin"
- "user-svc:org:*:admin" # Any org admin

Dynamic Role Assignment

Role Enrollment for New Users

# new-user-auto-enrollments.yaml
- id: "default-user-role"
role: "user-svc:user"
contactId: "*" # Auto-enroll all new users

- id: "developer-team-auto"
role: "dev-team:developer"
contactId: "*@company.com" # Auto-enroll company emails

- id: "premium-user-enrollment"
role: "app:premium"
contactId: "premium@company.com"

Conditional Role Assignment

# Enroll users based on conditions
oo enroll save dev-team:lead --contactId lead-dev@company.com
oo enroll save payment-svc:processor --userId usr_experienced_dev
oo enroll save user-svc:org:org_startup:admin --contactId founder@startup.com

Multi-Tenant Applications

App-Based Isolation

Environment Separation

# production-app-setup.yaml
app: "production.mycompany.com"
permits:
- id: "prod-api-access"
permissionId: "api-svc:access"
roles: ["api-svc:user"]

enrollments:
- id: "prod-admin"
role: "user-svc:admin"
contactId: "admin@mycompany.com"
app: "production.mycompany.com"
# staging-app-setup.yaml
app: "staging.mycompany.com"
permits:
- id: "staging-api-access"
permissionId: "api-svc:access"
roles: ["api-svc:user"]

enrollments:
- id: "staging-admin"
role: "user-svc:admin"
contactId: "dev@mycompany.com"
app: "staging.mycompany.com"

Client Separation

# client-a-setup.yaml
app: "client-a.com"
permits:
- id: "client-a-data-access"
permissionId: "data-svc:read"
roles: ["client-a:user"]

enrollments:
- id: "client-a-admin"
role: "client-a:admin"
contactId: "admin@client-a.com"
app: "client-a.com"
# client-b-setup.yaml
app: "client-b.com"
permits:
- id: "client-b-data-access"
permissionId: "data-svc:read"
roles: ["client-b:user"]

enrollments:
- id: "client-b-admin"
role: "client-b:admin"
contactId: "admin@client-b.com"
app: "client-b.com"

App Context Management

# Configure different app environments
oo env add client-a https://client-a.1backend.com
oo env add client-b https://client-b.1backend.com
oo env add production https://prod.1backend.com

# Switch between clients
oo env use client-a
oo login admin@client-a.com

oo env use client-b
oo login admin@client-b.com

# Verify app isolation
oo whoami # Shows different roles per app

Security Patterns

Token Management

Token Verification

// Verify JWT token in service
func verifyToken(tokenString string) (*auth.Claims, error) {
// Get public key from User Svc
publicKey, _, err := userClient.GetPublicKey(context.Background()).Execute()
if err != nil {
return nil, err
}

// Parse and verify token
authorizer := auth.AuthorizerImpl{}
claims, err := authorizer.ParseJWT(publicKey.PublicKey, tokenString)
if err != nil {
return nil, err
}

return claims, nil
}

Token Refresh Handling

// Handle token refresh in client
async function makeAuthenticatedRequest(url, options = {}) {
let token = localStorage.getItem('auth_token');

options.headers = {
...options.headers,
'Authorization': `Bearer ${token}`
};

let response = await fetch(url, options);

// Handle token expiration
if (response.status === 401) {
// Refresh token
const refreshResponse = await fetch('/user-svc/refresh-token', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` }
});

if (refreshResponse.ok) {
const { token: newToken } = await refreshResponse.json();
localStorage.setItem('auth_token', newToken);

// Retry original request
options.headers['Authorization'] = `Bearer ${newToken}`;
response = await fetch(url, options);
}
}

return response;
}

Permission Checking

Service Permission Validation

// Check permissions in service handler
func requirePermission(permission string) middleware.Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := extractTokenFromHeader(r)
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}

// Check permission with User Svc
hasPermResponse, _, err := userClient.HasPermission(
r.Context(),
permission,
).Execute()

if err != nil || !hasPermResponse.Authorized {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}

// Add user context to request
ctx := context.WithValue(r.Context(), "user", hasPermResponse.User)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}

// Usage in handlers
func setupRoutes() {
http.Handle("/api/payments",
requirePermission("payment-svc:process")(
http.HandlerFunc(handlePayments)
)
)

http.Handle("/api/admin",
requirePermission("payment-svc:admin")(
http.HandlerFunc(handleAdmin)
)
)
}

Frontend Permission Checks

// Permission-based UI rendering
class PermissionGuard extends React.Component {
constructor(props) {
super(props);
this.state = { hasPermission: false, loading: true };
}

async componentDidMount() {
try {
const response = await makeAuthenticatedRequest(
`/user-svc/self/has/${this.props.permission}`,
{ method: 'POST' }
);
const result = await response.json();
this.setState({
hasPermission: result.authorized,
loading: false
});
} catch (error) {
this.setState({ hasPermission: false, loading: false });
}
}

render() {
if (this.state.loading) {
return <div>Loading...</div>;
}

if (!this.state.hasPermission) {
return this.props.fallback || null;
}

return this.props.children;
}
}

// Usage
function AdminPanel() {
return (
<PermissionGuard
permission="admin-svc:access"
fallback={<div>Access Denied</div>}
>
<AdminDashboard />
</PermissionGuard>
);
}

Production Deployment Patterns

Service Bootstrap

Service Registration Script

#!/bin/bash
# scripts/bootstrap-services.sh

# Services to register
SERVICES=(
"api-gateway"
"user-service"
"payment-service"
"notification-service"
"data-service"
)

echo "Bootstrapping 1Backend services..."

for service in "${SERVICES[@]}"; do
echo "Setting up $service..."

# Generate secure password
PASSWORD=$(openssl rand -base64 32)

# Register service
oo register "$service" "$PASSWORD"

# Store credentials securely
oo secret save "service-credentials/$service" "$PASSWORD"

echo "$service registered successfully"
done

echo "Applying service permissions..."
oo permit save deploy/permissions/
oo enroll save deploy/enrollments/

echo "Bootstrap complete!"

Kubernetes Service Setup

# k8s/service-auth-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: service-auth-config
data:
onebackend-url: "https://api.company.com"
service-slug: "payment-processor"

---
# k8s/service-auth-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: service-auth-secret
type: Opaque
stringData:
service-password: "secure-service-password"

---
# k8s/payment-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
spec:
replicas: 3
selector:
matchLabels:
app: payment-service
template:
metadata:
labels:
app: payment-service
spec:
containers:
- name: payment-service
image: company/payment-service:latest
env:
- name: ONEBACKEND_URL
valueFrom:
configMapKeyRef:
name: service-auth-config
key: onebackend-url
- name: SERVICE_SLUG
valueFrom:
configMapKeyRef:
name: service-auth-config
key: service-slug
- name: SERVICE_PASSWORD
valueFrom:
secretKeyRef:
name: service-auth-secret
key: service-password

Load Balancing & High Availability

Service Node Registration

// Shared service account across multiple nodes
func main() {
// All nodes use same service account
token, err := boot.RegisterServiceAccount(
clientFactory.Client().UserSvcAPI,
"payment-processor", // Same slug for all nodes
"Payment Processing Service",
credentialStore,
)
if err != nil {
log.Fatal("Failed to authenticate service:", err)
}

// Register node-specific health check
nodeID := os.Getenv("NODE_ID")
registerHealthCheck(nodeID, token.Token)

// Start processing with shared authentication
startProcessor(token.Token)
}

Database Access Pattern

// Shared database access across service nodes
func connectToDatabase(serviceToken string) *sql.DB {
// Load database credentials using service authentication
secrets, _, err := secretClient.ListSecrets(context.Background()).
Body(openapi.SecretSvcListSecretsRequest{
Keys: []string{
"DATABASE_URL",
"DATABASE_PASSWORD",
"REDIS_URL",
},
}).Execute()
if err != nil {
log.Fatal("Failed to load database credentials:", err)
}

// All service nodes connect with same credentials
dbURL := findSecret(secrets.Secrets, "DATABASE_URL")
db, err := sql.Open("postgres", dbURL)
if err != nil {
log.Fatal("Database connection failed:", err)
}

return db
}

Troubleshooting

Common Authentication Issues

Invalid Token Errors

# Check token validity
oo token
oo whoami

# Refresh expired token
oo login alice
Enter password: [hidden]

# Verify token format
echo $(oo token) | base64 -d | jq .

# Test token with API
curl -H "Authorization: Bearer $(oo token)" \
https://api.1backend.com/user-svc/self

Permission Denied Issues

# Check user permissions
oo whoami

# List user's permits
oo permit list

# Check specific permission
curl -H "Authorization: Bearer $(oo token)" \
-X POST https://api.1backend.com/user-svc/self/has/target-permission

# Debug permission hierarchy
oo enroll list --userId $(oo whoami | grep id: | cut -d' ' -f2)

Service Authentication Failures

# Verify service registration
oo user list --contactId service-name

# Check service permissions
oo login service-name
oo permit list

# Test service-to-service call
curl -H "Authorization: Bearer $(oo token)" \
https://api.1backend.com/target-service/endpoint

Role and Organization Issues

Role Assignment Problems

# Check role enrollment
oo enroll list --role target-role

# Verify role ownership
oo whoami | grep roles:

# Check organization membership
oo enroll list --role "user-svc:org:*"

# Fix role assignment
oo enroll save correct-role --userId target-user-id

Organization Access Issues

# List organization enrollments
oo enroll list --role "user-svc:org:org_id:admin"
oo enroll list --role "user-svc:org:org_id:user"

# Check organization-scoped permissions
oo permit list | grep "org:org_id"

# Debug organization context
oo env current
oo whoami --all

Multi-Tenant Issues

App Context Problems

# Check current app context
oo env current

# Switch app environment
oo env use correct-environment

# Verify app-specific permissions
oo whoami
oo permit list

# Test cross-app isolation
oo env use app-a
oo whoami
oo env use app-b
oo whoami # Should show different permissions

Integration Examples

Frontend Authentication

React Authentication Hook

// hooks/useAuth.js
import { useState, useEffect, createContext, useContext } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
loadUser();
}, []);

async function loadUser() {
const token = localStorage.getItem('auth_token');
if (!token) {
setLoading(false);
return;
}

try {
const response = await fetch('/user-svc/self', {
headers: { 'Authorization': `Bearer ${token}` }
});

if (response.ok) {
const userData = await response.json();
setUser(userData);
} else {
localStorage.removeItem('auth_token');
}
} catch (error) {
console.error('Failed to load user:', error);
localStorage.removeItem('auth_token');
} finally {
setLoading(false);
}
}

async function login(slug, password) {
const response = await fetch('/user-svc/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ slug, password, device: 'web' })
});

if (response.ok) {
const { token } = await response.json();
localStorage.setItem('auth_token', token.token);
await loadUser();
return true;
}

return false;
}

function logout() {
localStorage.removeItem('auth_token');
setUser(null);
}

return (
<AuthContext.Provider value={{
user,
loading,
login,
logout,
isAuthenticated: !!user
}}>
{children}
</AuthContext.Provider>
);
}

export const useAuth = () => useContext(AuthContext);

Vue.js Authentication Store

// stores/auth.js
import { defineStore } from 'pinia'

export const useAuthStore = defineStore('auth', {
state: () => ({
user: null,
token: localStorage.getItem('auth_token'),
loading: false
}),

getters: {
isAuthenticated: (state) => !!state.user,
hasRole: (state) => (role) => {
return state.user?.roles?.includes(role) || false;
},
hasPermission: (state) => async (permission) => {
if (!state.token) return false;

try {
const response = await fetch(`/user-svc/self/has/${permission}`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${state.token}` }
});
const result = await response.json();
return result.authorized;
} catch {
return false;
}
}
},

actions: {
async login(slug, password) {
this.loading = true;

try {
const response = await fetch('/user-svc/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ slug, password, device: 'web' })
});

if (response.ok) {
const { token } = await response.json();
this.token = token.token;
localStorage.setItem('auth_token', token.token);
await this.loadUser();
return true;
}
} catch (error) {
console.error('Login failed:', error);
} finally {
this.loading = false;
}

return false;
},

async loadUser() {
if (!this.token) return;

try {
const response = await fetch('/user-svc/self', {
headers: { 'Authorization': `Bearer ${this.token}` }
});

if (response.ok) {
this.user = await response.json();
} else {
this.logout();
}
} catch (error) {
console.error('Failed to load user:', error);
this.logout();
}
},

logout() {
this.user = null;
this.token = null;
localStorage.removeItem('auth_token');
}
}
});

Mobile Authentication

Flutter Authentication Service

// lib/services/auth_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

class AuthService {
static const String baseUrl = 'https://api.company.com';
static const String tokenKey = 'auth_token';

Future<bool> login(String slug, String password) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/user-svc/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'slug': slug,
'password': password,
'device': 'mobile'
}),
);

if (response.statusCode == 200) {
final data = jsonDecode(response.body);
final token = data['token']['token'];

final prefs = await SharedPreferences.getInstance();
await prefs.setString(tokenKey, token);

return true;
}
} catch (e) {
print('Login error: $e');
}

return false;
}

Future<Map<String, dynamic>?> getCurrentUser() async {
final token = await getToken();
if (token == null) return null;

try {
final response = await http.post(
Uri.parse('$baseUrl/user-svc/self'),
headers: {'Authorization': 'Bearer $token'},
);

if (response.statusCode == 200) {
return jsonDecode(response.body);
}
} catch (e) {
print('Failed to get user: $e');
}

return null;
}

Future<bool> hasPermission(String permission) async {
final token = await getToken();
if (token == null) return false;

try {
final response = await http.post(
Uri.parse('$baseUrl/user-svc/self/has/$permission'),
headers: {'Authorization': 'Bearer $token'},
);

if (response.statusCode == 200) {
final data = jsonDecode(response.body);
return data['authorized'] ?? false;
}
} catch (e) {
print('Permission check error: $e');
}

return false;
}

Future<String?> getToken() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(tokenKey);
}

Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(tokenKey);
}
}

API Reference Summary

EndpointMethodDescription
/user-svc/registerPOSTCreate new user account
/user-svc/loginPOSTAuthenticate user/service
/user-svc/selfPOSTGet current user info
/user-svc/self/has/{permission}POSTCheck permission
/user-svc/usersPOSTList users (admin)
/user-svc/permitsPUTSave permission grants
/user-svc/permitsPOSTList permits
/user-svc/enrollsPUTAssign roles
/user-svc/enrollsPOSTList role enrollments
/user-svc/organizationsPOSTList organizations
/user-svc/organizationPUTCreate/update organization
/user-svc/refresh-tokenPOSTRefresh authentication token
/user-svc/public-keyGETGet JWT verification key