Перейти к содержанию

Keycloak Auth

Current auth architecture and Keycloak integration status.

Status (March 2026)

Phases 3–4 of Keycloak migration are deferred indefinitely. ShivaVPN continues using custom JWT auth with a Keycloak bridge enabled. The bridge is ON — all clients receive Keycloak tokens, but via the legacy V1 endpoint with auto-fallback.


Current Architecture

Four systems coexist:

Client → POST /api/auth/login (header: AuthToken)
AuthServiceImpl.loginV2()
  ├─ IF keycloakBridgeEnabled (= true in prod):
  │   ├─ Try mintUserV2ViaKeycloak()     ← Keycloak path
  │   │   ├─ KeycloakUserService.authenticateUserOrCreate()
  │   │   ├─ DeviceUnificationService.migrateRedisDevicesToKeycloak()
  │   │   ├─ KeycloakDeviceService.getOrCreateDeviceClient()
  │   │   └─ KeycloakDeviceService.authenticateDevice() → KC JWT
  │   │
  │   └─ CATCH → mintUserV2()             ← Legacy fallback
  │       └─ JwtUtils.generateAccessToken() → custom HMAC JWT
  └─ ELSE: mintUserV2()                   ← Legacy only
System File Status
Custom JWT (JwtUtils.java, 809 lines) security/JwtUtils.java @Deprecated
Redis Sessions (RedisTokenService.java, 1598 lines) security/RedisTokenService.java Partial legacy
Keycloak (KeycloakDeviceService + KeycloakUserService) security/keycloak/ TARGET
Migration Bridge (DeviceUnificationService) security/DeviceUnificationService.java @Deprecated

Keycloak Architecture

Realm: shiva-users

  • Users = VPN accounts (username = 16-digit account number)
  • Password = DEFAULT_PASSWORD (same for all — KC used for identity, not authentication)
  • Attributes: account_id (UUID), primary_device_id

Realm: shiva-devices

  • Clients = device sessions
  • ClientId format: dev_{accountId}.{sha256(deviceId)}
  • Auth: client_credentials grant
  • Tokens: access (30 min) + refresh (90 days via offline_access scope)
  • JWKS endpoint: http://10.99.87.249:8180/realms/shiva-devices/protocol/openid-connect/certs

Admin clients:

Client Realm Purpose
shiva-backend shiva-users User CRUD
shiva-device-manager shiva-devices Device CRUD

Client Auth Summary

Client Endpoint Header Notes
Android /api/auth/login AuthToken V1, gets KC token via bridge
iOS /api/auth/login authToken V1, gets KC token via bridge
Admin /auth/login/admin authToken V1, separate admin flow
Desktop RPC (daemon) N/A No direct HTTP auth

All clients receive Keycloak tokens because bridge is ON — but they still use the legacy V1 endpoints.


Migration Phases

Phase Status Description
Phase 1: Metrics Done auth.login{path=keycloak} / auth.login{path=legacy} counters
Phase 2: Deprecation Done @Deprecated on JwtUtils, AuthTokenFilter, DeviceUnificationService
Phase 3: Remove Fallback Deferred Disable legacy JWT fallback (auth.legacy.fallback.enabled=false)
Phase 4: Client Migration + Cleanup Deferred Move clients to V2 endpoints, delete ~3300 lines

Monitoring Auth Traffic

# Login distribution (Grafana/Prometheus)
rate(auth_login_total{path="keycloak"}[5m])
rate(auth_login_total{path="legacy"}[5m])

# Refresh distribution
rate(auth_refresh_total{path="keycloak"}[5m])
rate(auth_refresh_total{path="legacy"}[5m])

# Legacy ratio (should trend toward 0 as migration progresses)
auth_login_total{path="legacy"} / auth_login_total

Config

Relevant application-prod.properties settings:

# Bridge is ON
auth.keycloak.bridge.enabled=true

# Migration speed (currently conservative)
keycloak.migration.auto.batch-size=3
keycloak.migration.auto.max-users-per-run=5
keycloak.migration.auto.fixed-delay-ms=1800000  # 30 min

# To disable legacy fallback (Phase 3, not yet enabled):
# auth.legacy.fallback.enabled=false

What Will Be Deleted After Migration (Phase 4)

File Lines Reason
JwtUtils.java 809 Custom JWT → Keycloak JWT
AuthTokenFilter.java ~150 Legacy JWT filter → OAuth2 Resource Server
DeviceUnificationServiceImpl.java 265 Redis↔KC bridge → all devices in KC
KeycloakMigrationServiceImpl.java 337 One-time migration → done
AuthServiceImpl.java (simplify) -958 Remove fallback paths
RedisTokenService.java (simplify) -798 Remove token storage (keep locks/cache)
Total ~3300

Keycloak Admin Access

Keycloak admin console runs on 10.99.87.249:8180. Access via ssh-internal tunnel:

./scripts/ssh-internal.sh 10.99.87.249
# Then access http://10.99.87.249:8180/admin from browser via SSH port forward