Home Authentication
Post
Cancel
Authentication | SEG

Authentication

This post introduces authentication fundamentals. If you need a refresher on HTTP basics and APIs, this guide will help you understand how security fits into web communication!

What Is Authentication?

Authentication is the process of verifying the identity of a user, system, or application. In simple terms, it’s like showing your ID card at the entrance, you prove who you are before being granted access to protected resources.

In software engineering, authentication is critical for:

  • Protecting sensitive data from unauthorized access
  • Tracking user actions and maintaining audit logs
  • Personalizing user experiences based on identity
  • Ensuring compliance with security regulations

Important: Don’t confuse authentication with authorization! Authentication verifies who you are, while authorization determines what you can do.

Authentication vs Authorization

Let’s clarify these two commonly confused concepts:

AspectAuthenticationAuthorization
PurposeVerify identityGrant permissions
Question“Who are you?”“What can you do?”
ExampleLogin with username/passwordAccess to admin panel
TimingHappens firstHappens after authentication
ResultAuthenticated or notAllowed or denied

"I remember my first project where I mixed up authentication and authorization. I spent hours debugging why authenticated users couldn't access their own data, turns out I forgot to implement proper authorization checks after authentication!"

How Authentication Works

Authentication typically follows this flow:

sequenceDiagram
    participant User
    participant Client
    participant Server
    participant Database

    User->>Client: Enter credentials
    Client->>Server: Send authentication request
    Server->>Database: Verify credentials
    Database-->>Server: Credentials valid/invalid
    alt Credentials valid
        Server-->>Client: Authentication successful
        Client-->>User: Grant access
    else Credentials invalid
        Server-->>Client: Authentication failed
        Client-->>User: Show error message
    end

The authentication process involves:

  1. Credential Collection: User provides identifying information (username, password, token, etc.)
  2. Credential Transmission: Client sends credentials to the server
  3. Credential Verification: Server validates credentials against stored data
  4. Response: Server grants or denies access based on validation result

Types of Authentication

There are several authentication methods, each with different levels of security and complexity. For junior developers, understanding the foundational methods is essential.

flowchart TB
    subgraph Auth[Authentication Methods]
        direction TB
        subgraph Basic["Basic Authentication"]
            BA[Base64 Encoded<br/>Username:Password]
        end
        subgraph Digest["Digest Authentication"]
            DA[Hashed Credentials<br/>with Nonce]
        end
        subgraph Modern["Modern Methods"]
            OAuth[OAuth 2.0]
            JWT[JWT Tokens]
            SSO[Single Sign-On]
        end
    end
    
    Basic --> |Evolution| Digest
    Digest --> |Evolution| Modern
    
    style Basic fill:#ffcccc
    style Digest fill:#ffffcc
    style Modern fill:#ccffcc

In this post, we’ll focus on the two foundational methods that every junior developer should understand: Basic Authentication and Digest Authentication.

Basic Authentication

Basic Authentication is the simplest HTTP authentication scheme. It transmits credentials as username and password pairs, encoded using Base64.

How Basic Auth Works

Basic Auth Flow

step-by-step explanation of the authentication process.

  • Client requests a protected resource without credentials
  • Server responds with 401 Unauthorized and includes a WWW-Authenticate header
  • Client prompts user for username and password
  • Client encodes credentials in Base64 format (username:password)
  • Client resends request with Authorization header containing encoded credentials
  • Server decodes and validates credentials
  • Server grants or denies access based on validation

The client must include credentials with every subsequent request to protected resources.

Basic Auth Sequence Diagram

sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: GET /api/protected
    Server-->>Client: 401 Unauthorized<br/>WWW-Authenticate: Basic realm="API"
    
    Note over Client: User enters<br/>username & password
    Note over Client: Encode to Base64:<br/>dXNlcjpwYXNzd29yZA==
    
    Client->>Server: GET /api/protected<br/>Authorization: Basic dXNlcjpwYXNzd29yZA==
    
    Note over Server: Decode Base64<br/>Verify credentials
    
    Server-->>Client: 200 OK<br/>Protected data

Basic Auth Implementation

Here’s how to implement Basic Authentication in Java:

