diff --git a/.env b/.env
new file mode 100644
index 0000000000000000000000000000000000000000..20bfb0a74c8e88ae613364a837696f2fba800197
--- /dev/null
+++ b/.env
@@ -0,0 +1,9 @@
+AUTH_SERVICE_HOSTNAME=auth-api
+USERS_SERVICE_HOSTNAME=users-api
+HOTELS_SERVICE_HOSTNAME=hotels-api
+BOOKINGS_SERVICE_HOSTNAME=bookings-api
+ROOMS_BOOKING_SERVICE_HOSTNAME=rooms-booking-api
+DB_SERVICE_HOSTNAME=RoomsBooking-database
+DB_DATABASE_NAME=RoomsBooking
+DB_USER=user
+BD_PASSWORD=password
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 103bd2c647aba772a54dd4140f55da75da9f20f9..9d69baa07a9349a0ed0d73d664ad697aa5e65a9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,10 @@
 ### Own ###
 taller
-*.pdf
\ No newline at end of file
+*.pdf
+**/target/
+**/.vscode
+**/tmp
+**/*.tmp
+*.ln
+*.log
+
diff --git a/DBCS_2.mp4 b/DBCS_2.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..e5ebadf041596dd44e4abf085f9a7439152a903d
Binary files /dev/null and b/DBCS_2.mp4 differ
diff --git a/angular/RestClient/Dockerfile b/angular/RestClient/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..3557c5fae54aafd2c685c1cfffd9080037b02932
--- /dev/null
+++ b/angular/RestClient/Dockerfile
@@ -0,0 +1,28 @@
+# Etapa 1: Construcción de la aplicación Angular
+FROM node:20 AS build
+
+# Establecer el directorio de trabajo
+WORKDIR /app
+
+# Copiar archivos de package.json e instalar dependencias
+COPY package*.json ./
+RUN npm install
+
+# Copiar el código fuente de Angular
+COPY . .
+
+# Compilar la aplicación Angular para producción
+RUN npm run build -- --output-path=dist/app --c production
+
+# Etapa 2: Servidor Nginx para producción
+FROM nginx:alpine AS production
+
+# Copiar los archivos de construcción generados en la etapa anterior a la carpeta de Nginx
+COPY --from=build /app/dist/app/browser /usr/share/nginx/html
+COPY --from=build /app/nginx-custom.conf /etc/nginx/conf.d/default.conf
+
+# Exponer el puerto 80 para Nginx
+EXPOSE 80
+
+# Iniciar Nginx
+CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file
diff --git a/angular/RestClient/angular.json b/angular/RestClient/angular.json
index b9017eb47cbc0d902128a7211d7d8f08c6d1f48a..6f0edcd86785afd8cc8db5af00a9e2da641eb1d0 100644
--- a/angular/RestClient/angular.json
+++ b/angular/RestClient/angular.json
@@ -32,20 +32,21 @@
             "scripts": [
               "node_modules/jquery/dist/jquery.min.js",
               "node_modules/bootstrap/dist/js/bootstrap.min.js"
-            ],
-            "server": "src/main.server.ts",
-            "prerender": true,
-            "ssr": {
-              "entry": "server.ts"
-            }
+            ]
           },
           "configurations": {
             "production": {
+              "fileReplacements": [
+                {
+                  "replace": "src/environments/environment.ts",
+                  "with": "src/environments/environment.prod.ts"
+                }
+              ],
               "budgets": [
                 {
                   "type": "initial",
-                  "maximumWarning": "500kB",
-                  "maximumError": "1MB"
+                  "maximumWarning": "1.2MB",
+                  "maximumError": "1.3MB"
                 },
                 {
                   "type": "anyComponentStyle",
@@ -71,6 +72,12 @@
             },
             "development": {
               "buildTarget": "RestClient:build:development"
+            },
+            "monolith": {
+              "buildTarget": "RestClient:build:monolith"
+            },
+            "microservices": {
+              "buildTarget": "RestClient:build:microservices"
             }
           },
           "defaultConfiguration": "development"
@@ -102,8 +109,5 @@
         }
       }
     }
-  },
-  "cli": {
-    "analytics": false
   }
 }
diff --git a/angular/RestClient/nginx-custom.conf b/angular/RestClient/nginx-custom.conf
new file mode 100644
index 0000000000000000000000000000000000000000..7bb593987ce2b7ab6fc25a2fa746a5f387a662da
--- /dev/null
+++ b/angular/RestClient/nginx-custom.conf
@@ -0,0 +1,12 @@
+server {
+  listen 80;
+
+  root /usr/share/nginx/html;
+  index index.html;
+  
+  location / {
+    try_files $uri $uri/ /index.html =404;
+  }
+  
+  error_page 404 /index.html;
+}
\ No newline at end of file
diff --git a/angular/RestClient/package-lock.json b/angular/RestClient/package-lock.json
index a5d201bf11869266cc967b0e328660d5d8870bc7..af241069cc8acddc06a48c70b6d1dd25125a07ac 100644
--- a/angular/RestClient/package-lock.json
+++ b/angular/RestClient/package-lock.json
@@ -25,6 +25,7 @@
         "bootstrap": "^3.4.0",
         "express": "^4.18.2",
         "jquery": "^3.4.1",
+        "jwt-decode": "^4.0.0",
         "rxjs": "~7.8.0",
         "tslib": "^2.3.0",
         "zone.js": "~0.14.10"
@@ -5468,9 +5469,9 @@
       "license": "ISC"
     },
     "node_modules/bootstrap": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.0.tgz",
-      "integrity": "sha512-F1yTDO9OHKH0xjl03DsOe8Nu1OWBIeAugGMhy3UTIYDdbbIPesQIhCEbj+HEr6wqlwweGAlP8F3OBC6kEuhFuw==",
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz",
+      "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA==",
       "license": "MIT",
       "engines": {
         "node": ">=6"
@@ -6329,9 +6330,9 @@
       }
     },
     "node_modules/cross-spawn": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
-      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "version": "7.0.6",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -8555,9 +8556,9 @@
       }
     },
     "node_modules/jquery": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
-      "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==",
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
+      "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
       "license": "MIT"
     },
     "node_modules/js-tokens": {
@@ -8654,6 +8655,15 @@
       ],
       "license": "MIT"
     },
+    "node_modules/jwt-decode": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+      "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
     "node_modules/karma": {
       "version": "6.4.4",
       "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
diff --git a/angular/RestClient/package.json b/angular/RestClient/package.json
index 262eacd1144f0eaab113dda4aad24eedab9ff958..a17778b07370cfdb71c9dcc5f6137b413ab929cf 100644
--- a/angular/RestClient/package.json
+++ b/angular/RestClient/package.json
@@ -28,6 +28,7 @@
     "bootstrap": "^3.4.0",
     "express": "^4.18.2",
     "jquery": "^3.4.1",
+    "jwt-decode": "^4.0.0",
     "rxjs": "~7.8.0",
     "tslib": "^2.3.0",
     "zone.js": "~0.14.10"
diff --git a/angular/RestClient/src/app/app.config.ts b/angular/RestClient/src/app/app.config.ts
index 1f668ea1741efbdb605f6d418a9a93f53f1327fa..c0664f634af159df0208b1a877011ce66c1471ac 100644
--- a/angular/RestClient/src/app/app.config.ts
+++ b/angular/RestClient/src/app/app.config.ts
@@ -1,19 +1,22 @@
 import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
 import { provideRouter } from '@angular/router';
 import { routes } from './app.routes';
-import { provideHttpClient, withFetch } from '@angular/common/http';
+import {
+  provideHttpClient,
+  withFetch,
+  withInterceptors,
+} from '@angular/common/http';
 import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
 import { ReactiveFormsModule } from '@angular/forms'; // Added import for ReactiveFormsModule
 import { provideNativeDateAdapter } from '@angular/material/core';
-import { provideClientHydration } from '@angular/platform-browser';
+import { authRequest } from './security/auth.interceptor';
 
 export const appConfig: ApplicationConfig = {
   providers: [
     provideNativeDateAdapter(),
     provideZoneChangeDetection({ eventCoalescing: true }),
     provideRouter(routes),
-    provideClientHydration(),
-    provideHttpClient(withFetch()),
+    provideHttpClient(withFetch(), withInterceptors([authRequest])),
     provideAnimationsAsync(),
     ReactiveFormsModule,
   ],
diff --git a/angular/RestClient/src/app/app.routes.ts b/angular/RestClient/src/app/app.routes.ts
index b8ee77b36e9b121a4cf804746bde023134978c1c..4be490fc5813f4e0bca3846bfd83e67c06ce0654 100644
--- a/angular/RestClient/src/app/app.routes.ts
+++ b/angular/RestClient/src/app/app.routes.ts
@@ -1,43 +1,184 @@
-import { Routes } from '@angular/router';
+import { Route, Routes } from '@angular/router';
 import { HotelListComponent } from './core/features/hotel/hotel-list/hotel-list.component';
 import { BookingComponent } from './core/features/bookings/booking/booking.component';
 import { HotelRegisterComponent } from './core/features/hotel/hotel-register/hotel-register.component';
 import { MainPageComponent } from './core/features/user/main-page/main-page.component';
 import { BookingListComponent } from './core/features/bookings/booking-list/booking-list.component';
 import { UserBookingListComponent } from './core/features/user/user-booking-list/user-booking-list.component';
+import { UserFormComponent } from './core/features/user/user-form/user-form.component';
+import { UnauthorizedComponent } from './page/unauthorized/unauthorized.component';
+import { rolGuard } from './security/rol.guard';
+import { UserRol, UserRolesArray } from './types';
 
-export const routes: Routes = [
+interface RouteData {
+  expectedRole: UserRol | UserRol[];
+}
+
+type AppRoute = Omit<Route, 'data'> & {
+  data?: RouteData;
+};
+
+export const routes: AppRoute[] = [
+  // Auth
+  {
+    path: 'login',
+    component: UserFormComponent,
+  },
+  {
+    path: 'register',
+    component: UserFormComponent,
+  },
+
+  // Hoteles
   {
-    path: '', // Ruta principal
-    component: MainPageComponent, // Componente de la página principal
+    path: 'hotels', // Ruta para la lista de hoteles
+    component: HotelListComponent,
   },
   {
-    path: 'bookings/search',
-    component: BookingListComponent,
+    path: 'hotels/register', // Registrar nuevo hotel
+    component: HotelRegisterComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'HOTEL_ADMIN' },
   },
   {
-    path: 'bookings/new',
+    path: 'hotels/:id', // Hotel concreto
+    component: HotelRegisterComponent,
+  },
+
+  // Usuario
+  {
+    path: 'me', // Main
+    canActivate: [rolGuard],
+    data: { expectedRole: UserRolesArray },
+    component: UserFormComponent,
+  },
+  {
+    path: 'me/edit', // Main
+    component: UserFormComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: UserRolesArray },
+  },
+  {
+    path: 'me/change-passwd', // Main
+    component: UserFormComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: UserRolesArray },
+  },
+  // Usuario HOTEL admin
+  {
+    path: 'me/hotels',
+    component: HotelListComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'HOTEL_ADMIN' },
+  },
+  {
+    path: 'me/hotels/:id',
+    component: HotelRegisterComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'HOTEL_ADMIN' },
+  },
+  // {
+  //   path: 'me/hotels/:id/bookings',
+  //   component: BookingListComponent,
+  // },
+  // {
+  //   path: 'me/hotels/:id/rooms/:id/bookings',
+  //   component: BookingListComponent,
+  // },
+
+  // Usuario Cliente
+  {
+    path: 'me/bookings',
+    component: UserBookingListComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'CLIENT' },
+  },
+  {
+    path: 'me/bookings/:id',
     component: BookingComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'CLIENT' },
+  },
+  {
+    path: 'me/bookings/new',
+    component: BookingComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'CLIENT' },
+  },
+
+  // Administrador
+  {
+    path: 'admin', // Main
+    component: UserFormComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'ADMIN' },
+  },
+  {
+    path: 'admin/users', // Main
+    component: MainPageComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'ADMIN' },
   },
   {
-    path: 'users/:id/bookings',
+    path: 'admin/users/:id', // Main
+    component: UserFormComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'ADMIN' },
+  },
+  {
+    path: 'admin/users/:id/edit', // Main
+    component: UserFormComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'ADMIN' },
+  },
+  {
+    path: 'admin/users/:id/change-passwd', // Main
+    component: UserFormComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'ADMIN' },
+  },
+  {
+    path: 'admin/users/:id/bookings',
     component: UserBookingListComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'ADMIN' },
   },
+  // {
+  //   path: 'admin/users/:id/bookings/:bookingId',
+  //   component: BookingComponent,
+  // },
   {
-    path: 'hotels',
+    path: 'admin/users/:id/hotels',
     component: HotelListComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'ADMIN' },
   },
   {
-    path: 'hotels/new',
+    path: 'admin/users/:userId/hotels/:id',
     component: HotelRegisterComponent,
+    canActivate: [rolGuard],
+    data: { expectedRole: 'ADMIN' },
   },
+  // {
+  //   path: 'admin/users/:userId/hotels/:id/bookings',
+  //   component: BookingListComponent,
+  //   canActivate: [rolGuard],
+  //   data: { expectedRole: 'ADMIN' },
+  // },
+  // {
+  //   path: 'admin/users/:userId/hotels/:hotelId/rooms/:id/bookings',
+  //   component: BookingListComponent,
+  //   canActivate: [rolGuard],
+  //   data: { expectedRole: 'ADMIN' },
+  // },
+
   {
-    path: 'hotels/:id',
-    component: HotelRegisterComponent,
+    path: 'unauthorized',
+    component: UnauthorizedComponent,
   },
   {
     path: '**',
-    redirectTo: '',
+    redirectTo: '/login',
     pathMatch: 'full',
   },
 ];
diff --git a/angular/RestClient/src/app/core/features/auth/login/login.component.css b/angular/RestClient/src/app/core/features/auth/login/login.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..79b4834ee543b62f19df182154eeb62fa0911db1
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/auth/login/login.component.css
@@ -0,0 +1,21 @@
+.container {
+  max-width: 600px;
+  margin-top: 2rem;
+  padding: 20px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  background-color: #f9f9f9;
+}
+
+h2 {
+  text-align: center;
+  margin-bottom: 20px;
+}
+
+.form-group {
+  margin-bottom: 15px;
+}
+
+label {
+  font-weight: bold;
+}
diff --git a/angular/RestClient/src/app/core/features/auth/login/login.component.html b/angular/RestClient/src/app/core/features/auth/login/login.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..f6fcf96c6ad1294dad508f3a339c7f9f900c4647
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/auth/login/login.component.html
@@ -0,0 +1,73 @@
+<div class="container">
+  <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
+    <mat-card>
+      <mat-card-title class="flex text-center p-4">
+        <strong class="text-5xl">Login</strong>
+      </mat-card-title>
+      <mat-card-content>
+        <div class="form-group">
+          <label class="text-2xl" for="email">Email:</label>
+          <input
+            id="email"
+            type="email"
+            formControlName="email"
+            class="form-control"
+          />
+          @if (loginForm.get('email')?.invalid &&
+          loginForm.get('email')?.touched) {
+          <div>
+            <small class="text-red-500 font-bold"
+              >Email is required and must be valid.</small
+            >
+          </div>
+          }
+        </div>
+
+        <div class="form-group">
+          <label class="text-2xl" for="password">Password:</label>
+          <input
+            id="password"
+            type="password"
+            formControlName="password"
+            class="form-control"
+          />
+          @if (loginForm.get('password')?.invalid &&
+          loginForm.get('password')?.touched){
+
+          <div>
+            <small class="text-red-500 font-bold">Password is required.</small>
+          </div>
+          }
+        </div>
+
+        <div class="form-group text-2xl">
+          <label class="text-2xl" for="rol">Rol:</label>
+          <mat-form-field class="w-full" formControlName="rol">
+            <mat-label class="text-2xl">Seleccione un rol</mat-label>
+            <mat-select [(value)]="selectedRol" name="rol">
+              @for (rol of rolOptions; track rol) {
+              <mat-option [value]="rol">
+                <span class="text-2xl">{{ rol }}</span>
+              </mat-option>
+              }
+            </mat-select>
+          </mat-form-field>
+        </div>
+        <mat-card-actions class="flex justify-center mb-5">
+          <button
+            type="submit"
+            class="btn btn-success text-4xl"
+            [disabled]="loginForm.invalid"
+          >
+            Login
+          </button>
+        </mat-card-actions>
+        @if (errorMessage) {
+        <div class="text-red-500 font-bold">
+          {{ errorMessage }}
+        </div>
+        }
+      </mat-card-content>
+    </mat-card>
+  </form>
+</div>
diff --git a/angular/RestClient/src/app/core/features/auth/login/login.component.spec.ts b/angular/RestClient/src/app/core/features/auth/login/login.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..18f3685d74d58daabcc34d3a66be081c6256a69d
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/auth/login/login.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LoginComponent } from './login.component';
+
+describe('LoginComponent', () => {
+  let component: LoginComponent;
+  let fixture: ComponentFixture<LoginComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [LoginComponent]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(LoginComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/angular/RestClient/src/app/core/features/auth/login/login.component.ts b/angular/RestClient/src/app/core/features/auth/login/login.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d3bed7a4132fa619c110a0ebd74c3e8225e16216
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/auth/login/login.component.ts
@@ -0,0 +1,64 @@
+import { Component } from '@angular/core';
+import {
+  FormBuilder,
+  FormGroup,
+  ReactiveFormsModule,
+  Validators,
+} from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { MatCardModule } from '@angular/material/card';
+import { MatInputModule } from '@angular/material/input';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSlideToggleModule } from '@angular/material/slide-toggle';
+import { Router } from '@angular/router';
+import { SessionService } from '../../../../shared/session.service';
+import { UserRol, UserRolesArray } from '../../../../types';
+
+@Component({
+  standalone: true,
+  selector: 'app-login',
+  templateUrl: './login.component.html',
+  styleUrls: ['./login.component.css'],
+  imports: [
+    ReactiveFormsModule,
+    CommonModule,
+    MatCardModule,
+    MatInputModule,
+    MatFormFieldModule,
+    MatSelectModule,
+    MatSlideToggleModule,
+  ],
+})
+export class LoginComponent {
+  loginForm: FormGroup;
+  selectedRol?: UserRol;
+  rolOptions: UserRol[] = UserRolesArray;
+  errorMessage: string | null = null;
+
+  constructor(
+    private fb: FormBuilder,
+    private sessionManager: SessionService,
+    private router: Router
+  ) {
+    this.loginForm = this.fb.group({
+      email: ['', [Validators.required, Validators.email]],
+      password: ['', [Validators.required]],
+    });
+  }
+
+  onSubmit() {
+    if (this.loginForm.valid) {
+      const { email, password } = this.loginForm.value;
+      this.sessionManager.login(email, password).subscribe({
+        next: (response) => {
+          this.router.navigateByUrl(response.mainPage);
+        },
+      });
+    }
+  }
+
+  isAuthenticated(): boolean {
+    return !!localStorage.getItem('authToken');
+  }
+}
diff --git a/angular/RestClient/src/app/core/features/auth/register/register.component.css b/angular/RestClient/src/app/core/features/auth/register/register.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..79b4834ee543b62f19df182154eeb62fa0911db1
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/auth/register/register.component.css
@@ -0,0 +1,21 @@
+.container {
+  max-width: 600px;
+  margin-top: 2rem;
+  padding: 20px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  background-color: #f9f9f9;
+}
+
+h2 {
+  text-align: center;
+  margin-bottom: 20px;
+}
+
+.form-group {
+  margin-bottom: 15px;
+}
+
+label {
+  font-weight: bold;
+}
diff --git a/angular/RestClient/src/app/core/features/auth/register/register.component.html b/angular/RestClient/src/app/core/features/auth/register/register.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..90cc523dc3e5fc2d2a12e9c2f259f5827c0a36ef
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/auth/register/register.component.html
@@ -0,0 +1,85 @@
+<div class="container">
+  <form [formGroup]="registerForm" (ngSubmit)="onSubmit()">
+    <mat-card>
+      <mat-card-title class="flex text-center p-4">
+        <strong class="text-5xl">Regístrate</strong>
+      </mat-card-title>
+
+      <mat-card-content>
+        <div class="form-group">
+          <label class="text-2xl" for="name">Nombre</label>
+          <input
+            id="name"
+            class="form-control"
+            formControlName="name"
+            placeholder="Introduce tu nombre"
+            required
+          />
+          @if (registerForm.get('name')?.invalid &&
+          registerForm.get('name')?.touched) {
+
+          <div>
+            <small class="text-red-500 font-bold">
+              El nombre es obligatorio y debe tener al menos 3 caracteres.
+            </small>
+          </div>
+          }
+        </div>
+
+        <div class="form-group">
+          <label class="text-2xl" for="email">Correo Electrónico</label>
+          <input
+            id="email"
+            type="email"
+            class="form-control"
+            formControlName="email"
+            placeholder="Introduce tu correo"
+            required
+          />
+          @if (registerForm.get('email')?.invalid &&
+          registerForm.get('email')?.touched) {
+          <div>
+            <small class="text-red-500 font-bold">
+              El correo electrónico no es válido.
+            </small>
+          </div>
+          }
+        </div>
+
+        <div class="form-group">
+          <label class="text-2xl" for="password">Contraseña</label>
+          <input
+            id="password"
+            type="password"
+            class="form-control"
+            formControlName="password"
+            placeholder="Introduce tu contraseña"
+            required
+          />
+          @if (registerForm.get('password')?.invalid &&
+          registerForm.get('password')?.touched) {
+          <div>
+            <small class="text-red-500 font-bold">
+              La contraseña debe tener al menos 6 caracteres.
+            </small>
+          </div>
+          }
+        </div>
+        <mat-card-actions class="flex justify-center mb-5">
+          <button
+            type="submit"
+            class="btn btn-success text-4xl"
+            [disabled]="registerForm.invalid"
+          >
+            Registrarse
+          </button>
+        </mat-card-actions>
+        @if (errorMessage) {
+        <div class="text-red-500 font-bold">
+          {{ errorMessage }}
+        </div>
+        }
+      </mat-card-content>
+    </mat-card>
+  </form>
+</div>
diff --git a/angular/RestClient/src/app/core/features/auth/register/register.component.spec.ts b/angular/RestClient/src/app/core/features/auth/register/register.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..757b8952cf4bbb851f3b079286c5c711dad73119
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/auth/register/register.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RegisterComponent } from './register.component';
+
+describe('RegisterComponent', () => {
+  let component: RegisterComponent;
+  let fixture: ComponentFixture<RegisterComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [RegisterComponent]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(RegisterComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/angular/RestClient/src/app/core/features/auth/register/register.component.ts b/angular/RestClient/src/app/core/features/auth/register/register.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e594c19b132e84967d2509e2c3e4a5053a66f048
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/auth/register/register.component.ts
@@ -0,0 +1,79 @@
+import { CommonModule } from '@angular/common';
+import { Component } from '@angular/core';
+import {
+  FormBuilder,
+  FormGroup,
+  ReactiveFormsModule,
+  Validators,
+} from '@angular/forms';
+import { MatCardModule } from '@angular/material/card';
+import { MatInputModule } from '@angular/material/input';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatSelectModule } from '@angular/material/select';
+import { MatSlideToggleModule } from '@angular/material/slide-toggle';
+import { Router } from '@angular/router';
+import { AuthClientService } from '../../../../shared/auth-client.service';
+import { SessionService } from '../../../../shared/session.service';
+
+// TODO agregar selector de roles
+
+@Component({
+  standalone: true,
+  selector: 'app-register',
+  templateUrl: './register.component.html',
+  styleUrls: ['./register.component.css'],
+  imports: [
+    ReactiveFormsModule,
+    CommonModule,
+    MatCardModule,
+    MatInputModule,
+    MatFormFieldModule,
+    MatSelectModule,
+    MatSlideToggleModule,
+  ],
+})
+export class RegisterComponent {
+  registerForm: FormGroup;
+  errorMessage: string | null = null;
+
+  constructor(
+    private fb: FormBuilder,
+    private authClient: AuthClientService,
+    private sessionManager: SessionService,
+    private router: Router
+  ) {
+    if (this.sessionManager.isValid()) {
+      const s = this.sessionManager.getSession();
+      console.log({ s });
+    }
+    this.registerForm = this.fb.group({
+      name: ['', [Validators.required, Validators.minLength(3)]],
+      email: ['', [Validators.required, Validators.email]],
+      password: ['', [Validators.required, Validators.minLength(6)]],
+    });
+  }
+
+  onSubmit() {
+    if (this.registerForm.valid) {
+      const { name, email, password } = this.registerForm.value;
+
+      this.authClient.register(name, email, password).subscribe({
+        next: (res: any) => {
+          console.log({ res });
+          this.sessionManager.login(res);
+          alert('Usuario registrado con éxito.');
+          this.router.navigate(['/']); // Redirigir al login
+        },
+        error: (err) => {
+          if (err.error instanceof ErrorEvent) {
+            this.errorMessage = `Error: ${err.error.message}`;
+          } else {
+            // Si el backend devuelve un objeto de error
+            this.errorMessage =
+              err.error.message || 'Ocurrió un error al registrar el usuario.';
+          }
+        },
+      });
+    }
+  }
+}
diff --git a/angular/RestClient/src/app/core/features/bookings/booking-list/booking-list.component.ts b/angular/RestClient/src/app/core/features/bookings/booking-list/booking-list.component.ts
index 06aab259dd9efcd5efaccb4defdacf03a55f3518..0e573456823b7cc0cbb4e098075f835141678a21 100644
--- a/angular/RestClient/src/app/core/features/bookings/booking-list/booking-list.component.ts
+++ b/angular/RestClient/src/app/core/features/bookings/booking-list/booking-list.component.ts
@@ -7,12 +7,12 @@ import {
 import { FormsModule } from '@angular/forms';
 import { MatInputModule } from '@angular/material/input';
 import { MatSelectModule } from '@angular/material/select';
-import { Hotel, Room, RoomType, roomTypeArray } from '../../../../../types';
-
+import { Hotel, Room, RoomType, roomTypeArray } from '../../../../types';
 import { Router } from '@angular/router';
 import { MatCardModule } from '@angular/material/card';
 import { MatChipsModule } from '@angular/material/chips';
-import { ClienteApiRestService } from '../../../../shared/cliente-api-rest.service';
+import { LocalStorageService } from '../../../../shared/local-storage.service';
+import { HotelClientService } from '../../../../shared/hotel-client.service';
 
 type SelectableRoomType = 'All' | RoomType;
 const selectableRoomTypeArray: SelectableRoomType[] = ['All', ...roomTypeArray];
@@ -36,23 +36,27 @@ const selectableRoomTypeArray: SelectableRoomType[] = ['All', ...roomTypeArray];
 })
 export class BookingListComponent {
   searched: boolean = false;
+  hotels!: Hotel[];
   start?: Date;
   end?: Date;
-  hotels!: Hotel[];
   hotelSelected?: Hotel;
   roomTypeSelected?: SelectableRoomType;
   roomTypes = selectableRoomTypeArray;
   rooms: Room[] = [];
   trateRooms: Room[] = [];
 
-  constructor(private router: Router, private client: ClienteApiRestService) {}
+  constructor(
+    private router: Router,
+    private hotelClient: HotelClientService,
+    private storage: LocalStorageService
+  ) {}
 
   ngOnInit() {
     this.getHotels();
   }
 
   getHotels() {
-    this.client.getAllHotels().subscribe({
+    this.hotelClient.getAllHotels().subscribe({
       next: (resp) => {
         if (resp != null) this.hotels = [...resp];
       },
@@ -72,7 +76,7 @@ export class BookingListComponent {
   }
 
   search() {
-    this.client
+    this.hotelClient
       .getRoomsAvailableInDateRange(
         this.hotelSelected!.id,
         this.start!,
@@ -95,14 +99,11 @@ export class BookingListComponent {
   }
 
   bookingRoom(roomId: number) {
-    localStorage.setItem(
-      'booking-data',
-      JSON.stringify({
-        roomId,
-        startDate: this.start,
-        endDate: this.end,
-      })
-    );
+    this.storage.save('booking-data', {
+      roomId,
+      startDate: this.start,
+      endDate: this.end,
+    });
     this.router.navigate(['/bookings', 'new'], { queryParams: { roomId } });
   }
 }
diff --git a/angular/RestClient/src/app/core/features/bookings/booking/booking.component.html b/angular/RestClient/src/app/core/features/bookings/booking/booking.component.html
index 4b09b386e32b53641cb4c00d522958fabcb21e07..fb7485fc16360e63fe916ee75b1f53f320895776 100644
--- a/angular/RestClient/src/app/core/features/bookings/booking/booking.component.html
+++ b/angular/RestClient/src/app/core/features/bookings/booking/booking.component.html
@@ -1,7 +1,7 @@
 <div class="container">
-  <h2>Crear Reserva</h2>
+  <h2><strong class="text-5xl font-bold">Confirmar reserva</strong></h2>
   <form [formGroup]="bookingForm" (ngSubmit)="submitBooking()">
-    <div class="form-group">
+    <!-- <div class="form-group">
       <label for="userId">ID del Usuario:</label>
       <select
         type="number"
@@ -13,7 +13,7 @@
         <option value="{{ user.id }}">{{ user.name }}</option>
         }
       </select>
-    </div>
+    </div> -->
 
     <div class="form-group">
       <label for="roomId">ID del Habitación:</label>
@@ -26,7 +26,7 @@
     </div>
 
     <div class="form-group">
-      <label for="startDate">Fecha de Inicio:</label>
+      <label for="startDate">Fecha de Inicio (mm/dd/yyyy):</label>
       <input
         type="date"
         id="startDate"
@@ -36,7 +36,7 @@
     </div>
 
     <div class="form-group">
-      <label for="endDate">Fecha de Fin:</label>
+      <label for="endDate">Fecha de Fin (mm/dd/yyyy):</label>
       <input
         type="date"
         id="endDate"
@@ -45,8 +45,6 @@
       />
     </div>
 
-    <button [disabled]="!isUserSelected" type="submit" class="btn btn-primary">
-      Reservar
-    </button>
+    <button type="submit" class="btn btn-primary">Reservar</button>
   </form>
 </div>
diff --git a/angular/RestClient/src/app/core/features/bookings/booking/booking.component.ts b/angular/RestClient/src/app/core/features/bookings/booking/booking.component.ts
index cd63902449b286409ab2ae045a9b273a7033a1f8..6691027e241bff16a2b25fb71e23a1309ec1929e 100644
--- a/angular/RestClient/src/app/core/features/bookings/booking/booking.component.ts
+++ b/angular/RestClient/src/app/core/features/bookings/booking/booking.component.ts
@@ -6,10 +6,18 @@ import {
   Validators,
 } from '@angular/forms';
 
-import { BookingService } from '../../../../shared/booking.service'; // Asegúrate de que el servicio exista
 import { ActivatedRoute, Router } from '@angular/router';
-import { Booking, User } from '../../../../../types';
-import { ClienteApiRestService } from '../../../../shared/cliente-api-rest.service';
+import { Booking, User } from '../../../../types';
+import { LocalStorageService } from '../../../../shared/local-storage.service';
+import { BookingClientService } from '../../../../shared/booking-client.service';
+import { UserClientService } from '../../../../shared/user-client.service';
+import { SessionService } from '../../../../shared/session.service';
+
+type communication = {
+  roomId: number;
+  startDate: Date;
+  endDate: Date;
+};
 
 @Component({
   standalone: true,
@@ -18,111 +26,98 @@ import { ClienteApiRestService } from '../../../../shared/cliente-api-rest.servi
   templateUrl: './booking.component.html',
   styleUrls: ['./booking.component.css'],
 })
-export class BookingComponent implements OnInit {
-  users: User[] = [];
+export class BookingComponent {
+  user: User = { id: 0, email: '', name: '', rol: 'CLIENT' };
   bookingForm: FormGroup;
-  bookingLocal: { roomId: number; startDate: Date; endDate: Date };
+  bookingLocal: { roomId: number; startDate: Date; endDate: Date } = {
+    roomId: 0,
+    endDate: new Date(),
+    startDate: new Date(),
+  };
   roomId: number = 0;
 
   constructor(
     private router: Router,
     private route: ActivatedRoute,
     private fb: FormBuilder,
-    private bookingService: BookingService,
-    private client: ClienteApiRestService
+    private sessionService: SessionService,
+    private bookingClient: BookingClientService,
+    private userClient: UserClientService,
+    private storage: LocalStorageService
   ) {
     // Inicialización del formulario con validaciones
     this.bookingForm = this.fb.group({
-      userId: ['', Validators.required],
-      roomId: ['', Validators.required],
-      startDate: ['', Validators.required],
-      endDate: ['', Validators.required],
+      roomId: [{ value: '', disabled: true }, Validators.required],
+      startDate: [{ value: '', disabled: true }, Validators.required],
+      endDate: [{ value: '', disabled: true }, Validators.required],
     });
-    const localBookingStr = localStorage.getItem('booking-data');
-    if (localBookingStr === null) {
+    const localBooking = storage.read<communication | null>('booking-data');
+    if (localBooking === null) {
       this.router.navigate(['/booking', 'search']);
+      return;
     }
-    const localBooking = JSON.parse(localBookingStr!);
-    this.bookingLocal = localBooking;
+    this.bookingLocal = localBooking!;
     this.route.queryParams.subscribe((params) => {
       const roomId = Number(params['roomId']);
       this.roomId = roomId;
-      if (localBooking.roomId !== roomId) {
+      if (this.bookingLocal.roomId !== roomId) {
         this.router.navigate(['/bookings', 'search']);
-        this.loadBooking(localBooking);
+        return;
       }
+      this.bookingLocal = {
+        ...this.bookingLocal,
+        startDate: new Date(this.bookingLocal.startDate),
+        endDate: new Date(this.bookingLocal.endDate),
+      };
+      this.loadBooking();
     });
-    this.client.getAllUsers().subscribe({
-      next: (resp) => {
-        this.users = resp;
+    this.sessionService.getSession().subscribe({
+      next: (session) => {
+        if (session) this.user = session;
       },
     });
   }
 
-  get userId() {
-    return this.bookingForm.get('userId')!.value;
-  }
-  get isUserSelected() {
-    return !isNaN(this.userId);
-  }
-
-  ngOnInit() {
-    this.loadBooking(this.bookingLocal);
-  }
-
-  loadBooking(booking: { roomId: number; startDate: Date; endDate: Date }) {
-    const start = new Date(booking.startDate).toISOString().split('T')[0];
-    const end = new Date(booking.endDate).toISOString().split('T')[0];
+  loadBooking() {
+    const booking = this.bookingLocal;
+    if (!booking) return;
+    const start = new Date(booking.startDate).toISOString();
+    const end = new Date(booking.endDate).toISOString();
     this.bookingForm = this.fb.group({
-      userId: [Validators.required],
-      roomId: [booking.roomId, Validators.required],
-      startDate: [start, Validators.required],
-      endDate: [end, Validators.required],
+      roomId: [{ value: booking.roomId, disabled: true }, Validators.required],
+      startDate: [{ value: start, disabled: true }, Validators.required],
+      endDate: [{ value: end, disabled: true }, Validators.required],
     });
-    this.bookingForm.get('roomId')?.disable();
-    this.bookingForm.get('startDate')?.disable();
-    this.bookingForm.get('endDate')?.disable();
   }
 
   submitBooking() {
-    if (this.bookingForm.valid) {
-      const formValue = this.bookingForm.value;
-      const userId = Number(formValue.userId);
-      const bookingRequest: any = {
-        ...this.bookingLocal,
-        userId: { id: userId },
-        roomId: { id: this.roomId },
-      };
+    const { id } = this.user;
+    const bookingRequest: any = {
+      ...this.bookingLocal,
+      userId: { id },
+      roomId: { id: this.roomId },
+    };
 
-      // Llama al servicio para crear una nueva reserva
-      this.bookingService.createBooking(bookingRequest).subscribe({
-        next: (response) => {
-          console.log('Reserva creada con éxito', response);
-          // Llama al servicio para actualizar el estado del usuario
-          this.client
-            .alterUserStatus(userId, 'WITH_ACTIVE_BOOKINGS')
-            .subscribe({
-              next: (response) => {
-                console.log(
-                  'Estado de usuario actualizado con exito',
-                  response
-                );
-                localStorage.removeItem('booking-data');
-                this.router.navigate(['/user', userId, 'bookings']);
-              },
-              error: (error) => {
-                console.error('Error al cambiar el estado del usuario', error);
-              },
-            });
-        },
-        error: (error) => {
-          console.error('Error al crear la reserva', error);
-          // Manejo de errores
-        },
-      });
-    } else {
-      console.warn('El formulario no es válido');
-      // Puedes mostrar un mensaje al usuario sobre la validez del formulario
-    }
+    // Llama al servicio para crear una nueva reserva
+    this.bookingClient.createBooking(bookingRequest).subscribe({
+      next: (response) => {
+        console.log('Reserva creada con éxito', response);
+        // Llama al servicio para actualizar el estado del usuario
+        this.userClient.alterUserStatus(id, 'WITH_ACTIVE_BOOKINGS').subscribe({
+          next: (response) => {
+            console.log('Estado de usuario actualizado con exito', response);
+            this.storage.remove('booking-data');
+            this.router.navigate(['/me', 'bookings']);
+          },
+          error: (error) => {
+            console.error('Error al cambiar el estado del usuario', error);
+          },
+        });
+      },
+      error: (error) => {
+        console.error('Error al crear la reserva', error);
+        // Manejo de errores
+      },
+    });
   }
 }
diff --git a/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.css b/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.css
index fcbb63ff2da3990e7eb2bffbfedcd84cadac8004..53ab55f4a45f24f76a2278c0c6b7bed895e56751 100644
--- a/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.css
+++ b/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.css
@@ -6,3 +6,7 @@
   border-radius: 5px;
   background-color: #f9f9f9;
 }
+.example-card {
+  max-width: 400px;
+  margin-bottom: 8px;
+}
diff --git a/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.html b/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.html
index a97254ba286fcb168c472d0f717d7bff62409a50..d0f0b21a8390db0bffa538c150073391b3abcc54 100644
--- a/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.html
+++ b/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.html
@@ -1,5 +1,6 @@
 <div class="container">
   <h2 class="text-center text-5xl font-bold mb-4">Hotel List</h2>
+  @if (isManaging) {
   <mat-accordion>
     <mat-expansion-panel disabled class="cursor-default">
       <mat-expansion-panel-header>
@@ -23,17 +24,18 @@
       </mat-expansion-panel-header>
 
       <div class="text-end mb-4">
-        <button
-          mat-raised-button
-          (click)="goToHotelDetails(hotel.id)"
-          style="
-            font-size: medium;
-            background-color: rgb(28, 197, 248);
-            color: rgb(250, 250, 250);
-          "
-        >
-          View hotel
-        </button>
+        <a [routerLink]="getHotelUri(hotel.id)">
+          <button
+            mat-raised-button
+            style="
+              font-size: medium;
+              background-color: rgb(28, 197, 248);
+              color: rgb(250, 250, 250);
+            "
+          >
+            View hotel
+          </button>
+        </a>
         <button
           mat-raised-button
           (click)="deleteHotel(hotel.id)"
@@ -90,4 +92,101 @@
     </mat-expansion-panel>
     }
   </mat-accordion>
+  } @else {
+  <div class="form-group text-xl flex justify-center gap-20">
+    <mat-form-field>
+      <mat-label class="text-2xl">Enter a date range</mat-label>
+      <form [formGroup]="dateRangeForm">
+        <mat-date-range-input [rangePicker]="picker" formGroupName="dateRange">
+          <input
+            matStartDate
+            formControlName="start"
+            placeholder="Fecha de inicio"
+          />
+          <input matEndDate formControlName="end" placeholder="Fecha de fin" />
+        </mat-date-range-input>
+        <mat-datepicker-toggle
+          matIconSuffix
+          [for]="picker"
+        ></mat-datepicker-toggle>
+        <mat-date-range-picker #picker></mat-date-range-picker>
+      </form>
+    </mat-form-field>
+    <mat-form-field>
+      <mat-label class="text-2xl">Hotel</mat-label>
+      <mat-select
+        [(value)]="hotelSelected"
+        class="text-2xl"
+        (selectionChange)="update()"
+      >
+        <mat-option [value]="undefined" class="text-3xl">All</mat-option>
+        @for (hotel of _hotels; track hotel.id) {
+        <mat-option [value]="hotel" class="text-3xl">{{
+          hotel.name
+        }}</mat-option>
+        }
+      </mat-select>
+    </mat-form-field>
+    <mat-form-field>
+      <mat-label>Filter by Room Type</mat-label>
+      <mat-select [(value)]="roomTypeSelected" (selectionChange)="update()">
+        @for (type of roomTypes; track type) {
+        <mat-option [value]="type">{{ type }}</mat-option>
+        }
+      </mat-select>
+    </mat-form-field>
+  </div>
+  @for(hotel of hotels; track hotel.id) {
+  <div class="mt-10 shadow-md">
+    <mat-card appearance="raised">
+      <mat-card-header class="flex justify-center p-4 mb-4">
+        <mat-card-title class="text-center">
+          <a [routerLink]="getHotelUri(hotel.id)">
+            <strong class="text-4xl flex items-center justify-center gap-4">
+              <mat-icon>hotel</mat-icon> {{ hotel.name }}</strong
+            >
+            <p class="mt-5 text-2xl italic">
+              {{ hotel.address.streetKind }} {{ hotel.address.streetName }}. Nº
+              {{ hotel.address.number }} [{{ hotel.address.postCode }}]
+            </p>
+            @if (hotel.address.otherInfo) {
+            <small>{{ hotel.address.otherInfo }}</small>
+            }
+          </a>
+        </mat-card-title>
+      </mat-card-header>
+      <mat-card-content>
+        <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
+          @for(room of hotel.rooms; track room.id) { @if (showRequested(room)) {
+          <div class="drop-shadow-lg hover:shadow-2xl">
+            <mat-card appearance="raised">
+              <mat-card-header class="flex justify-center">
+                <mat-card-title>
+                  <strong
+                    >(#{{ $index + 1 }}) Habitación {{ room.roomNumber }}
+                  </strong>
+                  <span class="italic">({{ room.type }})</span>
+                </mat-card-title>
+              </mat-card-header>
+              <mat-card-content class="mt-4">
+                <button
+                  [disabled]="!isAvailable(room)"
+                  mat-raised-button
+                  class="w-full text-center py-3 rounded-lg shadow-md hover:shadow-lg bg-sky-600 text-slate-200 font-bold"
+                  (click)="bookingRoom(room.id)"
+                >
+                  <span class="flex items-center justify-center text-2xl">
+                    <mat-icon>booking</mat-icon>
+                    Reservar
+                  </span>
+                </button>
+              </mat-card-content>
+            </mat-card>
+          </div>
+          }}
+        </div>
+      </mat-card-content>
+    </mat-card>
+  </div>
+  } }
 </div>
diff --git a/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.ts b/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.ts
index bdd87d3d36ce178cbb994508955b05bec3474803..1b6e34d096b35b4ef53be036aa767258592dee54 100644
--- a/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.ts
+++ b/angular/RestClient/src/app/core/features/hotel/hotel-list/hotel-list.component.ts
@@ -1,6 +1,6 @@
-import { Component } from '@angular/core';
-import { RouterModule, Router } from '@angular/router';
-import { Hotel } from '../../../../../types';
+import { Component, NgModule } from '@angular/core';
+import { RouterModule, Router, ActivatedRoute, Data } from '@angular/router';
+import { Hotel, Room, RoomType, roomTypeArray } from '../../../../types';
 import {
   MatAccordion,
   MatExpansionPanel,
@@ -11,13 +11,29 @@ import {
 import { MatSlideToggle } from '@angular/material/slide-toggle';
 import { MatTable, MatTableModule } from '@angular/material/table';
 import { MatButton } from '@angular/material/button';
-import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
-import { ClienteApiRestService } from '../../../../shared/cliente-api-rest.service';
+import {
+  NgbAccordionModule,
+  NgbDatepickerModule,
+} from '@ng-bootstrap/ng-bootstrap';
+import { HotelClientService } from '../../../../shared/hotel-client.service';
+import { MatCardModule } from '@angular/material/card';
+import { MatIconModule } from '@angular/material/icon';
+import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatDatepickerModule } from '@angular/material/datepicker';
+import { MatSelectModule } from '@angular/material/select';
+import { LocalStorageService } from '../../../../shared/local-storage.service';
+import { SessionService } from '../../../../shared/session.service';
+import { getBasePath } from '../../../../utils/utils';
+
+type SelectableRoomType = 'All' | RoomType;
+const selectableRoomTypeArray: SelectableRoomType[] = ['All', ...roomTypeArray];
 
 @Component({
   selector: 'app-hotel-list',
   standalone: true,
   imports: [
+    NgbDatepickerModule,
     RouterModule,
     MatAccordion,
     MatSlideToggle,
@@ -29,25 +45,118 @@ import { ClienteApiRestService } from '../../../../shared/cliente-api-rest.servi
     MatExpansionPanelTitle,
     MatExpansionPanelDescription,
     NgbAccordionModule,
+    MatCardModule,
+    MatIconModule,
+    MatFormFieldModule,
+    MatDatepickerModule,
+    MatSelectModule,
+    ReactiveFormsModule,
   ],
   templateUrl: './hotel-list.component.html',
   styleUrl: './hotel-list.component.css',
 })
 export class HotelListComponent {
+  _hotels!: Hotel[];
   hotels!: Hotel[];
-  mostrarMensaje!: boolean;
-  mensaje!: string;
+  isEditing = false;
+  isManaging = false;
+  dateRangeForm: FormGroup;
+  hotelSelected?: Hotel = undefined;
+  roomTypeSelected: SelectableRoomType = 'All';
+  roomTypes = selectableRoomTypeArray;
+  rooms: Room[] = [];
+  trateRooms: Room[] = [];
+  userId = 0
+
+  constructor(
+    private fb: FormBuilder,
+    private hotelClient: HotelClientService,
+    private router: Router,
+    private route: ActivatedRoute,
+    private storage: LocalStorageService,
+    private sessionService: SessionService
+  ) {
+    const isHotelManger = this.route.snapshot.url[0].path === 'me'
+    const isAdmin = this.route.snapshot.url[0].path === 'admin'
+    this.isManaging = isHotelManger || isAdmin;
+    const today = new Date();
 
-  constructor(private router: Router, private client: ClienteApiRestService) {}
+    // Inicializa el formulario con las fechas predefinidas
+    this.dateRangeForm = this.fb.group({
+      dateRange: this.fb.group({
+        start: [today], // Fecha de inicio
+        end: [today], // Fecha de fin
+      }),
+    });
+
+    this.sessionService.getSession().subscribe({
+      next: (session) => {
+        if (session && session.rol !== 'CLIENT') {
+          this.isEditing = true;
+          this.userId = isHotelManger 
+            ? session.id
+            : Number(this.route.snapshot.paramMap.get('id'));
+        }
+      },
+    });
+  }
 
-  ngOnInit() {
+  ngOnInit(): void {
     this.getHotels();
+    this.dateRangeForm.get('dateRange')?.valueChanges.subscribe(() => {
+      this.getHotels();
+    });
+  }
+
+  update() {
+    this.hotels = (
+      !!this.hotelSelected
+        ? [...this._hotels].filter((h) => h.id === this.hotelSelected!.id)
+        : [...this._hotels]
+    )
+      .map((h) => {
+        h = { ...h, rooms: [...h.rooms] };
+        h.rooms = h.rooms.filter(
+          (r) =>
+            r.available &&
+            (this.roomTypeSelected === 'All' ||
+              (r.type as SelectableRoomType) === this.roomTypeSelected)
+        );
+        return h;
+      })
+      .filter((h) => h.rooms.length > 0);
+  }
+
+  showRequested(room: Room) {
+    return (
+      this.roomTypeSelected === 'All' ||
+      (room.type as SelectableRoomType) === this.roomTypeSelected
+    );
+  }
+
+  isAvailable(room: Room) {
+    const value =
+      !this.isEditing &&
+      room.available &&
+      (this.roomTypeSelected === 'All' ||
+        (room.type as SelectableRoomType) === this.roomTypeSelected);
+
+    return value;
   }
 
   getHotels() {
-    this.client.getAllHotels().subscribe({
+    const { start, end } = this.dateRangeForm.value.dateRange;
+    
+    const observable = this.isManaging
+      ? this.hotelClient.getAllHotelsByUser(this.userId, start, end)
+      : this.hotelClient.getAllHotels(start, end)
+    console.log({...this})
+    observable.subscribe({
       next: (resp) => {
-        if (!!resp || (resp as never[]).length != 0) this.hotels = [...resp];
+        if (!!resp && (resp as never[]).length >= 0) {
+          this._hotels = resp;
+          this.update();
+        }
       },
       error(err) {
         console.log('Error al traer la lista: ' + err.message);
@@ -58,17 +167,9 @@ export class HotelListComponent {
 
   deleteHotel(id: number) {
     if (!confirm(`Borrar hotel con id ${id}. Continuar?`)) return;
-
-    this.client.deleteHotel(id).subscribe({
-      next: (resp) => {
-        if (resp.status < 400) {
-          this.mostrarMensaje = true;
-          this.mensaje = resp.body as string;
-          this.getHotels();
-        } else {
-          this.mostrarMensaje = true;
-          this.mensaje = 'Error al eliminar registro';
-        }
+    this.hotelClient.deleteHotel(id).subscribe({
+      next: () => {
+        this.getHotels();
       },
       error: (err) => {
         console.log('Error al borrar: ' + err.message);
@@ -82,26 +183,43 @@ export class HotelListComponent {
     roomId: number,
     availability: boolean
   ) {
-    this.client.alterRoomAvailability(hotelId, roomId, availability).subscribe({
-      next: (resp) => {
-        if (resp.status < 400) {
-          this.mostrarMensaje = true;
-          this.mensaje = resp.body as string;
-          this.getHotels();
-        } else {
-          this.mostrarMensaje = true;
-          this.mensaje = 'Error al cambiar disponibilidad';
-          console.error(this.mensaje);
-        }
-      },
-      error: (error) => {
-        console.log('Error al cambiar disponibilidad: ' + error.message);
-        throw error;
-      },
-    });
+    this.hotelClient
+      .alterRoomAvailability(hotelId, roomId, availability)
+      .subscribe({
+        next: (resp) => {
+          if (resp) {
+            this.getHotels();
+          }
+        },
+        error: (error) => {
+          console.log('Error al cambiar disponibilidad: ' + error.message);
+          throw error;
+        },
+      });
   }
 
-  goToHotelDetails(hotelId: number): void {
-    this.router.navigate(['/hotels', hotelId]);
+  getHotelUri(hotelId: number) {
+    var base;
+    try {
+      base = getBasePath(this.route) + '/';
+    } catch (error) {
+      base = '';
+    }
+    return (this.isManaging ? base : '/') + 'hotels/' + hotelId;
+  }
+
+  bookingRoom(roomId: number) {
+    const { start, end } = this.dateRangeForm.value.dateRange as {
+      start: Date;
+      end: Date;
+    };
+    this.storage.save('booking-data', {
+      roomId,
+      startDate: start.toString(),
+      endDate: end.toString(),
+    });
+    this.router.navigate(['/me', 'bookings', 'new'], {
+      queryParams: { roomId, startDate: start.toLocaleDateString() },
+    });
   }
 }
diff --git a/angular/RestClient/src/app/core/features/hotel/hotel-register/hotel-register.component.ts b/angular/RestClient/src/app/core/features/hotel/hotel-register/hotel-register.component.ts
index fd09a256f2380ae5c144b373f0406228079dcd73..9408ea3e1082fb50c4277a6912870e6472979518 100644
--- a/angular/RestClient/src/app/core/features/hotel/hotel-register/hotel-register.component.ts
+++ b/angular/RestClient/src/app/core/features/hotel/hotel-register/hotel-register.component.ts
@@ -13,9 +13,10 @@ import { MatFormFieldModule } from '@angular/material/form-field';
 import { MatSelectModule } from '@angular/material/select';
 import { MatSlideToggleModule } from '@angular/material/slide-toggle';
 import { CommonModule } from '@angular/common';
-import { ClienteApiRestService } from '../../../../shared/cliente-api-rest.service';
-import { Address, Hotel, Room } from '../../../../../types';
+import { Address, Hotel, Room } from '../../../../types';
 import { ActivatedRoute, Router } from '@angular/router';
+import { HotelClientService } from '../../../../shared/hotel-client.service';
+import { MatIconModule } from '@angular/material/icon';
 
 const emptyRoom: Room = {
   id: 0,
@@ -59,19 +60,22 @@ export class HotelRegisterComponent {
     private router: Router,
     private route: ActivatedRoute,
     private fb: FormBuilder,
-    private client: ClienteApiRestService
+    private hotelClient: HotelClientService
   ) {
     this.hotelForm = this.setHotelForm();
     this.editMode = false;
     this.route.paramMap.subscribe({
       next: (params) => {
         const id = Number(params.get('id'));
+        if (!id) {
+          this.router.navigateByUrl('/hotels/register');
+        }
         this.editMode = id !== 0;
         if (this.editMode) {
-          this.client.getHotel(id).subscribe({
+          this.hotelClient.getHotel(id).subscribe({
             next: (h) => this.setHotelForm(h),
             error: (error) => {
-              this.router.navigate(['/hotels/new']);
+              this.router.navigateByUrl('/hotels/register');
             },
           });
         }
@@ -102,12 +106,12 @@ export class HotelRegisterComponent {
   onSubmit(): void {
     if (this.hotelForm.valid) {
       const hotel = this.hotelForm.value as Hotel;
-      this.client.addHotel(hotel).subscribe({
+      this.hotelClient.addHotel(hotel).subscribe({
         next: (resp) => {
-          if (resp.status < 400) {
+          if (resp) {
+            console.log({ resp });
             alert('Hotel guardado correctamente');
             this.router.navigate(['/hotels']);
-          } else {
           }
         },
         error: (err) => {
diff --git a/angular/RestClient/src/app/core/features/user/main-page/main-page.component.css b/angular/RestClient/src/app/core/features/user/main-page/main-page.component.css
index f3f3318a77b3200dc595486e2d703bec99a0811c..ccc36ce397f5384e2bcdbe39089b2c077bb444c4 100644
--- a/angular/RestClient/src/app/core/features/user/main-page/main-page.component.css
+++ b/angular/RestClient/src/app/core/features/user/main-page/main-page.component.css
@@ -1,112 +1,42 @@
-/* Contenedor principal con esquinas redondeadas y sombra sutil */
-.user-container {
-  max-width: 900px;
-  margin: 30px auto;
-  padding: 25px;
-  background-color: #1f1f1f;
-  border-radius: 12px;
-  box-shadow: 0px 8px 20px rgba(0, 0, 0, 0.6);
+.mat-mdc-row .mat-mdc-cell {
+  border-bottom: 2px solid transparent;
+  border-top: 2px solid transparent;
+  cursor: pointer;
+  font-size: 1.75rem;
+  line-height: 2rem;
 }
 
-/* Título centralizado y con un estilo moderno */
-h1 {
-  text-align: center;
-  font-size: 2em;
-  margin-bottom: 25px;
-  color: #ffffff;
-  letter-spacing: 1px;
-}
-
-/* Contenedor de filtro centrado y limpio */
-.filter-container {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-bottom: 20px;
-}
-
-label {
-  font-weight: 600;
-  margin-right: 10px;
-  color: #a1a1a1;
-}
-
-/* Select estilizado con borde suave y sombras */
-select {
-  padding: 10px;
-  font-size: 1em;
-  border: none;
-  outline: none;
-  background-color: #292929;
-  color: #e0e0e0;
-  border-radius: 6px;
-  box-shadow: inset 0px 3px 8px rgba(0, 0, 0, 0.5);
-  transition: all 0.3s ease;
-}
-
-select:hover {
-  background-color: #3a3a3a;
-}
-
-/* Lista de usuarios sin puntos y con bordes modernos */
-.user-list {
-  list-style-type: none;
-  padding: 0;
-  margin: 0;
-}
-
-/* Elementos de la lista con espaciado y efecto hover futurista */
-.user-item {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 15px 20px;
-  margin-bottom: 8px;
-  background-color: #2c2c2c;
-  border-radius: 8px;
-  transition: transform 0.3s ease, background-color 0.3s ease;
+.table {
+  border: 0.7rem #ccc solid;
+  border-radius: 1rem;
 }
 
-.user-item:hover {
-  background-color: #333333;
-  transform: translateY(-2px);
-  box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.3);
+.mat-mdc-row:hover .mat-mdc-cell {
+  border-color: currentColor;
+  background-color: #feffab;
 }
 
-/* Estilos de usuario con tipografía clara y futurista */
-.user-name {
-  flex: 0.5;
-  font-weight: 700;
-  color: #ffffff;
+.demo-row-is-clicked {
+  font-weight: bold;
 }
-
-.user-email {
-  flex: 1.5;
-  color: #b0b0b0;
-  font-style: italic;
-  text-align: center;
-  padding-left: 15px;
+.container {
+  max-width: 1000px;
+  margin-top: 2rem;
+  padding: 20px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  background-color: #f9f9f9;
 }
 
-.user-status {
-  flex: 1;
-  font-weight: 500;
-  color: #76c7c0;
-  text-transform: uppercase;
+h2 {
   text-align: center;
+  margin-bottom: 20px;
 }
 
-.user-bookings {
-  flex: 1;
-  font-weight: 500;
-  color: #76c7c0;
-  text-transform: uppercase;
-  text-align: center;
+.form-group {
+  margin-bottom: 15px;
 }
 
-.user-bookings button {
-  padding: 0.5rem 1rem;
-  background-color: #3d6b74;
-  color: white;
-  border-radius: 20px;
+label {
+  font-weight: bold;
 }
diff --git a/angular/RestClient/src/app/core/features/user/main-page/main-page.component.html b/angular/RestClient/src/app/core/features/user/main-page/main-page.component.html
index 9c34f26569237817882140eb2960e3fc98cc0fcf..8f3659e24b6181f5a54781dc36376aae20b3affe 100644
--- a/angular/RestClient/src/app/core/features/user/main-page/main-page.component.html
+++ b/angular/RestClient/src/app/core/features/user/main-page/main-page.component.html
@@ -1,34 +1,85 @@
-<div class="user-container">
-  <h1>Listado de Usuarios</h1>
+<div class="container">
+  <mat-card>
+    <mat-card-title class="flex text-center p-4">
+      <strong class="text-5xl">Usuarios</strong>
+    </mat-card-title>
+    <mat-card-content>
+      <div class="filter-container">
+        <label for="filter">Filtrar por estado:</label>
+        <select
+          id="filter"
+          [(ngModel)]="selectedStatus"
+          (change)="filterUsers()"
+        >
+          <option value="All">Todos</option>
+          <option value="NO_BOOKINGS">Sin reservas</option>
+          <option value="WITH_ACTIVE_BOOKINGS">Con reservas activas</option>
+          <option value="WITH_INACTIVE_BOOKINGS">Con reservas inactivas</option>
+        </select>
+      </div>
+      <div class="mat-elevation-z8 demo-table table">
+        <table mat-table [dataSource]="dataSource">
+          <ng-container matColumnDef="id">
+            <th mat-header-cell *matHeaderCellDef>
+              <span class="text-3xl font-bold">ID</span>
+            </th>
+            <td mat-cell *matCellDef="let element">
+              <span class="text-2xl">
+                {{ element.id }}
+              </span>
+            </td>
+          </ng-container>
 
-  <div class="filter-container">
-    <label for="filter">Filtrar por estado:</label>
-    <select id="filter" [(ngModel)]="selectedStatus" (change)="filterUsers()">
-      <option value="All">Todos</option>
-      <option value="NO_BOOKINGS">Sin reservas</option>
-      <option value="WITH_ACTIVE_BOOKINGS">Con reservas activas</option>
-      <option value="WITH_INACTIVE_BOOKINGS">Con reservas inactivas</option>
-    </select>
-  </div>
+          <!-- Name Column -->
+          <ng-container matColumnDef="name">
+            <th mat-header-cell *matHeaderCellDef>
+              <span class="text-3xl font-bold">Name</span>
+            </th>
+            <td mat-cell *matCellDef="let element">
+              <span class="text-2xl">
+                {{ element.name }}
+              </span>
+            </td>
+          </ng-container>
 
-  <ul class="user-list">
-    <li class="user-item">
-      <span class="user-name">Nombre</span>
-      <span class="user-email">Dirección de correo electronica</span>
-      <span class="user-status">Estado de usuario</span>
-      <span class="user-bookings">Reservas</span>
-    </li>
-    @for (user of filteredUsers; track user.id) {
-    <li class="user-item">
-      <span class="user-name">{{ user.name }}</span>
-      <span class="user-email">{{ user.email }}</span>
-      <span class="user-status">{{ getState(user) }}</span>
-      <span class="user-bookings">
-        <a [routerLink]="['/users', user.id, 'bookings']">
-          <button>Reservas</button>
-        </a>
-      </span>
-    </li>
-    }
-  </ul>
+          <!-- Weight Column -->
+          <ng-container matColumnDef="email">
+            <th mat-header-cell *matHeaderCellDef>
+              <span class="text-3xl font-bold">Email</span>
+            </th>
+            <td mat-cell *matCellDef="let element">
+              <span class="text-2xl">
+                {{ element.email }}
+              </span>
+            </td>
+          </ng-container>
+
+          <!-- Symbol Column -->
+          <ng-container matColumnDef="rol">
+            <th mat-header-cell *matHeaderCellDef>
+              <span class="text-3xl font-bold">Rol</span>
+            </th>
+            <td mat-cell *matCellDef="let element">
+              <span class="text-2xl">
+                {{ element.rol }}
+              </span>
+            </td>
+          </ng-container>
+
+          <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+          <tr
+            mat-row
+            *matRowDef="let row; columns: displayedColumns"
+            (click)="userDetails(row.id)"
+          ></tr>
+        </table>
+      </div>
+      <mat-paginator
+        [pageSizeOptions]="[5, 10, 20]"
+        showFirstLastButtons
+        aria-label="Select page of periodic elements"
+      >
+      </mat-paginator>
+    </mat-card-content>
+  </mat-card>
 </div>
diff --git a/angular/RestClient/src/app/core/features/user/main-page/main-page.component.ts b/angular/RestClient/src/app/core/features/user/main-page/main-page.component.ts
index 55760b59690b884457e25d55f8410a50c81cc0bd..09770a81445c949b6754000aeb014d66933e95c1 100644
--- a/angular/RestClient/src/app/core/features/user/main-page/main-page.component.ts
+++ b/angular/RestClient/src/app/core/features/user/main-page/main-page.component.ts
@@ -1,44 +1,66 @@
-// main-page.component.ts
-import { Component, OnInit } from '@angular/core';
-import { ClienteApiRestService } from '../../../../shared/cliente-api-rest.service';
-import { User, UserStateFilter } from '../../../../../types';
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { Client, User, UserStateFilter } from '../../../../types';
 import { FormsModule } from '@angular/forms';
 import { CommonModule } from '@angular/common';
-import users from '../../../../../mocks/users.json';
-import { RouterModule } from '@angular/router';
+import { Router, RouterModule } from '@angular/router';
+import { UserClientService } from '../../../../shared/user-client.service';
+import { users } from '../../../../../mocks/users'; // Renombrado para claridad
+import { MatTableDataSource, MatTableModule } from '@angular/material/table';
+import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
+import { MatCardModule } from '@angular/material/card';
+
 @Component({
   standalone: true,
-  imports: [FormsModule, CommonModule, RouterModule],
+  imports: [
+    FormsModule,
+    CommonModule,
+    RouterModule,
+    MatTableModule,
+    MatCardModule,
+    MatPaginatorModule,
+  ],
   selector: 'app-main-page',
   templateUrl: './main-page.component.html',
   styleUrls: ['./main-page.component.css'],
 })
 export class MainPageComponent implements OnInit {
-  users: User[] = [];
-  filteredUsers: User[] = [];
+  users: Client[] = [];
+  filteredUsers: Client[] = [];
   selectedStatus: UserStateFilter = 'All';
+  displayedColumns: string[] = ['id', 'name', 'email', 'rol'];
+  dataSource = new MatTableDataSource<User>();
 
-  constructor(private ClienteApiRestService: ClienteApiRestService) {}
+  constructor(private userClient: UserClientService, private router: Router) {}
 
   ngOnInit(): void {
-    this.users = users as unknown as User[];
-    this.ClienteApiRestService.getAllUsers().subscribe((data: User[]) => {
-      this.users = data;
-      this.filteredUsers = data; // Inicialmente, muestra todos los usuarios
+    this.users = users;
+    this.filteredUsers = [...this.users];
+
+    // Sobrescribir con datos reales si están disponibles
+    this.userClient.getAllUsers().subscribe({
+      next: (data: Client[]) => {
+        this.users = data;
+        this.filterUsers();
+      },
+      error: (err) => console.error('Error al cargar usuarios:', err),
     });
   }
 
+  @ViewChild(MatPaginator) paginator?: MatPaginator;
+
   filterUsers(): void {
     if (this.selectedStatus === 'All') {
-      this.filteredUsers = this.users;
+      this.filteredUsers = [...this.users];
     } else {
       this.filteredUsers = this.users.filter(
         (user) => user.status === this.selectedStatus
       );
     }
+    this.dataSource = new MatTableDataSource<User>(this.filteredUsers);
+    this.dataSource.paginator = this.paginator!;
   }
 
-  getState(user: User) {
+  getState(user: Client): string {
     switch (user.status) {
       case 'NO_BOOKINGS':
         return 'SIN RESERVAS';
@@ -46,6 +68,12 @@ export class MainPageComponent implements OnInit {
         return 'CON RESERVAS ACTIVAS';
       case 'WITH_INACTIVE_BOOKINGS':
         return 'CON RESERVAS INACTIVAS';
+      default:
+        return 'ESTADO DESCONOCIDO';
     }
   }
+
+  userDetails(id: number) {
+    this.router.navigate(['/admin', 'users', id]);
+  }
 }
diff --git a/angular/RestClient/src/app/core/features/user/user-booking-list/user-booking-list.component.html b/angular/RestClient/src/app/core/features/user/user-booking-list/user-booking-list.component.html
index 88e7c25943f832f6984cd1ee0c5bcf34729f5ced..6a845bc703fc7e279909d96f39e55c2f387e33c5 100644
--- a/angular/RestClient/src/app/core/features/user/user-booking-list/user-booking-list.component.html
+++ b/angular/RestClient/src/app/core/features/user/user-booking-list/user-booking-list.component.html
@@ -1,6 +1,6 @@
 <div class="booking-container">
   <h1>Listado de Reservas</h1>
-  <h3>para el usuario {{ user?.name }} (ID: {{ userId }})</h3>
+  <h3>para el usuario {{ user.name }} (ID: {{ user.id }})</h3>
 
   <div class="filter-container">
     <label for="filter">Filtrar por estado:</label>
diff --git a/angular/RestClient/src/app/core/features/user/user-booking-list/user-booking-list.component.ts b/angular/RestClient/src/app/core/features/user/user-booking-list/user-booking-list.component.ts
index 5f1b3f1374342c3f9633486e6ce12027c2ebf7c5..f5ca26c52374160a4a16fe933a03cec8a8fed823 100644
--- a/angular/RestClient/src/app/core/features/user/user-booking-list/user-booking-list.component.ts
+++ b/angular/RestClient/src/app/core/features/user/user-booking-list/user-booking-list.component.ts
@@ -1,11 +1,13 @@
 import { Component } from '@angular/core';
-import { ClienteApiRestService } from '../../../../shared/cliente-api-rest.service';
-import { Booking, User } from '../../../../../types';
+
+import { Booking, User } from '../../../../types';
 import { ActivatedRoute, RouterModule } from '@angular/router';
-import { MatSelectModule } from '@angular/material/select';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { BookingService } from '../../../../shared/booking.service';
+import { UserClientService } from '../../../../shared/user-client.service';
+import { BookingClientService } from '../../../../shared/booking-client.service';
+import { SessionService } from '../../../../shared/session.service';
+import { Observable } from 'rxjs';
 
 type state = 'all' | 'active' | 'inactive';
 
@@ -20,31 +22,35 @@ export class UserBookingListComponent {
   selectedState: state = 'all';
   search = false;
   bookings: Booking[] = [];
-  userId: number = 0;
-  user?: User;
+  user: User = { id: 0, name: '', email: '', rol: 'CLIENT' };
 
   constructor(
-    private client: ClienteApiRestService,
-    private bookingClient: BookingService,
+    private userClient: UserClientService,
+    private bookingClient: BookingClientService,
+    private sessionService: SessionService,
     private route: ActivatedRoute
   ) {
-    this.route.paramMap.subscribe({
-      next: (params) => {
-        this.userId = Number(params.get('id'));
+    this.loadUser();
+  }
+
+  resolve(): Observable<any> {
+    const id = this.route.snapshot.paramMap.get('id');
+    return id
+      ? this.userClient.getUser(Number(id))
+      : this.sessionService.getSession();
+  }
+
+  loadUser() {
+    this.resolve().subscribe({
+      next: (user) => {
+        this.user = user;
         this.updateBookings();
       },
     });
-    this.client
-      .getUser(this.userId)
-      .subscribe({ next: (user) => (this.user = user) });
-  }
-
-  ngOnInit() {
-    this.updateBookings();
   }
 
   updateBookings() {
-    this.client.getUserBookings(this.userId).subscribe({
+    this.bookingClient.getBookingsByUser(this.user.id).subscribe({
       next: (bookings) => {
         this.search = true;
         switch (this.selectedState) {
@@ -70,9 +76,10 @@ export class UserBookingListComponent {
   }
 
   genBookingState(booking: Booking) {
-    return new Date(booking.endDate).getTime() < Date.now()
-      ? 'Reserva inactiva'
-      : 'Reserva activa';
+    return new Date().setHours(0, 0, 0, 0) <=
+      new Date(booking.endDate).getTime()
+      ? 'Reserva activa'
+      : 'Reserva inactiva';
   }
 
   deleteBooking(bookingId: number) {
@@ -88,7 +95,7 @@ export class UserBookingListComponent {
   }
 
   updateUserStatus() {
-    this.client.getUserBookings(this.userId).subscribe({
+    this.bookingClient.getBookingsByUser(this.user.id).subscribe({
       next: (bookings) => {
         const withActive = bookings.find(
           (booking) => this.genBookingState(booking) === 'Reserva activa'
@@ -97,8 +104,8 @@ export class UserBookingListComponent {
           (booking) => this.genBookingState(booking) === 'Reserva inactiva'
         );
         if (withActive) {
-          this.client
-            .alterUserStatus(this.userId, 'WITH_ACTIVE_BOOKINGS')
+          this.userClient
+            .alterUserStatus(this.user.id, 'WITH_ACTIVE_BOOKINGS')
             .subscribe({
               next: (response) => {
                 console.log('Cambio de estado en el usuario a activo correcto');
@@ -108,8 +115,8 @@ export class UserBookingListComponent {
               },
             });
         } else if (withInactive) {
-          this.client
-            .alterUserStatus(this.userId, 'WITH_INACTIVE_BOOKINGS')
+          this.userClient
+            .alterUserStatus(this.user.id, 'WITH_INACTIVE_BOOKINGS')
             .subscribe({
               next: (response) => {
                 console.log(
@@ -123,18 +130,20 @@ export class UserBookingListComponent {
               },
             });
         } else {
-          this.client.alterUserStatus(this.userId, 'NO_BOOKINGS').subscribe({
-            next: (response) => {
-              console.log(
-                'Cambio de estado en el usuario a sin reservas correcto'
-              );
-            },
-            error: (err) => {
-              console.error(
-                'Error al cambiar de estado al usuario sin reservas'
-              );
-            },
-          });
+          this.userClient
+            .alterUserStatus(this.user.id, 'NO_BOOKINGS')
+            .subscribe({
+              next: (response) => {
+                console.log(
+                  'Cambio de estado en el usuario a sin reservas correcto'
+                );
+              },
+              error: (err) => {
+                console.error(
+                  'Error al cambiar de estado al usuario sin reservas'
+                );
+              },
+            });
         }
       },
     });
diff --git a/angular/RestClient/src/app/core/features/user/user-form/user-form.component.css b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..79b4834ee543b62f19df182154eeb62fa0911db1
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.css
@@ -0,0 +1,21 @@
+.container {
+  max-width: 600px;
+  margin-top: 2rem;
+  padding: 20px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  background-color: #f9f9f9;
+}
+
+h2 {
+  text-align: center;
+  margin-bottom: 20px;
+}
+
+.form-group {
+  margin-bottom: 15px;
+}
+
+label {
+  font-weight: bold;
+}
diff --git a/angular/RestClient/src/app/core/features/user/user-form/user-form.component.html b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..677115e1ef16e7735abdfe809b92fdb65262747e
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.html
@@ -0,0 +1,148 @@
+<div class="container">
+  <mat-card>
+    <mat-card-title class="flex text-center p-4">
+      <strong class="text-5xl">{{ titleText }}</strong>
+    </mat-card-title>
+    <mat-card-content>
+      @if (!isAuth) {
+      <div class="grid grid-flow-col mb-5">
+        @if (!isEditing) {
+        <div>
+          @if (isHotelManager) {
+          <a [routerLink]="[getHotelsUri()]">
+            <button class="btn btn-primary">Mis hoteles</button>
+          </a>
+          }@else if(!isAdmin){
+          <a [routerLink]="[getBookingsUri()]">
+            <button class="btn btn-primary">Mis Reservas</button>
+          </a>
+          }
+        </div>
+        }
+        <div>
+          <div class="flex items-center gap-5">
+            <span class="text-3xl font-bold ml-auto"> Is Editing </span>
+            <mat-slide-toggle
+              [(ngModel)]="isEditing"
+              (toggleChange)="switchMode()"
+            />
+          </div>
+          @if (isEditing) {
+          <div class="flex items-center gap-5 mt-3">
+            <span class="text-3xl font-bold ml-auto"> Change password </span>
+            <mat-slide-toggle
+              [(ngModel)]="isChangePassword"
+              (toggleChange)="togglePassword()"
+            />
+          </div>
+          }
+        </div>
+      </div>
+      }
+      <form [formGroup]="userForm" (submit)="onSubmit()">
+        @if (!isChangePassword) { @if (!isLogin){
+        <!-- Campo Nombre -->
+        <div class="form-group">
+          <label for="name">Nombre:</label>
+          <input
+            id="name"
+            type="text"
+            class="form-control"
+            autocomplete="name"
+            formControlName="name"
+            placeholder="Introduce tu nombre"
+          />
+        </div>
+        }
+        <!-- Campo Email -->
+        <div class="form-group">
+          <label for="email">Email:</label>
+          <input
+            id="email"
+            type="email"
+            autocomplete="email"
+            class="form-control"
+            formControlName="email"
+            placeholder="Introduce tu email"
+          />
+        </div>
+        } @if ((isChangePassword || isAuth) && (isMeRoute || isAuth)) {
+        <!-- Campo Contraseña Actual (solo en edición) -->
+        <div class="form-group">
+          <label for="currentPassword">{{ currentPasswordText }}:</label>
+          <input
+            id="currentPassword"
+            type="password"
+            class="form-control"
+            formControlName="currentPassword"
+            autocomplete="current-password"
+            placeholder="Introduce tu {{ currentPasswordText }}"
+          />
+        </div>
+        } @if (isChangePassword) {
+        <!-- Campo Nueva Contraseña (solo en edición) -->
+        <div class="form-group">
+          <label for="newPassword">Nueva contraseña:</label>
+          <input
+            id="newPassword"
+            type="password"
+            class="form-control"
+            formControlName="newPassword"
+            autocomplete="new-password"
+            placeholder="Introduce la nueva contraseña"
+          />
+        </div>
+        }@if (isChangePassword || isRegister) {
+        <!-- Campo Contraseña Actual (solo en edición) -->
+        <div class="form-group">
+          <label for="confirmPassword">Confirma contraseña:</label>
+          <input
+            id="confirmPassword"
+            type="password"
+            class="form-control"
+            formControlName="confirmPassword"
+            autocomplete="current-password"
+            placeholder="Confirma {{
+              isChangePassword ? 'tu nueva contraseña' : 'la contraseña'
+            }}"
+          />
+        </div>
+        } @if (isRegister) {
+        <div class="form-group text-2xl">
+          <label class="text-2xl" for="rol">Rol:</label>
+          <mat-form-field class="w-full">
+            <mat-label class="text-2xl">Seleccione un rol</mat-label>
+            <mat-select name="rol" formControlName="rol">
+              @for (rol of rolOptions; track rol) {
+              <mat-option [value]="rol">
+                <span class="text-2xl">{{ rol }}</span>
+              </mat-option>
+              }
+            </mat-select>
+          </mat-form-field>
+        </div>
+        } @if (!isViewUser) { @if (isAuth) {
+        <p class="text-right">
+          @if (isLogin) {
+          <a [routerLink]="['/register']">¿No tienes cuenta?</a>
+          }@else {
+          <a [routerLink]="['/login']">¿Ya tienes cuenta?</a>
+          }
+        </p>
+        }
+        <!-- Grupo de Botones -->
+        <mat-card-actions class="flex justify-center mb-5">
+          <!-- [disabled]="registerForm.invalid" -->
+          <button
+            type="submit"
+            class="btn btn-success text-4xl"
+            [disabled]="!validForm()"
+          >
+            {{ submitButtonText }}
+          </button>
+        </mat-card-actions>
+        }
+      </form>
+    </mat-card-content>
+  </mat-card>
+</div>
diff --git a/angular/RestClient/src/app/core/features/user/user-form/user-form.component.spec.ts b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..292f234075dbcba5482794cf23e4a231e666ba17
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.spec.ts
@@ -0,0 +1,91 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { HttpResponse } from '@angular/common/http';
+import { UserFormComponent } from './user-form.component';
+import { UserClientService } from '../../../../shared/user-client.service';
+import { of } from 'rxjs';
+
+describe('UserFormComponent', () => {
+  let component: UserFormComponent;
+  let fixture: ComponentFixture<UserFormComponent>;
+  let userService: UserClientService;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [ReactiveFormsModule, FormsModule],
+      declarations: [UserFormComponent],
+      providers: [UserClientService],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(UserFormComponent);
+    component = fixture.componentInstance;
+    userService = TestBed.inject(UserClientService);
+
+    spyOn(userService, 'getCurrentUser').and.returnValue(
+      of({
+        id: 1,
+        name: 'John Doe',
+        email: 'johndoe@example.com',
+        rol: 'CONSUMER',
+        status: 'WITH_ACTIVE_BOOKINGS',
+      })
+    );
+
+    spyOn(userService, 'updateUser').and.returnValue(of(new HttpResponse({ body: 'User updated successfully' })));
+    spyOn(userService, 'updatePassword').and.returnValue(of(new HttpResponse({ body: 'Password updated successfully' })));
+
+    fixture.detectChanges();
+  });
+
+  it('should create the component', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should load user data on initialization', () => {
+    expect(component.userForm.value).toEqual({
+      name: 'John Doe',
+      email: 'johndoe@example.com',
+      password: '',
+      confirmPassword: '',
+    });
+  });
+
+  it('should call updateUser when saving valid user data', () => {
+    component.userForm.patchValue({
+      name: 'Jane Doe',
+      email: 'janedoe@example.com',
+    });
+
+    component.saveChanges();
+
+    expect(userService.updateUser).toHaveBeenCalledWith({
+      name: 'Jane Doe',
+      email: 'janedoe@example.com',
+    });
+  });
+
+  it('should call updatePassword when password is updated and matches confirmPassword', () => {
+    component.userForm.patchValue({
+      password: 'newpassword123',
+      confirmPassword: 'newpassword123',
+    });
+
+    component.saveChanges();
+
+    expect(userService.updatePassword).toHaveBeenCalledWith(
+      '',
+      'newpassword123'
+    );
+  });
+
+  it('should not call updatePassword if password and confirmPassword do not match', () => {
+    component.userForm.patchValue({
+      password: 'newpassword123',
+      confirmPassword: 'differentpassword',
+    });
+
+    component.saveChanges();
+
+    expect(userService.updatePassword).not.toHaveBeenCalled();
+  });
+});
diff --git a/angular/RestClient/src/app/core/features/user/user-form/user-form.component.ts b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c6d272d01c5a9b899b1c77a84773fe47c43c7bb
--- /dev/null
+++ b/angular/RestClient/src/app/core/features/user/user-form/user-form.component.ts
@@ -0,0 +1,385 @@
+import { Component, OnInit } from '@angular/core';
+import {
+  FormBuilder,
+  FormGroup,
+  Validators,
+  ReactiveFormsModule,
+  FormsModule,
+} from '@angular/forms';
+import { UserClientService } from '../../../../shared/user-client.service';
+import { ActivatedRoute, Router, RouterModule } from '@angular/router';
+import { MatCardModule } from '@angular/material/card';
+import { MatInputModule } from '@angular/material/input';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatSlideToggleModule } from '@angular/material/slide-toggle';
+import { SessionService } from '../../../../shared/session.service';
+import { Session, UserRol, UserRolesArray } from '../../../../types';
+import { MatSelectModule } from '@angular/material/select';
+import { Observable } from 'rxjs';
+import { getBasePath } from '../../../../utils/utils';
+import { environment } from '../../../../../environments/environment';
+
+type EditMode =
+  | 'Login'
+  | 'Register'
+  | 'ViewUser'
+  | 'EditUser'
+  | 'ChangePassword'
+  | 'Other';
+const defaultUser: Session = {
+  id: 0,
+  name: 'test',
+  email: 'test@dev.com',
+  rol: 'ADMIN',
+};
+
+@Component({
+  standalone: true,
+  selector: 'app-user-form',
+  templateUrl: './user-form.component.html',
+  styleUrls: ['./user-form.component.css'],
+  imports: [
+    ReactiveFormsModule,
+    FormsModule,
+    RouterModule,
+    MatSlideToggleModule,
+    MatCardModule,
+    MatInputModule,
+    MatSelectModule,
+    MatFormFieldModule,
+  ],
+})
+export class UserFormComponent implements OnInit {
+  userForm!: FormGroup;
+  rolOptions: UserRol[] = UserRolesArray;
+  mode: EditMode = 'Other';
+  isMeRoute = false;
+
+  /** is editing the user data */
+  isEditing: boolean = false;
+  /** is showing the user data */
+  isViewUser: boolean = false;
+  /** is trying to do an auth action*/
+  isAuth: boolean = false;
+  /** is trying to login */
+  isLogin: boolean = false;
+  /** is trying to register */
+  isRegister: boolean = false;
+  /** don't want to update the password */
+  isChangePassword = false;
+
+  titleText = 'Mis datos';
+  submitButtonText = 'Submit';
+  currentPasswordText = 'Contraseña actual';
+
+  user = defaultUser;
+  isHotelManager = false;
+  isAdmin = false;
+
+  constructor(
+    private fb: FormBuilder,
+    private sessionService: SessionService,
+    private userService: UserClientService,
+    private route: ActivatedRoute,
+    private router: Router
+  ) {}
+
+  isEditRoute(urlSegments: any[], isMeRoute: boolean): boolean {
+    return isMeRoute
+      ? urlSegments.length >= 2 && urlSegments[1].path === 'edit'
+      : urlSegments.length >= 4 && urlSegments[3].path === 'edit';
+  }
+
+  isChangePasswordRoute(urlSegments: any[], isMeRoute: boolean): boolean {
+    return isMeRoute
+      ? urlSegments.length >= 2 && urlSegments[1].path === 'change-passwd'
+      : urlSegments.length >= 4 && urlSegments[3].path === 'change-passwd';
+  }
+
+  isViewUserRoute(urlSegments: any[], isMeRoute: boolean): boolean {
+    return isMeRoute
+      ? urlSegments.length === 1
+      : (urlSegments.length === 1 && urlSegments[0].path === 'admin') ||
+          urlSegments.length === 3;
+  }
+
+  isAuthRoute(urlSegments: any[], route: string): boolean {
+    return urlSegments.length === 1 && urlSegments[0].path === route;
+  }
+
+  ngOnInit(): void {
+    this.setUp();
+
+    // const auth = this.session.getSession();
+    // this.user = auth;
+    // this.userForm.patchValue({
+    //   name: this.user.name,
+    //   email: this.user.email,
+    // });
+  }
+
+  private initializeForm(): void {
+    // Solicitar contraseña actual
+    const confirmIdentity =
+      !this.isViewUser &&
+      (this.isChangePassword || this.isAuth) &&
+      (this.isMeRoute || this.isAuth);
+    // Solicitar nueva contraseña
+    const isChangePassword = this.isChangePassword;
+    // Solicitar confirmación de contraseña
+    const confirmPassword =
+      !this.isViewUser && (this.isChangePassword || this.isRegister);
+    // Solicitar email
+    const emailNotRequired = this.isViewUser || isChangePassword;
+    // Solicitar nombre
+    const nameNotRequired = emailNotRequired || this.isLogin;
+    // Solicitar rol
+    const rolNotRequired = !this.isRegister;
+    // console.log({
+    //   confirmIdentity,
+    //   isChangePassword,
+    //   confirmPassword,
+    //   emailNotRequired,
+    //   nameNotRequired,
+    //   rolNotRequired,
+    // });
+
+    this.userForm = this.fb.group({
+      name: [{ value: '', disabled: nameNotRequired }, Validators.required],
+      email: [
+        { value: '', disabled: emailNotRequired },
+        [Validators.required, Validators.email],
+      ],
+      currentPassword: [
+        { value: '', disabled: !confirmIdentity },
+        Validators.required,
+      ], // Solo habilitado en edición
+      newPassword: [
+        { value: '', disabled: !isChangePassword },
+        Validators.required,
+      ], // Solo habilitado en edición
+      confirmPassword: [
+        { value: '', disabled: !confirmPassword },
+        Validators.required,
+      ],
+      rol: [{ value: '', disabled: rolNotRequired }, Validators.required], // Solo habilitado en registro
+    });
+  }
+
+  setUp() {
+    const urlSeg = this.route.snapshot.url;
+    if (this.isAuthRoute(urlSeg, 'login')) {
+      // Login
+      this.isAuth = true;
+      this.isLogin = true;
+      this.mode = 'Login';
+      this.currentPasswordText = 'Contraseña';
+      this.submitButtonText = 'Login';
+      this.titleText = 'Login';
+    } else if (this.isAuthRoute(urlSeg, 'register')) {
+      // Register
+      this.isAuth = true;
+      this.isRegister = true;
+      this.mode = 'Register';
+      this.currentPasswordText = 'Contraseña';
+      this.submitButtonText = 'Create';
+      this.titleText = 'Register';
+    } else {
+      // Identificar si estamos usando /me o /users/:id
+      getBasePath(this.route);
+      const isMeRoute = urlSeg[0].path === 'me';
+      this.isMeRoute = isMeRoute;
+
+      if (this.isEditRoute(urlSeg, isMeRoute)) {
+        this.isEditing = true;
+        this.mode = 'EditUser';
+        this.titleText = 'Editar mis datos';
+      } else if (this.isChangePasswordRoute(urlSeg, isMeRoute)) {
+        this.mode = 'ChangePassword';
+        this.isEditing = true;
+        this.isChangePassword = true;
+        this.currentPasswordText = 'Contraseña actual';
+        this.titleText = 'Cambiar mi contraseña';
+      } else if (this.isViewUserRoute(urlSeg, isMeRoute)) {
+        this.mode = 'ViewUser';
+        this.isViewUser = true;
+        this.titleText = 'Mis datos';
+      }
+
+      this.submitButtonText = 'Update';
+    }
+
+    this.initializeForm();
+    if (!this.isAuth) {
+      this.loadUser();
+    } else {
+      this.sessionService.getSession().subscribe({
+        next: (session) => {
+          if (session) {
+            this.router.navigateByUrl(
+              this.sessionService.getMainPage(session.rol)
+            );
+          }
+        },
+      });
+    }
+  }
+
+  getHotelsUri() {
+    const basePath = getBasePath(this.route); // Obtener la base: '/me' o '/users/:id'
+    return `${basePath}/hotels`;
+  }
+
+  getBookingsUri() {
+    const basePath = getBasePath(this.route); // Obtener la base: '/me' o '/users/:id'
+    return `${basePath}/bookings`;
+  }
+
+  togglePassword() {
+    const basePath = getBasePath(this.route); // Obtener la base: '/me' o '/users/:id'
+
+    if (this.mode === 'EditUser') {
+      this.router.navigateByUrl(`${basePath}/change-passwd`);
+    } else if (this.mode === 'ChangePassword') {
+      this.router.navigateByUrl(`${basePath}/edit`);
+    }
+  }
+
+  switchMode() {
+    const basePath = getBasePath(this.route); // Obtener la base: '/me' o '/users/:id'
+    console.log({ ...this });
+    if (this.mode === 'EditUser') {
+      this.router.navigateByUrl(basePath);
+    } else if (this.mode === 'ViewUser') {
+      this.router.navigateByUrl(`${basePath}/edit`);
+    }
+  }
+
+  private resolve(): Observable<any> {
+    const userId = this.route.snapshot.paramMap.get('id');
+    console.log({ userId });
+    return userId
+      ? this.userService.getUser(Number(userId))
+      : this.sessionService.getSession();
+  }
+
+  private loadUser() {
+    // this.setData();
+    this.resolve().subscribe({
+      next: (user) => {
+        this.user = user;
+        this.isHotelManager = (user.rol as UserRol) === 'HOTEL_ADMIN';
+        this.isAdmin = (user.rol as UserRol) === 'ADMIN';
+        this.setData();
+      },
+      error: (error) => {
+        console.error('Error:', error);
+      },
+    });
+  }
+
+  private setData() {
+    this.userForm.patchValue({
+      name: this.user.name,
+      email: this.user.email,
+      rol: this.user.rol,
+    });
+  }
+
+  validForm() {
+    const validForm = this.userForm.valid;
+    const differentData =
+      this.isEditing && !this.isChangePassword && this.modifiedData();
+    const validatePassword = this.validatePassword();
+
+    return validForm && (differentData || validatePassword || this.isLogin);
+  }
+
+  private modifiedData() {
+    return (
+      this.userForm.get('name')?.value !== this.user.name ||
+      this.userForm.get('email')?.value !== this.user.email
+    );
+  }
+
+  private validatePassword() {
+    const { currentPassword, newPassword, confirmPassword } =
+      this.userForm.value;
+    const updatePasswordValidate =
+      this.isEditing &&
+      this.isChangePassword &&
+      newPassword === confirmPassword &&
+      currentPassword !== newPassword;
+    const registerPasswordValidate =
+      this.isRegister && currentPassword === confirmPassword;
+    return updatePasswordValidate || registerPasswordValidate;
+  }
+
+  onSubmit() {
+    const data = this.userForm.value;
+    console.log({ data });
+
+    switch (this.mode) {
+      case 'Login':
+        this.login(data.email, data.currentPassword);
+        break;
+      case 'Register':
+        this.register(data.name, data.email, data.currentPassword, data.rol);
+        break;
+      case 'EditUser':
+        this.updateUser(data.name, data.email);
+        break;
+      case 'ChangePassword':
+        this.changePassword(data.currentPassword, data.newPassword);
+        break;
+      default:
+        break;
+    }
+  }
+
+  private login(email: string, password: string) {
+    this.sessionService.login(email, password).subscribe({
+      next: (r: any) => {
+        this.router.navigateByUrl(r.mainPage);
+      },
+      error: (error) => {
+        console.error(error);
+        // this.toastr.error('Invalid email or password');
+      },
+    });
+  }
+
+  private register(
+    name: string,
+    email: string,
+    password: string,
+    rol: UserRol
+  ) {
+    console.log({ name, email, password, rol });
+    this.sessionService.register(name, email, password, rol).subscribe({
+      next: (r: any) => {
+        this.router.navigateByUrl(r.mainPage);
+      },
+      error: (error) => {
+        console.error(error);
+        // this.toastr.error('Invalid email or password');
+      },
+    });
+  }
+
+  private updateUser(name: string, email: string) {
+    this.userService.updateUser(this.user.id, { name, email }).subscribe({
+      next: () => {
+        this.router.navigateByUrl(getBasePath(this.route));
+      },
+      error: (error) => {
+        console.error(error);
+        // this.toastr.error('Invalid email or password');
+      },
+    });
+  }
+
+  private changePassword(password: string | undefined, newPassword: string) {
+    alert('Unimplemented yet');
+  }
+}
diff --git a/angular/RestClient/src/app/core/navigation/navigation.component.css b/angular/RestClient/src/app/core/navigation/navigation.component.css
index 200c21a3a62ba294324b6a77c75d043c2bd5574b..dd40d570ba45e75417e8615f515a70fea3667aec 100644
--- a/angular/RestClient/src/app/core/navigation/navigation.component.css
+++ b/angular/RestClient/src/app/core/navigation/navigation.component.css
@@ -4,7 +4,7 @@ nav {
   padding: 1em;
 }
 
-ul {
+ul.nav {
   list-style: none;
   padding: 0;
   display: flex;
@@ -14,18 +14,18 @@ li {
   margin-right: 20px;
 }
 
-a,
-a:visited {
+a.nav-link,
+a.nav-link:visited {
   color: white;
   text-decoration: none;
   transform: scale(1);
   transition: transform 0.3s ease;
 }
 
-a:hover {
+a.nav-link:hover {
   font-weight: bold;
   text-decoration: underline;
-  color: yellow;
+  color: rgb(112, 112, 112);
   transition: transform 0.3s ease;
   transform: scale(1.5);
 }
@@ -34,6 +34,12 @@ a:hover {
   font-weight: bold;
 }
 
+a.simple,
+a.simple:visited {
+  color: white;
+  text-decoration: none;
+}
+
 @keyframes escalar {
   0% {
     transform: scale(1);
diff --git a/angular/RestClient/src/app/core/navigation/navigation.component.html b/angular/RestClient/src/app/core/navigation/navigation.component.html
index 63615464cbbf7aec69cbd99e5e33ea9d1e5e08d8..3cfc3e49603a529a745791cb68b9916d62fdbe45 100644
--- a/angular/RestClient/src/app/core/navigation/navigation.component.html
+++ b/angular/RestClient/src/app/core/navigation/navigation.component.html
@@ -1,12 +1,59 @@
 <nav>
-  <ul>
-    <li><a class="btn" [routerLink]="['/']">Home - Usuarios</a></li>
-    <li>
-      <a class="btn" [routerLink]="['/hotels', 'new']">Registrar Hotel</a>
-    </li>
-    <li><a class="btn" [routerLink]="['/hotels']">Hoteles</a></li>
-    <li>
-      <a class="btn" [routerLink]="['/bookings', 'search']">Nueva Reserva</a>
+  <ul class="nav">
+    <li><a class="btn nav-link" [routerLink]="['/hotels']">Hoteles</a></li>
+    <li class="ml-auto">
+      @if (isLogged){
+      <!-- Dropdown para usuario registrado -->
+
+      <div class="btn bg-blue-500 text-white rounded hover:bg-blue-600">
+        <!-- mat-icon-button -->
+        <button
+          [matMenuTriggerFor]="sessionOptions"
+          class="flex items-center gap-3"
+        >
+          <span class="text-4xl">{{ user.name }}</span>
+
+          @if (trigger?.menuOpen) {
+          <mat-icon class="text-4xl">arrow_drop_up</mat-icon>
+          }@else {
+          <mat-icon class="text-4xl">arrow_drop_down</mat-icon>
+          }
+        </button>
+      </div>
+      <mat-menu #sessionOptions="matMenu" xPosition="before">
+        @for (section of sections; track section.id) { @if (section.link) {
+        <a [routerLink]="section.link" class="simple">
+          <button mat-menu-item>
+            <mat-icon>{{ section.icon }}</mat-icon>
+            <span class="text-2xl">{{ section.text }}</span>
+          </button>
+        </a>
+        } @else {
+        <button mat-menu-item>
+          <mat-icon>{{ section.icon }}</mat-icon>
+          <span class="text-2xl">{{ section.text }}</span>
+        </button>
+        } }
+        <button mat-menu-item (click)="logout()">
+          <mat-icon>logout</mat-icon>
+          <span class="text-2xl">Cerrar sesión</span>
+        </button>
+      </mat-menu>
+
+      } @else {
+      <div
+        class="btn bg-blue-500 text-white hover:bg-blue-600 ml-auto"
+        (click)="login()"
+      >
+        <a class="simple" [routerLink]="['/login']">
+          <!-- <a class="simple"> -->
+          <button class="flex items-center gap-3">
+            <span class="text-4xl">Login</span>
+            <mat-icon>login</mat-icon>
+          </button>
+        </a>
+      </div>
+      }
     </li>
   </ul>
 </nav>
diff --git a/angular/RestClient/src/app/core/navigation/navigation.component.ts b/angular/RestClient/src/app/core/navigation/navigation.component.ts
index ccdab77b49379e3ed27abd65ada67e693882939a..effeb032ae23e20086be4661a92971ac5543cb36 100644
--- a/angular/RestClient/src/app/core/navigation/navigation.component.ts
+++ b/angular/RestClient/src/app/core/navigation/navigation.component.ts
@@ -1,10 +1,133 @@
-import { Component } from '@angular/core';
-import { Router, RouterModule } from '@angular/router';
+import { OnInit, Component, ViewChild } from '@angular/core';
+import { RouterModule } from '@angular/router';
+import { MatButtonModule } from '@angular/material/button';
+import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu';
+import { MatIconModule } from '@angular/material/icon';
+import { Session, User, UserRol } from '../../types';
+import { SessionService } from '../../shared/session.service';
+import { UserClientService } from '../../shared/user-client.service';
+import { AuthClientService } from '../../shared/auth-client.service';
+import { Observable } from 'rxjs';
+
+var comp_id = 0;
+
+export function genId() {
+  const comp = 'navigation';
+  return `${comp}-${comp_id++}`;
+}
+
+interface Section {
+  id: string;
+  icon: string;
+  text: string;
+  link: string;
+  allowRoles?: UserRol[];
+}
+
 @Component({
   selector: 'app-navigation',
   standalone: true,
-  imports: [RouterModule],
+  imports: [RouterModule, MatButtonModule, MatMenuModule, MatIconModule],
   templateUrl: './navigation.component.html',
   styleUrl: './navigation.component.css',
 })
-export class NavigationComponent {}
+export class NavigationComponent implements OnInit {
+  @ViewChild(MatMenuTrigger)
+  trigger?: MatMenuTrigger;
+  isLogged = false;
+  user: Session = {
+    id: 0,
+    name: '',
+    email: '',
+    rol: 'CLIENT',
+  };
+  sections: Section[] = [];
+
+  constructor(private sessionService: SessionService) {}
+
+  ngOnInit() {
+    this.loadUser();
+  }
+
+  loadUser() {
+    this.sessionService.getSession().subscribe({
+      next: (session) => {
+        if (session) {
+          this.user = session;
+          this.isLogged = true;
+          this.sections = this.genSections();
+        } else {
+          this.isLogged = false;
+        }
+      },
+    });
+  }
+
+  toggleDropdown() {
+    if (this.trigger) {
+      if (this.trigger.menuOpen) this.trigger.closeMenu();
+      else this.trigger.openMenu();
+    }
+  }
+
+  schemaSections: Section[] = [
+    {
+      id: genId(),
+      icon: 'person',
+      text: 'Información personal',
+      link: '/me',
+    },
+    {
+      id: genId(),
+      icon: 'calendar_today',
+      text: 'Reservas',
+      allowRoles: ['CLIENT'],
+      link: '/me/bookings',
+    },
+    {
+      id: genId(),
+      icon: 'hotel',
+      text: 'Hoteles',
+      allowRoles: ['HOTEL_ADMIN'],
+      link: '/me/hotels',
+    },
+    {
+      id: genId(),
+      icon: 'fiber_new',
+      text: 'Registrar hotel',
+      allowRoles: ['HOTEL_ADMIN'],
+      link: '/hotels/register',
+    },
+    {
+      id: genId(),
+      icon: 'settings',
+      text: 'Admin Zone',
+      allowRoles: ['ADMIN'],
+      link: '/admin',
+    },
+    {
+      id: genId(),
+      icon: 'group',
+      text: 'Users',
+      allowRoles: ['ADMIN'],
+      link: '/admin/users',
+    },
+  ];
+
+  genSections() {
+    return this.schemaSections.filter(
+      (section) =>
+        !section.allowRoles ||
+        section.allowRoles.length === 0 || // No tiene limitación
+        section.allowRoles.includes(this.user.rol) // El rol del usuario es aceptado
+    );
+  }
+
+  login() {}
+
+  logout() {
+    // if (confirm('You are trying to logout'))
+    this.sessionService.logout();
+    this.loadUser();
+  }
+}
diff --git a/angular/RestClient/src/app/page/unauthorized/unauthorized.component.css b/angular/RestClient/src/app/page/unauthorized/unauthorized.component.css
new file mode 100644
index 0000000000000000000000000000000000000000..8c77ba55f871f914a54f60a38e0419dafb94e1b0
--- /dev/null
+++ b/angular/RestClient/src/app/page/unauthorized/unauthorized.component.css
@@ -0,0 +1,44 @@
+/* El contenedor principal ocupa toda la pantalla disponible */
+.unauthorized-container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 88vh;
+  background-color: #f8f9fa;
+  flex-direction: column; /* Alinea los elementos en columna */
+}
+
+/* Contenedor interno centrado con margen */
+.content {
+  text-align: center;
+  padding: 2rem;
+  border: 1px solid #ddd;
+  border-radius: 10px;
+  background: #ffffff;
+  box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
+  max-width: 400px; /* Limitamos el ancho del contenido */
+  width: 100%; /* Asegura que no exceda el tamaño máximo */
+
+  .icon {
+    /* font-size: 80px; */
+    color: #ff5722;
+    margin-bottom: 1rem;
+  }
+
+  .title {
+    font-size: 2rem;
+    color: #333333;
+    margin-bottom: 0.5rem;
+  }
+
+  .message {
+    font-size: 1.2rem;
+    color: #555555;
+    margin-bottom: 2rem;
+  }
+
+  button {
+    padding: 0.5rem 2rem;
+    font-size: 1rem;
+  }
+}
diff --git a/angular/RestClient/src/app/page/unauthorized/unauthorized.component.html b/angular/RestClient/src/app/page/unauthorized/unauthorized.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..505aed70edb5c7f010c70db46e57ed4fc48f54d3
--- /dev/null
+++ b/angular/RestClient/src/app/page/unauthorized/unauthorized.component.html
@@ -0,0 +1,10 @@
+<div class="unauthorized-container">
+  <div class="content">
+    <mat-icon class="icon">lock</mat-icon>
+    <h1 class="title">Acceso Denegado</h1>
+    <p class="message">No tienes permisos para acceder a esta página.</p>
+    <a [routerLink]="[mainPage]">
+      <button mat-raised-button color="primary">Volver al inicio</button>
+    </a>
+  </div>
+</div>
diff --git a/angular/RestClient/src/app/page/unauthorized/unauthorized.component.spec.ts b/angular/RestClient/src/app/page/unauthorized/unauthorized.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bab5ad8d9e3b005c4068dffa26dacb9de7417c15
--- /dev/null
+++ b/angular/RestClient/src/app/page/unauthorized/unauthorized.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { UnauthorizedComponent } from './unauthorized.component';
+
+describe('UnauthorizedComponent', () => {
+  let component: UnauthorizedComponent;
+  let fixture: ComponentFixture<UnauthorizedComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      imports: [UnauthorizedComponent]
+    })
+    .compileComponents();
+
+    fixture = TestBed.createComponent(UnauthorizedComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/angular/RestClient/src/app/page/unauthorized/unauthorized.component.ts b/angular/RestClient/src/app/page/unauthorized/unauthorized.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..061a73dbe09d717285d19188179a450cd780a699
--- /dev/null
+++ b/angular/RestClient/src/app/page/unauthorized/unauthorized.component.ts
@@ -0,0 +1,26 @@
+import { Component } from '@angular/core';
+import { RouterModule } from '@angular/router';
+import { SessionService } from '../../shared/session.service';
+import { MatIconModule } from '@angular/material/icon';
+import { MatButtonModule } from '@angular/material/button';
+
+@Component({
+  selector: 'app-unauthorized',
+  standalone: true,
+  imports: [RouterModule, MatIconModule, MatButtonModule],
+  templateUrl: './unauthorized.component.html',
+  styleUrl: './unauthorized.component.css',
+})
+export class UnauthorizedComponent {
+  mainPage: string = '';
+
+  constructor(private sessionService: SessionService) {
+    this.sessionService.getSession().subscribe({
+      next: (session) => {
+        this.mainPage = session
+          ? sessionService.getMainPage(session.rol)
+          : '/login';
+      },
+    });
+  }
+}
diff --git a/angular/RestClient/src/app/security/auth.interceptor.ts b/angular/RestClient/src/app/security/auth.interceptor.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a3dadfdaa637c526d0beb436546a9117359f49f
--- /dev/null
+++ b/angular/RestClient/src/app/security/auth.interceptor.ts
@@ -0,0 +1,37 @@
+import { HttpInterceptorFn } from '@angular/common/http';
+import { inject } from '@angular/core';
+import { LocalStorageService } from '../shared/local-storage.service';
+import { SessionService } from '../shared/session.service';
+
+const excluded = ['/login', '/register'];
+
+function isExcludedUrl(url: string) {
+  return excluded.some((excluded) => url.includes(excluded));
+}
+
+export const authRequest: HttpInterceptorFn = (req, next) => {
+  // Obtener el token desde localStorage (o cualquier otro mecanismo)
+  const session = inject(SessionService); // Obtener instancia del servicio
+  const isLogged = session.isLogged();
+
+  if (isExcludedUrl(req.url) || !isLogged) {
+    return next(req); // No modificar la solicitud
+  }
+
+  const token = session.getToken();
+
+  console.log('TOKEN:', { token });
+
+  // Clonar la solicitud y agregar el token al encabezado si existe
+  const authReq = token
+    ? req.clone({
+        setHeaders: {
+          Authorization: `Bearer ${token}`,
+          'Content-Type': 'application/json',
+        },
+      })
+    : req;
+
+  // Pasar la solicitud modificada al siguiente manejador
+  return next(authReq);
+};
diff --git a/angular/RestClient/src/app/security/rol.guard.spec.ts b/angular/RestClient/src/app/security/rol.guard.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0086470eb8061f59d7eea03aed354b751b725743
--- /dev/null
+++ b/angular/RestClient/src/app/security/rol.guard.spec.ts
@@ -0,0 +1,17 @@
+import { TestBed } from '@angular/core/testing';
+import { CanActivateFn } from '@angular/router';
+
+import { rolGuard } from './rol.guard';
+
+describe('rolGuard', () => {
+  const executeGuard: CanActivateFn = (...guardParameters) => 
+      TestBed.runInInjectionContext(() => rolGuard(...guardParameters));
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+  });
+
+  it('should be created', () => {
+    expect(executeGuard).toBeTruthy();
+  });
+});
diff --git a/angular/RestClient/src/app/security/rol.guard.ts b/angular/RestClient/src/app/security/rol.guard.ts
new file mode 100644
index 0000000000000000000000000000000000000000..53017db2422c5f9e8c62c37622786bc4988673bf
--- /dev/null
+++ b/angular/RestClient/src/app/security/rol.guard.ts
@@ -0,0 +1,43 @@
+import { inject } from '@angular/core';
+import { CanActivateFn, Router } from '@angular/router';
+import { SessionService } from '../shared/session.service';
+import { UserRol, Session } from '../types';
+import { map } from 'rxjs';
+
+export const rolGuard: CanActivateFn = (route, state) => {
+  const sessionService = inject(SessionService);
+  const router = inject(Router);
+  // Obtén el rol esperado desde los datos de la ruta
+  const expectedRole = route.data?.['expectedRole'];
+
+  // Verifica si el usuario tiene sesión activa
+  const session = sessionService.isValid();
+
+  if (!session) {
+    console.log('no session');
+    router.navigate(['/login']);
+    return false;
+  }
+
+  return sessionService.getSession().pipe(
+    map((session: Session | null) => {
+      if (!session) return false;
+
+      if (
+        Array.isArray(expectedRole) &&
+        (expectedRole as UserRol[]).includes(session.rol)
+      ) {
+        console.log('Rol in Rol arry');
+        return true;
+      } else if (session.rol === expectedRole) {
+        console.log('Rol valido');
+        return true;
+      }
+      console.log('Unautorizado');
+
+      // Redirige si el usuario no tiene el rol necesario
+      router.navigate(['/unauthorized']);
+      return false;
+    })
+  );
+};
diff --git a/angular/RestClient/src/app/shared/data.service.spec.ts b/angular/RestClient/src/app/shared/auth-client.service.spec.ts
similarity index 52%
rename from angular/RestClient/src/app/shared/data.service.spec.ts
rename to angular/RestClient/src/app/shared/auth-client.service.spec.ts
index 38e8d9ec63f1f637f51a18393f66c56d564e49a9..718c264fe9fa26ade0a95ea726d68cf899584f1d 100644
--- a/angular/RestClient/src/app/shared/data.service.spec.ts
+++ b/angular/RestClient/src/app/shared/auth-client.service.spec.ts
@@ -1,13 +1,13 @@
 import { TestBed } from '@angular/core/testing';
 
-import { DataService } from './data.service';
+import { AuthClientService } from './auth-client.service';
 
-describe('DataService', () => {
-  let service: DataService;
+describe('AuthClientService', () => {
+  let service: AuthClientService;
 
   beforeEach(() => {
     TestBed.configureTestingModule({});
-    service = TestBed.inject(DataService);
+    service = TestBed.inject(AuthClientService);
   });
 
   it('should be created', () => {
diff --git a/angular/RestClient/src/app/shared/auth-client.service.ts b/angular/RestClient/src/app/shared/auth-client.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..99d3ebbfc0c91cc88ba2f4266cd1f895ccbfebd9
--- /dev/null
+++ b/angular/RestClient/src/app/shared/auth-client.service.ts
@@ -0,0 +1,46 @@
+import { Injectable } from '@angular/core';
+import { environment } from '../../environments/environment';
+import { HttpClient } from '@angular/common/http';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class AuthClientService {
+  private readonly URI = environment.authAPI;
+
+  constructor(private http: HttpClient) {}
+
+  login(email: String, password: String) {
+    return this.http.post(
+      `${this.URI}/login`,
+      { email, password }
+      // {
+      //   headers: {
+      //     'Content-Type': 'application/json',
+      //     'Access-Control-Allow-Origin': '*',
+      //     'Access-Control-Allow-Methods':
+      //       'GET, POST, OPTIONS, PUT, PATCH, DELETE',
+      //     'Access-Control-Allow-Headers': 'X-Requested-With,content-type',
+      //     'Access-Control-Allow-Credentials': 'true',
+      //   },
+      // }
+    );
+  }
+
+  register(name: String, email: String, password: String, rol?: String) {
+    return this.http.post(
+      `${this.URI}/register`,
+      {
+        name,
+        email,
+        password,
+        rol,
+      }
+      // {
+      //   headers: {
+      //     'Content-Type': 'application/json',
+      //   },
+      // }
+    );
+  }
+}
diff --git a/angular/RestClient/src/app/shared/booking-client.service.spec.ts b/angular/RestClient/src/app/shared/booking-client.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7155f3c6a365fd8f46220cabbf90d2889eeaa05a
--- /dev/null
+++ b/angular/RestClient/src/app/shared/booking-client.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { BookingClientService } from './booking-client.service';
+
+describe('BookingClientService', () => {
+  let service: BookingClientService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(BookingClientService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/angular/RestClient/src/app/shared/booking-client.service.ts b/angular/RestClient/src/app/shared/booking-client.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a15867105f00cf0bd6ea093a4a64bebbb3fd048f
--- /dev/null
+++ b/angular/RestClient/src/app/shared/booking-client.service.ts
@@ -0,0 +1,42 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { environment } from '../../environments/environment';
+import { Booking } from '../types/Booking'; // Ajusta la ruta a tu modelo Booking
+
+@Injectable({
+  providedIn: 'root',
+})
+export class BookingClientService {
+  private URI = environment.bookingAPI;
+
+  constructor(private http: HttpClient) {}
+
+  // Método para crear una nueva reserva
+  createBooking(bookingRequest: Booking): Observable<Booking> {
+    const { startDate, endDate } = bookingRequest;
+    const end = endDate.toISOString();
+    console.log({ bookingRequest, end });
+
+    return this.http.post<Booking>(this.URI, bookingRequest);
+  }
+
+  // Método para obtener todas las reservas
+  getAllBookings(): Observable<Booking[]> {
+    return this.http.get<Booking[]>(this.URI);
+  }
+
+  // Método para obtener una reserva por ID
+  getBookingById(id: number): Observable<Booking> {
+    return this.http.get<Booking>(`${this.URI}/${id}`);
+  }
+
+  getBookingsByUser(userId: number) {
+    return this.http.get<Booking[]>(`${this.URI}?userId=${userId}`);
+  }
+
+  // Método para eliminar una reserva
+  deleteBooking(id: number) {
+    return this.http.delete(`${this.URI}/${id}`);
+  }
+}
diff --git a/angular/RestClient/src/app/shared/booking.service.ts b/angular/RestClient/src/app/shared/booking.service.ts
deleted file mode 100644
index 535f4d55ae2c7492a18e0bbf8472c0e78ca99392..0000000000000000000000000000000000000000
--- a/angular/RestClient/src/app/shared/booking.service.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-// booking.service.ts
-import { Injectable } from '@angular/core';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
-import { Observable } from 'rxjs';
-
-import { Booking } from '../../types/Booking'; // Ajusta la ruta a tu modelo Booking
-import { User, UserState } from '../../types';
-
-@Injectable({
-  providedIn: 'root', // Esto hace que el servicio esté disponible en toda la aplicación
-})
-export class BookingService {
-  private apiUrl = 'http://localhost:8080/bookings';
-
-  constructor(private http: HttpClient) {}
-
-  // Método para crear una nueva reserva
-  createBooking(bookingRequest: Booking): Observable<Booking> {
-    return this.http.post<Booking>(this.apiUrl, bookingRequest, {
-      headers: new HttpHeaders({
-        'Content-Type': 'application/json',
-      }),
-    });
-  }
-
-  // Método para obtener todas las reservas
-  getAllBookings(): Observable<Booking[]> {
-    return this.http.get<Booking[]>(this.apiUrl);
-  }
-
-  // Método para obtener una reserva por ID
-  getBookingById(id: number): Observable<Booking> {
-    return this.http.get<Booking>(`${this.apiUrl}/${id}`);
-  }
-
-  // Método para eliminar una reserva
-  deleteBooking(id: number) {
-    return this.http.delete(`${this.apiUrl}/${id}`);
-  }
-}
diff --git a/angular/RestClient/src/app/shared/cliente-api-rest.service.spec.ts b/angular/RestClient/src/app/shared/cliente-api-rest.service.spec.ts
deleted file mode 100644
index 737c9449305eb7a05fdbb88e75ef1492b05990b3..0000000000000000000000000000000000000000
--- a/angular/RestClient/src/app/shared/cliente-api-rest.service.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { TestBed } from '@angular/core/testing';
-
-import { ClienteApiRestService } from './cliente-api-rest.service';
-
-describe('ClienteApiRestService', () => {
-  let service: ClienteApiRestService;
-
-  beforeEach(() => {
-    TestBed.configureTestingModule({});
-    service = TestBed.inject(ClienteApiRestService);
-  });
-
-  it('should be created', () => {
-    expect(service).toBeTruthy();
-  });
-});
diff --git a/angular/RestClient/src/app/shared/cliente-api-rest.service.ts b/angular/RestClient/src/app/shared/cliente-api-rest.service.ts
deleted file mode 100644
index cb4326ef52f9ed54bc172aa9fad9406872423815..0000000000000000000000000000000000000000
--- a/angular/RestClient/src/app/shared/cliente-api-rest.service.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-import { HttpClient } from '@angular/common/http';
-import { Injectable } from '@angular/core';
-import { Hotel, Booking, Room, UserState } from '../../types';
-import { User } from '../../types';
-
-@Injectable({
-  providedIn: 'root',
-})
-export class ClienteApiRestService {
-  private static readonly BASE_URI = 'http://localhost:8080';
-  private static readonly HOTEL_URI = `${ClienteApiRestService.BASE_URI}/hotels`;
-  private static readonly USER_URI = `${ClienteApiRestService.BASE_URI}/users`;
-  constructor(private http: HttpClient) {}
-
-  getHotel(id: number) {
-    const url = `${ClienteApiRestService.HOTEL_URI}/${id}`;
-    return this.http.get<Hotel>(url);
-  }
-
-  getAllHotels() {
-    const url = `${ClienteApiRestService.HOTEL_URI}`;
-    return this.http.get<Hotel[]>(url);
-  }
-
-  deleteHotel(id: number) {
-    const url = `${ClienteApiRestService.HOTEL_URI}/${id}`;
-    return this.http.delete(url, { observe: 'response', responseType: 'text' });
-  }
-
-  addHotel(hotel: Hotel) {
-    const url = `${ClienteApiRestService.HOTEL_URI}`;
-    return this.http.post(url, hotel, {
-      observe: 'response',
-      responseType: 'text',
-    });
-  }
-
-  alterRoomAvailability(
-    hotelId: number,
-    roomId: number,
-    availability: boolean
-  ) {
-    const url = `${ClienteApiRestService.HOTEL_URI}/${hotelId}/rooms/${roomId}`;
-    return this.http.patch(
-      url,
-      { available: availability },
-      {
-        observe: 'response',
-        responseType: 'text',
-      }
-    );
-  }
-
-  createBooking(bookingRequest: Booking) {
-    return this.http.post('http://localhost:8080/bookings', bookingRequest);
-  }
-
-  getRoomsAvailableInDateRange(hotelId: number, start: Date, end: Date) {
-    const startStr = start.toISOString().split('T')[0];
-    const endStr = end.toISOString().split('T')[0];
-    const url = `${ClienteApiRestService.HOTEL_URI}/${hotelId}/rooms?start=${startStr}&end=${endStr}`;
-    return this.http.get<Room[]>(url);
-  }
-
-  getUser(userId: number) {
-    return this.http.get<User>(`http://localhost:8080/users/${userId}`);
-  }
-
-  getAllUsers() {
-    return this.http.get<User[]>('http://localhost:8080/users', {
-      observe: 'body',
-    });
-  }
-
-  getUserBookings(userId: number) {
-    return this.http.get<Booking[]>(
-      `${ClienteApiRestService.BASE_URI}/users/${userId}/bookings`
-    );
-  }
-
-  alterUserStatus(userId: number, status: UserState) {
-    return this.http.patch(
-      `${ClienteApiRestService.BASE_URI}/users/${userId}`,
-      {
-        status,
-      },
-      {
-        observe: 'response',
-        responseType: 'text',
-      }
-    );
-  }
-}
diff --git a/angular/RestClient/src/app/shared/data.service.ts b/angular/RestClient/src/app/shared/data.service.ts
deleted file mode 100644
index f22afaeb6662ab48290b344d645945fc9a1c77b0..0000000000000000000000000000000000000000
--- a/angular/RestClient/src/app/shared/data.service.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Injectable } from '@angular/core';
-import { BehaviorSubject } from 'rxjs';
-
-@Injectable({
-  providedIn: 'root',
-})
-export class DataService {
-  private message = new BehaviorSubject('hotel list');
-  currentMessage = this.message.asObservable();
-
-  private showMessage = new BehaviorSubject<boolean>(false);
-  showCurrentMessage = this.showMessage.asObservable();
-
-  constructor() {}
-
-  setMessage(message: string) {
-    this.message.next(message);
-  }
-
-  setShowCurrentMessage(valor: boolean) {
-    this.showMessage.next(valor);
-  }
-}
diff --git a/angular/RestClient/src/app/shared/hotel-client.service.spec.ts b/angular/RestClient/src/app/shared/hotel-client.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5d32879cfc481ef393ffba104889798d7d982863
--- /dev/null
+++ b/angular/RestClient/src/app/shared/hotel-client.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { HotelClientService } from './hotel-client.service';
+
+describe('HotelClientService', () => {
+  let service: HotelClientService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(HotelClientService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/angular/RestClient/src/app/shared/hotel-client.service.ts b/angular/RestClient/src/app/shared/hotel-client.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5c50231651d1e0bc61b036e3e1220d468009a4f8
--- /dev/null
+++ b/angular/RestClient/src/app/shared/hotel-client.service.ts
@@ -0,0 +1,83 @@
+import { Injectable } from '@angular/core';
+import { environment } from '../../environments/environment';
+import { HttpClient } from '@angular/common/http';
+import { Hotel, Room } from '../types';
+import { SessionService } from './session.service';
+import { catchError, map, switchMap, throwError } from 'rxjs';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class HotelClientService {
+  private readonly URI = environment.hotelAPI;
+  constructor(
+    private http: HttpClient,
+    private sessionService: SessionService
+  ) {}
+
+  getHotel(id: number) {
+    const url = `${this.URI}/${id}`;
+    return this.http.get<Hotel>(url);
+  }
+
+  getAllHotels(startDate?: Date, endDate?: Date) {
+    const url = `${this.URI}`;
+    if (!startDate || !endDate) return this.http.get<Hotel[]>(url);
+    const start = new Date(startDate).toISOString().split('T')[0];
+    const end = new Date(endDate).toISOString().split('T')[0];
+    return this.http.get<Hotel[]>(url, { params: { start, end } });
+  }
+
+  getAllHotelsByUser(userId:number, startDate?: Date, endDate?: Date) {
+    const url = `${this.URI}`;
+    if (!startDate || !endDate)
+      return this.http.get<Hotel[]>(url, { params: { managerId:userId } });
+    const start = new Date(startDate).toISOString().split('T')[0];
+    const end = new Date(endDate).toISOString().split('T')[0];
+    return this.http.get<Hotel[]>(url, { params: { managerId:userId, start, end } });
+  }
+
+  deleteHotel(id: number) {
+    const url = `${this.URI}/${id}`;
+    return this.http.delete(url);
+  }
+
+  addHotel(hotel: Hotel) {
+    const url = `${this.URI}`;
+    return this.sessionService.getSession().pipe(
+      map((session) => {
+        if (!session) {
+          throw new Error('No session found');
+        }
+        const { id } = session;
+        const hotelWithHM = { ...hotel, hotelManager: { id } };
+        return hotelWithHM;
+      }),
+      switchMap((hotelWithHM) =>
+        this.http.post(url, hotelWithHM).pipe(
+          // Opcional: Puedes manejar transformaciones o errores aquí.
+          catchError((err) => {
+            console.error('Error al agregar hotel:', err);
+            return throwError(() => err);
+          })
+        )
+      )
+    );
+  }
+
+  alterRoomAvailability(
+    hotelId: number,
+    roomId: number,
+    availability: boolean
+  ) {
+    const url = `${this.URI}/${hotelId}/rooms/${roomId}`;
+    return this.http.patch(url, { available: availability });
+  }
+
+  getRoomsAvailableInDateRange(hotelId: number, start: Date, end: Date) {
+    const startStr = start.toISOString().split('T')[0];
+    const endStr = end.toISOString().split('T')[0];
+    const url = `${this.URI}/${hotelId}/rooms?start=${startStr}&end=${endStr}`;
+    return this.http.get<Room[]>(url);
+  }
+}
diff --git a/angular/RestClient/src/app/shared/local-storage.service.spec.ts b/angular/RestClient/src/app/shared/local-storage.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba1dbd4362ebcfddcd262fc30de07f4beeb466e3
--- /dev/null
+++ b/angular/RestClient/src/app/shared/local-storage.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { LocalStorageService } from './local-storage.service';
+
+describe('LocalStorageService', () => {
+  let service: LocalStorageService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(LocalStorageService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/angular/RestClient/src/app/shared/local-storage.service.ts b/angular/RestClient/src/app/shared/local-storage.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dc328655c80df4c87132a25305026eb3f00d1fec
--- /dev/null
+++ b/angular/RestClient/src/app/shared/local-storage.service.ts
@@ -0,0 +1,29 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class LocalStorageService {
+  save(key: string, value: object) {
+    const content = JSON.stringify(value);
+    localStorage.setItem(key, content);
+  }
+
+  read<T>(key: string) {
+    const json = localStorage.getItem(key);
+    const ret = json ? (JSON.parse(json) as T) : null;
+    return ret;
+  }
+
+  consume<T>(key: string) {
+    const value = this.read<T>(key);
+    if (value !== null) {
+      this.remove(key);
+    }
+    return value;
+  }
+
+  remove(key: string) {
+    localStorage.removeItem(key);
+  }
+}
diff --git a/angular/RestClient/src/app/shared/booking.service.spec.ts b/angular/RestClient/src/app/shared/session.service.spec.ts
similarity index 54%
rename from angular/RestClient/src/app/shared/booking.service.spec.ts
rename to angular/RestClient/src/app/shared/session.service.spec.ts
index 3992ef555abf9021f683a3b7fd42344842c4d6ed..4238e142b07aad273072a64a6df73c7960707501 100644
--- a/angular/RestClient/src/app/shared/booking.service.spec.ts
+++ b/angular/RestClient/src/app/shared/session.service.spec.ts
@@ -1,13 +1,13 @@
 import { TestBed } from '@angular/core/testing';
 
-import { BookingService } from './booking.service';
+import { SessionService } from './session.service';
 
-describe('BookingService', () => {
-  let service: BookingService;
+describe('SessionService', () => {
+  let service: SessionService;
 
   beforeEach(() => {
     TestBed.configureTestingModule({});
-    service = TestBed.inject(BookingService);
+    service = TestBed.inject(SessionService);
   });
 
   it('should be created', () => {
diff --git a/angular/RestClient/src/app/shared/session.service.ts b/angular/RestClient/src/app/shared/session.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c654e45b787c55dcfe7eae814056c55f6b87549d
--- /dev/null
+++ b/angular/RestClient/src/app/shared/session.service.ts
@@ -0,0 +1,159 @@
+import { Injectable } from '@angular/core';
+import { LocalStorageService } from './local-storage.service';
+import { PersistenToken, Session, UserRol } from '../types';
+import { BehaviorSubject, Observable, throwError } from 'rxjs';
+import { catchError, map, tap } from 'rxjs/operators';
+import { jwtDecode } from 'jwt-decode';
+import { AuthClientService } from './auth-client.service';
+import { Router } from '@angular/router';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class SessionService {
+  private tokenKey = 'token';
+  private session$: BehaviorSubject<Session | null>;
+  mainPage = '/me';
+
+  constructor(
+    private router: Router,
+    private storage: LocalStorageService,
+    private authService: AuthClientService
+  ) {
+    // Inicializar el estado de sesión desde el token almacenado
+    const initialSession = this.loadSessionFromToken();
+    console.log({ initialSession });
+
+    this.session$ = new BehaviorSubject<Session | null>(initialSession);
+  }
+
+  getMainPage(rol: UserRol) {
+    return rol === 'ADMIN' ? '/admin' : '/me';
+  }
+
+  private setSession(resp: any) {
+    const decoded = jwtDecode<{ user: Session }>(resp.token);
+    this.session$.next(decoded.user);
+    this.storage.save(this.tokenKey, { ...resp, session: decoded.user });
+    const mainPage = this.getMainPage(decoded.user.rol as UserRol);
+    return { ...resp, mainPage };
+  }
+
+  /**
+   * Realiza el login y actualiza el estado de la sesión.
+   */
+  login(email: string, password: string): Observable<any> {
+    return this.authService.login(email, password).pipe(
+      map((r) => this.setSession(r)),
+      catchError((error) => {
+        console.error('Login failed', error);
+        return throwError(() => new Error('Login failed'));
+      })
+    );
+  }
+
+  /**
+   * Realiza el registro, guarda el token y actualiza el estado de la sesión.
+   */
+  register(
+    name: string,
+    email: string,
+    password: string,
+    rol: UserRol
+  ): Observable<any> {
+    return this.authService.register(name, email, password, rol).pipe(
+      map((r) => this.setSession(r)),
+      catchError((error) => {
+        console.error('Registration failed', error);
+        return throwError(() => new Error('Registration failed'));
+      })
+    );
+  }
+
+  /**
+   * Realiza el logout, elimina el token y limpia el estado de sesión.
+   */
+  logout(): void {
+    this.storage.remove(this.tokenKey);
+    this.session$.next(null);
+    this.router.navigate(['/login']);
+  }
+
+  getSaved() {
+    return this.storage.read<PersistenToken>(this.tokenKey);
+  }
+
+  /**
+   * Obtiene el token almacenado. Lanza un error si no hay sesión activa.
+   */
+  getToken(): string {
+    const saved = this.getSaved();
+    if (!saved) {
+      throw new Error('No session');
+    }
+    return saved.token;
+  }
+
+  /**
+   * Proporciona un Observable del estado de la sesión.
+   */
+  getSession(): Observable<Session | null> {
+    return this.session$.asObservable();
+  }
+
+  updateData(data: Partial<Session>) {
+    // const session: Session = { ...this.session$.getValue() } as Session;
+    const saved = this.getSaved();
+    console.log({ saved, data });
+
+    if (!saved || data.id !== saved.session?.id) return;
+    const session = { ...saved.session, ...data } as Session;
+    this.storage.save(this.tokenKey, {
+      ...saved,
+      session,
+    });
+    this.session$.next(session);
+  }
+
+  /**
+   * Verifica si el usuario está logueado.
+   */
+  isLogged(): boolean {
+    return !!this.session$.getValue();
+  }
+
+  /**
+   * Valida si el token almacenado es válido (no expirado).
+   */
+  isValid(): boolean {
+    if (!this.isLogged()) return false;
+
+    try {
+      const token = this.getToken();
+      const decoded = jwtDecode<{ exp: number }>(token);
+      const valid = decoded.exp > Math.floor(Date.now() / 1000);
+      console.log({ valid, rem: decoded.exp - Math.floor(Date.now() / 1000) });
+      if (!valid) {
+        this.logout();
+      }
+      return valid;
+    } catch (error) {
+      console.error('Token validation failed', error);
+      return false;
+    }
+  }
+
+  /**
+   * Carga la sesión desde el token almacenado.
+   */
+  private loadSessionFromToken(): Session | null {
+    try {
+      // const token = this.getToken();
+      // const decoded = jwtDecode<{ user: Session }>(token);
+      // return decoded.user;
+      return this.getSaved()!.session!;
+    } catch {
+      return null; // Retornar null si no hay token válido.
+    }
+  }
+}
diff --git a/angular/RestClient/src/app/shared/user-client.service.spec.ts b/angular/RestClient/src/app/shared/user-client.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3af6ef2803edf936358c39133305278bb689dd4c
--- /dev/null
+++ b/angular/RestClient/src/app/shared/user-client.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { UserClientService } from './user-client.service';
+
+describe('UserClientService', () => {
+  let service: UserClientService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(UserClientService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/angular/RestClient/src/app/shared/user-client.service.ts b/angular/RestClient/src/app/shared/user-client.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..63575df4f440973090cde151f50137fb002b9d5d
--- /dev/null
+++ b/angular/RestClient/src/app/shared/user-client.service.ts
@@ -0,0 +1,68 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { environment } from '../../environments/environment';
+import { Client, Session, User, UserState } from '../types';
+import { SessionService } from './session.service';
+import { tap } from 'rxjs';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class UserClientService {
+  private readonly URI = environment.userAPI;
+
+  constructor(
+    private http: HttpClient,
+    private sessionService: SessionService
+  ) {}
+
+  // Obtener un usuario por ID
+  getUser(userId: number) {
+    return this.http.get<User>(`${this.URI}/${userId}`);
+  }
+
+  // Obtener todos los usuarios
+  getAllUsers() {
+    return this.http.get<Client[]>(this.URI, {
+      observe: 'body',
+    });
+  }
+
+  // Cambiar estado de un usuario
+  alterUserStatus(userId: number, status: UserState) {
+    return this.http.patch(
+      `${this.URI}/${userId}`,
+      {
+        status,
+      },
+      {
+        observe: 'response',
+        responseType: 'text',
+      }
+    );
+  }
+
+  // Actualizar los datos del usuario
+  updateUser(userId: number, user: Partial<User>) {
+    return this.http.put(`${this.URI}/${userId}`, user).pipe(
+      tap(() => {
+        this.sessionService.updateData({
+          id: userId,
+          ...user,
+        } as Partial<Session>);
+      })
+    );
+  }
+
+  // Cambiar la contraseña del usuario
+  updatePassword(currentPassword: string, newPassword: string) {
+    return this.http.patch(
+      `${this.URI}/me/password`,
+      { currentPassword, newPassword },
+      {
+        observe: 'response',
+        responseType: 'text',
+      }
+    );
+  }
+}
diff --git a/angular/RestClient/src/types/Address.d.ts b/angular/RestClient/src/app/types/Address.d.ts
similarity index 100%
rename from angular/RestClient/src/types/Address.d.ts
rename to angular/RestClient/src/app/types/Address.d.ts
diff --git a/angular/RestClient/src/types/Booking.d.ts b/angular/RestClient/src/app/types/Booking.d.ts
similarity index 100%
rename from angular/RestClient/src/types/Booking.d.ts
rename to angular/RestClient/src/app/types/Booking.d.ts
diff --git a/angular/RestClient/src/types/Hotel.d.ts b/angular/RestClient/src/app/types/Hotel.d.ts
similarity index 100%
rename from angular/RestClient/src/types/Hotel.d.ts
rename to angular/RestClient/src/app/types/Hotel.d.ts
diff --git a/angular/RestClient/src/types/Room.d.ts b/angular/RestClient/src/app/types/Room.d.ts
similarity index 100%
rename from angular/RestClient/src/types/Room.d.ts
rename to angular/RestClient/src/app/types/Room.d.ts
diff --git a/angular/RestClient/src/app/types/Session.d.ts b/angular/RestClient/src/app/types/Session.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4dd1f742866d586be8be2ac02487d23ede92a486
--- /dev/null
+++ b/angular/RestClient/src/app/types/Session.d.ts
@@ -0,0 +1,11 @@
+export interface Session {
+  id: number;
+  name: string;
+  email: string;
+  rol: UserRol;
+}
+
+interface PersistenToken {
+  token: string;
+  session?: Session;
+}
diff --git a/angular/RestClient/src/types/User.d.ts b/angular/RestClient/src/app/types/User.d.ts
similarity index 51%
rename from angular/RestClient/src/types/User.d.ts
rename to angular/RestClient/src/app/types/User.d.ts
index 1ecc3d7512e7ff0b644e5555d290c48e88518219..277f806a74fb1b2f854d733cc10acca67f18b840 100644
--- a/angular/RestClient/src/types/User.d.ts
+++ b/angular/RestClient/src/app/types/User.d.ts
@@ -2,10 +2,20 @@ export interface User {
   id: number;
   name: string;
   email: String;
-  // status: "noBookings" | "withActiveBookings" | "withInactiveBookings";
+  rol: UserRol;
+}
+
+export interface Client extends User {
   status: UserState;
+  // bookings: number[] // Booking[]
 }
 
+export interface HotelAdmin extends User {
+  // hotels: number[] // Hotel[]
+}
+
+export type UserRol = 'ADMIN' | 'CLIENT' | 'HOTEL_ADMIN';
+
 export type UserStateFilter = 'All' | UserState;
 
 export type UserState =
diff --git a/angular/RestClient/src/types/index.ts b/angular/RestClient/src/app/types/index.ts
similarity index 66%
rename from angular/RestClient/src/types/index.ts
rename to angular/RestClient/src/app/types/index.ts
index b974a05f55ecc2a2b9a6e953882d7cc526689ab6..09338254dd1b87fdc9aa00cb7342f0865fc382de 100644
--- a/angular/RestClient/src/types/index.ts
+++ b/angular/RestClient/src/app/types/index.ts
@@ -1,4 +1,5 @@
 import { RoomType } from './Room';
+import { UserRol } from './User';
 
 export type * from './User';
 export type * from './Address';
@@ -7,3 +8,5 @@ export type * from './Room';
 export const roomTypeArray: RoomType[] = ['SINGLE', 'DOUBLE', 'SUITE'];
 export type * from './Booking';
 export type * from './User';
+export const UserRolesArray: UserRol[] = ['CLIENT', 'HOTEL_ADMIN', 'ADMIN'];
+export type * from './Session';
diff --git a/angular/RestClient/src/app/utils/utils.ts b/angular/RestClient/src/app/utils/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c97d18b18ba30a2588acf79ecddb5a0f9747146
--- /dev/null
+++ b/angular/RestClient/src/app/utils/utils.ts
@@ -0,0 +1,18 @@
+import { ActivatedRoute } from '@angular/router';
+
+export function getBasePath(route: ActivatedRoute): string {
+  const urlSegments = route.snapshot.url;
+  if (urlSegments[0].path === 'me') {
+    return '/me';
+  } else if (
+    urlSegments.length >= 3 &&
+    urlSegments[0].path === 'admin' &&
+    urlSegments[1].path === 'users' &&
+    urlSegments[2]
+  ) {
+    return `/admin/users/${urlSegments[2]}`; // Devuelve la ruta con el ID del usuario
+  } else if (urlSegments[0].path === 'admin') {
+    return '/me';
+  }
+  throw new Error('Invalid route structure'); // Manejo de errores si la URL no es válida
+}
diff --git a/angular/RestClient/src/environments/environment.prod.ts b/angular/RestClient/src/environments/environment.prod.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4449314c617cbe840dd565f8465279c597b0dd0a
--- /dev/null
+++ b/angular/RestClient/src/environments/environment.prod.ts
@@ -0,0 +1,18 @@
+// const monolithUrl = 'http://rooms-booking-api:8080';
+
+// export const environment = {
+//   production: true,
+//   authAPI: 'http://auth-api:8101',
+//   userAPI: `http://${monolithUrl}/users`,
+//   hotelAPI: `http://${monolithUrl}/hotels`,
+//   bookingAPI: `http://${monolithUrl}/bookings`,
+// };
+const monolithUrl = 'localhost:8080';
+
+export const environment = {
+  production: false,
+  authAPI: 'http://localhost:8101',
+  userAPI: `http://${monolithUrl}/users`,
+  hotelAPI: `http://${monolithUrl}/hotels`,
+  bookingAPI: `http://${monolithUrl}/bookings`,
+};
diff --git a/angular/RestClient/src/environments/environment.ts b/angular/RestClient/src/environments/environment.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4264ad5b8fcbc528de02ea9e038a19aa2f617254
--- /dev/null
+++ b/angular/RestClient/src/environments/environment.ts
@@ -0,0 +1,9 @@
+const monolithUrl = 'localhost:8080';
+
+export const environment = {
+  production: false,
+  authAPI: 'http://localhost:8101',
+  userAPI: `http://${monolithUrl}/users`,
+  hotelAPI: `http://${monolithUrl}/hotels`,
+  bookingAPI: `http://${monolithUrl}/bookings`,
+};
diff --git a/angular/RestClient/src/mocks/bookings.json b/angular/RestClient/src/mocks/bookings.json
deleted file mode 100644
index 7db55ff76f1627de21762d95a7732a4f7be1ce34..0000000000000000000000000000000000000000
--- a/angular/RestClient/src/mocks/bookings.json
+++ /dev/null
@@ -1,30 +0,0 @@
-[
-  {
-    "user": {
-      "name": "John Doe",
-      "email": "john.doe@example.com",
-      "status": "NO_BOOKINGS"
-    },
-    "room": {
-      "roomNumber": "101",
-      "type": "SINGLE",
-      "available": true
-    },
-    "startDate": "2024-03-01",
-    "endDate": "2024-03-08"
-  },
-  {
-    "user": {
-      "name": "Pepe",
-      "email": "pepe@example.com",
-      "status": "WITH_ACTIVE_BOOKINGS"
-    },
-    "room": {
-      "roomNumber": "101",
-      "type": "SINGLE",
-      "available": true
-    },
-    "startDate": "2024-03-15",
-    "endDate": "2024-03-22"
-  }
-]
diff --git a/angular/RestClient/src/mocks/hotels.json b/angular/RestClient/src/mocks/hotels.json
deleted file mode 100644
index 11381112f37ac1123d22f06a2a0526c07825f211..0000000000000000000000000000000000000000
--- a/angular/RestClient/src/mocks/hotels.json
+++ /dev/null
@@ -1,46 +0,0 @@
-[
-  {
-    "id": 1,
-    "name": "Hotel 1",
-    "address": {
-      "id": 1,
-      "streetName": "Aca al lao",
-      "streetKind": "Alargada",
-      "number": 12,
-      "postCode": "12345"
-    },
-    "rooms": [
-      {
-        "id": 1,
-        "roomNumber": "101",
-        "type": "SINGLE",
-        "available": true
-      },
-      {
-        "id": 2,
-        "roomNumber": "102",
-        "type": "DOUBLE",
-        "available": false
-      }
-    ]
-  },
-  {
-    "id": 2,
-    "name": "Hotel 2",
-    "address": {
-      "id": 2,
-      "streetName": "Calle de la plaza",
-      "streetKind": "Alargada",
-      "number": 12,
-      "postCode": "12345"
-    },
-    "rooms": [
-      {
-        "id": 3,
-        "roomNumber": "103",
-        "type": "SUITE",
-        "available": true
-      }
-    ]
-  }
-]
diff --git a/angular/RestClient/src/mocks/users.json b/angular/RestClient/src/mocks/users.json
deleted file mode 100644
index 2f02d00125b0deaa2a86db47ea26f0c91f2e5fa3..0000000000000000000000000000000000000000
--- a/angular/RestClient/src/mocks/users.json
+++ /dev/null
@@ -1,12 +0,0 @@
-[
-  {
-    "name": "John Doe",
-    "email": "john.doe@example.com",
-    "status": "NO_BOOKINGS"
-  },
-  {
-    "name": "Pepe",
-    "email": "pepe@example.com",
-    "status": "WITH_ACTIVE_BOOKINGS"
-  }
-]
diff --git a/angular/RestClient/src/mocks/users.ts b/angular/RestClient/src/mocks/users.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f6fafe67e13993f0e5b1a48c5443d43f6d3c2c3d
--- /dev/null
+++ b/angular/RestClient/src/mocks/users.ts
@@ -0,0 +1,18 @@
+import { Client, User } from '../app/types';
+
+export const users: Client[] = [
+  {
+    id: 1,
+    name: 'John Doe',
+    email: 'jon@com',
+    rol: 'CLIENT',
+    status: 'NO_BOOKINGS',
+  },
+  {
+    id: 2,
+    name: 'Angela Doe',
+    email: 'angle@com',
+    rol: 'CLIENT',
+    status: 'NO_BOOKINGS',
+  },
+];
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6f86e46bad45d0d7f2a56bc0ce180ea343d0353e
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,73 @@
+volumes:
+  kong_data: {}
+  users_data: {}
+
+networks:
+  kong-net: {}
+
+services:
+  Auth-API:
+    image: auth-api-image
+    hostname: ${AUTH_SERVICE_HOSTNAME}
+    build:
+      context: ./java/services/auth
+      dockerfile: Dockerfile
+    restart: unless-stopped
+    ports:
+      - 8101:8101
+    networks:
+      - kong-net
+    environment:
+      SPRING_DATASOURCE_URL: jdbc:mysql://${DB_SERVICE_HOSTNAME}:3306/${DB_DATABASE_NAME}?createDatabaseIfNotExist=true
+    depends_on:
+      - RoomsBooking-database
+
+  RoomsBooking-API:
+    image: rooms-booking-api-image
+    hostname: ${ROOMS_BOOKING_SERVICE_HOSTNAME}
+    build:
+      context: ./java/roomBooking
+      dockerfile: Dockerfile
+    restart: unless-stopped
+    ports:
+      - 8080:8080
+    networks:
+      - kong-net
+    environment:
+      SPRING_DATASOURCE_URL: jdbc:mysql://${DB_SERVICE_HOSTNAME}:3306/${DB_DATABASE_NAME}?createDatabaseIfNotExist=true
+    depends_on:
+      - RoomsBooking-database
+
+  RoomsBooking-database:
+    image: mysql
+    hostname: ${DB_SERVICE_HOSTNAME}
+    cap_add:
+      - SYS_NICE
+    restart: unless-stopped
+    ports:
+      - "3307:3306"
+    networks:
+      - kong-net
+    volumes:
+      - users_data:/var/lib/mysql
+    environment:
+      MYSQL_ROOT_PASSWORD: ClaveRoot
+      MYSQL_USER: user
+      MYSQL_PASSWORD: password
+      MYSQL_DATABASE: RoomsBooking
+      MYSQL_ROOT_HOST: "%"
+
+  RoomsBooking-Web:
+    image: roomsbooking-web-image
+    build:
+      context: ./angular/RestClient
+      dockerfile: ./Dockerfile
+    restart: unless-stopped
+    ports:
+      - 4200:80
+    networks:
+      - kong-net
+    environment:
+      SPRING_DATASOURCE_URL: jdbc:mysql://${DB_SERVICE_HOSTNAME}:3306/${DB_DATABASE_NAME}?createDatabaseIfNotExist=true
+    depends_on:
+      - RoomsBooking-database
diff --git a/java/roomBooking/Dockerfile b/java/roomBooking/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..8d0b79d6514534deddac782fc9367bcdbcaf89c3
--- /dev/null
+++ b/java/roomBooking/Dockerfile
@@ -0,0 +1,10 @@
+FROM maven:3-openjdk-17 AS maven
+WORKDIR /app
+COPY ./ ./
+RUN mvn -Dmaven.test.skip clean package
+FROM openjdk:17-jdk-oracle
+ARG JAR_FILE=/app/target/*.jar
+COPY --from=maven ${JAR_FILE} app.jar
+ENV PORT 8080
+EXPOSE $PORT
+ENTRYPOINT ["java","-jar", "/app.jar"]
\ No newline at end of file
diff --git a/java/roomBooking/pom.xml b/java/roomBooking/pom.xml
index 476f735b695a459292a258d72d89261c89dca993..f6fa75bdb21b812320db5afa07b770891fb9bd95 100644
--- a/java/roomBooking/pom.xml
+++ b/java/roomBooking/pom.xml
@@ -49,6 +49,32 @@
 			<artifactId>spring-boot-starter-test</artifactId>
 			<scope>test</scope>
 		</dependency>
+		<dependency>
+    <groupId>org.springframework.boot</groupId>
+    <artifactId>spring-boot-starter-security</artifactId>
+	</dependency>
+	<dependency>
+		<groupId>io.jsonwebtoken</groupId>
+		<artifactId>jjwt-api</artifactId>
+		<version>0.11.5</version>
+	</dependency>
+	<dependency>
+		<groupId>io.jsonwebtoken</groupId>
+		<artifactId>jjwt-impl</artifactId>
+		<version>0.11.5</version>
+		<scope>runtime</scope>
+	</dependency>
+	<dependency>
+		<groupId>io.jsonwebtoken</groupId>
+		<artifactId>jjwt-jackson</artifactId>
+		<version>0.11.5</version>
+		<scope>runtime</scope>
+	</dependency>
+	<dependency>
+        <groupId>jakarta.servlet</groupId>
+        <artifactId>jakarta.servlet-api</artifactId>
+        <scope>provided</scope>
+    </dependency>
 	</dependencies>
 
 	<build>
@@ -60,4 +86,4 @@
 		</plugins>
 	</build>
 
-</project>
+</project>
\ No newline at end of file
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/RoomBookingApplication.java b/java/roomBooking/src/main/java/com/uva/monolith/RoomBookingApplication.java
similarity index 90%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/RoomBookingApplication.java
rename to java/roomBooking/src/main/java/com/uva/monolith/RoomBookingApplication.java
index 1a5f312d49b2d1db4ffe587e4ae02c35e1e9a506..0a5db248da6e6be909302e323931f79151b0ed62 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/RoomBookingApplication.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/RoomBookingApplication.java
@@ -1,4 +1,4 @@
-package com.uva.roomBooking;
+package com.uva.monolith;
 
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/config/SecurityConfig.java b/java/roomBooking/src/main/java/com/uva/monolith/config/SecurityConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..8264f84a2e9f57dabaedc5892ea8a3ca0e931232
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/config/SecurityConfig.java
@@ -0,0 +1,49 @@
+package com.uva.monolith.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import com.uva.monolith.filter.JwtAuthenticationFilter;
+import com.uva.monolith.services.users.models.UserRol;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+    private final JwtAuthenticationFilter jwtAuthenticationFilter;
+
+    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
+        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
+    }
+
+    @Bean
+    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+        http.csrf(csrf -> csrf.disable())
+                .authorizeHttpRequests(authorize -> authorize
+                        // Permitir OPTIONS sin autenticación
+                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
+                        // Acceso restringido a usuarios y administradores
+                        .requestMatchers("users", "users/**").hasAnyRole(
+                                UserRol.CLIENT.toString(), UserRol.HOTEL_ADMIN.toString(), UserRol.ADMIN.toString())
+                        // Acceso restringido a gestores de hoteles y administradores
+                        .requestMatchers(HttpMethod.GET, "hotels", "hotels/*").hasAnyRole(
+                                UserRol.CLIENT.toString(), UserRol.HOTEL_ADMIN.toString(), UserRol.ADMIN.toString())
+
+                        .requestMatchers("hotels", "hotels/**")
+                        .hasAnyRole(UserRol.ADMIN.toString(), UserRol.HOTEL_ADMIN.toString())
+                        // Acceso restringido a cualquier usuario del sistema
+                        .requestMatchers("bookings", "bookings/**")
+                        .hasAnyRole(UserRol.ADMIN.toString(), UserRol.HOTEL_ADMIN.toString(), UserRol.CLIENT.toString())
+                        // Rechazar el resto
+                        .anyRequest().denyAll())
+                // Registra el filtro antes del filtro estándar de autenticación
+                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+
+        return http.build();
+    }
+}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/GlobalExceptionHandler.java b/java/roomBooking/src/main/java/com/uva/monolith/exceptions/GlobalExceptionHandler.java
similarity index 97%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/GlobalExceptionHandler.java
rename to java/roomBooking/src/main/java/com/uva/monolith/exceptions/GlobalExceptionHandler.java
index 03b21e27ffa7e4e921cc9801187911d9f1be6a52..9428c51a9c63c3623d44752c9e3cbe6cf78ac19f 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/GlobalExceptionHandler.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/exceptions/GlobalExceptionHandler.java
@@ -1,4 +1,4 @@
-package com.uva.roomBooking.Exceptions;
+package com.uva.monolith.exceptions;
 
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/HotelNotFoundException.java b/java/roomBooking/src/main/java/com/uva/monolith/exceptions/HotelNotFoundException.java
similarity index 68%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/HotelNotFoundException.java
rename to java/roomBooking/src/main/java/com/uva/monolith/exceptions/HotelNotFoundException.java
index 3d47f5efad71b6fbf3b3f5912493c9c0af1ca412..129a0b1086b4b78eb1f1725b9f241f51ce5540f8 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/HotelNotFoundException.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/exceptions/HotelNotFoundException.java
@@ -1,9 +1,9 @@
-package com.uva.roomBooking.Exceptions;
+package com.uva.monolith.exceptions;
 
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.ResponseStatus;
 
-@ResponseStatus(HttpStatus.NOT_FOUND)  // Devuelve un 404 cuando se lanza la excepción
+@ResponseStatus(HttpStatus.NOT_FOUND) // Devuelve un 404 cuando se lanza la excepción
 public class HotelNotFoundException extends RuntimeException {
     public HotelNotFoundException(int id) {
         super("Hotel not found with id: " + id);
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/InvalidDateRangeException.java b/java/roomBooking/src/main/java/com/uva/monolith/exceptions/InvalidDateRangeException.java
similarity index 78%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/InvalidDateRangeException.java
rename to java/roomBooking/src/main/java/com/uva/monolith/exceptions/InvalidDateRangeException.java
index 17a8420453ef402411b61b965d001560a4dd51ce..5fea986ef1e9279c459bc5aff10932049f283333 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/InvalidDateRangeException.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/exceptions/InvalidDateRangeException.java
@@ -1,8 +1,7 @@
-package com.uva.roomBooking.Exceptions;
+package com.uva.monolith.exceptions;
 
 public class InvalidDateRangeException extends RuntimeException {
     public InvalidDateRangeException(String message) {
         super(message);
     }
 }
-
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/InvalidRequestException.java b/java/roomBooking/src/main/java/com/uva/monolith/exceptions/InvalidRequestException.java
similarity index 88%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/InvalidRequestException.java
rename to java/roomBooking/src/main/java/com/uva/monolith/exceptions/InvalidRequestException.java
index a8433b6f620da742dab87dae96a2e0f0193709ff..ca09e054420dd174c4d2c3424dcc8fe74b6c8576 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Exceptions/InvalidRequestException.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/exceptions/InvalidRequestException.java
@@ -1,4 +1,4 @@
-package com.uva.roomBooking.Exceptions;
+package com.uva.monolith.exceptions;
 
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.ResponseStatus;
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java b/java/roomBooking/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..9b31d1a0e80f529075ef20bc4e9a955cfa825d30
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/filter/JwtAuthenticationFilter.java
@@ -0,0 +1,124 @@
+package com.uva.monolith.filter;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.UnsupportedJwtException;
+import io.jsonwebtoken.io.Decoders;
+import io.jsonwebtoken.security.Keys;
+import io.jsonwebtoken.security.SignatureException;
+
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+
+import com.uva.monolith.services.users.models.UserRol;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.Filter;
+import java.io.IOException;
+import java.security.Key;
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.Date;
+
+@Component
+public class JwtAuthenticationFilter implements Filter {
+
+    private final String SECRET_KEY = "3cfa76ef14937c1c0ea519f8fc057a80fcd04a7420f8e8bcd0a7567c272e007b";
+
+    private Key getSignInKey() {
+        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
+        return Keys.hmacShaKeyFor(keyBytes);
+    }
+
+    private String getTokenFromRequest(HttpServletRequest request) {
+        String authHeader = request.getHeader("Authorization");
+        if (authHeader == null || !authHeader.startsWith("Bearer "))
+            return null;
+        return authHeader.substring(7);
+    }
+
+    private Claims getClaimsFromToken(String token) {
+        return Jwts.parserBuilder()
+                .setSigningKey(getSignInKey())
+                .build()
+                .parseClaimsJws(token)
+                .getBody();
+    }
+
+    private boolean validateToken(String token) {
+        if (token == null)
+            return false;// no token
+        try {
+            // Verifica y analiza el token
+            Claims claims = getClaimsFromToken(token);
+
+            // Verifica que el token no esté expirado
+            return claims.getExpiration().after(new Date());
+        } catch (ExpiredJwtException e) {
+            System.out.println("[" + LocalDateTime.now().toString() + "] Token expirado: " + e.getMessage());
+        } catch (UnsupportedJwtException e) {
+            System.out.println("[" + LocalDateTime.now().toString() + "] Token no soportado: " + e.getMessage());
+        } catch (MalformedJwtException e) {
+            System.out.println("[" + LocalDateTime.now().toString() + "] Token malformado: " + e.getMessage());
+        } catch (SignatureException e) {
+            System.out.println("[" + LocalDateTime.now().toString() + "] Firma inválida: " + e.getMessage());
+        } catch (IllegalArgumentException e) {
+            System.out.println("[" + LocalDateTime.now().toString() + "] Token vacío o nulo: " + e.getMessage());
+        }
+        return false; // Si ocurre cualquier excepción, el token es inválido
+
+    }
+
+    private String getEmailFromToken(String token) {
+        return getClaimsFromToken(token).getSubject();
+    }
+
+    private UserRol getRoleFromToken(String token) {
+        String rol = getClaimsFromToken(token).get("rol", String.class);
+        return UserRol.valueOf(rol);
+    }
+
+    public static String getRol(UserRol rol) {
+        return String.format("ROLE_%s", rol.toString());
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        String token = getTokenFromRequest(httpRequest);
+
+        System.out.println("[" + LocalDateTime.now().toString() + "] TOKEN " + token);
+
+        if (validateToken(token)) {
+
+            String email = getEmailFromToken(token);
+            UserRol role = getRoleFromToken(token); // Extraer el rol del token
+
+            if (email != null && SecurityContextHolder.getContext().getAuthentication() == null) {
+                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
+                        email, null, null);
+                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
+                SecurityContextHolder.getContext().setAuthentication(authentication);
+            }
+
+            // Agregar el rol como autoridad
+            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(getRol(role));
+            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(email, null,
+                    Collections.singletonList(authority));
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+        }
+
+        chain.doFilter(request, response);
+    }
+
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/controllers/BookingController.java b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/controllers/BookingController.java
new file mode 100644
index 0000000000000000000000000000000000000000..602f16a2f96e27d3e855440136cbcb41f9b57ab0
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/controllers/BookingController.java
@@ -0,0 +1,51 @@
+package com.uva.monolith.services.bookings.controllers;
+
+import com.uva.monolith.services.bookings.models.Booking;
+import com.uva.monolith.services.bookings.services.BookingService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@RestController
+@RequestMapping("/bookings")
+@CrossOrigin(origins = "*")
+public class BookingController {
+
+    private final BookingService bookingService;
+
+    public BookingController(BookingService bookingService) {
+        this.bookingService = bookingService;
+    }
+
+    @GetMapping
+    public List<Booking> getAllBookings(
+            @RequestParam(required = false) LocalDate start,
+            @RequestParam(required = false) LocalDate end,
+            @RequestParam(required = false) Integer roomId,
+            @RequestParam(required = false) Integer userId) {
+        return bookingService.getBookings(start, end, roomId, userId);
+    }
+
+    @PostMapping
+    public Booking createBooking(@RequestBody Booking booking) {
+        return bookingService.createBooking(booking);
+    }
+
+    @GetMapping("/{id}")
+    public Booking getBookingById(@PathVariable Integer id) {
+        return bookingService.getBookingById(id);
+    }
+
+    @DeleteMapping("/{id}")
+    public ResponseEntity<Void> deleteBooking(@PathVariable Integer id) {
+        try {
+            bookingService.deleteBooking(id);
+            return new ResponseEntity<>(HttpStatus.ACCEPTED);
+        } catch (RuntimeException e) {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+    }
+}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Booking.java b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/models/Booking.java
similarity index 84%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Models/Booking.java
rename to java/roomBooking/src/main/java/com/uva/monolith/services/bookings/models/Booking.java
index c546d1916b1c29a10f95c97f03db34e64ffbe7d2..533ee0c7a1fa053dce449b855f1e46e08dabbe66 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Booking.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/models/Booking.java
@@ -1,4 +1,4 @@
-package com.uva.roomBooking.Models;
+package com.uva.monolith.services.bookings.models;
 
 import jakarta.persistence.Basic;
 import jakarta.persistence.CascadeType;
@@ -13,6 +13,9 @@ import jakarta.persistence.ManyToOne;
 import jakarta.persistence.Table;
 import java.time.LocalDate;
 
+import com.uva.monolith.services.hotels.models.Room;
+import com.uva.monolith.services.users.models.Client;
+
 @Entity
 @Table(name = "bookings")
 public class Booking {
@@ -22,7 +25,7 @@ public class Booking {
     private int id;
     @JoinColumn(name = "user_id", referencedColumnName = "id")
     @ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
-    private User userId;
+    private Client userId;
     @JoinColumn(name = "room_id", referencedColumnName = "id")
     @ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
     private Room roomId;
@@ -34,7 +37,7 @@ public class Booking {
     public Booking() {
     }
 
-    public Booking(int id, User userId, Room roomID, LocalDate startDate, LocalDate endDate) {
+    public Booking(int id, Client userId, Room roomID, LocalDate startDate, LocalDate endDate) {
         this.id = id;
         this.userId = userId;
         this.roomId = roomID;
@@ -50,11 +53,11 @@ public class Booking {
         return this.id;
     }
 
-    public void setUserId(User userId) {
+    public void setUserId(Client userId) {
         this.userId = userId;
     }
 
-    public User getUserId() {
+    public Client getUserId() {
         return this.userId;
     }
 
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/repositories/BookingRepository.java b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/repositories/BookingRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5ace65939b898798e6e5416fedda793388f2615
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/repositories/BookingRepository.java
@@ -0,0 +1,41 @@
+// BookingRepository.java
+package com.uva.monolith.services.bookings.repositories;
+
+import jakarta.transaction.Transactional;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import com.uva.monolith.services.bookings.models.Booking;
+
+public interface BookingRepository extends JpaRepository<Booking, Integer> {
+        @Query("SELECT b FROM Booking b WHERE b.userId.id = ?1")
+        List<Booking> findByUserId(int userId);
+
+        @Query("SELECT b FROM Booking b WHERE b.roomId.id = ?1")
+        List<Booking> findByRoomId(int roomId);
+
+        @Query("SELECT b FROM Booking b WHERE b.startDate >= ?1 AND b.endDate <= ?2")
+        List<Booking> findByDateRange(@Param("startDate") LocalDate startDate,
+                        @Param("endDate") LocalDate endDate);
+
+        @Query("SELECT b FROM Booking b WHERE b.roomId.id = ?1 AND b.startDate < ?2 AND b.endDate > ?3")
+        List<Booking> findByRoomIdAndDateRange(@Param("roomId") int roomId, @Param("startDate") LocalDate startDate,
+                        @Param("endDate") LocalDate endDate);
+
+        @Transactional
+        @Modifying
+        @Query("DELETE FROM Booking b WHERE b.id = ?1")
+        void deleteBookingById(@Param("id") Integer id);
+
+        @Transactional
+        @Modifying
+        @Query("DELETE FROM Booking b WHERE b.roomId.hotel.id = ?1")
+        void deleteAllByHotelId(int hotelId);
+
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/services/BookingService.java b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/services/BookingService.java
new file mode 100644
index 0000000000000000000000000000000000000000..176f49dd951630c9118b24536e8a7dd6f5a15521
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/bookings/services/BookingService.java
@@ -0,0 +1,89 @@
+package com.uva.monolith.services.bookings.services;
+
+import com.uva.monolith.services.bookings.models.Booking;
+import com.uva.monolith.services.bookings.repositories.BookingRepository;
+import com.uva.monolith.services.hotels.models.Room;
+import com.uva.monolith.services.hotels.repositories.RoomRepository;
+import com.uva.monolith.services.users.models.Client;
+import com.uva.monolith.services.users.repositories.ClientRepository;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Service
+public class BookingService {
+
+    @Autowired
+    private BookingRepository bookingRepository;
+
+    @Autowired
+    private RoomRepository roomRepository;
+
+    @Autowired
+    private ClientRepository clientRepository;
+
+    public List<Booking> getBookings(LocalDate start, LocalDate end, Integer roomId, Integer userId) {
+        List<Booking> bookings = null;
+
+        if (start != null && end != null) {
+            bookings = bookingRepository.findByDateRange(start, end);
+        }
+        if (roomId != null) {
+            if (bookings == null) {
+                bookings = bookingRepository.findByRoomId(roomId);
+            } else {
+                bookings = bookings.stream()
+                        .filter(booking -> booking.getRoomId().getId() == roomId)
+                        .toList();
+            }
+        }
+        if (userId != null) {
+            if (bookings == null) {
+                bookings = bookingRepository.findByUserId(userId);
+            } else {
+                bookings = bookings.stream()
+                        .filter(booking -> booking.getUserId().getId() == userId)
+                        .toList();
+            }
+        }
+        if (start == null && end == null && roomId == null && userId == null) {
+            bookings = bookingRepository.findAll();
+        }
+
+        return bookings;
+    }
+
+    public Booking createBooking(Booking booking) {
+        Client user = clientRepository.findById(booking.getUserId().getId())
+                .orElseThrow(() -> new RuntimeException("User not found"));
+        Room room = roomRepository.findById(booking.getRoomId().getId())
+                .orElseThrow(() -> new RuntimeException("Room not found"));
+
+        // Check availability
+        List<Booking> existingBookings = bookingRepository.findByRoomIdAndDateRange(
+                room.getId(), booking.getStartDate(), booking.getEndDate());
+
+        if (!existingBookings.isEmpty()) {
+            throw new RuntimeException("Room is not available for the selected dates");
+        }
+
+        booking.setUserId(user);
+        booking.setRoomId(room);
+        return bookingRepository.save(booking);
+    }
+
+    public Booking getBookingById(Integer id) {
+        return bookingRepository.findById(id)
+                .orElseThrow(() -> new RuntimeException("Booking not found"));
+    }
+
+    public void deleteBooking(Integer id) {
+        if (!bookingRepository.existsById(id)) {
+            throw new RuntimeException("Booking not found");
+        }
+        bookingRepository.deleteBookingById(id);
+    }
+}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/HotelController.java b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/controllers/HotelController.java
similarity index 62%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/HotelController.java
rename to java/roomBooking/src/main/java/com/uva/monolith/services/hotels/controllers/HotelController.java
index 803edc35fdcbf2b5ec7fc96972e8ee2a7777391d..781cb6278b0b40d237b603d7cfc61a63b7b2af0f 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/HotelController.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/controllers/HotelController.java
@@ -1,28 +1,33 @@
-package com.uva.roomBooking.Controllers;
+package com.uva.monolith.services.hotels.controllers;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.time.LocalDate;
 
-import com.uva.roomBooking.Exceptions.HotelNotFoundException;
-import com.uva.roomBooking.Exceptions.InvalidDateRangeException;
-import com.uva.roomBooking.Exceptions.InvalidRequestException;
-import com.uva.roomBooking.Models.Booking;
-import com.uva.roomBooking.Models.Hotel;
-import com.uva.roomBooking.Models.Room;
-import com.uva.roomBooking.Repositories.BookingRepository;
-import com.uva.roomBooking.Repositories.HotelRepository;
-import com.uva.roomBooking.Repositories.RoomRepository;
-
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
+import com.uva.monolith.exceptions.HotelNotFoundException;
+import com.uva.monolith.exceptions.InvalidDateRangeException;
+import com.uva.monolith.exceptions.InvalidRequestException;
+import com.uva.monolith.services.bookings.repositories.BookingRepository;
+import com.uva.monolith.services.hotels.models.Hotel;
+import com.uva.monolith.services.hotels.models.Room;
+import com.uva.monolith.services.hotels.repositories.HotelRepository;
+import com.uva.monolith.services.hotels.repositories.RoomRepository;
+import com.uva.monolith.services.users.models.HotelManager;
+import com.uva.monolith.services.users.repositories.HotelManagerRepository;
+
 @RestController
 @RequestMapping("hotels")
 @CrossOrigin(origins = "*")
 public class HotelController {
+    @Autowired
+    private HotelManagerRepository hotelManagerRepository;
     private final HotelRepository hotelRepository;
     private final RoomRepository roomRepository;
     private final BookingRepository bookingRepository;
@@ -36,13 +41,36 @@ public class HotelController {
 
     // Obtener todos los hoteles
     @GetMapping
-    public List<Hotel> getAllHotels() {
-        return hotelRepository.findAll();
+    public List<Hotel> getAllHotels(
+            @RequestParam(required = false) Integer managerId,
+            @RequestParam(required = false) LocalDate start,
+            @RequestParam(required = false) LocalDate end) {
+        List<Hotel> hotels = (managerId != null)
+                ? hotelRepository.findAllByHotelManager(managerId)
+                : hotelRepository.findAll();
+        if (start != null && end != null) {
+            // Filtramos para los hoteles que
+            // tengan habitaciones disponibles para ese rango de fechas
+            System.out.println(start);
+            System.out.println(end);
+            hotels = hotels.stream().map(h -> {
+                if (h.getRooms().size() == 0)
+                    return h;
+                h.setRooms(roomRepository.findAvailableRoomsByHotelAndDates(h.getId(), start, end));
+                return h;
+            }).filter(h -> h.getRooms().size() >= 0).toList();
+        }
+        return hotels;
     }
 
     // Añadir un hotel con sus habitaciones
     @PostMapping
     public ResponseEntity<Hotel> addHotel(@RequestBody Hotel hotel) {
+        Optional<HotelManager> hm = hotelManagerRepository.findById(hotel.getHotelManager().getId());
+        if (!hm.isPresent()) {
+            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+        }
+        hotel.setHotelManager(hm.get());
         Hotel savedHotel = hotelRepository.save(hotel);
         return new ResponseEntity<>(savedHotel, HttpStatus.CREATED);
     }
@@ -61,6 +89,10 @@ public class HotelController {
         Hotel target = hotelRepository.findById(id)
                 .orElseThrow(() -> new HotelNotFoundException(id));
         bookingRepository.deleteAllByHotelId(id);
+        HotelManager hm = target.getHotelManager();
+        hm.getHotels().removeIf(h -> h.getId() == target.getId());
+        hotelManagerRepository.save(hm);
+        bookingRepository.flush();
         hotelRepository.delete(target);
         return new ResponseEntity<>(HttpStatus.NO_CONTENT);
     }
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Address.java b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Address.java
similarity index 97%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Models/Address.java
rename to java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Address.java
index 5e51f55e710c68ac38d4cb1c5769e574b06654bf..5f31a2a530da46c00460ad6cc6151b0769c1da61 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Address.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Address.java
@@ -1,4 +1,4 @@
-package com.uva.roomBooking.Models;
+package com.uva.monolith.services.hotels.models;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Hotel.java b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Hotel.java
similarity index 76%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Models/Hotel.java
rename to java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Hotel.java
index 5a23005de99c022b5fff9570e2c515fcfc75e07e..21f5cec8b44f9fae1a566b7af92964d30b654546 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Hotel.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Hotel.java
@@ -1,9 +1,8 @@
-package com.uva.roomBooking.Models;
+package com.uva.monolith.services.hotels.models;
 
 import java.util.List;
 
-import com.fasterxml.jackson.annotation.JsonIdentityInfo;
-import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.uva.monolith.services.users.models.HotelManager;
 
 import jakarta.persistence.Basic;
 import jakarta.persistence.CascadeType;
@@ -13,6 +12,7 @@ import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
 import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
 import jakarta.persistence.OneToMany;
 import jakarta.persistence.OneToOne;
 import jakarta.persistence.Table;
@@ -38,14 +38,19 @@ public class Hotel {
   @OneToMany(mappedBy = "hotel", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
   private List<Room> rooms;
 
+  @ManyToOne(optional = false)
+  @JoinColumn(name = "hotel_manager", referencedColumnName = "id")
+  private HotelManager hotelManager;
+
   public Hotel() {
   }
 
-  public Hotel(int id, String name, Address address, List<Room> rooms) {
+  public Hotel(int id, String name, Address address, List<Room> rooms, HotelManager hotelManager) {
     setId(id);
     setName(name);
     setAddress(address);
     setRooms(rooms);
+    setHotelManager(hotelManager);
   }
 
   public int getId() {
@@ -81,4 +86,11 @@ public class Hotel {
     rooms.forEach(room -> room.setHotel(this));
   }
 
+  public void setHotelManager(HotelManager hotelManager) {
+    this.hotelManager = hotelManager;
+  }
+
+  public HotelManager getHotelManager() {
+    return hotelManager;
+  }
 }
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Room.java b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Room.java
similarity index 86%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Models/Room.java
rename to java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Room.java
index 639dfdb0bd3be629b16c719373fa51c9a7584898..72a6a728f729dc7b93b727606172de7cbe385ebb 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Room.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/Room.java
@@ -1,10 +1,9 @@
-package com.uva.roomBooking.Models;
+package com.uva.monolith.services.hotels.models;
 
 import java.util.List;
 
-import com.fasterxml.jackson.annotation.JsonIdentityInfo;
 import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.uva.monolith.services.bookings.models.Booking;
 
 import jakarta.persistence.Basic;
 import jakarta.persistence.CascadeType;
@@ -36,7 +35,7 @@ public class Room {
     @Column(name = "room_number", nullable = false)
     private String roomNumber;
     @Column(name = "type", nullable = false)
-    private Tipo type;
+    private RoomType type;
     @Column(name = "available", nullable = false)
     private boolean available;
     @JsonIgnore
@@ -46,7 +45,7 @@ public class Room {
     public Room() {
     }
 
-    public Room(int id, Hotel hotelId, String roomNumber, Tipo type, boolean available, List<Booking> bookings) {
+    public Room(int id, Hotel hotelId, String roomNumber, RoomType type, boolean available, List<Booking> bookings) {
         this.id = id;
         this.hotel = hotelId;
         this.roomNumber = roomNumber;
@@ -79,11 +78,11 @@ public class Room {
         return this.roomNumber;
     }
 
-    public void setType(Tipo type) {
+    public void setType(RoomType type) {
         this.type = type;
     }
 
-    public Tipo getType() {
+    public RoomType getType() {
         return this.type;
     }
 
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/RoomType.java b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/RoomType.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9e82584850795afa7c7392248e3a6472ce24ac0
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/models/RoomType.java
@@ -0,0 +1,7 @@
+package com.uva.monolith.services.hotels.models;
+
+public enum RoomType {
+    SINGLE,
+    DOUBLE,
+    SUITE
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/repositories/HotelRepository.java b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/repositories/HotelRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..eddb6640a275284cb70bde60a29afd33039ba454
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/repositories/HotelRepository.java
@@ -0,0 +1,13 @@
+package com.uva.monolith.services.hotels.repositories;
+
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+import com.uva.monolith.services.hotels.models.Hotel;
+
+public interface HotelRepository extends JpaRepository<Hotel, Integer> {
+    @Query("SELECT h FROM Hotel h WHERE h.hotelManager.id = ?1")
+    List<Hotel> findAllByHotelManager(Integer hotelManager);
+}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/RoomRepository.java b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/repositories/RoomRepository.java
similarity index 83%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/RoomRepository.java
rename to java/roomBooking/src/main/java/com/uva/monolith/services/hotels/repositories/RoomRepository.java
index 3df9937f1ba7dc7c8bc0aa839a7a93ffd0baa8de..15cc3d370129e0753b9ac9b1eb24136c93bf5405 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/RoomRepository.java
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/hotels/repositories/RoomRepository.java
@@ -1,9 +1,10 @@
-package com.uva.roomBooking.Repositories;
+package com.uva.monolith.services.hotels.repositories;
 
-import com.uva.roomBooking.Models.Room;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 
+import com.uva.monolith.services.hotels.models.Room;
+
 import java.time.LocalDate;
 import java.util.List;
 import java.util.Optional;
@@ -24,9 +25,9 @@ public interface RoomRepository extends JpaRepository<Room, Integer> {
                     SELECT b FROM Booking b
                     WHERE b.roomId.id = r.id
                     AND (
-                        b.endDate > ?2
+                        b.endDate >= ?2
                         OR
-                        b.startDate > ?3
+                        ?3 >= b.startDate
                     )
                 )
             """)
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/controllers/UserController.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/controllers/UserController.java
new file mode 100644
index 0000000000000000000000000000000000000000..070fb5772a4f49f622617abf7d55958f1b6007c8
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/controllers/UserController.java
@@ -0,0 +1,112 @@
+package com.uva.monolith.services.users.controllers;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.HttpClientErrorException;
+
+import com.uva.monolith.services.users.models.User;
+import com.uva.monolith.services.users.models.UserStatus;
+import com.uva.monolith.services.users.services.UserService;
+
+@RestController
+@RequestMapping("users")
+@CrossOrigin(origins = "*")
+public class UserController {
+
+  @Autowired
+  private UserService userService;
+
+  @GetMapping
+  public ResponseEntity<List<User>> getAllUsers() {
+    List<User> users = userService.getAllUsers();
+    return ResponseEntity.ok(users);
+  }
+
+  @GetMapping(params = { "email" })
+  public ResponseEntity<?> getUserByEmail(@RequestParam String email) {
+    try {
+      return ResponseEntity.ok(userService.getUserByEmail(email));
+    } catch (HttpClientErrorException e) {
+      if (e.getStatusCode() == HttpStatus.NOT_FOUND)
+        return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
+      throw e;
+    }
+  }
+
+  @PostMapping
+  public ResponseEntity<?> addUser(@RequestBody User user) {
+    userService.addUser(user);
+    return new ResponseEntity<>(HttpStatus.ACCEPTED);
+  }
+
+  @GetMapping("/{id}")
+  public ResponseEntity<?> getUserById(@PathVariable int id) {
+    return ResponseEntity.ok(userService.getUserById(id));
+  }
+
+  @PutMapping("/{id}")
+  public ResponseEntity<?> updateUserData(@PathVariable int id, @RequestBody Map<String, String> json) {
+    System.err.println(json.entrySet().size());
+    json.keySet().forEach(k -> System.err.println(k));
+    String name = json.get("name");
+    String email = json.get("email");
+    if (name == null || email == null) {
+      return new ResponseEntity<String>("Missing required fields", HttpStatus.BAD_REQUEST);
+    }
+    try {
+      User user = userService.updateUserData(id, name, email);
+      return new ResponseEntity<User>(user, HttpStatus.OK);
+    } catch (HttpClientErrorException e) {
+      if (e.getStatusCode() == HttpStatus.NOT_FOUND)
+        return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
+      throw e;
+    }
+  }
+
+  @PatchMapping("/{id}")
+  public ResponseEntity<?> updateUserState(@PathVariable int id, @RequestBody Map<String, String> json) {
+
+    String strStatus = json.get("status");
+    if (strStatus == null) {
+      return new ResponseEntity<String>("Missing required fields", HttpStatus.BAD_REQUEST);
+    }
+    try {
+      UserStatus userStatus = UserStatus.valueOf(strStatus);
+      return ResponseEntity.ok(userService.updateUserStatus(id, userStatus));
+    } catch (IllegalArgumentException e) {
+      return new ResponseEntity<String>("Unknown user state", HttpStatus.BAD_REQUEST);
+    } catch (HttpClientErrorException e) {
+      if (e.getStatusCode() == HttpStatus.NOT_FOUND)
+        return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
+      throw e;
+    }
+
+  }
+
+  @DeleteMapping("/{id}")
+  public ResponseEntity<?> deleteUser(@PathVariable Integer id) {
+    try {
+      return ResponseEntity.ok(userService.deleteUserById(id));
+    } catch (HttpClientErrorException e) {
+      if (e.getStatusCode() == HttpStatus.NOT_FOUND)
+        return new ResponseEntity<String>(HttpStatus.NOT_FOUND);
+      throw e;
+    }
+  }
+
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/AuthResponse.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/AuthResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..8f334813beee8cb95caa275c66e46c9c539f2bd5
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/AuthResponse.java
@@ -0,0 +1,51 @@
+package com.uva.monolith.services.users.models;
+
+public class AuthResponse {
+
+  private int id;
+  private String username;
+  private String email;
+  private String password;
+  private UserRol rol;
+
+  public int getId() {
+    return this.id;
+  }
+
+  public void setId(int id) {
+    this.id = id;
+  }
+
+  public String getUsername() {
+    return this.username;
+  }
+
+  public void setUsername(String username) {
+    this.username = username;
+  }
+
+  public String getEmail() {
+    return this.email;
+  }
+
+  public void setEmail(String email) {
+    this.email = email;
+  }
+
+  public String getPassword() {
+    return this.password;
+  }
+
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+  public UserRol getRol() {
+    return this.rol;
+  }
+
+  public void setRol(UserRol rol) {
+    this.rol = rol;
+  }
+
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/Client.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/Client.java
new file mode 100644
index 0000000000000000000000000000000000000000..e106ecd3789a0237602e3194feacab7ddcbf4dfd
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/Client.java
@@ -0,0 +1,65 @@
+package com.uva.monolith.services.users.models;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.uva.monolith.services.bookings.models.Booking;
+
+import jakarta.persistence.Basic;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "user_client")
+public class Client extends User {
+
+  @Basic(optional = false)
+  @Column(nullable = false)
+  @Enumerated(EnumType.STRING)
+  private UserStatus status;
+
+  @JsonIgnore
+  @OneToMany(mappedBy = "userId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+  private List<Booking> bookings;
+
+  public Client() {
+    super();
+    bookings = new ArrayList<>();
+    status = UserStatus.NO_BOOKINGS;
+  }
+
+  public Client(int id, String name, String email, String password, UserStatus status,
+      List<Booking> bookings) {
+    super(id, name, email, password, UserRol.CLIENT);
+    setStatus(status);
+    setBookings(bookings);
+  }
+
+  public UserStatus getStatus() {
+    if (getBookings() == null || getBookings().isEmpty())
+      return UserStatus.NO_BOOKINGS;
+    boolean activeBookings = getBookings().stream()
+        .anyMatch(booking -> !booking.getEndDate().isBefore(LocalDate.now())); // reserva >= ahora
+    return activeBookings ? UserStatus.WITH_ACTIVE_BOOKINGS : UserStatus.WITH_INACTIVE_BOOKINGS;
+  }
+
+  public void setStatus(UserStatus status) {
+    this.status = status;
+  }
+
+  public List<Booking> getBookings() {
+    return this.bookings;
+  }
+
+  public void setBookings(List<Booking> bookings) {
+    this.bookings = bookings;
+  }
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/HotelManager.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/HotelManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e6f4b0aafa35ab8b23d202814c1fabefdcf86ed
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/HotelManager.java
@@ -0,0 +1,38 @@
+package com.uva.monolith.services.users.models;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.uva.monolith.services.hotels.models.Hotel;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "hotel_manager_user")
+public class HotelManager extends User {
+
+  @JsonIgnore
+  @OneToMany(mappedBy = "hotelManager", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+  private List<Hotel> hotels;
+
+  public HotelManager() {
+    super();
+    hotels = new ArrayList<>();
+  }
+
+  public HotelManager(int id, String name, String email, String password, List<Hotel> hotels) {
+    super(id, name, email, password, UserRol.HOTEL_ADMIN);
+    setHotels(hotels);
+  }
+
+  public List<Hotel> getHotels() {
+    return this.hotels;
+  }
+
+  public void setHotels(List<Hotel> hotels) {
+    this.hotels = hotels;
+  }
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/User.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/User.java
new file mode 100644
index 0000000000000000000000000000000000000000..45decd686b3972058eb920f6c2e07cd4293f1e05
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/User.java
@@ -0,0 +1,95 @@
+package com.uva.monolith.services.users.models;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import jakarta.persistence.Basic;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Inheritance;
+import jakarta.persistence.InheritanceType;
+import jakarta.persistence.Table;
+
+@Entity
+@Inheritance(strategy = InheritanceType.JOINED)
+@Table(name = "users")
+public class User {
+
+  @Id
+  @GeneratedValue(strategy = GenerationType.IDENTITY)
+  @Basic(optional = false)
+  @Column(nullable = false)
+  private int id;
+
+  @Basic(optional = false)
+  @Column(nullable = false)
+  private String name;
+
+  @Basic(optional = false)
+  @Column(nullable = false, unique = true)
+  private String email;
+
+  @JsonIgnore
+  @Basic(optional = false)
+  @Column(nullable = false)
+  private String password;
+
+  @Basic(optional = false)
+  @Column(nullable = false)
+  @Enumerated(EnumType.STRING)
+  private UserRol rol = UserRol.CLIENT;
+
+  public User() {
+  }
+
+  public User(int id, String name, String email, String password, UserRol rol) {
+    setId(id);
+    setName(name);
+    setEmail(email);
+    setRol(rol);
+  }
+
+  public int getId() {
+    return this.id;
+  }
+
+  public void setId(int id) {
+    this.id = id;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+  public String getEmail() {
+    return this.email;
+  }
+
+  public void setEmail(String email) {
+    this.email = email;
+  }
+
+  public String getPassword() {
+    return password;
+  }
+
+  public void setPassword(String rawPassword) {
+    this.password = rawPassword;
+  }
+
+  public UserRol getRol() {
+    return this.rol;
+  }
+
+  public void setRol(UserRol rol) {
+    this.rol = rol;
+  }
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/UserRol.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/UserRol.java
new file mode 100644
index 0000000000000000000000000000000000000000..f408ba5ef9d34d96c32d3c42a6c2c51b1c6f22b1
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/UserRol.java
@@ -0,0 +1,5 @@
+package com.uva.monolith.services.users.models;
+
+public enum UserRol {
+  ADMIN, HOTEL_ADMIN, CLIENT
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/UserStatus.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/UserStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..362b8688260d4c13dc4a8eae205411c9d5533d79
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/models/UserStatus.java
@@ -0,0 +1,5 @@
+package com.uva.monolith.services.users.models;
+
+public enum UserStatus {
+  NO_BOOKINGS, WITH_ACTIVE_BOOKINGS, WITH_INACTIVE_BOOKINGS;
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/ClientRepository.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/ClientRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c1b46fbe665075b8f817367ff14ee65cf69ff76
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/ClientRepository.java
@@ -0,0 +1,10 @@
+package com.uva.monolith.services.users.repositories;
+
+import java.util.Optional;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.uva.monolith.services.users.models.Client;
+
+public interface ClientRepository extends JpaRepository<Client, Integer> {
+  Optional<Client> findByEmail(String email);
+}
\ No newline at end of file
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/HotelManagerRepository.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/HotelManagerRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..092a251b199fdecd80a2654fc3e6c96d1b7eb7f4
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/HotelManagerRepository.java
@@ -0,0 +1,10 @@
+package com.uva.monolith.services.users.repositories;
+
+import java.util.Optional;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.uva.monolith.services.users.models.HotelManager;
+
+public interface HotelManagerRepository extends JpaRepository<HotelManager, Integer> {
+  Optional<HotelManager> findByEmail(String email);
+}
\ No newline at end of file
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/UserRepository.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/UserRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..e5b44c976f095719854aa8070abc843b898036fe
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/repositories/UserRepository.java
@@ -0,0 +1,11 @@
+package com.uva.monolith.services.users.repositories;
+
+import java.util.Optional;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.uva.monolith.services.users.models.User;
+
+public interface UserRepository extends JpaRepository<User, Integer> {
+  Optional<User> findByEmail(String email);
+}
diff --git a/java/roomBooking/src/main/java/com/uva/monolith/services/users/services/UserService.java b/java/roomBooking/src/main/java/com/uva/monolith/services/users/services/UserService.java
new file mode 100644
index 0000000000000000000000000000000000000000..193d736853b1f0d84ff86a4b24cfd0f8db52172d
--- /dev/null
+++ b/java/roomBooking/src/main/java/com/uva/monolith/services/users/services/UserService.java
@@ -0,0 +1,107 @@
+package com.uva.monolith.services.users.services;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Optional;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.HttpClientErrorException;
+
+import com.uva.monolith.services.users.models.AuthResponse;
+import com.uva.monolith.services.users.models.Client;
+import com.uva.monolith.services.users.models.User;
+import com.uva.monolith.services.users.models.UserStatus;
+import com.uva.monolith.services.users.repositories.ClientRepository;
+import com.uva.monolith.services.users.repositories.UserRepository;
+
+@Service
+public class UserService {
+
+  @Autowired
+  private UserRepository userRepository;
+
+  @Autowired
+  private ClientRepository clientRepository;
+
+  public List<User> getAllUsers() {
+    return userRepository.findAll();
+  }
+
+  private User assertUser(Optional<? extends User> opUser) {
+    return opUser.orElseThrow(() -> new HttpClientErrorException(HttpStatus.NOT_FOUND));
+  }
+
+  public User getUserById(int id) {
+    return assertUser(userRepository.findById(id));
+  }
+
+  public AuthResponse getUserByEmail(String email) {
+    User u = assertUser(userRepository.findByEmail(email));
+    AuthResponse auth = new AuthResponse();
+    BeanUtils.copyProperties(u, auth);
+    auth.setUsername(u.getName());
+    return auth;
+  }
+
+  public User addUser(User user) {
+    // Actualmente está en el servicio AUTH
+    // TODO adaptar adecuadamente
+    throw new HttpClientErrorException(HttpStatus.MOVED_PERMANENTLY, "servicio actual en http://localhost:8101/login");
+    // user.setStatus(UserStatus.NO_BOOKINGS);
+    // if (user.getRol() == null) // Rol por defecto
+    // user.setRol(UserRol.CONSUMER);
+    // // Guardamos
+    // return userRepository.save(user);
+  }
+
+  public User updateUserData(int id, String name, String email) {
+    User user = getUserById(id);
+    user.setName(name);
+    user.setEmail(email);
+    return userRepository.save(user);
+  }
+
+  public User updateUserStatus(int id, UserStatus status) {
+
+    Client user = (Client) assertUser(clientRepository.findById(id));
+
+    boolean activeBookings = user.getBookings().stream()
+        .anyMatch(booking -> !booking.getEndDate().isBefore(LocalDate.now())); // reserva >= ahora
+    boolean inactiveBookings = user.getBookings().stream()
+        .anyMatch(booking -> booking.getStartDate().isBefore(LocalDate.now())); // reserva < ahora
+
+    switch (status) {
+      case NO_BOOKINGS:
+        if (!user.getBookings().isEmpty())
+          throw new IllegalArgumentException("Invalid State: The user has at least one booking");
+        break;
+      case WITH_ACTIVE_BOOKINGS:
+        if (user.getBookings().isEmpty())
+          throw new IllegalArgumentException("Invalid State: The user don't has bookings");
+        if (!activeBookings)
+          throw new IllegalArgumentException("Invalid State: The user don't has active bookings");
+        break;
+      case WITH_INACTIVE_BOOKINGS:
+        if (user.getBookings().isEmpty())
+          throw new IllegalArgumentException("Invalid State: The user don't has bookings");
+        if (!inactiveBookings)
+          throw new IllegalArgumentException("Invalid State: The user don't has inactive bookings");
+        break;
+      default:
+        break;
+    }
+    user.setStatus(status);
+    return userRepository.save(user);
+  }
+
+  public User deleteUserById(int id) {
+    User user = getUserById(id);
+    // TODO eliminar reservas de usuario ahora mismo no por el modo cascada pero a
+    // futuro sí, después de la disgregación en microservicios
+    userRepository.deleteById(id);
+    return user;
+  }
+}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/BookingController.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/BookingController.java
deleted file mode 100644
index c52866c2ca28e056ad9551b42e007c0f2f487a80..0000000000000000000000000000000000000000
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/BookingController.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// BookingController.java
-package com.uva.roomBooking.Controllers;
-
-import com.uva.roomBooking.Models.Booking;
-import com.uva.roomBooking.Models.Room;
-import com.uva.roomBooking.Models.User;
-import com.uva.roomBooking.Repositories.BookingRepository;
-import com.uva.roomBooking.Repositories.RoomRepository;
-import com.uva.roomBooking.Repositories.UserRepository;
-
-import jakarta.transaction.Transactional;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.HttpStatusCode;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-@RestController
-@RequestMapping("/bookings")
-@CrossOrigin(origins = "http://localhost:4200")
-public class BookingController {
-
-    private final BookingRepository bookingRepository;
-    private final UserRepository userRepository;
-    private final RoomRepository roomRepository;
-
-    public BookingController(BookingRepository bookingRepository, UserRepository userRepository,
-            RoomRepository roomRepository) {
-        this.bookingRepository = bookingRepository;
-        this.userRepository = userRepository;
-        this.roomRepository = roomRepository;
-    }
-
-    @GetMapping
-    public List<Booking> getAllBookings() {
-        return bookingRepository.findAll();
-    }
-
-    @PostMapping
-    public Booking createBooking(@RequestBody Booking booking) {
-        User user = userRepository.findById(booking.getUserId().getId())
-                .orElseThrow(() -> new RuntimeException("User not found"));
-        Room room = roomRepository.findById(booking.getRoomId().getId())
-                .orElseThrow(() -> new RuntimeException("Room not found"));
-
-        // Verificar disponibilidad
-        List<Booking> existingBookings = bookingRepository.findByRoomIdAndDateRange(
-                room.getId(), booking.getStartDate(), booking.getEndDate());
-
-        if (!existingBookings.isEmpty()) {
-            throw new RuntimeException("Room is not available for the selected dates");
-        }
-
-        booking.setUserId(user);
-        booking.setRoomId(room);
-        return bookingRepository.save(booking);
-    }
-
-    @GetMapping("/{id}")
-    public Booking getBookingById(@PathVariable Integer id) {
-        return bookingRepository.findById(id)
-                .orElseThrow(() -> new RuntimeException("Booking not found"));
-    }
-
-    @DeleteMapping("/{id}")
-    @Transactional
-    public ResponseEntity<Void> deleteBooking(@PathVariable Integer id) {
-        try {
-            if (!bookingRepository.existsById(id))
-                return new ResponseEntity<>(HttpStatus.NOT_FOUND);
-
-            bookingRepository.deleteBookingById(id);
-            return new ResponseEntity<>(HttpStatus.ACCEPTED);
-        } catch (Exception e) {
-            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
-        }
-    }
-}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/UserController.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/UserController.java
deleted file mode 100644
index a0e6397b9cabc191f7d5d24ecde162e8da6b2f14..0000000000000000000000000000000000000000
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/UserController.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.uva.roomBooking.Controllers;
-
-import java.time.LocalDate;
-import java.util.List;
-import java.util.Map;
-
-import org.springframework.web.bind.annotation.CrossOrigin;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PatchMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.uva.roomBooking.Models.UserStatus;
-import com.uva.roomBooking.Models.Booking;
-import com.uva.roomBooking.Models.User;
-import com.uva.roomBooking.Repositories.UserRepository;
-
-@RestController
-@RequestMapping("users")
-@CrossOrigin(origins = "*")
-public class UserController {
-  private final UserRepository userRepository;
-
-  public UserController(UserRepository userRepository) {
-    this.userRepository = userRepository;
-  }
-
-  @GetMapping
-  public List<User> getAllUsers() {
-    return userRepository.findAll();
-  }
-
-  @PostMapping
-  public User addUser(@RequestBody User user) {
-    // TODO revisar como se desea manejar estado por defecto
-    user.setStatus(UserStatus.NO_BOOKINGS);
-    // Aunque se asegure a lo mejor no es la forma de manejo esperada
-    return userRepository.save(user);
-  }
-
-  @GetMapping("/{id}")
-  public User getUserById(@PathVariable int id) {
-    return userRepository.findById(id).orElseThrow();
-  }
-
-  @PutMapping("/{id}")
-  public User updateUserData(@PathVariable int id, @RequestBody Map<String, String> json) {
-    User target = userRepository.findById(id).orElseThrow();
-    if (!json.containsKey("name") || !json.containsKey("email")) {
-      // TODO cambiar manejo
-      throw new RuntimeException("Missing required fields");
-    }
-    target.setName(json.get("name"));
-    target.setEmail(json.get("email"));
-    return userRepository.save(target);
-  }
-
-  @PatchMapping("/{id}")
-  public User updateUserState(@PathVariable int id, @RequestBody Map<String, String> json) {
-    User target = userRepository.findById(id).orElseThrow();
-    String strStatus = json.get("status");
-    if (strStatus == null) {
-      // TODO cambiar manejo
-      throw new RuntimeException("Missing required fields");
-    }
-    UserStatus userStatus = UserStatus.valueOf(strStatus);
-
-    boolean activeBookings = target.getBookings().stream()
-        .anyMatch(booking -> !booking.getEndDate().isBefore(LocalDate.now())); // reserva >= ahora
-    boolean inactiveBookings = target.getBookings().stream()
-        .anyMatch(booking -> booking.getStartDate().isBefore(LocalDate.now())); // reserva < ahora
-
-    switch (userStatus) {
-      // TODO Buscar como validar las (in)active bookings
-      case NO_BOOKINGS:
-        if (!target.getBookings().isEmpty())
-          throw new IllegalArgumentException("Invalid State: The user has at least one booking");
-        break;
-      case WITH_ACTIVE_BOOKINGS:
-        if (target.getBookings().isEmpty())
-          throw new IllegalArgumentException("Invalid State: The user don't has bookings");
-        if (!activeBookings)
-          throw new IllegalArgumentException("Invalid State: The user don't has active bookings");
-        break;
-      case WITH_INACTIVE_BOOKINGS:
-        if (target.getBookings().isEmpty())
-          throw new IllegalArgumentException("Invalid State: The user don't has bookings");
-        if (!inactiveBookings)
-          throw new IllegalArgumentException("Invalid State: The user don't has inactive bookings");
-        break;
-      default:
-        break;
-    }
-    target.setStatus(userStatus);
-    return userRepository.save(target);
-  }
-
-  @DeleteMapping("/{id}")
-  public User deleteUser(@PathVariable Integer id) {
-    User target;
-    if ((target = userRepository.findById(id).orElseThrow()) != null) {
-      userRepository.deleteById(id);
-    }
-    return target;
-  }
-
-  @GetMapping("/{id}/bookings")
-  public List<Booking> getUserBookingsById(@PathVariable int id) {
-    User user = userRepository.findById(id).orElseThrow();
-    return user.getBookings();
-  }
-}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Tipo.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Tipo.java
deleted file mode 100644
index e1a48b53450e1f87ae070b1bf70bd6a9afc1ea34..0000000000000000000000000000000000000000
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Tipo.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.uva.roomBooking.Models;
-
-public enum Tipo {
-    SINGLE, 
-    DOUBLE, 
-    SUITE
-}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/BookingRepository.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/BookingRepository.java
deleted file mode 100644
index 16c860a30fd168d4e03018ef02466dcd5adb7f99..0000000000000000000000000000000000000000
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/BookingRepository.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// BookingRepository.java
-package com.uva.roomBooking.Repositories;
-
-import com.uva.roomBooking.Models.Booking;
-
-import jakarta.transaction.Transactional;
-
-import java.time.LocalDate;
-import java.util.List;
-
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Modifying;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-public interface BookingRepository extends JpaRepository<Booking, Integer> {
-
-    @Query("SELECT b FROM Booking b WHERE b.roomId.id = ?1 AND b.startDate < ?2 AND b.endDate > ?3")
-    List<Booking> findByRoomIdAndDateRange(@Param("roomId") int roomId, @Param("startDate") LocalDate startDate,
-            @Param("endDate") LocalDate endDate);
-
-    @Transactional
-    @Modifying
-    @Query("DELETE FROM Booking b WHERE b.id = ?1")
-    void deleteBookingById(@Param("id") Integer id);
-
-    @Transactional
-    @Modifying
-    @Query("DELETE FROM Booking b WHERE b.roomId.hotel.id = ?1")
-    void deleteAllByHotelId(int hotelId);
-
-}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/HotelRepository.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/HotelRepository.java
deleted file mode 100644
index 290f129a7be376279300062d4bf3d7a614abefcc..0000000000000000000000000000000000000000
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/HotelRepository.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.uva.roomBooking.Repositories;
-
-import com.uva.roomBooking.Models.Hotel;
-import org.springframework.data.jpa.repository.JpaRepository;
-
-public interface HotelRepository extends JpaRepository<Hotel, Integer> {
-
-}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/UserRepository.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/UserRepository.java
deleted file mode 100644
index 5f02f6d4a3b2147d181b8db36736d85ccfd884b5..0000000000000000000000000000000000000000
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/UserRepository.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.uva.roomBooking.Repositories;
-
-import org.springframework.data.jpa.repository.JpaRepository;
-
-import com.uva.roomBooking.Models.User;
-
-public interface UserRepository extends JpaRepository<User, Integer> {
-
-}
diff --git a/java/roomBooking/src/main/resources/application.properties b/java/roomBooking/src/main/resources/application.properties
index 563d4ad07ad10e7fb52bb3defcc3b736d889447b..e9a1304c8364b789ce008c161df0f98daf1238a9 100644
--- a/java/roomBooking/src/main/resources/application.properties
+++ b/java/roomBooking/src/main/resources/application.properties
@@ -7,4 +7,5 @@ spring.datasource.password=password
 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 
 # Usar esto para alternar entre las exposición del room repository ya que no es necesario su uso pero por defecto, al no cubrir su ruta, se expone
-# spring.data.rest.base-path=false
\ No newline at end of file
+# spring.data.rest.base-path=false
+external.services.auth.host=localhost:8101
\ No newline at end of file
diff --git a/java/services/auth/.gitignore b/java/services/auth/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..549e00a2a96fa9d7c5dbc9859664a78d980158c2
--- /dev/null
+++ b/java/services/auth/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/java/services/auth/Dockerfile b/java/services/auth/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..8d0b79d6514534deddac782fc9367bcdbcaf89c3
--- /dev/null
+++ b/java/services/auth/Dockerfile
@@ -0,0 +1,10 @@
+FROM maven:3-openjdk-17 AS maven
+WORKDIR /app
+COPY ./ ./
+RUN mvn -Dmaven.test.skip clean package
+FROM openjdk:17-jdk-oracle
+ARG JAR_FILE=/app/target/*.jar
+COPY --from=maven ${JAR_FILE} app.jar
+ENV PORT 8080
+EXPOSE $PORT
+ENTRYPOINT ["java","-jar", "/app.jar"]
\ No newline at end of file
diff --git a/java/services/auth/mvnw b/java/services/auth/mvnw
new file mode 100755
index 0000000000000000000000000000000000000000..19529ddf8c6eaa08c5c75ff80652d21ce4b72f8c
--- /dev/null
+++ b/java/services/auth/mvnw
@@ -0,0 +1,259 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.3.2
+#
+# Optional ENV vars
+# -----------------
+#   JAVA_HOME - location of a JDK home dir, required when download maven via java source
+#   MVNW_REPOURL - repo url base for downloading maven distribution
+#   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+#   MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
+# ----------------------------------------------------------------------------
+
+set -euf
+[ "${MVNW_VERBOSE-}" != debug ] || set -x
+
+# OS specific support.
+native_path() { printf %s\\n "$1"; }
+case "$(uname)" in
+CYGWIN* | MINGW*)
+  [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
+  native_path() { cygpath --path --windows "$1"; }
+  ;;
+esac
+
+# set JAVACMD and JAVACCMD
+set_java_home() {
+  # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
+  if [ -n "${JAVA_HOME-}" ]; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+      JAVACCMD="$JAVA_HOME/jre/sh/javac"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+      JAVACCMD="$JAVA_HOME/bin/javac"
+
+      if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
+        echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
+        echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
+        return 1
+      fi
+    fi
+  else
+    JAVACMD="$(
+      'set' +e
+      'unset' -f command 2>/dev/null
+      'command' -v java
+    )" || :
+    JAVACCMD="$(
+      'set' +e
+      'unset' -f command 2>/dev/null
+      'command' -v javac
+    )" || :
+
+    if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
+      echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
+      return 1
+    fi
+  fi
+}
+
+# hash string like Java String::hashCode
+hash_string() {
+  str="${1:-}" h=0
+  while [ -n "$str" ]; do
+    char="${str%"${str#?}"}"
+    h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
+    str="${str#?}"
+  done
+  printf %x\\n $h
+}
+
+verbose() { :; }
+[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
+
+die() {
+  printf %s\\n "$1" >&2
+  exit 1
+}
+
+trim() {
+  # MWRAPPER-139:
+  #   Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
+  #   Needed for removing poorly interpreted newline sequences when running in more
+  #   exotic environments such as mingw bash on Windows.
+  printf "%s" "${1}" | tr -d '[:space:]'
+}
+
+# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
+while IFS="=" read -r key value; do
+  case "${key-}" in
+  distributionUrl) distributionUrl=$(trim "${value-}") ;;
+  distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
+  esac
+done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
+
+case "${distributionUrl##*/}" in
+maven-mvnd-*bin.*)
+  MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
+  case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
+  *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
+  :Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
+  :Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
+  :Linux*x86_64*) distributionPlatform=linux-amd64 ;;
+  *)
+    echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
+    distributionPlatform=linux-amd64
+    ;;
+  esac
+  distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
+  ;;
+maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
+*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
+esac
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
+[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
+distributionUrlName="${distributionUrl##*/}"
+distributionUrlNameMain="${distributionUrlName%.*}"
+distributionUrlNameMain="${distributionUrlNameMain%-bin}"
+MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
+MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
+
+exec_maven() {
+  unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
+  exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
+}
+
+if [ -d "$MAVEN_HOME" ]; then
+  verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+  exec_maven "$@"
+fi
+
+case "${distributionUrl-}" in
+*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
+*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
+esac
+
+# prepare tmp dir
+if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
+  clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
+  trap clean HUP INT TERM EXIT
+else
+  die "cannot create temp dir"
+fi
+
+mkdir -p -- "${MAVEN_HOME%/*}"
+
+# Download and Install Apache Maven
+verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+verbose "Downloading from: $distributionUrl"
+verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+# select .zip or .tar.gz
+if ! command -v unzip >/dev/null; then
+  distributionUrl="${distributionUrl%.zip}.tar.gz"
+  distributionUrlName="${distributionUrl##*/}"
+fi
+
+# verbose opt
+__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
+[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
+
+# normalize http auth
+case "${MVNW_PASSWORD:+has-password}" in
+'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
+esac
+
+if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
+  verbose "Found wget ... using wget"
+  wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
+elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
+  verbose "Found curl ... using curl"
+  curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
+elif set_java_home; then
+  verbose "Falling back to use Java to download"
+  javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
+  targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
+  cat >"$javaSource" <<-END
+	public class Downloader extends java.net.Authenticator
+	{
+	  protected java.net.PasswordAuthentication getPasswordAuthentication()
+	  {
+	    return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
+	  }
+	  public static void main( String[] args ) throws Exception
+	  {
+	    setDefault( new Downloader() );
+	    java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
+	  }
+	}
+	END
+  # For Cygwin/MinGW, switch paths to Windows format before running javac and java
+  verbose " - Compiling Downloader.java ..."
+  "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
+  verbose " - Running Downloader.java ..."
+  "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
+fi
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+if [ -n "${distributionSha256Sum-}" ]; then
+  distributionSha256Result=false
+  if [ "$MVN_CMD" = mvnd.sh ]; then
+    echo "Checksum validation is not supported for maven-mvnd." >&2
+    echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+    exit 1
+  elif command -v sha256sum >/dev/null; then
+    if echo "$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
+      distributionSha256Result=true
+    fi
+  elif command -v shasum >/dev/null; then
+    if echo "$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
+      distributionSha256Result=true
+    fi
+  else
+    echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
+    echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
+    exit 1
+  fi
+  if [ $distributionSha256Result = false ]; then
+    echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
+    echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
+    exit 1
+  fi
+fi
+
+# unzip and move
+if command -v unzip >/dev/null; then
+  unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
+else
+  tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
+fi
+printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
+mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
+
+clean || :
+exec_maven "$@"
diff --git a/java/services/auth/mvnw.cmd b/java/services/auth/mvnw.cmd
new file mode 100644
index 0000000000000000000000000000000000000000..249bdf3822221aa612d1da2605316cabd7b07e50
--- /dev/null
+++ b/java/services/auth/mvnw.cmd
@@ -0,0 +1,149 @@
+<# : batch portion
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.3.2
+@REM
+@REM Optional ENV vars
+@REM   MVNW_REPOURL - repo url base for downloading maven distribution
+@REM   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
+@REM   MVNW_VERBOSE - true: enable verbose log; others: silence the output
+@REM ----------------------------------------------------------------------------
+
+@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
+@SET __MVNW_CMD__=
+@SET __MVNW_ERROR__=
+@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
+@SET PSModulePath=
+@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
+  IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
+)
+@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
+@SET __MVNW_PSMODULEP_SAVE=
+@SET __MVNW_ARG0_NAME__=
+@SET MVNW_USERNAME=
+@SET MVNW_PASSWORD=
+@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
+@echo Cannot start maven from wrapper >&2 && exit /b 1
+@GOTO :EOF
+: end batch / begin powershell #>
+
+$ErrorActionPreference = "Stop"
+if ($env:MVNW_VERBOSE -eq "true") {
+  $VerbosePreference = "Continue"
+}
+
+# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
+$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
+if (!$distributionUrl) {
+  Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
+}
+
+switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
+  "maven-mvnd-*" {
+    $USE_MVND = $true
+    $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
+    $MVN_CMD = "mvnd.cmd"
+    break
+  }
+  default {
+    $USE_MVND = $false
+    $MVN_CMD = $script -replace '^mvnw','mvn'
+    break
+  }
+}
+
+# apply MVNW_REPOURL and calculate MAVEN_HOME
+# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
+if ($env:MVNW_REPOURL) {
+  $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
+  $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
+}
+$distributionUrlName = $distributionUrl -replace '^.*/',''
+$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
+$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
+if ($env:MAVEN_USER_HOME) {
+  $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
+}
+$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
+$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
+
+if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
+  Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
+  Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
+  exit $?
+}
+
+if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
+  Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
+}
+
+# prepare tmp dir
+$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
+$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
+$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
+trap {
+  if ($TMP_DOWNLOAD_DIR.Exists) {
+    try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+    catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+  }
+}
+
+New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
+
+# Download and Install Apache Maven
+Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
+Write-Verbose "Downloading from: $distributionUrl"
+Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
+
+$webclient = New-Object System.Net.WebClient
+if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
+  $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
+}
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
+$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
+
+# If specified, validate the SHA-256 sum of the Maven distribution zip file
+$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
+if ($distributionSha256Sum) {
+  if ($USE_MVND) {
+    Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
+  }
+  Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
+  if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
+    Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
+  }
+}
+
+# unzip and move
+Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
+Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
+try {
+  Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
+} catch {
+  if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
+    Write-Error "fail to move MAVEN_HOME"
+  }
+} finally {
+  try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
+  catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
+}
+
+Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
diff --git a/java/services/auth/pom.xml b/java/services/auth/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..19b9923d7ad2a8b056e02681630234471de0a9d3
--- /dev/null
+++ b/java/services/auth/pom.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>3.3.4</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+	<groupId>com.uva</groupId>
+	<artifactId>authentication</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<name>authentication</name>
+	<description>Authentication microservicio</description>
+	<url/>
+	<licenses>
+		<license/>
+	</licenses>
+	<developers>
+		<developer/>
+	</developers>
+	<scm>
+		<connection/>
+		<developerConnection/>
+		<tag/>
+		<url/>
+	</scm>
+	<properties>
+		<java.version>17</java.version>
+	</properties>
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-jpa</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-data-rest</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>com.mysql</groupId>
+			<artifactId>mysql-connector-j</artifactId>
+			<scope>runtime</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
+		<dependency>
+			<groupId>org.springframework.security</groupId>
+			<artifactId>spring-security-core</artifactId>
+			<version>6.4.1</version>
+		</dependency>
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt-api</artifactId>
+			<version>0.11.5</version>
+		</dependency>
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt-impl</artifactId>
+			<version>0.11.5</version>
+		</dependency>
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt-jackson</artifactId>
+			<version>0.11.5</version>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>
\ No newline at end of file
diff --git a/java/services/auth/src/main/java/com/uva/authentication/AuthenticationApplication.java b/java/services/auth/src/main/java/com/uva/authentication/AuthenticationApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..7b4cb3cd886885343249e97189a911ef42014655
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/AuthenticationApplication.java
@@ -0,0 +1,13 @@
+package com.uva.authentication;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class AuthenticationApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(AuthenticationApplication.class, args);
+	}
+
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/api/UserAPI.java b/java/services/auth/src/main/java/com/uva/authentication/api/UserAPI.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4aa44728a593f54275339d99c9e603b20b802e9
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/api/UserAPI.java
@@ -0,0 +1,74 @@
+// TODO eliminar si realmente no necesitamos comunicar un servicio con otro
+package com.uva.authentication.api;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestTemplate;
+
+import com.uva.authentication.models.RegisterRequest;
+import com.uva.authentication.models.remote.User;
+import com.uva.authentication.models.remote.UserRol;
+import com.uva.authentication.utils.JwtUtil;
+
+@Component
+public class UserAPI {
+
+  @Autowired
+  private RestTemplate restTemplate;
+
+  @Value("${external.services.users.baseurl}")
+  private String USER_API_URL;
+
+  @Autowired
+  private JwtUtil jwtUtil;
+
+  private String token;
+  private final User USER = new User(-1, "admin", null, null, UserRol.ADMIN);
+
+  private String getAccessToken() {
+    if (token == null || token.isEmpty() || jwtUtil.isTokenValid(token, USER)) {
+      token = jwtUtil.generateToken(USER);
+    }
+    return token;
+  }
+
+  public User getUserByEmail(String email) {
+
+    // Implementación para acceder con autentificación
+    // String token = getAccessToken();
+    // HttpHeaders headers = new HttpHeaders();
+    // headers.set("Authorization", "Bearer " + token);
+    // HttpEntity<Void> entity = new HttpEntity<>(headers);
+
+    String url = USER_API_URL + "?email={" + email + "}";
+    try {
+      ResponseEntity<User> userResponse = restTemplate.getForEntity(url, User.class, email);
+      // restTemplate.exchange(url, HttpMethod.GET, entity, User.class);
+      return userResponse.getBody();
+    } catch (HttpClientErrorException e) {
+      if (e.getStatusCode() != HttpStatus.NOT_FOUND)
+        throw e;
+      return null;
+    }
+  }
+
+  public User registerUser(RegisterRequest registerRequest) {
+
+    String token = getAccessToken();
+    HttpHeaders headers = new HttpHeaders();
+    headers.set("Authorization", "Bearer " + token);
+
+    String url = USER_API_URL;
+    ResponseEntity<User> userResponse = restTemplate.postForEntity(url, registerRequest, User.class, headers);
+    if (!userResponse.getStatusCode().is2xxSuccessful())
+      throw new RuntimeException("Failed to register user");
+
+    return userResponse.getBody();
+  }
+
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/config/RestTemplateConfig.java b/java/services/auth/src/main/java/com/uva/authentication/config/RestTemplateConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad00e740247bc6562bc0cadb41e417dcec238caa
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/config/RestTemplateConfig.java
@@ -0,0 +1,14 @@
+package com.uva.authentication.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class RestTemplateConfig {
+
+    @Bean
+    public RestTemplate restTemplate() {
+        return new RestTemplate();
+    }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/controllers/AuthController.java b/java/services/auth/src/main/java/com/uva/authentication/controllers/AuthController.java
new file mode 100644
index 0000000000000000000000000000000000000000..23625a6e7824cadda471d847ecede0b94cb565da
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/controllers/AuthController.java
@@ -0,0 +1,50 @@
+package com.uva.authentication.controllers;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.client.HttpClientErrorException;
+import com.uva.authentication.models.*;
+import com.uva.authentication.services.AuthService;
+
+@RestController
+@CrossOrigin(origins = "*")
+public class AuthController {
+
+    @Autowired
+    private AuthService authService;
+
+    @PostMapping("/login")
+    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
+        try {
+            String token = authService.login(loginRequest);
+            return ResponseEntity.ok(new JwtAuthResponse(token));
+        } catch (HttpClientErrorException e) {
+            if (e.getStatusCode() == HttpStatus.FORBIDDEN) {
+                return new ResponseEntity<String>(e.getMessage(), HttpStatus.FORBIDDEN);
+            }
+        }
+        return new ResponseEntity<String>("Algo no fue bien", HttpStatus.UNAUTHORIZED);
+    }
+
+    @PostMapping("/register")
+    public ResponseEntity<?> register(@RequestBody RegisterRequest registerRequest) {
+        try {
+            LoginRequest loginRequest = new LoginRequest();
+            loginRequest.setEmail(registerRequest.getEmail());
+            loginRequest.setPassword(registerRequest.getPassword());
+
+            authService.register(registerRequest);
+            return login(loginRequest);
+        } catch (HttpClientErrorException e) {
+            if (e.getStatusCode() == HttpStatus.CONFLICT) {
+                return new ResponseEntity<String>(e.getMessage(), HttpStatus.CONFLICT);
+            }
+            e.fillInStackTrace();
+        }
+
+        return new ResponseEntity<String>("Algo no fue bien", HttpStatus.UNAUTHORIZED);
+    }
+
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/exceptions/GlobalExceptionHandler.java b/java/services/auth/src/main/java/com/uva/authentication/exceptions/GlobalExceptionHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d4b8d58dbb5560de50b5b2149cbe3f181687446
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/exceptions/GlobalExceptionHandler.java
@@ -0,0 +1,51 @@
+package com.uva.authentication.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+    @ExceptionHandler(HotelNotFoundException.class)
+    public ResponseEntity<Map<String, Object>> handleHotelNotFound(HotelNotFoundException ex) {
+        Map<String, Object> body = new HashMap<>();
+        body.put("timestamp", LocalDateTime.now());
+        body.put("message", ex.getMessage());
+
+        return new ResponseEntity<>(body, HttpStatus.NOT_FOUND);
+    }
+
+    @ExceptionHandler(InvalidRequestException.class)
+    public ResponseEntity<Map<String, Object>> handleInvalidRequest(InvalidRequestException ex) {
+        Map<String, Object> body = new HashMap<>();
+        body.put("timestamp", LocalDateTime.now());
+        body.put("message", ex.getMessage());
+
+        return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
+    }
+
+    @ExceptionHandler(InvalidDateRangeException.class)
+    public ResponseEntity<Map<String, Object>> handleInvalidDateRange(InvalidDateRangeException ex) {
+        Map<String, Object> body = new HashMap<>();
+        body.put("timestamp", LocalDateTime.now());
+        body.put("message", ex.getMessage());
+
+        return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
+    }
+
+    // @ExceptionHandler(Exception.class)
+    // public ResponseEntity<Map<String, Object>> handleGeneralException(Exception
+    // ex) {
+    // Map<String, Object> body = new HashMap<>();
+    // body.put("timestamp", LocalDateTime.now());
+    // body.put("message", "An unexpected error occurred: " + ex.getMessage());
+
+    // return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
+    // }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/exceptions/HotelNotFoundException.java b/java/services/auth/src/main/java/com/uva/authentication/exceptions/HotelNotFoundException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c642139b421a5cf864218fa2d0063f955335c5b7
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/exceptions/HotelNotFoundException.java
@@ -0,0 +1,11 @@
+package com.uva.authentication.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.NOT_FOUND) // Devuelve un 404 cuando se lanza la excepción
+public class HotelNotFoundException extends RuntimeException {
+    public HotelNotFoundException(int id) {
+        super("Hotel not found with id: " + id);
+    }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/exceptions/InvalidDateRangeException.java b/java/services/auth/src/main/java/com/uva/authentication/exceptions/InvalidDateRangeException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3dc917fb03495480007365b117e185521cf7bf2
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/exceptions/InvalidDateRangeException.java
@@ -0,0 +1,7 @@
+package com.uva.authentication.exceptions;
+
+public class InvalidDateRangeException extends RuntimeException {
+    public InvalidDateRangeException(String message) {
+        super(message);
+    }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/exceptions/InvalidRequestException.java b/java/services/auth/src/main/java/com/uva/authentication/exceptions/InvalidRequestException.java
new file mode 100644
index 0000000000000000000000000000000000000000..499a320e58ecd7576cbfff39101db14395f0edbe
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/exceptions/InvalidRequestException.java
@@ -0,0 +1,11 @@
+package com.uva.authentication.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.BAD_REQUEST)
+public class InvalidRequestException extends RuntimeException {
+    public InvalidRequestException(String message) {
+        super(message);
+    }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/AuthResponse.java b/java/services/auth/src/main/java/com/uva/authentication/models/AuthResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..e943a69108d5da38d4956509242935a6e4eb659e
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/AuthResponse.java
@@ -0,0 +1,53 @@
+package com.uva.authentication.models;
+
+import com.uva.authentication.models.remote.UserRol;
+
+public class AuthResponse {
+
+  private int id;
+  private String username;
+  private String email;
+  private String password;
+  private UserRol rol;
+
+  public int getId() {
+    return this.id;
+  }
+
+  public void setId(int id) {
+    this.id = id;
+  }
+
+  public String getUsername() {
+    return this.username;
+  }
+
+  public void setUsername(String username) {
+    this.username = username;
+  }
+
+  public String getEmail() {
+    return this.email;
+  }
+
+  public void setEmail(String email) {
+    this.email = email;
+  }
+
+  public String getPassword() {
+    return this.password;
+  }
+
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+  public UserRol getRol() {
+    return this.rol;
+  }
+
+  public void setRol(UserRol rol) {
+    this.rol = rol;
+  }
+
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/JwtAuthResponse.java b/java/services/auth/src/main/java/com/uva/authentication/models/JwtAuthResponse.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9566954c35ce71864881e320843e270a998b9da
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/JwtAuthResponse.java
@@ -0,0 +1,14 @@
+package com.uva.authentication.models;
+
+public class JwtAuthResponse {
+  private String token;
+
+  public JwtAuthResponse(String token) {
+    this.token = token;
+  }
+
+  // Getter
+  public String getToken() {
+    return token;
+  }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/LoginRequest.java b/java/services/auth/src/main/java/com/uva/authentication/models/LoginRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d829de96fd9a45ed458976f03107f38e8933e797
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/LoginRequest.java
@@ -0,0 +1,26 @@
+package com.uva.authentication.models;
+
+import java.util.Objects;
+
+public class LoginRequest {
+  private String email;
+  private String password = String.valueOf(Objects.hashCode("hi"));
+
+  // Getters and setters
+  public String getEmail() {
+    return email;
+  }
+
+  public void setEmail(String email) {
+    this.email = email;
+  }
+
+  public String getPassword() {
+    return password;
+  }
+
+  public void setPassword(String password) {
+    this.password = password;
+  }
+
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/RegisterRequest.java b/java/services/auth/src/main/java/com/uva/authentication/models/RegisterRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b3c98e8e71e7ff14f2bb90c9f1b4e0cecf7e8ae7
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/RegisterRequest.java
@@ -0,0 +1,25 @@
+package com.uva.authentication.models;
+
+import com.uva.authentication.models.remote.UserRol;
+
+public class RegisterRequest extends LoginRequest {
+  private UserRol rol;
+  private String name;
+
+  public UserRol getRol() {
+    return this.rol;
+  }
+
+  public void setRol(UserRol rol) {
+    this.rol = rol;
+  }
+
+  public String getName() {
+    return this.name;
+  }
+
+  public void setName(String name) {
+    this.name = name;
+  }
+
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/remote/Booking.java b/java/services/auth/src/main/java/com/uva/authentication/models/remote/Booking.java
new file mode 100644
index 0000000000000000000000000000000000000000..354c65d506674f162fa3009b593943be01a92e6f
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/remote/Booking.java
@@ -0,0 +1,49 @@
+package com.uva.authentication.models.remote;
+
+import jakarta.persistence.Basic;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "bookings")
+public class Booking {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Basic(optional = false)
+    private int id;
+    @JoinColumn(name = "user_id", referencedColumnName = "id")
+    @ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
+    private Client userId;
+
+    public Booking() {
+    }
+
+    public Booking(int id, Client userId) {
+        this.id = id;
+        this.userId = userId;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public int getId() {
+        return this.id;
+    }
+
+    public void setUserId(Client userId) {
+        this.userId = userId;
+    }
+
+    public Client getUserId() {
+        return this.userId;
+    }
+
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/remote/Client.java b/java/services/auth/src/main/java/com/uva/authentication/models/remote/Client.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ebf50a3b5b1135b6c6b1f7206486c95a9e95b92
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/remote/Client.java
@@ -0,0 +1,53 @@
+package com.uva.authentication.models.remote;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.persistence.Basic;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "user_client")
+public class Client extends User {
+
+  @Basic(optional = false)
+  @Column(nullable = false)
+  @Enumerated(EnumType.STRING)
+  private UserStatus status = UserStatus.NO_BOOKINGS;
+
+  @OneToMany(mappedBy = "userId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+  private List<Booking> bookings = new ArrayList<>();
+
+  public Client() {
+    super();
+  }
+
+  public Client(int id, String name, String email, String password, UserStatus status, List<Booking> bookings) {
+    super(id, name, email, password, UserRol.CLIENT);
+    setStatus(status);
+    setBookings(bookings);
+  }
+
+  public UserStatus getStatus() {
+    return this.status;
+  }
+
+  public void setStatus(UserStatus status) {
+    this.status = status;
+  }
+
+  public List<Booking> getBookings() {
+    return this.bookings;
+  }
+
+  public void setBookings(List<Booking> bookings) {
+    this.bookings = bookings;
+  }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/remote/Hotel.java b/java/services/auth/src/main/java/com/uva/authentication/models/remote/Hotel.java
new file mode 100644
index 0000000000000000000000000000000000000000..5710f9d545b57d39363435fa8776a856461291b6
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/remote/Hotel.java
@@ -0,0 +1,48 @@
+package com.uva.authentication.models.remote;
+
+import jakarta.persistence.Basic;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "hotels")
+public class Hotel {
+
+  @Id
+  @GeneratedValue(strategy = GenerationType.IDENTITY)
+  @Basic(optional = false)
+  private int id;
+
+  @ManyToOne(optional = false)
+  @JoinColumn(name = "hotel_manager", referencedColumnName = "id")
+  private HotelManager hotelManager;
+
+  public Hotel() {
+  }
+
+  public Hotel(int id, HotelManager hotelManager) {
+    setId(id);
+    setHotelManager(hotelManager);
+  }
+
+  public int getId() {
+    return this.id;
+  }
+
+  public void setId(int id) {
+    this.id = id;
+  }
+
+  public void setHotelManager(HotelManager hotelManager) {
+    this.hotelManager = hotelManager;
+  }
+
+  public HotelManager getHotelManager() {
+    return hotelManager;
+  }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/remote/HotelManager.java b/java/services/auth/src/main/java/com/uva/authentication/models/remote/HotelManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..dd58a508436be9e9e51a99d26038cf6efec373a6
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/remote/HotelManager.java
@@ -0,0 +1,32 @@
+package com.uva.authentication.models.remote;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
+
+@Entity
+@Table(name = "hotel_manager_user")
+public class HotelManager extends User {
+
+  @OneToMany(mappedBy = "hotelManager", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+  private List<Hotel> hotels = new ArrayList<>();
+
+  public HotelManager() {
+    super();
+  }
+
+  public HotelManager(int id, String name, String email, String password, List<Hotel> hotels) {
+    super(id, name, email, password, UserRol.HOTEL_ADMIN);
+    setHotels(hotels);
+  }
+
+  public void setHotels(List<Hotel> hotels) {
+    this.hotels = hotels;
+  }
+
+}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/User.java b/java/services/auth/src/main/java/com/uva/authentication/models/remote/User.java
similarity index 57%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Models/User.java
rename to java/services/auth/src/main/java/com/uva/authentication/models/remote/User.java
index c5373df6edc5e936d9c94f794ae36d3289d173d5..a72ecfa8ef8f896afda01b7b1ead124c09b81fab 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/User.java
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/remote/User.java
@@ -1,51 +1,56 @@
-package com.uva.roomBooking.Models;
-
-import java.util.List;
+package com.uva.authentication.models.remote;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
 import jakarta.persistence.Basic;
-import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
 import jakarta.persistence.EnumType;
 import jakarta.persistence.Enumerated;
-import jakarta.persistence.FetchType;
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.OneToMany;
+import jakarta.persistence.Inheritance;
+import jakarta.persistence.InheritanceType;
 import jakarta.persistence.Table;
 
 @Entity
+@Inheritance(strategy = InheritanceType.JOINED)
 @Table(name = "users")
 public class User {
+
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Basic(optional = false)
+  @Column(nullable = false)
   private int id;
 
   @Basic(optional = false)
+  @Column(nullable = false)
   private String name;
 
   @Basic(optional = false)
+  @Column(nullable = false, unique = true)
   private String email;
 
+  @JsonIgnore
   @Basic(optional = false)
-  @Enumerated(EnumType.STRING)
-  private UserStatus status = UserStatus.NO_BOOKINGS;
+  @Column(nullable = false)
+  private String password;
 
-  @JsonIgnore
-  @OneToMany(mappedBy = "userId", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
-  private List<Booking> bookings;
+  @Basic(optional = false)
+  @Column(nullable = false)
+  @Enumerated(EnumType.STRING)
+  private UserRol rol = UserRol.CLIENT;
 
   public User() {
   }
 
-  public User(int id, String name, String email, UserStatus status, List<Booking> bookings) {
+  public User(int id, String name, String email, String password, UserRol rol) {
     setId(id);
+    setName(name);
     setEmail(email);
-    setStatus(status);
-    setBookings(bookings);
+    setRol(rol);
   }
 
   public int getId() {
@@ -72,19 +77,19 @@ public class User {
     this.email = email;
   }
 
-  public UserStatus getStatus() {
-    return this.status;
+  public String getPassword() {
+    return password;
   }
 
-  public void setStatus(UserStatus status) {
-    this.status = status;
+  public void setPassword(String rawPassword) {
+    this.password = rawPassword;
   }
 
-  public List<Booking> getBookings() {
-    return this.bookings;
+  public UserRol getRol() {
+    return this.rol;
   }
 
-  public void setBookings(List<Booking> bookings) {
-    this.bookings = bookings;
+  public void setRol(UserRol rol) {
+    this.rol = rol;
   }
 }
diff --git a/java/services/auth/src/main/java/com/uva/authentication/models/remote/UserRol.java b/java/services/auth/src/main/java/com/uva/authentication/models/remote/UserRol.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe4d90dd1dd595f4c09ec699b452910352b406d5
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/remote/UserRol.java
@@ -0,0 +1,5 @@
+package com.uva.authentication.models.remote;
+
+public enum UserRol {
+  ADMIN, HOTEL_ADMIN, CLIENT
+}
diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/UserStatus.java b/java/services/auth/src/main/java/com/uva/authentication/models/remote/UserStatus.java
similarity index 65%
rename from java/roomBooking/src/main/java/com/uva/roomBooking/Models/UserStatus.java
rename to java/services/auth/src/main/java/com/uva/authentication/models/remote/UserStatus.java
index 41adce4ba6df6332286f0971b7f8e43b9126e401..5dd62bc5c75271eafa9daa58ad332aa8ad56d413 100644
--- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/UserStatus.java
+++ b/java/services/auth/src/main/java/com/uva/authentication/models/remote/UserStatus.java
@@ -1,4 +1,4 @@
-package com.uva.roomBooking.Models;
+package com.uva.authentication.models.remote;
 
 public enum UserStatus {
   NO_BOOKINGS, WITH_ACTIVE_BOOKINGS, WITH_INACTIVE_BOOKINGS;
diff --git a/java/services/auth/src/main/java/com/uva/authentication/repositories/ClientRepository.java b/java/services/auth/src/main/java/com/uva/authentication/repositories/ClientRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d2b8eeb777f2d3c9800c51dc34fe72ea504efd3
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/repositories/ClientRepository.java
@@ -0,0 +1,10 @@
+package com.uva.authentication.repositories;
+
+import java.util.Optional;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.uva.authentication.models.remote.Client;
+
+public interface ClientRepository extends JpaRepository<Client, Integer> {
+  Optional<Client> findByEmail(String email);
+}
\ No newline at end of file
diff --git a/java/services/auth/src/main/java/com/uva/authentication/repositories/HotelManagerRepository.java b/java/services/auth/src/main/java/com/uva/authentication/repositories/HotelManagerRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..c051ddd0425ff264337261645dbe6f17cfa55b51
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/repositories/HotelManagerRepository.java
@@ -0,0 +1,10 @@
+package com.uva.authentication.repositories;
+
+import java.util.Optional;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.uva.authentication.models.remote.HotelManager;
+
+public interface HotelManagerRepository extends JpaRepository<HotelManager, Integer> {
+  Optional<HotelManager> findByEmail(String email);
+}
\ No newline at end of file
diff --git a/java/services/auth/src/main/java/com/uva/authentication/repositories/UserRepository.java b/java/services/auth/src/main/java/com/uva/authentication/repositories/UserRepository.java
new file mode 100644
index 0000000000000000000000000000000000000000..65f98edd7f2d0700de933199ae54c484eb8c71a3
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/repositories/UserRepository.java
@@ -0,0 +1,10 @@
+package com.uva.authentication.repositories;
+
+import java.util.Optional;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import com.uva.authentication.models.remote.User;
+
+public interface UserRepository extends JpaRepository<User, Integer> {
+  Optional<User> findByEmail(String email);
+}
\ No newline at end of file
diff --git a/java/services/auth/src/main/java/com/uva/authentication/services/AuthService.java b/java/services/auth/src/main/java/com/uva/authentication/services/AuthService.java
new file mode 100644
index 0000000000000000000000000000000000000000..3ed67ececda2b0632e00f1b31ff04f3c05df866e
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/services/AuthService.java
@@ -0,0 +1,98 @@
+package com.uva.authentication.services;
+
+import java.util.Optional;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+import org.springframework.web.client.HttpClientErrorException;
+
+import com.uva.authentication.models.LoginRequest;
+import com.uva.authentication.models.RegisterRequest;
+import com.uva.authentication.models.remote.Client;
+import com.uva.authentication.models.remote.HotelManager;
+import com.uva.authentication.models.remote.User;
+import com.uva.authentication.models.remote.UserRol;
+import com.uva.authentication.repositories.ClientRepository;
+import com.uva.authentication.repositories.HotelManagerRepository;
+import com.uva.authentication.repositories.UserRepository;
+import com.uva.authentication.utils.JwtUtil;
+import com.uva.authentication.utils.SecurityUtils;
+
+@Service
+public class AuthService {
+
+  @Autowired
+  private JwtUtil jwtUtil;
+
+  @Autowired
+  private HotelManagerRepository hotelManagerRepository;
+
+  @Autowired
+  private ClientRepository clientRepository;
+
+  @Autowired
+  private UserRepository userRepository;
+
+  private boolean authenticateUser(LoginRequest request, User user) {
+    if (user == null)
+      return false;
+    return SecurityUtils.checkPassword(request.getPassword(), user.getPassword());
+  }
+
+  public String login(LoginRequest loginRequest) {
+    User user = userRepository.findByEmail(loginRequest.getEmail())
+        .orElseThrow(() -> new HttpClientErrorException(HttpStatus.FORBIDDEN,
+            "Invalid credentials"));
+
+    if (!authenticateUser(loginRequest, user)) {
+      throw new HttpClientErrorException(HttpStatus.FORBIDDEN, "Invalid credentials");
+    }
+
+    return jwtUtil.generateToken(user);
+  }
+
+  public User register(RegisterRequest registerRequest) {
+    Optional<User> user = userRepository.findByEmail(registerRequest.getEmail());
+    if (user.isPresent())
+      throw new HttpClientErrorException(HttpStatus.CONFLICT, "Email already in use");
+
+    return registerNewUser(registerRequest);
+  }
+
+  private User registerNewUser(RegisterRequest registerRequest) {
+    User newUser;
+
+    // Ciframos la contraseña
+    String hashPass = SecurityUtils.encrypt(registerRequest.getPassword());
+    registerRequest.setPassword(hashPass);
+
+    // Aseguramos que tenga un rol, por defecto es cliente
+    if (registerRequest.getRol() == null)
+      registerRequest.setRol(UserRol.CLIENT);
+
+    switch (registerRequest.getRol()) {
+      case HOTEL_ADMIN:
+        HotelManager hm = new HotelManager();
+        BeanUtils.copyProperties(registerRequest, hm);
+        newUser = hotelManagerRepository.save(hm);
+        break;
+
+      case ADMIN:
+        User admin = new User();
+        BeanUtils.copyProperties(registerRequest, admin);
+        newUser = userRepository.save(admin);
+        break;
+
+      case CLIENT: // Por defecto cliente normal
+      default:
+        Client client = new Client();
+        BeanUtils.copyProperties(registerRequest, client);
+        client.setRol(UserRol.CLIENT);
+        newUser = clientRepository.save(client);
+        break;
+    }
+    return newUser;
+  }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/utils/JwtUtil.java b/java/services/auth/src/main/java/com/uva/authentication/utils/JwtUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..e21efcdbabe51754bc2a2bf705eba0361f0696d6
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/utils/JwtUtil.java
@@ -0,0 +1,85 @@
+package com.uva.authentication.utils;
+
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import io.jsonwebtoken.io.Decoders;
+import io.jsonwebtoken.security.Keys;
+
+import java.security.Key;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import com.uva.authentication.models.remote.User;
+
+@Component
+public class JwtUtil {
+
+  @Value("${security.jwt.secret-key}")
+  private String secretKey;
+
+  @Value("${security.jwt.expiration-time}")
+  private long jwtExpiration;
+
+  public String extractUsername(String token) {
+    return extractClaim(token, Claims::getSubject);
+  }
+
+  public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
+    final Claims claims = extractAllClaims(token);
+    return claimsResolver.apply(claims);
+  }
+
+  public long getExpirationTime() {
+    return jwtExpiration;
+  }
+
+  public String generateToken(User user) {
+    Map<String, Object> extraClaims = new HashMap<>();
+    extraClaims.put("email", user.getEmail());
+    extraClaims.put("rol", user.getRol());
+    extraClaims.put("user", user);
+    long expiration = jwtExpiration;
+
+    return Jwts
+        .builder()
+        .setClaims(extraClaims)
+        .setSubject(String.valueOf(user.getId()))
+        .setIssuedAt(new Date(System.currentTimeMillis()))
+        .setExpiration(new Date(System.currentTimeMillis() + expiration))
+        .signWith(getSignInKey(), SignatureAlgorithm.HS256)
+        .compact();
+  }
+
+  public boolean isTokenValid(String token, User user) {
+    final String username = extractUsername(token);
+    return (username.equals(user.getName())) && !isTokenExpired(token);
+  }
+
+  private boolean isTokenExpired(String token) {
+    return extractExpiration(token).before(new Date());
+  }
+
+  private Date extractExpiration(String token) {
+    return extractClaim(token, Claims::getExpiration);
+  }
+
+  private Claims extractAllClaims(String token) {
+    return Jwts
+        .parserBuilder()
+        .setSigningKey(getSignInKey())
+        .build()
+        .parseClaimsJws(token)
+        .getBody();
+  }
+
+  private Key getSignInKey() {
+    byte[] keyBytes = Decoders.BASE64.decode(secretKey);
+    return Keys.hmacShaKeyFor(keyBytes);
+  }
+}
diff --git a/java/services/auth/src/main/java/com/uva/authentication/utils/SecurityUtils.java b/java/services/auth/src/main/java/com/uva/authentication/utils/SecurityUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..2df069eabbe53bdeaccbdbd58094bf8022ff35c7
--- /dev/null
+++ b/java/services/auth/src/main/java/com/uva/authentication/utils/SecurityUtils.java
@@ -0,0 +1,18 @@
+package com.uva.authentication.utils;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+public class SecurityUtils {
+
+  private static BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+
+  public static String encrypt(String value) {
+    return encoder.encode(value);
+  }
+
+  // Método para comparar la contraseña ingresada con el hash almacenado
+  public static boolean checkPassword(String rawPassword, String encodedPassword) {
+    return encoder.matches(rawPassword, encodedPassword); // Comparar la contraseña con el hash
+  }
+  
+}
diff --git a/java/services/auth/src/main/resources/application.properties b/java/services/auth/src/main/resources/application.properties
new file mode 100644
index 0000000000000000000000000000000000000000..f08d9f6bd82253e53914fa3c4b695d4c25ba6f24
--- /dev/null
+++ b/java/services/auth/src/main/resources/application.properties
@@ -0,0 +1,16 @@
+spring.application.name=authService
+server.port=8101
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
+spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/RoomsBooking?createDatabaseIfNotExist=true
+spring.datasource.username=user
+spring.datasource.password=password
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+# Usar esto para alternar entre las exposición del room repository ya que no es necesario su uso pero por defecto, al no cubrir su ruta, se expone
+# spring.data.rest.base-path=false
+security.jwt.secret-key=3cfa76ef14937c1c0ea519f8fc057a80fcd04a7420f8e8bcd0a7567c272e007b
+# 1h in millisecond
+security.jwt.expiration-time=3600000
+
+external.services.users.baseurl=http://localhost:8080/users
\ No newline at end of file
diff --git a/java/services/auth/src/test/java/com/uva/roomBooking/RoomBookingApplicationTests.java b/java/services/auth/src/test/java/com/uva/roomBooking/RoomBookingApplicationTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b50599492c36017391c0dcabd81573f123a809a
--- /dev/null
+++ b/java/services/auth/src/test/java/com/uva/roomBooking/RoomBookingApplicationTests.java
@@ -0,0 +1,13 @@
+package com.uva.roomBooking;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class RoomBookingApplicationTests {
+
+	@Test
+	void contextLoads() {
+	}
+
+}