Skip to main content

Overview

Trippins uses JWT (JSON Web Token) based authentication to secure user access. The platform supports three user types: Anonymous users, Registered users, and Administrators, each with different access levels.

How Authentication Works

JWT-Based Authentication

Trippins implements stateless authentication using JWT tokens. When you log in successfully, the server generates a JWT token that contains your user identity and roles. This token is stored in your browser’s local storage and included in all subsequent API requests.
// The AuthService manages authentication state
login(credentials: { email: string; password: string }): Observable<AuthenticationResponse> {
  return this.http.post<AuthenticationResponse>(`${environment.baseUrlApi}/login`, credentials).pipe(
    tap(response => {
      this.storeJwt(response.jwt);
      this.storeRoles(response.roles);
      this.username = credentials.email;
    })
  );
}

Authentication Flow

1

Submit credentials

User enters email and password through the login form
2

Server validation

Backend validates credentials using Spring Security
3

Token generation

If valid, server generates JWT token with user roles
4

Token storage

Frontend stores JWT and roles in localStorage
5

Authenticated requests

All API requests include the JWT in the Authorization header

Registration Process

Creating an Account

New users can register by providing the following information:
registerForm: FormGroup = this.fb.group({
  name: ['', [Validators.required]],
  email: ['', [Validators.required, Validators.email]],
  dni: ['', [
    Validators.required,
    Validators.pattern(/^[0-9]{8}[A-Z]$/),
    Validators.maxLength(9)
  ]],
  number: ['', [Validators.required]],
  password: ['', [
    Validators.required,
    Validators.minLength(8)
  ]],
  password2: ['', [Validators.required]]
}, { validator: this.passwordMatchValidator });

Validation Rules

Must be exactly 9 characters: 8 digits followed by 1 uppercase letter (e.g., 12345678A)
  • Must be a valid email format
  • Must be unique across the system
  • Used as the login username
  • Minimum 8 characters
  • Stored using BCrypt encryption
  • Must match confirmation password

Registration API Endpoint

POST /v1/api/users

Create a new user account
curl -X POST http://localhost:8080/v1/api/users \
  -H "Content-Type: application/json" \
  -d '{
    "name": "John Doe",
    "email": "[email protected]",
    "dni": "12345678A",
    "number": 123456789,
    "password": "securepass123"
  }'
This endpoint is publicly accessible and does not require authentication. New users are automatically assigned the ROLE_USER role.

Login Process

User Login

1

Navigate to login page

Access the login form at /new/login
2

Enter credentials

Provide your registered email and password
3

Authentication

System validates credentials and generates JWT
4

Redirect

On success, redirected to home page or return URL

Login API Endpoint

POST /v1/api/login

Authenticate user and receive JWT token
@PostMapping("/v1/api/login")
public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) {
    // Authenticate using Spring Security
    authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(
            authenticationRequest.getEmail(),
            authenticationRequest.getPassword()
        )
    );

    // Load user details
    final UserDetails userDetails = userDetailsService.loadUserByUsername(
        authenticationRequest.getEmail()
    );
    
    // Generate JWT token
    final String jwt = jwtUtil.generateToken(userDetails);

    // Extract roles
    List<String> roles = userDetails.getAuthorities().stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors.toList());

    return ResponseEntity.ok(new AuthenticationResponse(jwt, roles));
}

Response Format

{
  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "roles": ["ROLE_USER"]
}

Security Configuration

Access Control

Trippins implements role-based access control (RBAC) with different permission levels:
// Public endpoints - No authentication required
.requestMatchers("/", "/index", "/about", "/contact", "/register").permitAll()
.requestMatchers("/room", "/v1/api/query").permitAll()

// User registration - Public
.requestMatchers(HttpMethod.POST, "/addUser", "/v1/api/users").permitAll()

// Authenticated users only
.requestMatchers("/room/{code}", "/roomDetails").authenticated()
.requestMatchers("/newHotel", "/booking", "/profile").authenticated()

// Admin only endpoints
.requestMatchers("/admin/**", "/v1/api/admin/**").hasRole("ADMIN")
.requestMatchers("/v1/api/users/**", "/v1/api/reviews/**").hasRole("ADMIN")

Password Encryption

All passwords are encrypted using BCrypt before storage:
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
Never store or transmit passwords in plain text. Always use the encrypted version from the database.

Session Management

Stateless Architecture

Trippins uses stateless session management:
.sessionManagement(session -> session
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
This means:
  • No server-side session storage
  • JWT token carries all authentication info
  • Scales easily across multiple servers
  • Logout simply requires removing the token client-side

JWT Token Storage

// Store JWT in localStorage
private storeJwt(jwt: string): void {
  localStorage.setItem(this.JWT_KEY, jwt);
}

// Store user roles
private storeRoles(roles: string[]): void {
  localStorage.setItem(this.ROLES_KEY, JSON.stringify(roles));
}

// Check if user is logged in
isLoggedIn(): boolean {
  return !!this.getJwt();
}

Logout Process

To log out, simply clear the stored JWT and roles:
logout(): void {
  localStorage.removeItem(this.JWT_KEY);
  localStorage.removeItem(this.ROLES_KEY);
}
After logout, users are typically redirected to the home page or login screen. The JWT token becomes invalid client-side, preventing further authenticated requests.

Role-Based Access

Checking User Roles

// Check if user has a specific role
hasRole(role: string): boolean {
  return this.getRoles().includes(role);
}

// Check if user has any of the specified roles
hasAnyRole(roles: string[]): boolean {
  var currentRoles: string[] = this.getRoles();
  for (let index = 0; index < roles.length; index++) {
    if (currentRoles.includes(roles[index])){
      return true;
    }
  }
  return false;
}

Common Role Checks

Regular User

if (authService.hasRole('ROLE_USER')) {
  // Can book rooms, write reviews
}

Administrator

if (authService.hasRole('ROLE_ADMIN')) {
  // Can manage all resources
}

Best Practices

While localStorage is convenient, consider using httpOnly cookies for enhanced security in production environments to prevent XSS attacks.
Implement token refresh mechanisms for long-lived sessions. Currently, tokens don’t expire, but this should be addressed in production.
Always use HTTPS in production to encrypt authentication credentials during transmission.
Enforce strong password requirements and consider adding password strength indicators in the UI.

Troubleshooting

  • Check if JWT token is present in localStorage
  • Verify token hasn’t been manually deleted
  • Ensure token is being sent in Authorization header
  • Log in again to obtain a fresh token
  • Verify email is not already registered
  • Check DNI format (8 digits + 1 letter)
  • Ensure password is at least 8 characters
  • Confirm passwords match
  • Verify email and password are correct
  • Check if account exists in database
  • Ensure password encryption matches stored hash

User Roles

Learn about different user types and permissions

Making Reservations

Requires authentication to book rooms

Build docs developers (and LLMs) love