Server-Side (Java with Spring Boot)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class BasicAuthConfiguration {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            )
            .httpBasic(); // Enable HTTP Basic Authentication
        
        return http.build();
    }
    
    @Bean
    public UserDetailsService userDetailsService() {
        // Create in-memory user for demonstration
        UserDetails user = User.builder()
            .username("john")
            .password("{noop}password123") // {noop} means no password encoding
            .roles("USER")
            .build();
        
        UserDetails admin = User.builder()
            .username("admin")
            .password("{noop}admin123")
            .roles("USER", "ADMIN")
            .build();
        
        return new InMemoryUserDetailsManager(user, admin);
    }
}

Client-Side (Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.util.Base64;

public class BasicAuthClient {
    
    public static void main(String[] args) throws IOException, InterruptedException {
        String username = "john";
        String password = "password123";
        
        // Create Basic Auth header value
        String auth = username + ":" + password;
        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
        String authHeader = "Basic " + encodedAuth;
        
        // Create HTTP client and request
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("http://localhost:8080/api/protected"))
            .header("Authorization", authHeader)
            .GET()
            .build();
        
        // Send request and get response
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        
        System.out.println("Status Code: " + response.statusCode());
        System.out.println("Response Body: " + response.body());
    }
}

Basic Auth Headers

When using Basic Authentication, the HTTP headers look like this:

Request Header:

1
Authorization: Basic dXNlcjpwYXNzd29yZA==

Where dXNlcjpwYXNzd29yZA== is the Base64 encoding of user:password.

Advantages of Basic Auth

AdvantageDescription
SimplicityEasy to implement and understand
Universal SupportSupported by all browsers and HTTP clients
StatelessNo session management required
DebuggingEasy to test with tools like cURL or Postman

Disadvantages of Basic Auth

DisadvantageDescription
Not EncryptedCredentials sent in Base64 (easily decoded)
Repeated TransmissionCredentials sent with every request
No LogoutBrowser caches credentials until closed
Password ExposureVulnerable to man-in-the-middle attacks

Critical: Basic Auth should ONLY be used over HTTPS! Without encryption, credentials can be intercepted and decoded by attackers.

When to Use Basic Auth

Basic Authentication is appropriate for:

  • Development and testing environments
  • Internal APIs with HTTPS
  • Service-to-service communication in secure networks
  • Simple applications with basic security requirements

Avoid Basic Auth for:

  • ❌ Public-facing production applications
  • ❌ Applications handling sensitive data
  • ❌ Any communication over unencrypted HTTP
  • ❌ Long-lived authentication sessions

Digest Authentication

Digest Authentication is an improvement over Basic Auth that provides better security by never sending the actual password over the network. Instead, it uses cryptographic hashing.

How Digest Auth Works

Digest Auth Flow

detailed explanation of the challenge-response mechanism.

  • Client requests a protected resource without credentials
  • Server responds with 401 Unauthorized and sends a challenge containing:
    • Realm: Protection space identifier
    • Nonce: A unique, server-generated value (number used once)
    • Opaque: Optional server-defined data
    • Algorithm: Hash algorithm to use (usually MD5)
  • Client computes a hash using:
    • Username
    • Password
    • Nonce from server
    • HTTP method
    • Requested URI
  • Client sends hashed response back to server
  • Server computes its own hash using stored password and received data
  • Server compares hashes - if they match, authentication succeeds

The key advantage: the password itself is never transmitted, only a hash derived from it.

Digest Auth Sequence Diagram

sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: GET /api/protected
    Server-->>Client: 401 Unauthorized<br/>WWW-Authenticate: Digest realm="API"<br/>nonce="abc123xyz"
    
    Note over Client: User enters<br/>username & password
    Note over Client: Compute hash:<br/>MD5(username:realm:password)<br/>+ nonce + uri + method
    
    Client->>Server: GET /api/protected<br/>Authorization: Digest<br/>username="john", nonce="abc123xyz"<br/>response="computed-hash"
    
    Note over Server: Compute expected hash<br/>Compare with received hash
    
    Server-->>Client: 200 OK<br/>Protected data

Digest Auth Implementation

