Files
EchoHub/docs/flows/authentication.md
T

3.5 KiB

Authentication

User Registration

A new user creates an account on a server. The client sends credentials via REST, the server hashes the password, issues JWT tokens, and the client stores the refresh token for "Remember Me" sessions.

sequenceDiagram
    participant UI as ConnectDialog
    participant AO as AppOrchestrator
    participant CM as ConnectionManager
    participant API as ApiClient
    participant Auth as AuthController
    participant US as UserService
    participant JWT as JwtTokenService
    participant DB as SQLite

    UI->>AO: ConnectDialogResult(IsRegister: true)
    AO->>CM: ConnectAsync(dialogResult)
    CM->>API: RegisterAsync(username, password)
    API->>Auth: POST /api/auth/register
    Auth->>US: RegisterUserAsync(username, password, displayName)
    US->>US: Validate (regex, length, uniqueness)
    US->>DB: INSERT User (BCrypt hash)
    US-->>Auth: UserOperationResult.Success
    Auth->>JWT: GenerateAccessToken(user)
    JWT-->>Auth: (token, expiresAt) [15 min]
    Auth->>JWT: GenerateRefreshToken()
    JWT-->>Auth: Base64 random (64 bytes)
    Auth->>DB: INSERT RefreshToken (SHA256 hash)
    Auth-->>API: LoginResponse
    API->>API: SetTokens() — store in memory + set Bearer header
    API-->>CM: LoginResponse
    CM->>CM: Wire OnTokensRefreshed for config persistence
    CM->>CM: Continue to connection setup (see Connection Flow)

User Login

Returning user authenticates with username/password or a saved refresh token.

sequenceDiagram
    participant UI as ConnectDialog
    participant CM as ConnectionManager
    participant API as ApiClient
    participant Auth as AuthController
    participant US as UserService
    participant DB as SQLite

    alt Saved refresh token (Remember Me)
        UI->>CM: ConnectDialogResult(SavedRefreshToken: "...")
        CM->>API: LoginWithRefreshTokenAsync()
        API->>Auth: POST /api/auth/refresh
        Auth->>DB: Lookup token by SHA256 hash
        Auth->>DB: Revoke old token, issue new pair
        Auth-->>API: LoginResponse (rotated tokens)
    else Username + Password
        UI->>CM: ConnectDialogResult(IsRegister: false)
        CM->>API: LoginAsync(username, password)
        API->>Auth: POST /api/auth/login
        Auth->>US: AuthenticateUserAsync(username, password)
        US->>DB: Fetch user, BCrypt.Verify(password, hash)
        US->>DB: Update LastSeenAt
        US-->>Auth: UserOperationResult.Success
        Auth-->>API: LoginResponse
    end
    API->>API: SetTokens()

Token Refresh

Access tokens expire after 15 minutes. The client auto-refreshes transparently before requests and on 401 responses. Refresh tokens are rotated on each use.

sequenceDiagram
    participant SR as SignalR / HTTP Request
    participant API as ApiClient
    participant Auth as AuthController
    participant DB as SQLite
    participant Config as config.json

    SR->>API: GetValidTokenAsync() or HTTP 401
    API->>API: Token expires within 60s?
    alt Proactive refresh (SignalR token provider)
        API->>Auth: POST /api/auth/refresh (old refresh token)
    else Reactive refresh (HTTP 401 retry)
        API->>Auth: POST /api/auth/refresh (old refresh token)
    end
    Auth->>DB: Lookup by SHA256 hash
    Auth->>DB: Revoke old refresh token
    Auth->>DB: INSERT new RefreshToken
    Auth-->>API: LoginResponse (new token pair)
    API->>API: SetTokens() — update Bearer header
    API-->>API: Fire OnTokensRefreshed event
    API-->>Config: Persist new refresh token (if Remember Me)
    API->>SR: Retry original request with new token