Authentication System

Overview

FaceKom uses a multi-layered authentication system. Operators authenticate via Passport.js strategies on vuer_oss. Customers authenticate via session-based auth on vuer_css. The esign subsystem has its own JWT + 2FA flow.

Authentication Topology

graph TB
    subgraph "Operator Auth (vuer_oss)"
        Local[Local<br/>username/password]
        SAML[SAML SSO]
        AD[Active Directory<br/>LDAP]
        FIDO2[FIDO2/WebAuthn<br/>hardware keys]
        TOTP_S[TOTP<br/>MFA]
        Token[UniqueTokenStrategy<br/>JWE tokens]
    end

    subgraph "Customer Auth (vuer_css)"
        Session[Session-based<br/>Redis + JWT]
        SocketAuth[Socket Auth<br/>JWT token verification]
        KioskAuth[Kiosk Auth<br/>encrypted JWT]
        InviteAuth[Invite Token<br/>hook-validated]
    end

    subgraph "eSign Auth (esign_css)"
        ESignJWT[JWT Session<br/>app_session_token cookie]
        TwoFA[2FA SMS<br/>username + password + SMS token]
    end

    Local --> Passport[Passport.js]
    SAML --> Passport
    AD --> Passport
    FIDO2 --> Passport
    Token --> Passport

Operator Authentication (vuer_oss)

File: /workspace/vuer_oss/server/auth.js

Passport Strategies

StrategyMethodUse Case
LocalUsername + passwordDefault operator login
SAMLSSO tokensEnterprise single sign-on
Active DirectoryLDAP bindCorporate directory auth
FIDO2/WebAuthnHardware keysStrong MFA (stored in WebAuthnCredential model)
TOTPTime-based OTPSoftware MFA (stored in TotpKey model)
UniqueTokenStrategyJWE tokensAPI/programmatic access

ACL System

The operator auth system is ACL-based:

flowchart TD
    Request --> isAuthorized{isAuthorized?}
    isAuthorized -->|No| Reject[403 Forbidden]
    isAuthorized -->|Yes| hasRight{hasRight?}
    hasRight -->|No| Reject
    hasRight -->|Yes| hasRoom{hasRoom?<br/>if needed}
    hasRoom -->|No| Reject
    hasRoom -->|Yes| Execute[Execute Handler]

    isAuthorized -->|Check| UserModel[client.user instanceof User<br/>+ isEnabled]
    hasRight -->|Check| ACL[System ACL + Session ACL]
  • isAuthorized(): Verifies client.user is a User model instance and isEnabled
  • hasRight(client, right): Checks both system-level and session-level ACL rules
  • Guard functions: doIfAuthorized(), doIfHasRight(), doIfHasRoom() wrap socket event handlers
  • User reload: Every authorized call reloads the user from DB (client.user.reload()) for fresh state

UniqueTokenStrategy

File: /workspace/vuer_oss/server/util/UniqueTokenStrategy.js

Custom Passport strategy for token-based authentication:

  • Extracts tokens from: body, query, params, cookies, or headers
  • Supports JWE encryption with configurable key IDs
  • Case-insensitive by default (configurable)

Customer Authentication (vuer_css)

File: /workspace/vuer_css/server/auth.js

Customer auth is session-based with Redis storage:

CheckWhat It Verifies
isAuthorized()client.sessionData.customerId exists
hasRoom()client.sessionData.roomData.id exists
hasSelfServiceRoom()client.sessionData.selfServiceRoomData.id exists

Guard functions: doIfAuthorized(), doIfHasRoom(), doIfHasAnyRoom(), doIfHasSelfServiceRoom()

Socket Authentication

File: /workspace/vuer_css/server/socket/events/auth.js

sequenceDiagram
    participant Client as Browser
    participant CSS as vuer_css Socket
    participant Redis as Redis Session Store

    Client->>CSS: auth:auth(token)
    CSS->>CSS: socketTokenStorage.verifyToken(token)
    CSS->>Redis: Load session by sessionId
    Redis-->>CSS: Session data
    CSS->>CSS: Strip cookie from session data
    CSS->>CSS: Check disaster mode
    CSS-->>Client: auth:ok (or auth:fail)
  • Short-lived JWT tokens (2-minute expiry) for Socket.IO auth
  • Cookie stripped from session data before attaching to socket client
  • Disaster mode check prevents auth during incident scenarios

Kiosk Authentication

File: /workspace/vuer_oss/server/queue/rpc_server/JwtKioskAuth.js

  1. Receives encrypted JWT token from kiosk device
  2. Decrypts with facekomPont key
  3. Verifies with jwt.secret
  4. Extracts deviceId and location

Token Logging

JwtKioskAuth.js line 20 logs the raw JWT token on auth failure. This could leak tokens to log files.

JWT Token Flow

Self-Service V2 Registration

  1. Server creates JWT: jwt.sign({ customerId }, config.jwt.secret, { expiresIn: config.jwt.expiryPeriod })
  2. JWT is encrypted via tokenService.encrypt() (JWE layer)
  3. Encrypted token sent to customer
  4. Customer presents encrypted token for authentication

Invite Token Authentication

  • Validated via hook system (validateInviteToken hook)
  • Extensible per deployment

eSign Authentication

Customer 2FA Flow

sequenceDiagram
    participant Cust as Customer
    participant ECSS as esign_css
    participant EOSS as esign_oss

    Cust->>ECSS: Visit homepage
    ECSS->>ECSS: registerBrowser()
    ECSS->>ECSS: Create JWT session (app_session_token cookie)

    Cust->>ECSS: View offer at /offer-sign
    Cust->>ECSS: POST /api/signature/m1 (public key)
    ECSS->>EOSS: RPC: create Signature record
    EOSS->>Cust: Send 2FA SMS

    Cust->>ECSS: POST /api/customer/2f-auth (username + password + SMS)
    ECSS->>EOSS: RPC: verify 2FA
    EOSS-->>ECSS: Auth result

    Cust->>ECSS: POST /api/signature/m2 (signed data)
    ECSS->>EOSS: RPC: verify signature
    EOSS->>EOSS: Validate cryptographically
    EOSS-->>ECSS: Signature valid -> offer signed

Security Assessment

Positive Findings

Security Strengths

  • Multi-factor auth support (TOTP + WebAuthn/FIDO2)
  • JWT tokens encrypted before transmission (JWE layer)
  • Session-level ACL on top of role-based ACL
  • User state reloaded from DB on every authorized call
  • Short-lived socket tokens (2-minute expiry)
  • Redis server-side session storage
  • Disaster mode for incident response

Concerns

Security Concerns

  • config.jwt.secret accessed directly (not config.get()) in SelfServiceV2.js:50 — inconsistent config access
  • JwtKioskAuth logs raw JWT token on auth failure (line 20) — could leak tokens to logs
  • Passwords travel in plaintext over RabbitMQ RPC (esign_css esign_oss)
  • Single jwt.secret for all socket tokens — compromise allows forging all socket auth tokens
ModelServicePurpose
Uservuer_ossOperator users with roles/ACL
Sessionvuer_ossconnect-session-sequelize storage
PasswordHistoryvuer_ossPassword rotation enforcement
WebAuthnCredentialvuer_ossFIDO2/WebAuthn hardware key credentials
TotpKeyvuer_ossTOTP two-factor auth secret keys
OneTimeLoginvuer_ossOne-time login tokens
Customervuer_ossEnd customers (auth via session)
VerificationTokenesign_oss2FA verification tokens