Here’s how to implement Digest Authentication in Java:

Server-Side (Java with Spring Boot)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;

@Configuration
public class DigestAuthConfiguration {
    
    @Bean
    public DigestAuthenticationEntryPoint digestEntryPoint() {
        DigestAuthenticationEntryPoint entryPoint = new DigestAuthenticationEntryPoint();
        entryPoint.setRealmName("API Realm");
        entryPoint.setKey("unique-server-key-12345");
        return entryPoint;
    }
    
    @Bean
    public DigestAuthenticationFilter digestAuthenticationFilter(
            UserDetailsService userDetailsService) {
        DigestAuthenticationFilter filter = new DigestAuthenticationFilter();
        filter.setUserDetailsService(userDetailsService);
        filter.setAuthenticationEntryPoint(digestEntryPoint());
        return filter;
    }
    
    @Bean
    public UserDetailsService userDetailsService() {
        // Create in-memory users
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        
        manager.createUser(User.builder()
            .username("john")
            .password("password123") // Stored in plain text for digest
            .roles("USER")
            .build());
        
        manager.createUser(User.builder()
            .username("admin")
            .password("admin123")
            .roles("USER", "ADMIN")
            .build());
        
        return manager;
    }
}

Client-Side (Java)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

public class DigestAuthClient {
    
    public static void main(String[] args) throws IOException, InterruptedException {
        String url = "http://localhost:8080/api/protected";
        String username = "john";
        String password = "password123";
        
        HttpClient client = HttpClient.newHttpClient();
        
        // First request to get the challenge
        HttpRequest initialRequest = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .GET()
            .build();
        
        HttpResponse<String> initialResponse = client.send(initialRequest, 
            HttpResponse.BodyHandlers.ofString());
        
        if (initialResponse.statusCode() == 401) {
            // Parse WWW-Authenticate header
            String authHeader = initialResponse.headers()
                .firstValue("WWW-Authenticate")
                .orElseThrow();
            
            Map<String, String> authParams = parseDigestChallenge(authHeader);
            
            // Compute digest response
            String digestResponse = computeDigestResponse(
                username, password, "GET", "/api/protected", authParams);
            
            // Create authorization header
            String authValue = String.format(
                "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", " +
                "uri=\"%s\", response=\"%s\"",
                username, authParams.get("realm"), authParams.get("nonce"),
                "/api/protected", digestResponse
            );
            
            // Second request with digest authentication
            HttpRequest authenticatedRequest = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Authorization", authValue)
                .GET()
                .build();
            
            HttpResponse<String> response = client.send(authenticatedRequest,
                HttpResponse.BodyHandlers.ofString());
            
            System.out.println("Status Code: " + response.statusCode());
            System.out.println("Response Body: " + response.body());
        }
    }
    
    private static Map<String, String> parseDigestChallenge(String header) {
        Map<String, String> params = new HashMap<>();
        String[] parts = header.replace("Digest ", "").split(",");
        
        for (String part : parts) {
            String[] keyValue = part.trim().split("=");
            String key = keyValue[0];
            String value = keyValue[1].replaceAll("\"", "");
            params.put(key, value);
        }
        
        return params;
    }
    
    private static String computeDigestResponse(String username, String password,
            String method, String uri, Map<String, String> params) 
            throws NoSuchAlgorithmException {
        
        String realm = params.get("realm");
        String nonce = params.get("nonce");
        
        // HA1 = MD5(username:realm:password)
        String ha1Input = username + ":" + realm + ":" + password;
        String ha1 = md5Hash(ha1Input);
        
        // HA2 = MD5(method:uri)
        String ha2Input = method + ":" + uri;
        String ha2 = md5Hash(ha2Input);
        
        // Response = MD5(HA1:nonce:HA2)
        String responseInput = ha1 + ":" + nonce + ":" + ha2;
        return md5Hash(responseInput);
    }
    
    private static String md5Hash(String input) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(input.getBytes());
        
        StringBuilder hexString = new StringBuilder();
        for (byte b : digest) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        
        return hexString.toString();
    }
}

Digest Auth Calculation

The digest response is calculated using the following algorithm:

1
2
3
HA1 = MD5(username:realm:password)
HA2 = MD5(method:digestURI)
response = MD5(HA1:nonce:HA2)

Where:

  • HA1: Hash of credentials and realm
  • HA2: Hash of HTTP method and URI
  • response: Final hash sent to server
  • nonce: Server-provided unique value

Advantages of Digest Auth

AdvantageDescription
Password ProtectionPassword never sent over network
Replay ProtectionNonce prevents replay attacks
IntegrityHash includes request method and URI
Better SecuritySignificantly more secure than Basic Auth

Disadvantages of Digest Auth

DisadvantageDescription
ComplexityMore complex to implement
MD5 VulnerabilitiesMD5 algorithm has known weaknesses
Limited AdoptionLess common than modern alternatives
Server StorageRequires storing passwords in recoverable form
No EncryptionOnly authenticates, doesn’t encrypt payload

Important: While Digest Auth is more secure than Basic Auth, it still has limitations. Modern applications should consider OAuth 2.0, JWT, or other contemporary authentication methods.

When to Use Digest Auth

Digest Authentication is appropriate for:

  • Legacy system integration where it’s already implemented
  • Situations where HTTPS is unavailable (though HTTPS is always preferred)
  • APIs requiring better security than Basic Auth without major changes
  • Learning purposes to understand authentication evolution

Basic Auth vs Digest Auth Comparison

Let’s compare the two authentication methods side by side:

FeatureBasic AuthDigest Auth
Password TransmissionBase64 encoded (easily decoded)Never sent (only hash)
Security LevelLow (requires HTTPS)Medium (better but not perfect)
ImplementationVery simpleModerate complexity
Browser SupportUniversalGood but less common
PerformanceFastSlightly slower (hashing)
Replay ProtectionNoneYes (via nonce)
Man-in-the-MiddleVulnerable without HTTPSMore resistant
Use CasesDev/test, simple internal APIsLegacy systems, upgrade from Basic
flowchart TB
    subgraph Comparison[Security Comparison]
        direction LR
        subgraph Basic[Basic Auth]
            B1[Base64 Encoding]
            B2[Password Visible]
            B3[Requires HTTPS]
        end
        subgraph Digest[Digest Auth]
            D1[MD5 Hashing]
            D2[Password Hidden]
            D3[Works without HTTPS]
        end
    end
    
    Basic --> |Less Secure| Security[Security Level]
    Digest --> |More Secure| Security
    Security --> |Evolution| Modern[Modern Methods<br/>OAuth, JWT, SSO]
    
    style Basic fill:#ffcccc
    style Digest fill:#ffffcc
    style Modern fill:#ccffcc

Best Practices for Authentication

Regardless of which authentication method you use, follow these best practices:

General Security Principles

  1. Always Use HTTPS
    • Encrypt all communication, even with Digest Auth
    • Obtain SSL/TLS certificates for your domains
    • Redirect HTTP to HTTPS automatically
  2. Strong Password Policies
    • Require minimum length (at least 8-12 characters)
    • Enforce complexity (uppercase, lowercase, numbers, symbols)
    • Implement password expiration for sensitive systems
    • Prevent common passwords
  3. Rate Limiting
    • Limit authentication attempts per IP address
    • Implement exponential backoff after failures
    • Lock accounts after repeated failures
  4. Logging and Monitoring
    • Log all authentication attempts (success and failure)
    • Monitor for suspicious patterns
    • Alert on potential attacks

Pro Tip: Never log passwords or sensitive data! Log usernames, IP addresses, timestamps, and outcomes only.

Implementation Best Practices

Secure Password Storage

how to properly store user passwords.

Never store passwords in plain text! Always use strong hashing algorithms:

Good:

  • bcrypt (recommended)
  • scrypt
  • Argon2 (most secure)

Bad:

  • Plain text storage ❌
  • MD5 or SHA1 hashing ❌
  • Encryption without salt ❌

Example using bcrypt in Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordManager {
    private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
    
    public String hashPassword(String plainPassword) {
        return encoder.encode(plainPassword);
    }
    
    public boolean verifyPassword(String plainPassword, String hashedPassword) {
        return encoder.matches(plainPassword, hashedPassword);
    }
}

