ActivationService - Aligheri/jwt-owasp-based-starter GitHub Wiki
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.
Add the following properties to your application.yml
or application.properties
:
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
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
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 |
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);
}
}
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"));
}
}
}
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
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
Sends an activation email to the specified address.
-
Parameters:
-
email
- Recipient email address -
code
- Activation code to include
-
-
Throws:
EmailException
if sending fails
Immediately invalidates an activation code.
-
Parameters:
identifier
- Unique identifier - Use Case: Clean up after successful activation
@Configuration
public class ActivationConfig {
@Bean
@ConditionalOnMissingBean
public ActivationService customActivationService(
EmailConfig emailConfig,
SpringTemplateEngine templateEngine,
JavaMailSender mailSender
) {
return new CustomActivationService(emailConfig, templateEngine, mailSender);
}
}
@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"));
}
}
}
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