Skip to main content

Overview

The login endpoint authenticates users and returns a JWT token that must be included in subsequent requests to protected endpoints. The authentication process validates credentials against BCrypt-hashed passwords stored in the database.

Login Endpoint

curl -X POST "https://api.example.com/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "johndoe",
    "password": "SecurePass123!"
  }'

Request Format

Endpoint Details

  • URL: /api/v1/auth/login
  • Method: POST
  • Content-Type: application/json
  • Authentication: Not required (public endpoint)

Request Body Schema

The request body must conform to the AuthLoginRequest DTO:
{
  "username": "string",
  "password": "string"
}

Field Requirements

FieldTypeRequiredValidationDescription
usernameStringYesNot blankThe username associated with the account
passwordStringYesNot blankThe password for authentication
Both fields are mandatory. The API will return a validation error if either field is missing or empty.

Response Format

Success Response (201 Created)

{
  "username": "johndoe",
  "message": "User logged in correctly",
  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJsaWJyYXJ5LWFwaSIsInN1YiI6ImpvaG5kb2UiLCJhdXRob3JpdGllcyI6IiIsImlhdCI6MTcwOTY0MDAwMCwiZXhwIjoxNzA5NjQxODAwLCJqdGkiOiIxMjM0NTY3OC0xMjM0LTEyMzQtMTIzNC0xMjM0NTY3ODkwYWIiLCJuYmYiOjE3MDk2NDAwMDB9.signature",
  "status": true
}

Response Fields

FieldTypeDescription
usernameStringThe authenticated username
messageStringSuccess message (“User logged in correctly”)
jwtStringJWT access token for authenticated requests
statusBooleanAlways true for successful login
The JWT token is the most critical part of the response. Store it securely and include it in all subsequent API requests to protected endpoints.

Authentication Process

The login process follows these steps:
1

Validate Request

The API validates that both username and password are provided and not blank
2

Load User Details

The system retrieves the user from the database using the provided username
AuthUser user = authUserRepository.findAuthUserByUsername(username)
    .orElseThrow(() -> new UsernameNotFoundException(
        "The user " + username + " doesn't exist"));
3

Verify Password

BCrypt is used to compare the provided password with the stored hash
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
    throw new BadCredentialsException("Invalid password");
}
4

Create Authentication Object

A Spring Security Authentication object is created with user details
Authentication authentication = new UsernamePasswordAuthenticationToken(
    userDetails.getUsername(),
    userDetails.getPassword(),
    userDetails.getAuthorities()
);
5

Generate JWT Token

A JWT token is created using the JwtUtils utility
String accessToken = jwtUtils.createToken(authentication);
6

Return Response

The API returns the username, success message, JWT token, and status
return new AuthLoginResponse(
    username, 
    "User logged in correctly", 
    accessToken, 
    true
);

JWT Token Details

The JWT token returned upon successful login contains the following information:

Token Structure

header.payload.signature

Token Claims

ClaimDescriptionExample
issIssuer of the tokenConfigured in security.jwt.user.generator
subSubject (username)"johndoe"
authoritiesUser authorities/roles"" (empty in current implementation)
iatIssued at timestamp1709640000
expExpiration timestamp1709641800 (30 minutes after iat)
jtiUnique token ID"12345678-1234-1234-1234-1234567890ab"
nbfNot before timestamp1709640000
JWT tokens expire 30 minutes (1,800,000 milliseconds) after issuance. You must re-authenticate to obtain a new token after expiration.

Using the JWT Token

After successful login, include the JWT token in the Authorization header of all requests to protected endpoints:
curl -X GET "https://api.example.com/api/v1/books" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Always include the Bearer prefix before the token in the Authorization header. The format must be: Authorization: Bearer <token>

Error Responses

Invalid Credentials (401 Unauthorized)

Returned when username doesn’t exist or password is incorrect:
{
  "message": "Invalid password",
  "timestamp": "2024-03-06T10:30:00",
  "details": []
}
or
{
  "message": "The user johndoe doesn't exist",
  "timestamp": "2024-03-06T10:30:00",
  "details": []
}

Validation Error (400 Bad Request)

Returned when required fields are missing:
{
  "message": "Validation failed",
  "timestamp": "2024-03-06T10:30:00",
  "details": [
    "The username is obligatory",
    "The password is obligatory"
  ]
}

Complete Login Flow Example

Full Authentication Workflow

1

Collect Credentials

Gather username and password from user input
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
2

Send Login Request

POST credentials to the login endpoint
const response = await fetch('https://api.example.com/api/v1/auth/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ username, password })
});
3

Handle Response

