Mercurial
view mrjunejune/auth_api.h @ 273:e02e2036ef84 default tip
add Layer 2 JRPG component system
Add reusable content and window modals, an isolated component sandbox, shared cyberpunk scroll areas, production-safe cache freshness, and server-rendered JRPG panel state.
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sat, 08 Aug 2026 02:08:08 -0700 |
| parents | 04fee26ecce0 |
| children |
line wrap: on
line source
#ifndef MRJUNEJUNE_AUTH_API_H #define MRJUNEJUNE_AUTH_API_H #include "dowa/dowa.h" #include "auth/auth_store.h" #include "seobeo/seobeo.h" /* Cookie names */ #define AUTH_API_SESSION_COOKIE_NAME "mjj_session" #define AUTH_API_GUEST_COOKIE_NAME "mjj_guest" /* Default TTLs (seconds) */ #define AUTH_API_SESSION_IDLE_TTL_DEFAULT (7 * 24 * 3600) #define AUTH_API_SESSION_ABS_TTL_DEFAULT (30 * 24 * 3600) #define AUTH_API_GUEST_TTL_DEFAULT (30 * 24 * 3600) /* Auth-only paths permitted during forced-password-change */ #define AUTH_API_PATH_SESSION "/api/auth/session" #define AUTH_API_PATH_LOGIN "/api/auth/login" #define AUTH_API_PATH_LOGOUT "/api/auth/logout" #define AUTH_API_PATH_PASSWORD "/api/auth/password" #define AUTH_API_PATH_PASSWORD_PAGE "/account/password" typedef enum { AUTH_PRINCIPAL_GUEST = 0, AUTH_PRINCIPAL_USER = 1, } Auth_Principal_Kind; /* * Resolved identity for a single request. * For users: user_id, username, role, must_change_password are valid. * For guests: guest_id is valid. * csrf_token: a derived CSRF token safe to return to the client (never stored * raw; only its digest appears in the store). * _token_digest: internal session binding for CSRF derivation; not for logging. */ typedef struct { Auth_Principal_Kind kind; /* --- user fields --- */ char user_id[37]; char username[AUTH_STORE_USERNAME_MAX + 1]; char role[8]; boolean must_change_password; /* --- guest fields --- */ char guest_id[37]; /* --- common --- */ char csrf_token[AUTH_CRYPTO_TOKEN_SIZE]; /* base64url, return to client */ /* internal: session token digest (user) or guest_id (guest) used as CSRF binding */ char _binding[AUTH_CRYPTO_TOKEN_DIGEST_SIZE]; } Auth_Principal; /* * Optional callback that provides guest quota JSON for the session endpoint. * Registered by conversation_api on init; called from auth_session_handler. * json_out: buffer of json_capacity bytes; write null-terminated JSON or "null". * Returns TRUE on success; on FALSE the session response uses "null". */ typedef boolean (*Auth_API_Guest_Quota_Cb)( const char *guest_id, int64 current_unix, char *json_out, size_t json_capacity); void Auth_API_Register_Guest_Quota_Cb(Auth_API_Guest_Quota_Cb cb); /* * Hook called after a successful login to initiate guest-resource transfer. * Called with the logged-out guest_id and the newly authenticated user_id. * Must not call any Auth_API function; executes on the request thread. * * Returns TRUE on success. On FALSE the login handler revokes the new * session and returns 500; the guest cookie is preserved. * The hook must be idempotent: it may be called more than once for the * same (guest_id, user_id) pair during retries. */ typedef boolean (*Auth_Guest_Transfer_Hook)( const char *guest_id, const char *user_id, void *context); /* * Initialise the auth module. * * cookie_secret must be at least AUTH_CRYPTO_COOKIE_SECRET_MIN_BYTES. * bootstrap_username / bootstrap_password_hash: create bootstrap admin on * first startup only when no admin exists; pass NULL to skip. * trusted_proxy_ip exact direct peer IP that may forward X-Real-IP; NULL * to disable proxy trust. * dev_insecure_cookie TRUE allows non-Secure cookies; only valid on loopback. * * Returns FALSE and fails closed if cookie_secret is missing/too short. */ boolean Auth_API_Init( const char *database_path, const uint8 *cookie_secret, size_t cookie_secret_length, const char *bootstrap_username, const char *bootstrap_password_hash, const char *trusted_proxy_ip, int64 session_idle_ttl_secs, int64 session_absolute_ttl_secs, int64 guest_ttl_secs, boolean dev_insecure_cookie); void Auth_API_Destroy(void); void Auth_API_Register_Routes(void); /* * Register a hook for guest-to-user resource transfer on login. * Only one hook is supported; a second call replaces the previous one. */ void Auth_API_Register_Guest_Transfer_Hook( Auth_Guest_Transfer_Hook hook, void *context); /* * Resolve the caller's identity from request cookies. * Creates a guest identity if no valid session or guest cookie is found. * new_guest_cookie_out: if non-NULL and non-empty on return, the caller * should include a Set-Cookie header with this value in the response. * Returns TRUE on success; FALSE only on internal error (treat as 500). */ boolean Auth_API_Resolve_Principal( Seobeo_Request_Entry *p_request, Auth_Principal *p_principal, Dowa_Arena *p_arena, char *new_guest_cookie_out, size_t new_guest_cookie_capacity); /* * Resolve identity from request cookies WITHOUT creating a new guest. * Returns TRUE on success (no internal error): * - If an existing user session or valid guest cookie is found, * p_principal is filled and *p_found is set to TRUE. * - If no valid session/guest is found, *p_found is set to FALSE; * the caller must return HTTP 401. * Returns FALSE on internal error (treat as 500). * Never writes a guest identity row or generates a Set-Cookie directive. */ boolean Auth_API_Resolve_Existing_Principal( Seobeo_Request_Entry *p_request, Auth_Principal *p_principal, Dowa_Arena *p_arena, boolean *p_found); /* * Returns TRUE if the path is permitted for forced-password-change sessions * (i.e., the principal should NOT be blocked at this path). * Conversation and admin code gate their routes with: * if (principal.must_change_password && * !Auth_API_Is_Forced_Password_Change_Only(path)) { return 403; } */ boolean Auth_API_Is_Forced_Password_Change_Only(const char *http_path); /* * Verify same-origin AND CSRF for state-changing routes. * * Enforces: * 1. The Origin header matches the Host header (same-origin). * 2. The X-CSRF-Token request header is present and matches the token * derived from the principal's session binding. * * Use this as the single centralized CSRF gate. Do not duplicate the * origin-check or CSRF-derivation logic in other modules. * * Returns TRUE on success; the caller MUST return HTTP 403 on FALSE. */ boolean Auth_API_Verify_CSRF( Seobeo_Request_Entry *p_request, const Auth_Principal *p_principal); /* * Returns the initialized auth store pointer. * Valid only after Auth_API_Init returns TRUE; NULL before that. * Admin API uses this to issue store operations directly. */ Auth_Store *Auth_API_Get_Store(void); #ifdef AUTH_API_TEST_HOOKS typedef void (*Auth_API_Test_Login_Pre_Create_Hook)(void *p_context); void Auth_API_Test_Set_Login_Pre_Create_Hook( Auth_API_Test_Login_Pre_Create_Hook hook, void *p_context); /* * Direct handler entry-points for in-process testing. * Only available when AUTH_API_TEST_HOOKS is defined (test builds). */ Seobeo_Request_Entry *Auth_API_Test_Session_Handler( Seobeo_Request_Entry *p_req, Dowa_Arena *p_arena); Seobeo_Request_Entry *Auth_API_Test_Login_Handler( Seobeo_Request_Entry *p_req, Dowa_Arena *p_arena); Seobeo_Request_Entry *Auth_API_Test_Logout_Handler( Seobeo_Request_Entry *p_req, Dowa_Arena *p_arena); Seobeo_Request_Entry *Auth_API_Test_Password_Handler( Seobeo_Request_Entry *p_req, Dowa_Arena *p_arena); #endif /* AUTH_API_TEST_HOOKS */ #endif /* MRJUNEJUNE_AUTH_API_H */