ActivationService - Aligheri/jwt-owasp-based-starter GitHub Wiki

Spring Boot Activation Service

Overview

Starter's activation service provides a ready-to-use solution for implementing user account activation workflows in your Spring Boot applications. It handles activation code generation, validation, email sending, and code management with configurable expiration times.

Configuration

Add the following properties to your application.yml or application.properties:

YAML Configuration

app:
  email:
    enabled: true
    host: smtp.gmail.com
    port: 587
    username: [email protected]
    password: your-app-password
    from: [email protected]
    activation-base-url: https://yourapp.com/activate-account?code=
    template-name: activation-email

Properties Configuration

app.email.enabled=true
app.email.host=smtp.gmail.com
app.email.port=587
app.email.username[email protected]
app.email.password=your-app-password
app.email.from[email protected]
app.email.activation-base-url=https://yourapp.com/activate-account?code=
app.email.template-name=activation-email

Configuration Properties

Property Description Default Required
app.email.enabled Enable/disable email sending false No
app.email.host SMTP server host - Yes
app.email.port SMTP server port 587 No
app.email.username SMTP username - Yes
app.email.password SMTP password - Yes
app.email.from From email address - Yes
app.email.activation-base-url Base URL for activation links - Yes
app.email.template-name Thymeleaf template name activation-email No

Usage

Basic Integration

Inject the ActivationService into your authentication or user management service:

@Service
public class AuthenticationService {
    
    private final ActivationService activationService;
    private final UserRepository userRepository;
    
    public AuthenticationService(ActivationService activationService, 
                               UserRepository userRepository) {
        this.activationService = activationService;
        this.userRepository = userRepository;
    }
    
    public void registerUser(String email, String password) {
        // Create user account
        User user = createUser(email, password);
        userRepository.save(user);
        
        // Generate and send activation code
        String code = activationService.generateActivationCode(email);
        activationService.sendActivationEmail(email, code);
    }
    
    public void activateAccount(String email, String code) {
        if (!activationService.validateActivationCode(email, code)) {
            throw new InvalidActivationCodeException("Invalid or expired activation code");
        }
        
        User user = userRepository.findByEmail(email)
            .orElseThrow(() -> new UsernameNotFoundException("User not found"));
            
        user.setActivated(true);
        userRepository.save(user);
        
        // Clean up the activation code
        activationService.invalidateCode(email);
    }
}

Controller Implementation

Create a controller to handle activation requests:

@Controller
public class ActivationController {
    
    private final AuthenticationService authenticationService;
    
    public ActivationController(AuthenticationService authenticationService) {
        this.authenticationService = authenticationService;
    }
    
    @GetMapping("/activate-account")
    public String activateAccount(
            @RequestParam String email,
            @RequestParam String code,
            RedirectAttributes redirectAttributes
    ) {
        try {
            authenticationService.activateAccount(email, code);
            redirectAttributes.addFlashAttribute("message", "Account activated successfully!");
            return "redirect:/login";
        } catch (InvalidActivationCodeException e) {
            redirectAttributes.addFlashAttribute("error", e.getMessage());
            return "redirect:/activation-error";
        } catch (UsernameNotFoundException e) {
            redirectAttributes.addFlashAttribute("error", "User not found");
            return "redirect:/registration";
        }
    }
    
    @PostMapping("/resend-activation")
    @ResponseBody
    public ResponseEntity<?> resendActivation(@RequestParam String email) {
        try {
            String code = activationService.generateActivationCode(email);
            activationService.sendActivationEmail(email, code);
            return ResponseEntity.ok(Map.of("message", "Activation email sent successfully"));
        } catch (Exception e) {
            return ResponseEntity.badRequest()
                .body(Map.of("error", "Failed to send activation email"));
        }
    }
}

API Reference

ActivationService Interface

generateActivationCode(String identifier)

Generates a new activation code for the given identifier.

  • Parameters: identifier - Unique identifier (typically email)
  • Returns: Generated activation code (6 characters)
  • Expiration: 24 hours from generation

validateActivationCode(String identifier, String code)

Validates an activation code for the given identifier.

  • Parameters:
    • identifier - Unique identifier
    • code - Activation code to validate
  • Returns: true if valid and not expired, false otherwise

sendActivationEmail(String email, String code)

Sends an activation email to the specified address.

  • Parameters:
    • email - Recipient email address
    • code - Activation code to include
  • Throws: EmailException if sending fails

invalidateCode(String identifier)

Immediately invalidates an activation code.

  • Parameters: identifier - Unique identifier
  • Use Case: Clean up after successful activation

Examples

Custom Configuration Bean

@Configuration
public class ActivationConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public ActivationService customActivationService(
            EmailConfig emailConfig,
            SpringTemplateEngine templateEngine,
            JavaMailSender mailSender
    ) {
        return new CustomActivationService(emailConfig, templateEngine, mailSender);
    }
}

REST API Integration

@RestController
@RequestMapping("/api/auth")
public class AuthRestController {
    
    private final ActivationService activationService;
    
    @PostMapping("/activate")
    public ResponseEntity<?> activate(@RequestBody ActivationRequest request) {
        boolean isValid = activationService.validateActivationCode(
            request.getEmail(), 
            request.getCode()
        );
        
        if (isValid) {
            // Activate user account
            activationService.invalidateCode(request.getEmail());
            return ResponseEntity.ok(new ActivationResponse("Account activated successfully"));
        } else {
            return ResponseEntity.badRequest()
                .body(new ErrorResponse("Invalid or expired activation code"));
        }
    }
}

Email Templates

Create a Thymeleaf template at src/main/resources/templates/activation-email.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Account Activation</title>
    <style>
        body { font-family: Arial, sans-serif; }
        .container { max-width: 600px; margin: 0 auto; padding: 20px; }
        .button { 
            background-color: #007bff; 
            color: white; 
            padding: 10px 20px; 
            text-decoration: none; 
            border-radius: 5px; 
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>Welcome to Our Application!</h2>
        <p>Thank you for registering. Please click the but
⚠️ **GitHub.com Fallback** ⚠️