Check response status and extract JWT token
if (response.status === 201) {
  const data = await response.json();
  const token = data.jwt;
  
  // Store token securely
  localStorage.setItem('authToken', token);
  localStorage.setItem('username', data.username);
  
  // Redirect to main application
  window.location.href = '/dashboard';
} else {
  const error = await response.json();
  showError(error.message);
}
4

Use Token in Requests

Include token in subsequent API calls
const token = localStorage.getItem('authToken');

const apiResponse = await fetch('https://api.example.com/api/v1/books', {
  headers: {
    'Authorization': `Bearer ${token}`
  }
});
5

Handle Token Expiration

Detect expired tokens and re-authenticate
if (apiResponse.status === 401) {
  // Token expired
  localStorage.removeItem('authToken');
  localStorage.removeItem('username');
  window.location.href = '/login';
}

Token Storage Best Practices

Client-Side Storage Options

Storage MethodSecurityPersistenceUse Case
MemoryHighSession onlyHigh-security applications
SessionStorageMediumSession onlySingle-tab applications
LocalStorageLow-MediumPersistentGeneral web applications
Cookies (HttpOnly)HighPersistentServer-rendered applications

Recommendations

1

Choose Appropriate Storage

  • Use memory or sessionStorage for sensitive applications
  • LocalStorage is acceptable for general-purpose apps
  • Consider HttpOnly cookies for server-side rendering
2

Implement Token Refresh

  • Monitor token expiration time
  • Prompt user to re-authenticate before expiration
  • Clear stored token immediately upon logout
3

Secure Communication

  • Always use HTTPS in production
  • Never log or expose tokens in console
  • Don’t include tokens in URLs or query parameters
Never expose JWT tokens in client-side logs, error messages, or browser console. Treat tokens as sensitive credentials.

Token Expiration Handling

Client-Side Expiration Detection

function isTokenExpired(token) {
  try {
    // Decode JWT (without verification - just reading claims)
    const payload = JSON.parse(atob(token.split('.')[1]));
    const expirationTime = payload.exp * 1000; // Convert to milliseconds
    const currentTime = Date.now();
    
    return currentTime > expirationTime;
  } catch (error) {
    return true; // Treat invalid tokens as expired
  }
}

// Usage
const token = localStorage.getItem('authToken');
if (isTokenExpired(token)) {
  // Redirect to login
  window.location.href = '/login';
}

Handling 401 Responses

// Global axios interceptor for handling expired tokens
axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response && error.response.status === 401) {
      // Clear stored token
      localStorage.removeItem('authToken');
      localStorage.removeItem('username');
      
      // Redirect to login
      window.location.href = '/login?sessionExpired=true';
    }
    return Promise.reject(error);
  }
);

Password Security

BCrypt Verification

The API uses BCrypt to securely verify passwords:
// During login
if (!passwordEncoder.matches(password, userDetails.getPassword())) {
    throw new BadCredentialsException("Invalid password");
}

Security Features

  • Constant-Time Comparison: Prevents timing attacks
  • Salt Integration: Each password has unique salt
  • Adaptive Cost: Configurable computational complexity
  • One-Way Hash: Cannot reverse hash to get original password
BCrypt automatically handles salting and verification. The passwordEncoder.matches() method extracts the salt from the stored hash and performs the comparison.

Testing Login

Valid Login Test

curl -X POST "http://localhost:8080/api/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "johndoe",
    "password": "SecurePass123!"
  }'

Expected Response

{
  "username": "johndoe",
  "message": "User logged in correctly",
  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "status": true
}

Testing Protected Endpoint

# Extract token from login response
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

# Use token to access protected endpoint
curl -X GET "http://localhost:8080/api/v1/books" \
  -H "Authorization: Bearer $TOKEN"

Common Issues and Solutions

IssuePossible CauseSolution
401 UnauthorizedInvalid username or passwordVerify credentials are correct
401 UnauthorizedToken expiredRe-authenticate to get new token
400 Bad RequestMissing required fieldsInclude both username and password
403 ForbiddenAccount disabledContact administrator
Missing Bearer prefixToken format incorrectEnsure header is Bearer <token>

Security Considerations

For API Consumers

  • Store tokens securely and never expose them
  • Implement proper logout to clear tokens
  • Use HTTPS for all API communication
  • Handle token expiration gracefully
  • Never hardcode credentials in source code

For API Developers

  • Keep the JWT signing key (security.jwt.key.private) secure
  • Use environment variables for sensitive configuration
  • Implement rate limiting on login endpoint
  • Log failed authentication attempts
  • Consider implementing account lockout after failed attempts

JWT Tokens

Deep dive into JWT token structure and validation

Registration

Create a new account before logging in

Authentication Overview

Understand the complete authentication system

API Reference

Detailed API endpoint specifications

Build docs developers (and LLMs) love