--- title: Authentication API description: User registration, login, tokens, email verification, sessions icon: i-carbon-password --- # Authentication API User authentication, registration, API token management, and email verification. --- ## Registration ### Register New User **Pattern:** `POST /auth/register` **Authentication:** Public (no auth required) **Query Parameters:** - `invitation_token` (optional): Required if server is in invitation-only mode **Request Body:** ```json { "username": "alice", "email": "alice@example.com", "password": "secure_password_123" } ``` **Response:** ```json { "success": true, "message": "User created successfully", "email_verified": true } ``` **With Email Verification Enabled:** ```json { "success": true, "message": "User created. Please check your email to verify your account.", "email_verified": false } ``` **Validation:** - Username: Not reserved, not already taken, no normalized conflicts - Email: Valid format, not already registered - Password: Minimum length (enforced by server config) **Reserved Usernames:** `models`, `datasets`, `spaces`, `admin`, `api`, `organizations`, `settings`, `new`, `login`, `register`, `logout`, `docs`, and more (see validation.py) **Status Codes:** - `200 OK` - User created - `400 Bad Request` - Username/email taken, reserved name, or validation error - `403 Forbidden` - Invitation required but not provided --- ## Login & Sessions ### Login **Pattern:** `POST /auth/login` **Authentication:** Public **Request Body:** ```json { "username": "alice", "password": "secure_password_123" } ``` **Response:** ```json { "success": true, "message": "Logged in successfully", "username": "alice", "session_secret": "random_secret_for_client_side_encryption" } ``` **Response Headers:** ``` Set-Cookie: session_id=abc123...; HttpOnly; SameSite=Lax; Max-Age=2592000 ``` **Session Duration:** 30 days (default, configurable) **Checks:** - Username and password match - Account is active (`is_active = true`) - Email verified (if `require_email_verification = true`) **Status Codes:** - `200 OK` - Logged in successfully - `401 Unauthorized` - Invalid credentials - `403 Forbidden` - Account disabled or email not verified --- ### Logout **Pattern:** `POST /auth/logout` **Authentication:** Required (session cookie or token) **Response:** ```json { "success": true, "message": "Logged out successfully" } ``` **Behavior:** - Deletes all user sessions from database - Clears session_id cookie --- ### Get Current User **Pattern:** `GET /auth/me` **Authentication:** Required **Response:** ```json { "id": 1, "username": "alice", "email": "alice@example.com", "email_verified": true, "created_at": "2025-01-01T00:00:00Z" } ``` **Use Case:** Check if still logged in, get basic user info --- ## API Tokens ### List Tokens **Pattern:** `GET /auth/tokens` **Authentication:** Required (session cookie or existing token) **Response:** ```json { "tokens": [ { "id": 1, "name": "My API Token", "last_used": "2025-01-20T10:15:32Z", "created_at": "2025-01-01T00:00:00Z" }, { "id": 2, "name": "CI/CD Token", "last_used": null, "created_at": "2025-01-15T08:00:00Z" } ] } ``` **Notes:** - Token values are never shown after creation - `last_used` updates when token is used - Tokens don't expire by default --- ### Create Token **Pattern:** `POST /auth/tokens/create` **Authentication:** Required **Request Body:** ```json { "name": "My API Token" } ``` **Response:** ```json { "success": true, "token": "hf_abc123def456ghi789jkl...", "token_id": 3, "session_secret": "encryption_secret", "message": "Token created. Save it securely - you won't see it again!" } ``` **Important:** - Token value shown **only once** - Copy immediately and store securely - Cannot retrieve token value later - Prefix: `hf_` (HuggingFace compatible) --- ### Revoke Token **Pattern:** `DELETE /auth/tokens/{token_id}` **Authentication:** Required (must own token) **Response:** ```json { "success": true, "message": "Token revoked successfully" } ``` **Status Codes:** - `200 OK` - Token revoked - `404 Not Found` - Token not found or not owned by user --- ## Email Verification ### Verify Email **Pattern:** `GET /auth/verify-email?token={verification_token}` **Authentication:** Public **Purpose:** Verify email address after registration **Response:** - Redirects to user profile on success: `302 -> /{username}` - Redirects to login with error on failure: `302 -> /?error=invalid_token` **Behavior:** 1. Validates verification token 2. Checks token not expired (24 hours default) 3. Marks user email as verified 4. Deletes verification token 5. Creates session and auto-logs in user **Cookie Set:** ``` Set-Cookie: session_id=abc123...; HttpOnly; SameSite=Lax ``` --- ### Resend Verification Email **Pattern:** `POST /auth/resend-verification` **Authentication:** Required (can be unverified user) **Purpose:** Resend verification email if not received **Response:** ```json { "success": true, "message": "Verification email sent" } ``` **Validation:** - User must not be already verified - Rate limit: Once per hour (TODO: implement) --- ## Usage Examples ### Register and Login ```python import requests API_BASE = "http://localhost:28080" # Register register_resp = requests.post( f"{API_BASE}/auth/register", json={ "username": "alice", "email": "alice@example.com", "password": "secure123" } ) result = register_resp.json() print(result["message"]) # If email verification required if not result.get("email_verified"): print("Check your email and click verification link") # After clicking link, user is auto-logged in # Login (if email verification not required) login_resp = requests.post( f"{API_BASE}/auth/login", json={ "username": "alice", "password": "secure123" } ) # Session cookie set automatically session_cookie = login_resp.cookies.get("session_id") print(f"Session ID: {session_cookie}") ``` --- ### Create and Use API Token ```python import requests API_BASE = "http://localhost:28080" # Login first (or use existing session) session = requests.Session() session.post( f"{API_BASE}/auth/login", json={"username": "alice", "password": "secure123"} ) # Create token token_resp = session.post( f"{API_BASE}/auth/tokens/create", json={"name": "My API Token"} ) token_data = token_resp.json() token = token_data["token"] # hf_abc123... print(f"Token created: {token}") print("SAVE THIS TOKEN - you won't see it again!") # Use token in API requests headers = {"Authorization": f"Bearer {token}"} # List repositories repos_resp = requests.get( f"{API_BASE}/api/models", headers=headers ) print(f"Found {len(repos_resp.json())} models") ``` --- ### Manage Tokens ```python # List all tokens tokens_resp = session.get(f"{API_BASE}/auth/tokens") tokens = tokens_resp.json()["tokens"] for t in tokens: print(f"Token: {t['name']} (ID: {t['id']})") print(f" Created: {t['created_at']}") print(f" Last used: {t['last_used'] or 'Never'}") # Revoke old token old_token_id = tokens[0]["id"] revoke_resp = session.delete(f"{API_BASE}/auth/tokens/{old_token_id}") print(revoke_resp.json()["message"]) ``` --- ### Registration with Invitation ```python # For invitation-only servers invitation_token = "invitation_token_from_link" register_resp = requests.post( f"{API_BASE}/auth/register?invitation_token={invitation_token}", json={ "username": "bob", "email": "bob@example.com", "password": "secure456" } ) # If invitation was for joining an organization # User automatically added to that organization ``` --- ## Security ### Password Hashing - Algorithm: bcrypt - Salt rounds: Automatic (bcrypt default) - Passwords never stored in plaintext ### Session Security - HTTP-only cookies (not accessible via JavaScript) - SameSite: Lax (CSRF protection) - Secure flag in production (HTTPS only) - 30-day expiration (configurable) ### Token Security - SHA256 hashed in database - Prefix: `hf_` (64 characters total) - Never logged in plaintext - Single-use viewing (on creation only) ### Session Secret - Random 32-character string - Used for client-side token encryption - Unique per session - Returned on login and token creation --- ## Configuration ### Environment Variables ```bash # Session duration (hours) KOHAKU_HUB_SESSION_EXPIRE_HOURS=720 # 30 days default # Email verification KOHAKU_HUB_REQUIRE_EMAIL_VERIFICATION=false # Invitation-only registration KOHAKU_HUB_INVITATION_ONLY=false # Session secret (for cookie signing) KOHAKU_HUB_SESSION_SECRET=change_this_in_production # Email SMTP (for verification emails) KOHAKU_HUB_SMTP_HOST=smtp.gmail.com KOHAKU_HUB_SMTP_PORT=587 KOHAKU_HUB_SMTP_USERNAME=your_email@gmail.com KOHAKU_HUB_SMTP_PASSWORD=your_app_password KOHAKU_HUB_SMTP_FROM=noreply@yourdomain.com ``` --- ## Best Practices **For Users:** - Use strong passwords (mix of letters, numbers, symbols) - Create tokens for CI/CD (don't use password) - Revoke unused tokens - Don't share tokens in public repos **For Developers:** - Store tokens in environment variables - Use session cookies for browser apps - Use Bearer tokens for API/CLI - Handle 401/403 errors (prompt re-login) **For Admins:** - Enable email verification in production - Use invitation-only mode for private instances - Set strong SESSION_SECRET - Enable HTTPS in production - Monitor token usage via admin API --- ## Error Responses **401 Unauthorized:** ```json { "detail": "Invalid username or password" } ``` **403 Forbidden:** ```json { "detail": "Account is disabled" } ``` **400 Bad Request:** ```json { "detail": "Username already exists" } ``` --- ## Next Steps - [User Settings API](./settings.md) - Profile, preferences - [Organizations API](./organizations.md) - Org membership - [Invitations API](./invitations.md) - Invite users - [Admin API](./admin.md) - User management