Error Messages

avoiding information leakage through error messages.

Use generic error messages to prevent attackers from determining valid usernames:

Bad:

  • “Invalid password for user ‘john’” ❌
  • “Username ‘admin’ not found” ❌

Good:

  • “Invalid username or password” ✓
  • “Authentication failed” ✓

This prevents username enumeration attacks.

Session Management

handling authenticated sessions securely.

After successful authentication:

  • Generate secure session tokens
    • Use cryptographically random values
    • Make tokens sufficiently long (128+ bits)
  • Set appropriate session timeouts
    • Short timeouts for sensitive operations (banking: 5-15 min)
    • Longer timeouts for less sensitive apps (24 hours max)
  • Implement proper logout
    • Clear session tokens completely
    • Invalidate server-side sessions
    • Clear client-side cookies
  • Use secure cookie attributes
    1
    
     Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict
    

Testing Authentication

As a junior developer, you need to know how to test authentication implementations:

Manual Testing with cURL

Basic Auth:

1
2
3
4
5
6
# Using username and password directly
curl -u username:password http://localhost:8080/api/protected

# Using Authorization header
curl -H "Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=" \
     http://localhost:8080/api/protected

Digest Auth:

1
2
# cURL handles digest auth automatically
curl --digest -u username:password http://localhost:8080/api/protected

Automated Testing with JUnit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Base64;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
public class AuthenticationTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    public void testUnauthorizedAccessWithoutCredentials() throws Exception {
        // Should return 401 when accessing protected resource without auth
        mockMvc.perform(get("/api/protected"))
            .andExpect(status().isUnauthorized());
    }
    
    @Test
    public void testSuccessfulBasicAuthentication() throws Exception {
        // Create Basic Auth header
        String auth = "john:password123";
        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
        String authHeader = "Basic " + encodedAuth;
        
        // Should return 200 with valid credentials
        mockMvc.perform(get("/api/protected")
                .header("Authorization", authHeader))
            .andExpect(status().isOk());
    }
    
    @Test
    public void testFailedAuthenticationWithInvalidCredentials() throws Exception {
        // Create Basic Auth header with wrong password
        String auth = "john:wrongpassword";
        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
        String authHeader = "Basic " + encodedAuth;
        
        // Should return 401 with invalid credentials
        mockMvc.perform(get("/api/protected")
                .header("Authorization", authHeader))
            .andExpect(status().isUnauthorized());
    }
}

Real-World Examples

Let’s look at practical scenarios where authentication is used:

Example 1: RESTful API Authentication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import org.springframework.web.bind.annotation.*;
import org.springframework.security.core.Authentication;

@RestController
@RequestMapping("/api")
public class UserController {
    
    // Public endpoint - no authentication required
    @GetMapping("/public/health")
    public String healthCheck() {
        return "API is running";
    }
    
    // Protected endpoint - authentication required
    @GetMapping("/user/profile")
    public UserProfile getUserProfile(Authentication authentication) {
        String username = authentication.getName();
        
        // Return user profile based on authenticated user
        return userService.findByUsername(username);
    }
    
    // Admin-only endpoint - authentication + authorization required
    @GetMapping("/admin/users")
    public List<User> getAllUsers(Authentication authentication) {
        // This endpoint requires ROLE_ADMIN
        return userService.findAll();
    }
}

Example 2: Microservices Communication

In microservice architectures, services often authenticate with each other:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.*;
import java.util.Base64;

@Service
public class OrderService {
    
    private final RestTemplate restTemplate = new RestTemplate();
    private final String inventoryServiceUrl = "http://inventory-service:8080";
    
    public boolean checkInventory(String productId) {
        // Create Basic Auth credentials for service-to-service communication
        String serviceUsername = "order-service";
        String servicePassword = "secure-service-password";
        
        String auth = serviceUsername + ":" + servicePassword;
        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
        
        // Create headers with authentication
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Basic " + encodedAuth);
        
        HttpEntity<String> entity = new HttpEntity<>(headers);
        
        // Call inventory service with authentication
        ResponseEntity<InventoryStatus> response = restTemplate.exchange(
            inventoryServiceUrl + "/api/inventory/" + productId,
            HttpMethod.GET,
            entity,
            InventoryStatus.class
        );
        
        return response.getBody().isAvailable();
    }
}

