Security & Authentication
Build secure authentication systems with password hashing, JWT tokens, role-based access control, and session management.
Password Security
Hashing Passwords
Always hash passwords before storing them:
.input('password', 'password', { required: true })
.hashPassword('$input.password', 'password_hash')
.dbAdd('"users"', {
email: '$input.email',
password: '$password_hash',
created_at: 'now'
}, 'new_user')
The hashPassword operation uses a secure one-way hashing algorithm (bcrypt) that cannot be reversed.
Verifying Passwords
Verify user-provided passwords against stored hashes:
.input('email', 'email', { required: true })
.input('password', 'password', { required: true })
.dbGet('"users"', { email: '$input.email' }, 'user')
.precondition('$user != null', 'Invalid credentials')
.verifyPassword('$input.password', '$user.password', 'is_valid')
.precondition('$is_valid == true', 'Invalid credentials')
The verifyPassword operation compares the plain text password against the stored hash securely.
JWT Tokens
Creating Tokens
Generate JWT tokens for authenticated sessions:
.createToken({
user_id: '$user.id',
email: '$user.email',
role: '$user.role'
}, 86400, 'auth_token')
Token parameters:
- Payload - Data to embed in the token (claims)
- Expiration - Time to live in seconds (86400 = 24 hours)
- Alias - Variable name to store the token
Token Expiration
Common expiration times:
.createToken({ user_id: '$user.id' }, 3600, 'token') // 1 hour
.createToken({ user_id: '$user.id' }, 86400, 'token') // 24 hours
.createToken({ user_id: '$user.id' }, 604800, 'token') // 7 days
.createToken({ user_id: '$user.id' }, 2592000, 'token') // 30 days
Requiring Authentication
Protect endpoints with authentication:
const protectedEndpoint = XanoScript.create('users/profile', 'GET')
.description('Get user profile')
.requiresAuth('users') // Requires valid JWT token
.dbGet('"users"', { id: '$auth.id' }, 'user')
.response({
user: '$user'
});
When authentication is required:
- Request must include a valid JWT token in the Authorization header
- Token data is available via
$authvariable $auth.idcontains the user ID from the token- Token expiration is automatically validated
Verifying Tokens
Manually verify tokens in your logic:
.input('token', 'text', { required: true })
.verifyToken('$input.token', 'token_data')
.conditional('$token_data.valid == true')
.then()
.var('user_id', 'int', '$token_data.user_id')
.response({ user_id: '$user_id', valid: true })
.else()
.return({ error: 'Invalid token' })
.endConditional()
Complete Authentication Flow
User Registration
Create a secure registration endpoint:
const register = XanoScript.create('auth/register', 'POST')
.description('Register new user')
.input('email', 'email', { required: true })
.input('password', 'password', { required: true })
.input('first_name', 'text', { required: true })
.input('last_name', 'text', { required: true })
// Check if email already exists
.dbGet('"users"', { email: '$input.email' }, 'existing')
.precondition('$existing == null', 'Email already registered')
// Hash password
.hashPassword('$input.password', 'password_hash')
// Create user
.dbAdd('"users"', {
email: '$input.email',
password: '$password_hash',
first_name: '$input.first_name',
last_name: '$input.last_name',
role: 'user',
created_at: 'now',
status: 'active'
}, 'new_user')
// Generate token
.createToken({
user_id: '$new_user.id',
email: '$new_user.email',
role: '$new_user.role'
}, 86400, 'auth_token')
.response({
user: {
id: '$new_user.id',
email: '$new_user.email',
first_name: '$new_user.first_name',
last_name: '$new_user.last_name'
},
token: '$auth_token'
});
User Login
Create a login endpoint:
const login = XanoScript.create('auth/login', 'POST')
.description('User login')
.input('email', 'email', { required: true })
.input('password', 'password', { required: true })
// Find user
.dbGet('"users"', { email: '$input.email' }, 'user')
.precondition('$user != null', 'Invalid credentials')
.precondition('$user.status == "active"', 'Account is inactive')
// Verify password
.verifyPassword('$input.password', '$user.password', 'is_valid')
.precondition('$is_valid == true', 'Invalid credentials')
// Update last login
.dbEdit('"users"',
{ id: '$user.id' },
{ last_login: 'now' },
'updated_user'
)
// Generate token
.createToken({
user_id: '$user.id',
email: '$user.email',
role: '$user.role'
}, 86400, 'auth_token')
.response({
user: {
id: '$user.id',
email: '$user.email',
first_name: '$user.first_name',
last_name: '$user.last_name',
role: '$user.role'
},
token: '$auth_token'
});
Get Current User
Retrieve authenticated user’s profile:
const me = XanoScript.create('auth/me', 'GET')
.description('Get current user profile')
.requiresAuth('users')
.dbGet('"users"', { id: '$auth.id' }, 'user')
.precondition('$user != null', 'User not found')
.response({
user: {
id: '$user.id',
email: '$user.email',
first_name: '$user.first_name',
last_name: '$user.last_name',
role: '$user.role',
created_at: '$user.created_at'
}
});
Logout
Invalidate user sessions:
const logout = XanoScript.create('auth/logout', 'POST')
.description('User logout')
.requiresAuth('users')
// Delete user sessions
.dbDelete('"sessions"', {
user_id: '$auth.id'
}, 'deleted_count')
.response({
message: 'Logged out successfully'
});
Role-Based Access Control
Checking User Roles
Implement role-based permissions:
const adminEndpoint = XanoScript.create('admin/users', 'GET')
.description('List all users (admin only)')
.requiresAuth('users')
.precondition('$auth.role == "admin"', 'Insufficient permissions')
.dbQuery('"users"', {
pagination: { page: 1, per_page: 50 },
sort: { field: 'created_at', direction: 'desc' }
})
.response({
users: '$results.items',
total: '$results.itemsTotal'
});
Multiple Role Check
Allow multiple roles to access an endpoint:
.requiresAuth('users')
.var('is_authorized', 'bool', false)
.conditional('$auth.role == "admin"')
.then()
.var('is_authorized', 'bool', true)
.elseIf('$auth.role == "moderator"')
.then()
.var('is_authorized', 'bool', true)
.endConditional()
.precondition('$is_authorized == true', 'Insufficient permissions')
Resource Ownership
Ensure users can only access their own resources:
.requiresAuth('users')
.input('order_id', 'int', { required: true })
.dbGet('"orders"', { id: '$input.order_id' }, 'order')
.precondition('$order != null', 'Order not found')
// Check ownership
.precondition(
'$order.user_id == $auth.id || $auth.role == "admin"',
'Insufficient permissions'
)
.response({
order: '$order'
})
Advanced Permissions
Complex Permission Logic
Implement granular permission checks:
const checkPermission = XanoScript.create('auth/check-permission', 'POST')
.description('Check user permissions for an action')
.requiresAuth('users')
.input('resource', 'text', { required: true })
.input('action', 'text', { required: true })
.var('has_permission', 'bool', false)
// Admin has all permissions
.conditional('$auth.role == "admin"')
.then()
.var('has_permission', 'bool', true)
// Moderators can read and update
.elseIf('$auth.role == "moderator"')
.then()
.conditional('$input.action == "read" || $input.action == "update"')
.then()
.var('has_permission', 'bool', true)
.endConditional()
// Users can only read
.elseIf('$auth.role == "user" && $input.action == "read"')
.then()
.var('has_permission', 'bool', true)
.endConditional()
.response({
granted: '$has_permission',
resource: '$input.resource',
action: '$input.action',
user_role: '$auth.role'
});
Permission-Based Content Filtering
Filter data based on user permissions:
.requiresAuth('users')
.conditional('$auth.role == "admin"')
.then()
// Admins see all records
.dbQuery('"documents"', {
pagination: { page: 1, per_page: 50 }
}, 'documents')
.else()
// Users see only their own records
.dbQuery('"documents"', {
filters: { user_id: '$auth.id' },
pagination: { page: 1, per_page: 50 }
}, 'documents')
.endConditional()
.response({
documents: '$documents.items',
total: '$documents.itemsTotal'
})
Security Best Practices
Password Strength Requirements
Validate password strength before hashing:
.input('password', 'password', { required: true })
.precondition(
'strlen($input.password) >= 8',
'Password must be at least 8 characters'
)
.hashPassword('$input.password', 'password_hash')
Email Verification
Track email verification status:
.dbAdd('"users"', {
email: '$input.email',
password: '$password_hash',
email_verified: false,
verification_token: '$verification_token',
created_at: 'now'
}, 'new_user')
Account Status
Implement account activation/deactivation:
.dbGet('"users"', { email: '$input.email' }, 'user')
.precondition('$user != null', 'Invalid credentials')
.precondition('$user.status == "active"', 'Account is inactive')
Rate Limiting
Track login attempts to prevent brute force:
.dbGet('"login_attempts"', {
email: '$input.email'
}, 'attempts')
.conditional('$attempts != null && $attempts.count >= 5')
.then()
.return({ error: 'Too many login attempts. Try again later.' })
.endConditional()
Token Refresh
Implement token refresh for long-lived sessions:
const refreshToken = XanoScript.create('auth/refresh', 'POST')
.description('Refresh authentication token')
.requiresAuth('users')
.createToken({
user_id: '$auth.id',
email: '$auth.email',
role: '$auth.role'
}, 86400, 'new_token')
.response({
token: '$new_token'
});
Secure Data Handling
Sanitizing Output
Never return sensitive fields:
.dbGet('"users"', { id: '$auth.id' }, 'user')
.response({
user: {
id: '$user.id',
email: '$user.email',
first_name: '$user.first_name',
last_name: '$user.last_name',
role: '$user.role'
// Password hash is NOT included
}
})
Input Validation
Always validate and sanitize inputs:
.input('email', 'email', { required: true })
.filter('$input.email', ['trim', 'lower'], 'clean_email')
.dbGet('"users"', { email: '$clean_email' }, 'user')
Environment Variables
Store secrets in environment variables:
.var('jwt_secret', 'text', 'env.JWT_SECRET')
.var('api_key', 'text', 'env.EXTERNAL_API_KEY')
Never hardcode secrets in your endpoints.
Next Steps
- Database Operations - Secure database access
- API Integration - Secure external API calls
- Recipes - Complete authentication examples
- API Reference - Full security method reference