Skip to main content

Authentication Guide

Happy Habitat uses JWT (JSON Web Token) based authentication to secure API endpoints and manage user sessions. This guide covers login, registration, password management, and security best practices.

Authentication Flow

The authentication system follows this flow:

Login

Endpoint

POST /api/auth/login Authorization: None (public endpoint)

Request

Headers:
Content-Type: application/json
Body:
{
  "username": "elgrandeahc",
  "password": "abc123"
}

Response

Success (200 OK):
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "username": "elgrandeahc",
  "email": "[email protected]",
  "role": "SYSTEM_ADMIN",
  "expiresAt": "2025-03-04T10:30:00Z"
}
Error (401 Unauthorized):
{
  "message": "Invalid username or password"
}

Implementation Details

The login process:
  1. Credential Validation - Username and password are validated
  2. Password Hashing - BCrypt is used for secure password comparison
  3. JWT Generation - A signed JWT token is created with user claims
  4. Token Expiration - Tokens expire after a configured time period
  5. Session Storage - Frontend stores token in localStorage
Source Code Reference:
  • Controller: HappyHabitat.API/Controllers/AuthController.cs:26
  • Service: HappyHabitat.Infrastructure/Services/AuthService.cs

Registration

Endpoint

POST /api/auth/register Authorization: None (public endpoint)

Request

Body:
{
  "firstName": "Ana",
  "lastName": "Martínez",
  "username": "amartinez",
  "email": "[email protected]",
  "password": "SecurePassword123!",
  "roleId": "44444444-4444-4444-4444-444444444444"
}
The roleId field determines the user’s role. See the Roles section for available role IDs.

Response

Success (200 OK):
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "username": "amartinez",
  "email": "[email protected]",
  "role": "RESIDENT",
  "expiresAt": "2025-03-04T10:30:00Z"
}
Error (400 Bad Request):
{
  "message": "Username already exists"
}

Password Requirements

Implement strong password requirements in production:
  • Minimum 8 characters
  • At least one uppercase letter
  • At least one lowercase letter
  • At least one number
  • At least one special character

JWT Token Structure

Token Claims

The JWT token contains the following claims:
{
  "nameid": "user-guid",
  "unique_name": "username",
  "email": "[email protected]",
  "role": "SYSTEM_ADMIN",
  "nbf": 1709548800,
  "exp": 1709635200,
  "iat": 1709548800,
  "iss": "HappyHabitat",
  "aud": "HappyHabitatUsers"
}
Claim Descriptions:
  • nameid - User ID (ClaimTypes.NameIdentifier)
  • unique_name - Username
  • email - User email address
  • role - User role code
  • nbf - Not valid before (Unix timestamp)
  • exp - Expiration time (Unix timestamp)
  • iat - Issued at (Unix timestamp)
  • iss - Issuer (configured in appsettings.json)
  • aud - Audience (configured in appsettings.json)

Using the Token

Include the JWT token in the Authorization header for protected endpoints:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Roles

Happy Habitat supports six user roles, each with specific permissions.

Available Roles

SYSTEM_ADMIN

ID: 11111111-1111-1111-1111-111111111111Full system access. Can manage all communities and users.

ADMIN_COMPANY

ID: 22222222-2222-2222-2222-222222222222Manage assigned communities. Limited to specific communities.

COMITEE_MEMBER

ID: 33333333-3333-3333-3333-333333333333Committee member with elevated permissions in their community.

RESIDENT

ID: 44444444-4444-4444-4444-444444444444Standard resident access. Can manage own profile and submit tickets.

RENTER

ID: 55555555-5555-5555-5555-555555555555Tenant with limited access compared to property owners.

VIGILANCE

ID: 66666666-6666-6666-6666-666666666666Security personnel with access to visitor and vehicle logs.

Role-Based Authorization

Endpoints are protected using role-based authorization attributes:
[Authorize(Roles = "ADMIN_COMPANY,SYSTEM_ADMIN")]
public async Task<ActionResult> GetCommunities()
{
    // Only admins can access this endpoint
}

Password Management

Change Password (Authenticated Users)

Endpoint: POST /api/auth/change-password Authorization: Required (Bearer token) Request Body:
{
  "currentPassword": "OldPassword123!",
  "newPassword": "NewSecurePassword456!"
}
Response:
{
  "message": "Password changed successfully"
}
Source: AuthController.cs:58

Forgot Password

Endpoint: POST /api/auth/forgot-password Authorization: None (public endpoint) Request Body:
{
  "email": "[email protected]"
}
Response:
{
  "message": "If the email exists, a password reset link has been sent."
}
For security reasons, the API returns a success message regardless of whether the email exists.
Source: AuthController.cs:76

Reset Password

Endpoint: POST /api/auth/reset-password Authorization: None (requires reset token) Request Body:
{
  "email": "[email protected]",
  "token": "reset-token-from-email",
  "newPassword": "NewSecurePassword789!"
}
Response:
{
  "message": "Password reset successfully"
}
Source: AuthController.cs:91

