diff --git a/RR3CommunityServer/Pages/Admin.cshtml.cs b/RR3CommunityServer/Pages/Admin.cshtml.cs
index 64332e9..ff9a69a 100644
--- a/RR3CommunityServer/Pages/Admin.cshtml.cs
+++ b/RR3CommunityServer/Pages/Admin.cshtml.cs
@@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using static RR3CommunityServer.Data.RR3DbContext;
namespace RR3CommunityServer.Pages;
+[Authorize]
public class AdminModel : PageModel
{
private readonly RR3DbContext _context;
diff --git a/RR3CommunityServer/Pages/Catalog.cshtml.cs b/RR3CommunityServer/Pages/Catalog.cshtml.cs
index 8a092a3..6d1faba 100644
--- a/RR3CommunityServer/Pages/Catalog.cshtml.cs
+++ b/RR3CommunityServer/Pages/Catalog.cshtml.cs
@@ -1,11 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using static RR3CommunityServer.Data.RR3DbContext;
namespace RR3CommunityServer.Pages;
+[Authorize]
public class CatalogModel : PageModel
{
private readonly RR3DbContext _context;
diff --git a/RR3CommunityServer/Pages/DeviceSettings.cshtml.cs b/RR3CommunityServer/Pages/DeviceSettings.cshtml.cs
index d53d7a3..4d7b02b 100644
--- a/RR3CommunityServer/Pages/DeviceSettings.cshtml.cs
+++ b/RR3CommunityServer/Pages/DeviceSettings.cshtml.cs
@@ -1,11 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using RR3CommunityServer.Models;
namespace RR3CommunityServer.Pages;
+[Authorize]
public class DeviceSettingsModel : PageModel
{
private readonly RR3DbContext _context;
diff --git a/RR3CommunityServer/Pages/Login.cshtml b/RR3CommunityServer/Pages/Login.cshtml
new file mode 100644
index 0000000..9a411ca
--- /dev/null
+++ b/RR3CommunityServer/Pages/Login.cshtml
@@ -0,0 +1,163 @@
+@page
+@model RR3CommunityServer.Pages.LoginModel
+@{
+ ViewData["Title"] = "Login";
+ Layout = null;
+}
+
+
+
+
+
+
+ Login - RR3 Community Server
+
+
+
+
+
+
🏎️ RR3 Community Server
+
Admin Panel Login
+
+
+ @if (!string.IsNullOrEmpty(Model.ErrorMessage))
+ {
+
+ @Model.ErrorMessage
+
+ }
+
+
+
+
+
+
+
diff --git a/RR3CommunityServer/Pages/Login.cshtml.cs b/RR3CommunityServer/Pages/Login.cshtml.cs
new file mode 100644
index 0000000..a0b2759
--- /dev/null
+++ b/RR3CommunityServer/Pages/Login.cshtml.cs
@@ -0,0 +1,86 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using System.Security.Claims;
+using RR3CommunityServer.Services;
+using RR3CommunityServer.Models;
+
+namespace RR3CommunityServer.Pages;
+
+public class LoginModel : PageModel
+{
+ private readonly IAuthService _authService;
+ private readonly ILogger _logger;
+
+ public LoginModel(IAuthService authService, ILogger logger)
+ {
+ _authService = authService;
+ _logger = logger;
+ }
+
+ [BindProperty]
+ public string Username { get; set; } = string.Empty;
+
+ [BindProperty]
+ public string Password { get; set; } = string.Empty;
+
+ public string? ErrorMessage { get; set; }
+
+ public void OnGet()
+ {
+ // If already logged in, redirect to admin panel
+ if (User.Identity?.IsAuthenticated == true)
+ {
+ Response.Redirect("/admin");
+ }
+ }
+
+ public async Task OnPostAsync()
+ {
+ if (string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Password))
+ {
+ ErrorMessage = "Username and password are required";
+ return Page();
+ }
+
+ var loginRequest = new LoginRequest
+ {
+ UsernameOrEmail = Username,
+ Password = Password
+ };
+
+ var (success, response, error) = await _authService.LoginAsync(loginRequest);
+
+ if (!success || response == null)
+ {
+ ErrorMessage = error ?? "Invalid username or password";
+ _logger.LogWarning("Failed login attempt for: {Username}", Username);
+ return Page();
+ }
+
+ // Create authentication cookie
+ var claims = new List
+ {
+ new Claim(ClaimTypes.NameIdentifier, response.AccountId.ToString()),
+ new Claim(ClaimTypes.Name, response.Username),
+ new Claim(ClaimTypes.Email, response.Email)
+ };
+
+ var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
+ var authProperties = new AuthenticationProperties
+ {
+ IsPersistent = true, // Remember me
+ ExpiresUtc = response.ExpiresAt
+ };
+
+ await HttpContext.SignInAsync(
+ CookieAuthenticationDefaults.AuthenticationScheme,
+ new ClaimsPrincipal(claimsIdentity),
+ authProperties);
+
+ _logger.LogInformation("User logged in to admin panel: {Username}", response.Username);
+
+ return RedirectToPage("/Admin");
+ }
+}
diff --git a/RR3CommunityServer/Pages/Logout.cshtml.cs b/RR3CommunityServer/Pages/Logout.cshtml.cs
new file mode 100644
index 0000000..d90d1c3
--- /dev/null
+++ b/RR3CommunityServer/Pages/Logout.cshtml.cs
@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+
+namespace RR3CommunityServer.Pages;
+
+public class LogoutModel : PageModel
+{
+ private readonly ILogger _logger;
+
+ public LogoutModel(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public async Task OnGetAsync()
+ {
+ var username = User.Identity?.Name ?? "Unknown";
+
+ await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
+
+ _logger.LogInformation("User logged out: {Username}", username);
+
+ return RedirectToPage("/Login");
+ }
+}
diff --git a/RR3CommunityServer/Pages/Purchases.cshtml.cs b/RR3CommunityServer/Pages/Purchases.cshtml.cs
index 522f70b..7201e93 100644
--- a/RR3CommunityServer/Pages/Purchases.cshtml.cs
+++ b/RR3CommunityServer/Pages/Purchases.cshtml.cs
@@ -1,11 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using static RR3CommunityServer.Data.RR3DbContext;
namespace RR3CommunityServer.Pages;
+[Authorize]
public class PurchasesModel : PageModel
{
private readonly RR3DbContext _context;
diff --git a/RR3CommunityServer/Pages/Register.cshtml b/RR3CommunityServer/Pages/Register.cshtml
new file mode 100644
index 0000000..cb808d6
--- /dev/null
+++ b/RR3CommunityServer/Pages/Register.cshtml
@@ -0,0 +1,208 @@
+@page
+@model RR3CommunityServer.Pages.RegisterModel
+@{
+ ViewData["Title"] = "Register";
+ Layout = null;
+}
+
+
+
+
+
+
+ Register - RR3 Community Server
+
+
+
+
+
+
🏎️ RR3 Community Server
+
Create Account
+
+
+
+ Starting Resources:
+ • 100,000 Gold
+ • 500,000 Cash
+ • Access to admin panel
+
+
+ @if (!string.IsNullOrEmpty(Model.ErrorMessage))
+ {
+
+ @Model.ErrorMessage
+
+ }
+
+ @if (!string.IsNullOrEmpty(Model.SuccessMessage))
+ {
+
+ @Model.SuccessMessage
+
+ }
+
+
+
+
+
+
+
diff --git a/RR3CommunityServer/Pages/Register.cshtml.cs b/RR3CommunityServer/Pages/Register.cshtml.cs
new file mode 100644
index 0000000..48fac2e
--- /dev/null
+++ b/RR3CommunityServer/Pages/Register.cshtml.cs
@@ -0,0 +1,110 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using System.Security.Claims;
+using RR3CommunityServer.Services;
+using RR3CommunityServer.Models;
+
+namespace RR3CommunityServer.Pages;
+
+public class RegisterModel : PageModel
+{
+ private readonly IAuthService _authService;
+ private readonly ILogger _logger;
+
+ public RegisterModel(IAuthService authService, ILogger logger)
+ {
+ _authService = authService;
+ _logger = logger;
+ }
+
+ [BindProperty]
+ public string Username { get; set; } = string.Empty;
+
+ [BindProperty]
+ public string Email { get; set; } = string.Empty;
+
+ [BindProperty]
+ public string Password { get; set; } = string.Empty;
+
+ [BindProperty]
+ public string ConfirmPassword { get; set; } = string.Empty;
+
+ public string? ErrorMessage { get; set; }
+ public string? SuccessMessage { get; set; }
+
+ public void OnGet()
+ {
+ // If already logged in, redirect to admin panel
+ if (User.Identity?.IsAuthenticated == true)
+ {
+ Response.Redirect("/admin");
+ }
+ }
+
+ public async Task OnPostAsync()
+ {
+ if (string.IsNullOrWhiteSpace(Username) || string.IsNullOrWhiteSpace(Email) ||
+ string.IsNullOrWhiteSpace(Password) || string.IsNullOrWhiteSpace(ConfirmPassword))
+ {
+ ErrorMessage = "All fields are required";
+ return Page();
+ }
+
+ var registerRequest = new RegisterRequest
+ {
+ Username = Username,
+ Email = Email,
+ Password = Password,
+ ConfirmPassword = ConfirmPassword
+ };
+
+ var (success, token, error) = await _authService.RegisterAsync(registerRequest);
+
+ if (!success || string.IsNullOrEmpty(token))
+ {
+ ErrorMessage = error ?? "Registration failed";
+ _logger.LogWarning("Failed registration attempt for: {Username}", Username);
+ return Page();
+ }
+
+ _logger.LogInformation("New account registered: {Username} ({Email})", Username, Email);
+
+ // Auto-login after registration
+ var loginRequest = new LoginRequest
+ {
+ UsernameOrEmail = Username,
+ Password = Password
+ };
+
+ var (loginSuccess, response, loginError) = await _authService.LoginAsync(loginRequest);
+
+ if (loginSuccess && response != null)
+ {
+ var claims = new List
+ {
+ new Claim(ClaimTypes.NameIdentifier, response.AccountId.ToString()),
+ new Claim(ClaimTypes.Name, response.Username),
+ new Claim(ClaimTypes.Email, response.Email)
+ };
+
+ var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
+ var authProperties = new AuthenticationProperties
+ {
+ IsPersistent = true,
+ ExpiresUtc = response.ExpiresAt
+ };
+
+ await HttpContext.SignInAsync(
+ CookieAuthenticationDefaults.AuthenticationScheme,
+ new ClaimsPrincipal(claimsIdentity),
+ authProperties);
+
+ return RedirectToPage("/Admin");
+ }
+
+ SuccessMessage = "Account created successfully! Please login.";
+ return RedirectToPage("/Login");
+ }
+}
diff --git a/RR3CommunityServer/Pages/Rewards.cshtml.cs b/RR3CommunityServer/Pages/Rewards.cshtml.cs
index 39896f3..49b891e 100644
--- a/RR3CommunityServer/Pages/Rewards.cshtml.cs
+++ b/RR3CommunityServer/Pages/Rewards.cshtml.cs
@@ -1,11 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using static RR3CommunityServer.Data.RR3DbContext;
namespace RR3CommunityServer.Pages;
+[Authorize]
public class RewardsModel : PageModel
{
private readonly RR3DbContext _context;
diff --git a/RR3CommunityServer/Pages/Sessions.cshtml.cs b/RR3CommunityServer/Pages/Sessions.cshtml.cs
index 58c314f..5bbd608 100644
--- a/RR3CommunityServer/Pages/Sessions.cshtml.cs
+++ b/RR3CommunityServer/Pages/Sessions.cshtml.cs
@@ -1,11 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using static RR3CommunityServer.Data.RR3DbContext;
namespace RR3CommunityServer.Pages;
+[Authorize]
public class SessionsModel : PageModel
{
private readonly RR3DbContext _context;
diff --git a/RR3CommunityServer/Pages/Settings.cshtml.cs b/RR3CommunityServer/Pages/Settings.cshtml.cs
index 2cb1e6e..1c772c1 100644
--- a/RR3CommunityServer/Pages/Settings.cshtml.cs
+++ b/RR3CommunityServer/Pages/Settings.cshtml.cs
@@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
namespace RR3CommunityServer.Pages;
+[Authorize]
public class SettingsModel : PageModel
{
private readonly RR3DbContext _context;
diff --git a/RR3CommunityServer/Pages/Users.cshtml.cs b/RR3CommunityServer/Pages/Users.cshtml.cs
index f5947e7..6614181 100644
--- a/RR3CommunityServer/Pages/Users.cshtml.cs
+++ b/RR3CommunityServer/Pages/Users.cshtml.cs
@@ -1,11 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using RR3CommunityServer.Data;
using static RR3CommunityServer.Data.RR3DbContext;
namespace RR3CommunityServer.Pages;
+[Authorize]
public class UsersModel : PageModel
{
private readonly RR3DbContext _context;
diff --git a/RR3CommunityServer/Pages/_Layout.cshtml b/RR3CommunityServer/Pages/_Layout.cshtml
index e2368ea..a2a4e6d 100644
--- a/RR3CommunityServer/Pages/_Layout.cshtml
+++ b/RR3CommunityServer/Pages/_Layout.cshtml
@@ -108,6 +108,14 @@
API
+
+
+ @User.Identity?.Name
+
+
+
diff --git a/RR3CommunityServer/Program.cs b/RR3CommunityServer/Program.cs
index e72d0ac..7f3f50b 100644
--- a/RR3CommunityServer/Program.cs
+++ b/RR3CommunityServer/Program.cs
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
+using Microsoft.AspNetCore.Authentication.Cookies;
using RR3CommunityServer.Data;
using RR3CommunityServer.Services;
using RR3CommunityServer.Middleware;
@@ -8,6 +9,20 @@ var builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllers();
builder.Services.AddRazorPages(); // Add Razor Pages support
+
+// Add cookie authentication
+builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
+ .AddCookie(options =>
+ {
+ options.LoginPath = "/Login";
+ options.LogoutPath = "/Logout";
+ options.AccessDeniedPath = "/Login";
+ options.ExpireTimeSpan = TimeSpan.FromDays(30);
+ options.SlidingExpiration = true;
+ });
+
+builder.Services.AddAuthorization();
+
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
@@ -53,16 +68,19 @@ using (var scope = app.Services.CreateScope())
app.UseHttpsRedirection();
app.UseCors();
+// Authentication & Authorization
+app.UseAuthentication();
+app.UseAuthorization();
+
// Custom middleware
app.UseMiddleware();
app.UseMiddleware();
-app.UseAuthorization();
app.MapControllers();
app.MapRazorPages(); // Add Razor Pages routing
-// Redirect root to admin panel
-app.MapGet("/", () => Results.Redirect("/admin"));
+// Redirect root to login page
+app.MapGet("/", () => Results.Redirect("/Login"));
Console.WriteLine("╔══════════════════════════════════════════════════════════╗");
Console.WriteLine("║ Real Racing 3 Community Server - RUNNING ║");