| name | user-authentication-system |
| description | Role-based access control for Greek accounting firms. Login, role hierarchy, per-client permissions, session management, audit logging. |
| version | 1.0.0 |
| author | openclaw-greek-accounting |
| homepage | https://github.com/satoshistackalotto/openclaw-greek-accounting |
| tags | ["greek","accounting","authentication","rbac","security"] |
| metadata | {"openclaw":{"requires":{"bins":"[Truncated]","env":"[Truncated]"},"notes":"Manages local user accounts and role-based access. Credentials are stored as salted SHA-256 hashes in OPENCLAW_DATA_DIR/auth/. 2FA uses SHA-256 TOTP. No external auth services — fully local.","path_prefix":"/data/ in examples refers to $OPENCLAW_DATA_DIR (default: /data/)"}} |
User Authentication System
This skill provides a complete authentication and authorization system for Greek accounting firm operations through OpenClaw. It manages user identities, role-based permissions, per-client access controls, and session security for multi-user accounting environments.
Setup
export OPENCLAW_DATA_DIR="/data"
which jq openssl || sudo apt install jq openssl
mkdir -p $OPENCLAW_DATA_DIR/auth
chmod 700 $OPENCLAW_DATA_DIR/auth
No external auth services. User credentials are stored as salted SHA-256 hashes locally. 2FA uses SHA-256 TOTP generated by openssl.
Core Philosophy
- Role-Based Access: Hierarchical permissions matching real accounting firm structures
- Per-Client Authorization: Granular control over which users access which client data
- Session Security: Secure session management with timeout and device tracking
- Audit Integration: Every authentication and authorization event logged
- OpenClaw Artifact Ready: File-based auth suitable for OpenClaw deployment
OpenClaw Commands
User Management
openclaw auth user-create --username "maria.g" --role assistant --full-name "Maria Georgiou" --email "maria@firm.gr"
openclaw auth user-update --username "maria.g" --role accountant --effective-date 2026-03-01
openclaw auth user-deactivate --username "maria.g" --reason "resignation" --revoke-sessions
openclaw auth user-list --active --role assistant --format table
openclaw auth password-reset --username "maria.g" --send-reset-link
openclaw auth password-policy --min-length 12 --require-special --max-age-days 90
Role & Permission Management
openclaw auth role-list --include-permissions
openclaw auth role-create --name "tax_specialist" --base-role accountant --add-permissions "tax_filing,tax_optimization"
openclaw auth assign-clients --username "maria.g" --clients EL123456789,EL987654321
openclaw auth assign-clients --username "maria.g" --all-clients
openclaw auth check-access --username "maria.g" --client EL123456789 --action "view_financials"
openclaw auth access-matrix --all-users --all-clients --format xlsx
Security & Audit
openclaw auth security-log --last-24h --include-failures
openclaw auth failed-logins --threshold 3 --lockout-duration 30m
openclaw auth audit-report --user "maria.g" --period last-30-days
openclaw auth audit-report --client EL123456789 --who-accessed --period last-week
openclaw auth 2fa-enable --username "maria.g" --method totp
openclaw auth sessions-list --active --format table
openclaw auth session-revoke --username "maria.g" --all-devices
File System Architecture
Auth_File_Structure:
user_data:
- /data/auth/users/{username}/profile.json
- /data/auth/users/{username}/credentials.json
- /data/auth/users/{username}/permissions.json
- /data/auth/users/{username}/sessions/
- /data/auth/users/{username}/2fa/
role_definitions:
- /data/auth/roles/senior_accountant.json
- /data/auth/roles/accountant.json
- /data/auth/roles/assistant.json
- /data/auth/roles/viewer.json
- /data/auth/roles/custom/
access_control:
- /data/auth/access/client_assignments.json
- /data/auth/access/policies.json
- /data/auth/access/ip_whitelist.json
security_logs:
- /data/auth/logs/logins/
- /data/auth/logs/access/
- /data/auth/logs/admin/
- /data/auth/logs/security/
Role Hierarchy & Permissions
Role Definitions
Roles:
senior_accountant:
description: "Senior accountant - full system access"
level: 4
inherits: "accountant"
permissions:
- all_client_access
- user_management
- role_assignment
- system_configuration
- data_export_all
- compliance_override
- audit_log_access
- gdpr_operations
- billing_management
- skill_configuration
client_access: "all"
accountant:
description: "Accountant - broad access to assigned clients"
level: 3
inherits: "assistant"
permissions:
- client_data_full_access
- tax_filing_submit
- tax_optimization
- compliance_management
- financial_reporting
- efka_submissions
-
Permission Matrix
Permission_Matrix:
view_dashboard: "viewer"
configure_dashboard: "accountant"
view_client_profile: "viewer"
edit_client_profile: "assistant"
create_client: "accountant"
delete_client: "senior_accountant"
export_client_data: "accountant"
gdpr_operations: "senior_accountant"
view_documents: "viewer"
upload_documents: "assistant"
process_documents: "assistant"
delete_documents: "accountant"
view_financials: "viewer"
enter_financial_data: "assistant"
modify_financial_records: "accountant"
submit_tax_filings: "accountant"
view_compliance_status: "viewer"
manage_compliance: "accountant"
override_compliance: "senior_accountant"
view_employee_data: "viewer"
manage_employees: "accountant"
submit_efka: "accountant"
view_transactions: "viewer"
reconcile_transactions: "assistant"
configure_banking: "accountant"
Authentication Engine
Core Authentication
class AuthenticationEngine:
"""Handles user authentication, sessions, and credential management."""
def __init__(self):
self.session_timeout = 30 * 60
self.idle_timeout = 15 * 60
self.max_failed_attempts = 5
self.lockout_duration = 30 * 60
def authenticate(self, username, password, device_info=None):
"""Authenticate user and create session."""
if self.is_account_locked(username):
self.log_auth_event(username, 'login_blocked', 'account_locked')
return {'success': False, 'error': 'Account is locked. Contact administrator.'}
user = self.load_user(username)
if not user:
self.log_auth_event(username, 'login_failed', 'user_not_found')
return {'success': False, 'error': 'Invalid credentials'}
.verify_password(password, user[]):
.record_failed_attempt(username)
.log_auth_event(username, , )
{: , : }
user[] != :
.log_auth_event(username, , )
{: , : }
user.get(, ):
{: , : ,
: .create_pending_session(username)}
session = .create_session(username, device_info)
.clear_failed_attempts(username)
.log_auth_event(username, , device_info=device_info)
{: , : session, : .get_user_summary(username)}
():
raw_token = generate_secure_token()
token_hash = hash_session_token(raw_token)
session = {
: token_hash,
: username,
: current_timestamp(),
: current_timestamp() + .session_timeout,
: current_timestamp(),
: device_info,
: get_request_ip(),
: .get_user_role(username),
: .get_user_client_access(username)
}
session_path =
write_json(session_path, session)
session[] = raw_token
session
():
session = .find_session(session_id)
session:
{: , : }
current_timestamp() > session[]:
.destroy_session(session_id)
{: , : }
current_timestamp() - session[] > .idle_timeout:
.destroy_session(session_id)
{: , : }
session[] = current_timestamp()
.update_session(session)
{: , : session}
():
bcrypt_hash(password, rounds=)
():
errors = []
(password) < :
errors.append()
(c.isupper() c password):
errors.append()
(c.islower() c password):
errors.append()
(c.isdigit() c password):
errors.append()
(c c password):
errors.append()
{: (errors) == , : errors}
Authorization Engine
class AuthorizationEngine:
"""Handles permission checks and access control decisions."""
def __init__(self):
self.roles = self.load_roles()
self.access_matrix = self.load_access_matrix()
def check_permission(self, session, action, client_vat=None):
"""Check if user has permission to perform an action."""
username = session['username']
user_role = session['role']
required_role = self.access_matrix.get(action)
if not required_role:
self.log_authorization(username, action, client_vat, 'denied', 'unknown_action')
return {'allowed': False, 'reason': f'Unknown action: {action}'}
user_level = self.roles[user_role]['level']
required_level = self.roles[required_role]['level']
if user_level < required_level:
self.log_authorization(username, action, client_vat, 'denied', 'insufficient_role')
return {'allowed': False,
'reason': f'Requires {required_role} role (you have )'}
client_vat:
client_access = session.get(, [])
client_access client_vat client_access:
.log_authorization(username, action, client_vat, , )
{: ,
: }
.log_authorization(username, action, client_vat, )
{: }
():
role = .roles.get(role_name, {})
permissions = (role.get(, []))
parent = role.get()
parent:
permissions.update(.resolve_permissions(parent))
permissions
():
assignments = read_json()
user_entry = assignments.get(username, {})
user_entry.get(, ):
{: , : }
{: , : user_entry.get(, [])}
User Management
class UserManager:
"""Manages user lifecycle operations."""
def __init__(self):
self.auth_engine = AuthenticationEngine()
self.authz_engine = AuthorizationEngine()
def create_user(self, admin_session, user_data):
"""Create a new user account."""
perm_check = self.authz_engine.check_permission(admin_session, 'manage_users')
if not perm_check['allowed']:
return {'success': False, 'error': perm_check['reason']}
username = user_data['username']
if self.user_exists(username):
return {'success': False, 'error': f'Username {username} already exists'}
if user_data['role'] not in self.authz_engine.roles:
return {'success': False, 'error': f'Invalid role: {user_data["role"]}'}
profile = {
: username,
: user_data[],
: user_data.get(),
: user_data[],
: ,
: current_timestamp(),
: admin_session[],
: ,
:
}
temp_password = generate_secure_password()
credentials = {
: .auth_engine.hash_password(temp_password),
: current_timestamp(),
:
}
user_dir =
create_directory(user_dir)
create_directory()
create_directory()
write_json(, profile)
write_json(, credentials)
write_json(, {: user_data[], : []})
.log_admin_action(admin_session[], , {
: username, : user_data[]
})
{
: ,
: username,
: temp_password,
:
}
():
perm_check = .authz_engine.check_permission(admin_session, )
perm_check[]:
{: , : perm_check[]}
assignments_path =
assignments = read_json(assignments_path) file_exists(assignments_path) {}
client_vats == :
assignments[username] = {: , : []}
:
current = assignments.get(username, {: , : []})
current[] = ((current.get(, []) + client_vats))
assignments[username] = current
write_json(assignments_path, assignments)
.log_admin_action(admin_session[], , {
: username, : client_vats
})
{: , : username, : assignments[username]}
Security Features
File System Permissions (Production Hardening)
The /data/auth/ directory contains sensitive credential and session data. In production, OS-level file permissions must be hardened:
chmod 700 /data/auth/
chown -R openclaw:openclaw /data/auth/
chmod 600 /data/auth/users/*/credentials.json
chmod 600 /data/auth/users/*/sessions/*.json
chmod 644 /data/auth/roles/*.json
chmod 600 /data/auth/logs/**/*.json
Note: The OpenClaw agent and all skills share the same file system context. Without OS-level permission restrictions, any skill could read credential hashes. These permissions ensure that only the authentication skill's process can access sensitive auth data.
Account Lockout
Lockout_Policy:
max_failed_attempts: 5
lockout_duration: "30 minutes"
lockout_escalation:
- "5 failures: 30 minute lockout"
- "10 failures: 2 hour lockout"
- "15 failures: account disabled, admin notification"
notification: "email admin on 3+ consecutive failures"
Two-Factor Authentication
2FA_Configuration:
methods: ["totp"]
mandatory_for: ["senior_accountant"]
optional_for: ["accountant", "assistant"]
recovery_codes: 10
totp_settings:
algorithm: "SHA256"
digits: 6
period: 30
Password Policy
Password_Policy:
min_length: 12
require_uppercase: true
require_lowercase: true
require_digit: true
require_special: true
max_age_days: 90
history_count: 5
common_password_check: true
IP & Device Security
Security_Controls:
ip_whitelist:
enabled: true
allowed_ranges: ["office_ip_range"]
action_on_violation: "block_and_alert"
device_tracking:
track_devices: true
alert_new_device: true
max_concurrent_sessions: 3
session_controls:
absolute_timeout: "8 hours"
idle_timeout: "15 minutes"
single_session_per_device: true
Integration with Other Skills
Skill_Integration:
dashboard_greek_accounting:
provides: ["user_session", "role_permissions", "accessible_clients"]
enforces: "dashboard view permissions per user role"
client_data_management:
provides: ["access_decisions", "user_identity"]
enforces: "per-client data access based on user assignments"
integration: "authorization check before every data operation"
greek_compliance_aade:
enforces: "only accountant+ can submit filings"
efka_api_integration:
enforces: "only accountant+ can submit EFKA data"
all_skills:
provides: "user context for audit trail entries"
enforces: "role-based action restrictions across all operations"
Audit & Compliance Reporting
Audit Log Structure
Audit_Events:
authentication:
- login_success
- login_failed
- login_blocked
- logout
- session_expired
- session_revoked
- 2fa_success
- 2fa_failed
- password_changed
- password_reset
authorization:
- access_granted
- access_denied
- client_access_checked
- permission_checked
administration:
- user_created
- user_updated
- user_deactivated
- role_changed
- clients_assigned
- clients_revoked
- policy_changed
Audit_Entry_Format:
timestamp: "ISO 8601 with timezone"
event_type: "category.action"
username: "acting user"
target:
Success Metrics
A successful authentication system deployment should achieve:
- ✅ Secure Authentication: bcrypt password hashing, optional 2FA, session management
- ✅ Role Hierarchy: Four-level role system matching accounting firm structures
- ✅ Per-Client Access: Granular client data access assignment per user
- ✅ Session Security: Timeout, idle detection, concurrent session limits
- ✅ Complete Audit Trail: Every auth/authz event logged with context
- ✅ Account Protection: Lockout policy, password requirements, brute-force prevention
- ✅ Cross-Skill Enforcement: Authorization integrated into all data operations
- ✅ Admin Tools: User management, access matrix, security reporting
- ✅ GDPR Compatible: Access controls support data protection requirements
- ✅ Scalable: Handle 50+ users across 500+ clients
Remember: The authentication system is the security foundation for the entire Greek accounting platform. Every data access must pass through authorization checks, and every action must leave an audit trail.