Registering Users

The Registration Endpoint

public record RegisterRequest(
        @Email @NotBlank String email,
        @NotBlank @Size(min = 8, max = 64) String password) {}

public record UserResponse(Long id, String email, String role) {}

@Service
public class RegistrationService {

    private final UserRepository users;
    private final PasswordEncoder passwordEncoder;

    public RegistrationService(UserRepository users, PasswordEncoder passwordEncoder) {
        this.users = users;
        this.passwordEncoder = passwordEncoder;
    }

    @Transactional
    public UserResponse register(RegisterRequest req) {
        String email = req.email().toLowerCase();
        if (users.existsByEmail(email)) {
            throw new ResponseStatusException(HttpStatus.CONFLICT, "Email already registered");
        }
        User saved = users.save(new User(email, passwordEncoder.encode(req.password()), "CUSTOMER"));
        return new UserResponse(saved.getId(), saved.getEmail(), saved.getRole());
    }
}

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private final RegistrationService registrationService;

    public AuthController(RegistrationService registrationService) {
        this.registrationService = registrationService;
    }

    @PostMapping("/register")
    @ResponseStatus(HttpStatus.CREATED)
    public UserResponse register(@Valid @RequestBody RegisterRequest req) {
        return registrationService.register(req);
    }
}
  • The role is set by the server (CUSTOMER), never taken from the request body. Otherwise anyone could register as ADMIN.
  • The response never contains the password or its hash.
  • /api/auth/register must be permitAll() in the security config.

Register, Then Log In

Example

Sign up, try again with the same email, then use the new account:

curl -i -X POST localhost:8080/api/auth/register -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"Neha@2024secure"}'

curl -i -X POST localhost:8080/api/auth/register -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"another-pass"}'

curl -i -u [email protected]:Neha@2024secure localhost:8080/api/cart

Example output

HTTP/1.1 201
Content-Type: application/json

{"id":42,"email":"[email protected]","role":"CUSTOMER"}

HTTP/1.1 409
Content-Type: application/problem+json

{"type":"about:blank","title":"Conflict","status":409,"detail":"Email already registered","instance":"/api/auth/register"}

HTTP/1.1 200
Content-Type: application/json

{"items":[],"total":0.0}

What Is in the Database

Stored Row

SELECT id, email, password_hash, role FROM users WHERE email = '[email protected]';

Example output

 id |     email      |                        password_hash                         |   role
----+----------------+--------------------------------------------------------------+----------
 42 | [email protected] | $2a$12$Qm9yN3VhY2hTYWx0VmFsdWUu8yHk3f1QmZ0uZc6rT0qJ2a1Xv9bKe | CUSTOMER

Exercise: Add a rule to RegistrationService that rejects passwords containing the user's email name (the part before @), ignoring case.

Show answer
String name = email.substring(0, email.indexOf('@'));
if (req.password().toLowerCase().contains(name)) {
    throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Password must not contain your email name");
}