Password Reset
Comprehensive guide for the forgot password flow
Overview
The password reset flow uses a 6-digit verification code sent via email. The process is secure, rate-limited, and includes multiple validation steps.
Security Features
- 6-digit verification codes
- 1-hour code expiration
- SHA-256 code hashing
- Rate limiting (2 minutes between requests)
- Maximum 5 attempts per 24 hours
- Email enumeration prevention
- Session invalidation on reset
- API key authentication required
Authentication Required
All password reset endpoints require an x-api-key header:
x-api-key: your-api-keyContact your platform administrator to obtain an API key for your application.
Password Reset Flow
Step 1: Request Password Reset
Endpoint: POST /auth/forgot-password
Headers:
Content-Type: application/json
x-api-key: your-api-keyRequest Body:
{
"email": "user@example.com"
}Response:
{
"success": true,
"message": "Password reset code sent to your email",
"data": {
"success": true,
"message": "Password reset code sent to your email"
}
}Notes:
- Always returns success to prevent email enumeration
- Code sent via email (check inbox and spam)
- Code expires in 1 hour
- Rate limited to 1 request per 2 minutes
- Maximum 5 requests per 24 hours
Try it out:
Step 2: Validate Reset Code (Optional)
Endpoint: POST /auth/validate-reset-code
This optional step allows you to verify the code before attempting to reset the password.
Headers:
Content-Type: application/json
x-api-key: your-api-keyRequest Body:
{
"email": "user@example.com",
"resetCode": "123456"
}Success Response:
{
"success": true,
"message": "Reset code is valid",
"data": {
"success": true,
"message": "Reset code is valid",
"userId": "60d5ec49f1b2c8b1f8e4e1a1"
}
}Error Response:
{
"success": false,
"message": "Invalid or expired reset code",
"statusCode": 400
}Step 3: Reset Password
Endpoint: POST /auth/reset-password
Headers:
Content-Type: application/json
x-api-key: your-api-keyRequest Body:
{
"email": "user@example.com",
"resetCode": "123456",
"newPassword": "NewSecure@Pass123"
}Success Response:
{
"success": true,
"message": "Password has been reset successfully. Please sign in with your new password.",
"data": {
"success": true,
"message": "Password has been reset successfully. Please sign in with your new password."
}
}Important:
- All existing sessions are invalidated
- User must sign in again with new password
- Confirmation email is sent
Password Requirements
Passwords must meet these criteria:
- Minimum 8 characters
- At least one uppercase letter (A-Z)
- At least one lowercase letter (a-z)
- At least one number (0-9)
- At least one special character (@$!%*?&#)
Valid examples: SecurePass@123, MyP@ssw0rd!, Strong#Pass1
Invalid examples: password (no uppercase, number, or special char); PASSWORD123 (no lowercase or special char); Pass@12 (too short, fewer than 8 characters)
Change Password (Authenticated Users)
Endpoint: POST /auth/change-password
For users who are already signed in and want to change their password.
Headers:
Content-Type: application/json
Authorization: Bearer <access_token>Request Body:
{
"currentPassword": "OldPassword@123",
"newPassword": "NewSecure@Pass123"
}Response:
{
"success": true,
"message": "Password changed successfully"
}Notes:
- Requires valid authentication token
- Current password must be correct
- New password must meet requirements
- Confirmation email is sent
Error Codes
| Status | Message | Cause |
|---|---|---|
| 400 | Invalid or expired reset code | Code is incorrect or has expired |
| 401 | API key is required | Missing x-api-key header |
| 401 | Current password is incorrect | Wrong password (change password) |
| 403 | Invalid API key | Incorrect x-api-key header |
| 403 | Account is deactivated | User account is disabled |
| 429 | Please wait 2 minutes before requesting another reset code | Rate limit exceeded |
| 429 | Too many reset attempts. Please try again in 24 hours | Maximum attempts exceeded |
Implementation Examples
React/JavaScript
// Development
const DEV_API_URL = 'https://api-gateway.dev.1houseglobalservices.com';
const DEV_API_KEY = process.env.REACT_APP_DEV_API_KEY;
// Production
const PROD_API_URL = 'https://api-gateway.prod.1houseglobalservices.com';
const PROD_API_KEY = process.env.REACT_APP_PROD_API_KEY;
// Use appropriate environment
const API_URL = process.env.NODE_ENV === 'production' ? PROD_API_URL : DEV_API_URL;
const API_KEY = process.env.NODE_ENV === 'production' ? PROD_API_KEY : DEV_API_KEY;
// 1. Request password reset
const requestReset = async (email) => {
const response = await fetch(`${API_URL}/v1/auth/forgot-password`, {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
const data = await response.json();
if (data.success) {
// Show code entry form
return true;
}
return false;
};
// 2. Validate code (optional)
const validateCode = async (email, resetCode) => {
const response = await fetch(`${API_URL}/v1/auth/forgot-password/validate`, {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, resetCode })
});
const data = await response.json();
return data.success;
};
// 3. Reset password
const resetPassword = async (email, resetCode, newPassword) => {
const response = await fetch(`${API_URL}/v1/auth/forgot-password/reset`, {
method: 'POST',
headers: {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, resetCode, newPassword })
});
const data = await response.json();
if (data.success) {
// Redirect to sign in
window.location.href = '/signin';
}
return data.success;
};iOS (Swift)
class PasswordResetManager {
// Development
private let devBaseURL = "https://api-gateway.dev.1houseglobalservices.com"
private let devAPIKey = Bundle.main.infoDictionary?["DEV_API_KEY"] as? String ?? ""
// Production
private let prodBaseURL = "https://api-gateway.prod.1houseglobalservices.com"
private let prodAPIKey = Bundle.main.infoDictionary?["PROD_API_KEY"] as? String ?? ""
// Use appropriate environment
private var baseURL: String {
return ProcessInfo.processInfo.environment["ENVIRONMENT"] == "production" ? prodBaseURL : devBaseURL
}
private var apiKey: String {
return ProcessInfo.processInfo.environment["ENVIRONMENT"] == "production" ? prodAPIKey : devAPIKey
}
func requestPasswordReset(email: String, completion: @escaping (Bool) -> Void) {
let url = URL(string: "\(baseURL)/v1/auth/forgot-password")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(apiKey, forHTTPHeaderField: "X-API-Key")
let body = ["email": email]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let success = json["success"] as? Bool else {
completion(false)
return
}
completion(success)
}.resume()
}
func resetPassword(email: String, code: String, newPassword: String, completion: @escaping (Bool) -> Void) {
let url = URL(string: "\(baseURL)/v1/auth/forgot-password/reset")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(apiKey, forHTTPHeaderField: "X-API-Key")
let body: [String: String] = [
"email": email,
"resetCode": code,
"newPassword": newPassword
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let success = json["success"] as? Bool else {
completion(false)
return
}
completion(success)
}.resume()
}
}Android (Kotlin)
class PasswordResetManager(private val context: Context) {
// Development
private val devBaseUrl = "https://api-gateway.dev.1houseglobalservices.com"
private val devApiKey = BuildConfig.DEV_API_KEY
// Production
private val prodBaseUrl = "https://api-gateway.prod.1houseglobalservices.com"
private val prodApiKey = BuildConfig.PROD_API_KEY
// Use appropriate environment
private val baseUrl: String
get() = if (BuildConfig.BUILD_TYPE == "release") prodBaseUrl else devBaseUrl
private val apiKey: String
get() = if (BuildConfig.BUILD_TYPE == "release") prodApiKey else devApiKey
private val client = OkHttpClient()
fun requestPasswordReset(email: String, callback: (Boolean) -> Unit) {
val json = JSONObject().apply {
put("email", email)
}
val body = RequestBody.create(
"application/json".toMediaType(),
json.toString()
)
val request = Request.Builder()
.url("$baseUrl/v1/auth/forgot-password")
.addHeader("X-API-Key", apiKey)
.post(body)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
val responseBody = response.body?.string()
val jsonResponse = JSONObject(responseBody ?: "{}")
callback(jsonResponse.getBoolean("success"))
}
override fun onFailure(call: Call, e: IOException) {
callback(false)
}
})
}
fun resetPassword(email: String, code: String, newPassword: String, callback: (Boolean) -> Unit) {
val json = JSONObject().apply {
put("email", email)
put("resetCode", code)
put("newPassword", newPassword)
}
val body = RequestBody.create(
"application/json".toMediaType(),
json.toString()
)
val request = Request.Builder()
.url("$baseUrl/v1/auth/forgot-password/reset")
.addHeader("X-API-Key", apiKey)
.post(body)
.build()
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
val responseBody = response.body?.string()
val jsonResponse = JSONObject(responseBody ?: "{}")
callback(jsonResponse.getBoolean("success"))
}
override fun onFailure(call: Call, e: IOException) {
callback(false)
}
})
}
}UX Recommendations
1. Forgot Password Screen
- Clear "Forgot Password?" link on sign-in page
- Simple email input field
- Informative success message
- Link back to sign-in
2. Code Entry Screen
- Large input field for 6-digit code
- Auto-focus on page load
- Numeric keyboard on mobile
- "Resend code" option (respecting rate limits)
- Clear error messages
3. New Password Screen
- Password strength indicator
- Show password requirements
- Password visibility toggle
- Confirm password field
- Clear success message
4. Email Design
- Users receive professional HTML email
- 6-digit code prominently displayed
- Reset link button for convenience
- 1-hour expiration notice
- Security warning
Testing
Test Flow
Environment-Specific Testing
When testing, make sure to use the correct environment URL and API key:
- Development:
https://api-gateway.dev.1houseglobalservices.comwith your development API key - Production:
https://api-gateway.prod.1houseglobalservices.comwith your production API key
# Development - Set your development API key
export DEV_API_KEY="your-development-api-key"
export DEV_API_URL="https://api-gateway.dev.1houseglobalservices.com"
# Production - Set your production API key
export PROD_API_KEY="your-production-api-key"
export PROD_API_URL="https://api-gateway.prod.1houseglobalservices.com"
# Use appropriate environment (example uses development)
export API_KEY=$DEV_API_KEY
export API_URL=$DEV_API_URL
# 1. Request reset code
curl -X POST $API_URL/v1/auth/forgot-password \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{"email":"test@example.com"}'
# 2. Check email for 6-digit code
# 3. Validate code (optional)
curl -X POST $API_URL/v1/auth/forgot-password/validate \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{"email":"test@example.com","resetCode":"123456"}'
# 4. Reset password
curl -X POST $API_URL/v1/auth/forgot-password/reset \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{
"email":"test@example.com",
"resetCode":"123456",
"newPassword":"NewSecure@Pass123"
}'Email Templates
Users receive beautiful, branded emails:
Password Reset Email
- Subject: "Reset Your Password - 1House Global"
- Content: 6-digit code, reset link button, expiration notice
- Design: Gradient header, clean layout, security warnings
Password Changed Email
- Subject: "Password Changed Successfully - 1House Global"
- Content: Confirmation message, security alert
- Design: Green theme, clear alert box
Troubleshooting
"API key is required"
- Add
x-api-keyheader to your request - Contact administrator for API key
"Invalid or expired reset code"
- Code may have expired (1 hour limit)
- Verify email matches exactly
- Check for typos in 6-digit code
- Request a new code
"Please wait 2 minutes"
- Rate limit protection
- Wait 2 minutes before requesting again
- This prevents abuse
"Too many reset attempts"
- Maximum 5 attempts per 24 hours reached
- Wait 24 hours or contact support
- This is a security measure
Email not received
- Check spam/junk folder
- Verify email address is correct
- Wait a few minutes (emails can be delayed)
- Request a new code after 2 minutes
Best Practices
For Developers
-
Store API key securely
- Use environment variables
- Never hardcode in source code
- Keep separate keys for dev/prod
-
Handle errors gracefully
- Show user-friendly error messages
- Don't expose technical details
- Provide alternative support options
-
Implement proper UX
- Clear instructions
- Progress indicators
- Success confirmations
- Easy navigation back to sign-in
-
Security
- Always use HTTPS
- Validate input client-side
- Show password strength
- Confirm password field
For Users
- Check email inbox and spam
- Code expires in 1 hour - use it promptly
- Never share your reset code
- Contact support if issues persist
API Reference Quick Links
- Authentication - Sign in/sign up
- User Management - User endpoints
- Platform Types - Service IDs and platforms
Support
If you encounter issues:
- Review error message carefully
- Check troubleshooting section above
- Verify API key is correct
- Test with the provided curl commands
- Contact platform support with error details