Token Refresh

The refresh token endpoint is prepared but requires full implementation.
Endpoint: POST /api/auth/refresh Request Body:
{
  "refreshToken": "refresh-token-string"
}
Implementation Status:
  • Frontend prepared to use refresh tokens
  • Backend endpoint exists but needs token storage logic
  • Should be implemented before production deployment
Source: AuthController.cs:105

Security Configuration

JWT Settings

JWT configuration is managed in appsettings.json:
{
  "Jwt": {
    "Key": "YourSuperSecretKeyThatShouldBeAtLeast32CharactersLong!",
    "Issuer": "HappyHabitat",
    "Audience": "HappyHabitatUsers"
  }
}
Production Security:
  • Change the JWT key to a strong, random secret
  • Store the key in environment variables, not appsettings.json
  • Use at least 32 characters for the key
  • Never commit production keys to source control
Configuration Validation: The application validates JWT configuration at startup (Program.cs:75-88):
if (isProduction && string.IsNullOrWhiteSpace(jwtKeyConfig))
    throw new InvalidOperationException("Jwt:Key is required in production");

if (isProduction && jwtKeyConfig == defaultInsecureJwtKey)
    throw new InvalidOperationException("Don't use default JWT key in production");

Token Validation

Token validation parameters (Program.cs:101-110):
options.TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidateIssuerSigningKey = true,
    ValidIssuer = jwtIssuer,
    ValidAudience = jwtAudience,
    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey))
};

Password Hashing

Passwords are hashed using BCrypt with the following settings:
  • Algorithm: BCrypt
  • Work Factor: 10 (configurable)
  • Salt: Auto-generated per password
  • Hash Prefix: $2a$ or $2b$
Implementation: PasswordHasherService in Infrastructure layer

Session Management

Frontend Session Storage

The frontend stores session data in localStorage: Keys:
  • hh_token - JWT access token
  • hh_refresh_token - Refresh token (if available)
  • hh_user - User information (JSON)
  • hh_token_expiry - Token expiration timestamp

Checking Authentication Status

On App Initialization:
  1. Retrieve token from localStorage
  2. Check if token exists
  3. Validate token expiration
  4. If valid, restore user session
  5. If invalid, clear storage and redirect to login

Logout

Process:
  1. Clear all session data from localStorage
  2. Invalidate authentication signals/state
  3. Redirect to login page
  4. (Optional) Call logout endpoint if server-side session tracking is implemented

CORS Configuration

CORS (Cross-Origin Resource Sharing) is configured to allow frontend access. Development Origins:
http://localhost:4200
https://localhost:4200
http://localhost:5080
https://localhost:7177
Production Configuration: Set allowed origins in appsettings.json or environment variables:
{
  "Cors": {
    "Origins": "https://app.example.com;https://www.example.com"
  }
}
The application will not start in production without explicit CORS configuration.
Source: Program.cs:47-67

Testing Authentication

Using Swagger

  1. Navigate to the Swagger UI (development only)
  2. Click “Authorize” button
  3. Enter: Bearer <your-jwt-token>
  4. Click “Authorize”
  5. Test protected endpoints

Using curl

Login:
curl -X POST http://localhost:5080/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "elgrandeahc",
    "password": "abc123"
  }'
Protected Endpoint:
curl -X GET http://localhost:5080/api/users \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Using Postman

  1. Create a new request
  2. Set method and URL
  3. Go to “Authorization” tab
  4. Select “Bearer Token”
  5. Paste your JWT token
  6. Send request

Common Issues

401 Unauthorized

Causes:
  • Expired token
  • Invalid token
  • Missing Authorization header
  • Incorrect token format
Solutions:
  • Login again to get a new token
  • Verify token format: Bearer <token>
  • Check token expiration
  • Implement token refresh logic

403 Forbidden

Causes:
  • Valid token but insufficient permissions
  • User role doesn’t match endpoint requirements
Solutions:
  • Check your user role
  • Contact administrator for role upgrade
  • Verify endpoint authorization requirements

Token Expiration

Symptoms:
  • Suddenly logged out
  • API returns 401 after working previously
Solutions:
  • Implement automatic token refresh
  • Show expiration warning to users
  • Redirect to login on expiration

Best Practices

Security

  • Store tokens securely (httpOnly cookies in production)
  • Implement token refresh to avoid repeated logins
  • Use HTTPS in production
  • Validate JWT on every request
  • Implement rate limiting on auth endpoints
  • Log authentication failures for security monitoring

User Experience

  • Show clear error messages for login failures
  • Implement “Remember Me” functionality
  • Provide password strength indicators
  • Send password reset emails promptly
  • Auto-logout on token expiration with notification

Development

  • Use environment variables for sensitive config
  • Don’t commit tokens or secrets to version control
  • Test with different roles and permissions
  • Implement proper error handling
  • Add logging for debugging authentication issues

Additional Resources

Build docs developers (and LLMs) love