Moving Beyond Basic and Digest Auth

While Basic and Digest Authentication are important to understand, modern applications typically use more advanced methods:

Modern Authentication Methods

MethodDescriptionUse Case
OAuth 2.0Industry-standard authorization frameworkThird-party integrations, social login
JWT (JSON Web Tokens)Stateless token-based authenticationMicroservices, mobile apps
Single Sign-On (SSO)One login for multiple applicationsEnterprise applications
Multi-Factor Authentication (MFA)Multiple verification methodsHigh-security applications
Biometric AuthenticationFingerprint, face recognitionMobile applications

Learning Path: Once you’re comfortable with Basic and Digest Auth, explore OAuth 2.0 and JWT tokens as your next steps in authentication mastery!

Common Pitfalls to Avoid

1. Storing Passwords Incorrectly

Don’t do this:

1
2
3
// NEVER store passwords in plain text!
String password = "password123";
database.save(username, password);

Do this instead:

1
2
3
4
// Always hash passwords before storing
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String hashedPassword = encoder.encode("password123");
database.save(username, hashedPassword);

2. Using Basic Auth Over HTTP

Don’t do this:

1
2
// NEVER use Basic Auth without HTTPS!
String url = "http://api.example.com/data";

Do this instead:

1
2
// Always use HTTPS with Basic Auth
String url = "https://api.example.com/data";

3. Hardcoding Credentials

Don’t do this:

1
2
3
// NEVER hardcode credentials in source code!
String username = "admin";
String password = "admin123";

Do this instead:

1
2
3
// Use environment variables or configuration files
String username = System.getenv("API_USERNAME");
String password = System.getenv("API_PASSWORD");

4. Ignoring Rate Limiting

Without rate limiting, your authentication endpoints are vulnerable to brute-force attacks. Always implement throttling:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.time.Instant;

@Component
public class LoginAttemptService {
    
    private final int MAX_ATTEMPTS = 5;
    private final ConcurrentHashMap<String, Integer> attemptsCache = 
        new ConcurrentHashMap<>();
    
    public void loginSucceeded(String username) {
        attemptsCache.remove(username);
    }
    
    public void loginFailed(String username) {
        int attempts = attemptsCache.getOrDefault(username, 0);
        attemptsCache.put(username, attempts + 1);
    }
    
    public boolean isBlocked(String username) {
        return attemptsCache.getOrDefault(username, 0) >= MAX_ATTEMPTS;
    }
}

Conclusion

Authentication is a fundamental concept in software security that every junior developer must understand. In this post, we’ve covered:

  • What authentication is and why it matters
  • How authentication differs from authorization
  • Basic Authentication: Simple but requires HTTPS
  • Digest Authentication: More secure challenge-response mechanism
  • Best practices for implementing authentication securely
  • Common pitfalls to avoid
  • Testing strategies for authentication

Key Takeaways

  1. 🔐 Always use HTTPS with Basic Authentication
  2. 🔑 Never store passwords in plain text - use strong hashing algorithms
  3. 🚫 Implement rate limiting to prevent brute-force attacks
  4. 📊 Log authentication events for security monitoring
  5. 🎯 Use generic error messages to prevent information leakage

Next Steps

Now that you understand the fundamentals of authentication:

  1. Practice implementing Basic Auth in a simple REST API
  2. Experiment with Digest Auth to see the differences
  3. Learn about password hashing with bcrypt or Argon2
  4. Explore more advanced topics like OAuth 2.0 and JWT
  5. Study session management and token-based authentication

Remember: Security is not a one-time implementation but an ongoing process. Stay updated on security best practices and always prioritize user safety!

"Authentication might seem complex at first, but mastering these fundamentals will serve you throughout your career. Every application you build will need some form of authentication, so understanding these core concepts is time well spent!"

Additional Resources

Happy coding, and stay secure! 🔒

This post is licensed under CC BY 4.0 by the author.