The Bloom Housing API uses JWT tokens for authentication. Tokens are issued as HTTP-only cookies on login and must be present on subsequent requests to protected endpoints.
How it works
When you log in, the API sets two cookies on the response:
| Cookie | Description |
|---|
access-token | Short-lived JWT used to authenticate requests. Valid for 1 hour. |
refresh-token | Longer-lived token used to obtain a new access token without re-entering credentials. |
Because tokens are stored in HTTP-only cookies, your HTTP client must send cookies with every request (e.g., credentials: 'include' in fetch, or withCredentials: true in Axios).
Login
POST /auth/login
Send your email and password as JSON.curl -X POST http://localhost:3100/auth/login \
-H 'Content-Type: application/json' \
-c cookies.txt \
-d '{"email": "user@example.com", "password": "yourpassword"}'
On success, the server sets access-token and refresh-token cookies and returns: Use the access-token cookie on subsequent requests
Your HTTP client will automatically send the cookie with each subsequent request if cookie handling is enabled.curl http://localhost:3100/user \
-b cookies.txt
Multi-factor authentication (MFA)
MFA can be enabled per jurisdiction. When it is active, the login process requires a second step.
Request an MFA code
Call POST /auth/request-mfa-code with your email and password. This triggers a code to be sent via email or SMS (via Twilio).curl -X POST http://localhost:3100/auth/request-mfa-code \
-H 'Content-Type: application/json' \
-d '{"email": "user@example.com", "password": "yourpassword", "mfaType": "email"}'
Log in with the MFA code
Include mfaCode in your login request body.curl -X POST http://localhost:3100/auth/login \
-H 'Content-Type: application/json' \
-c cookies.txt \
-d '{"email": "user@example.com", "password": "yourpassword", "mfaCode": "123456"}'
Single-use code login
When the enableSingleUseCode feature flag is active for a jurisdiction, users can log in with a one-time code instead of a password.
# Request a single-use code
curl -X POST http://localhost:3100/user/request-single-use-code \
-H 'Content-Type: application/json' \
-d '{"email": "user@example.com"}'
# Log in using the code
curl -X POST http://localhost:3100/auth/loginViaSingleUseCode \
-H 'Content-Type: application/json' \
-c cookies.txt \
-d '{"email": "user@example.com", "singleUseCode": "123456"}'
Refreshing an access token
The access token expires after 1 hour. Use the refresh token to obtain a new one without requiring the user to log in again.
curl -X GET http://localhost:3100/auth/requestNewToken \
-b cookies.txt \
-c cookies.txt
If the refresh token is valid, the server issues a new access-token cookie. If no refresh token is present, the API returns 400 Bad Request.
Logout
Call GET /auth/logout to clear your session cookies. This endpoint requires an active access token.
curl -X GET http://localhost:3100/auth/logout \
-b cookies.txt \
-c cookies.txt
The server clears both the access-token and refresh-token cookies and returns { "success": true }.
Account lockout
After a configurable number of failed login attempts, an account is temporarily locked. The lockout duration is set by the server operator via environment variables (AUTH_LOCK_LOGIN_AFTER_FAILED_ATTEMPTS and AUTH_LOCK_LOGIN_COOLDOWN). Subsequent login attempts during the cooldown period will return 401 Unauthorized.
Public endpoints
The following endpoints do not require authentication:
| Endpoint | Method | Description |
|---|
/listings | GET | Browse available listings |
/applications | POST | Submit a new application |
/jurisdictions | GET | Read jurisdiction data |
/multiselectQuestions | GET | Read preferences and programs |
/auth/request-mfa-code | POST | Request an MFA code before login |
/auth/update-password | PUT | Set a new password via reset token |
/auth/confirm | PUT | Confirm a new user account |
Email confirmation
When a new account is created, the user receives a confirmation email. The account must be confirmed before login is permitted. Resend confirmation with POST /user/resendConfirmation, or confirm directly with PUT /auth/confirm using the token from the email.
curl -X PUT http://localhost:3100/auth/confirm \
-H 'Content-Type: application/json' \
-d '{"token": "<confirmation-token-from-email>"}'
Password reset
Request a reset link
curl -X PUT http://localhost:3100/user/forgot-password \
-H 'Content-Type: application/json' \
-d '{"email": "user@example.com", "appUrl": "https://yourapp.example.com"}'
Set a new password
Use the token from the reset email.curl -X PUT http://localhost:3100/auth/update-password \
-H 'Content-Type: application/json' \
-c cookies.txt \
-d '{"token": "<reset-token>", "password": "newpassword", "passwordConfirmation": "newpassword"}'
On success, the server sets new auth cookies and the user is logged in.