Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.App/Project.Api/.env.development
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PORT=5131
ASPNETCORE_ENVIRONMENT=Production
7 changes: 6 additions & 1 deletion Project.App/Project.Api/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ public IActionResult Login([FromQuery] string? returnUrl = null)
Console.WriteLine($"[AUTH] Login called with returnUrl: {returnUrl}");

// Allow frontend URLs (localhost:3000 or your production frontend)
var allowedOrigins = new[] { "http://localhost:3000", "https://localhost:3000" };
var allowedOrigins = new[]
{
"http://localhost:3000",
"https://localhost:3000",
"https://dannyscasino-abhqdhcwabdaccfg.westus3-01.azurewebsites.net/", // production frontend
};
var safe = "/swagger"; // default fallback

if (!string.IsNullOrEmpty(returnUrl))
Expand Down
2 changes: 2 additions & 0 deletions Project.App/Project.Api/Controllers/HandController.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Project.Api.DTOs;
using Project.Api.Models;
Expand All @@ -8,6 +9,7 @@ namespace Project.Api.Controllers
{
[ApiController]
[Route("api/rooms/{roomId}/hands")]
[Authorize] // Require authentication for all hand endpoints
public class HandController : ControllerBase
{
private readonly ILogger<HandController> _logger;
Expand Down
25 changes: 25 additions & 0 deletions Project.App/Project.Api/Controllers/RoomController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.AspNetCore.Mvc;
using Project.Api.DTOs;
using Project.Api.Models.Games;
using Project.Api.Repositories.Interface;
using Project.Api.Services.Interface;
using Project.Api.Utilities;
using Project.Api.Utilities.Enums;
Expand All @@ -11,6 +12,7 @@ namespace Project.Api.Controllers;

[ApiController]
[Route("api/[controller]")]
[Authorize] // Require authentication for all room endpoints
public class RoomController(
IRoomService roomService,
IRoomSSEService roomSSEService,
Expand Down Expand Up @@ -207,6 +209,28 @@ [FromBody] LeaveRoomRequest request
return Ok(room);
}

// GET: api/room/{roomId}/players
[HttpGet("{roomId}/players")]
public async Task<ActionResult> GetRoomPlayers(
Guid roomId,
[FromServices] IRoomPlayerRepository roomPlayerRepository
)
{
var players = await roomPlayerRepository.GetByRoomIdAsync(roomId);
var playerDtos = players.Select(p => new
{
id = p.Id,
userId = p.UserId,
userName = p.User?.Name ?? "Unknown",
userEmail = p.User?.Email ?? "",
role = p.Role.ToString(),
status = p.Status.ToString(),
balance = p.Balance,
balanceDelta = p.BalanceDelta,
});
return Ok(playerDtos);
}

#region SSE

private readonly IRoomSSEService _roomSSEService = roomSSEService;
Expand All @@ -216,6 +240,7 @@ [FromBody] LeaveRoomRequest request
/// </summary>
/// <param name="roomId"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("{roomId}/events")]
public async Task GetRoomEvents(Guid roomId)
{
Expand Down
70 changes: 35 additions & 35 deletions Project.App/Project.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.EntityFrameworkCore;
using Project.Api.Data;
using Project.Api.Models.Games;
Expand All @@ -22,7 +23,6 @@ public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

// use adminsetting.json as configuration
builder.Configuration.AddJsonFile(
"adminsetting.json",
optional: true,
Expand Down Expand Up @@ -52,14 +52,39 @@ public static void Main(string[] args)
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// if (File.Exists(".env.development"))
// {
// DotNetEnv.Env.Load(".env.development");
// }

var app = builder.Build();

if (!builder.Environment.IsEnvironment("Testing"))
app.ApplyMigrations(); // there's no database in testing

var fwd = new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor,
};
fwd.KnownNetworks.Clear();
fwd.KnownProxies.Clear();
app.UseForwardedHeaders(fwd);

app.UseMiddleware<GlobalExceptionHandler>();

// connection string print
// var port = Environment.GetEnvironmentVariable("PORT") ?? "80";
// var httpsPort = 7069;

// if (!File.Exists(".env.development"))
// {
// app.Urls.Add($"http://*:{port}");
// }
// else
// {
// app.Urls.Add($"http://localhost:{port}");
// app.Urls.Add($"https://localhost:{httpsPort}");
// }

app.MapGet(
"/string",
() =>
Expand All @@ -69,15 +94,14 @@ public static void Main(string[] args)
}
);

// is development envrionment
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseCors(ProgramExtensions.CorsPolicy); // Enable CORS with our policy
app.UseCors(ProgramExtensions.CorsPolicy);
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
Expand All @@ -91,9 +115,6 @@ public static class ProgramExtensions
{
public const string CorsPolicy = "FrontendCors";

/// <summary>
/// Applies the configuration for the CORS policy.
/// </summary>
public static IServiceCollection AddCorsPolicy(this IServiceCollection services)
{
services.AddCors(options =>
Expand All @@ -103,19 +124,20 @@ public static IServiceCollection AddCorsPolicy(this IServiceCollection services)
policy =>
{
policy
.WithOrigins("http://localhost:3000", "https://localhost:3000") // Next.js frontend
.WithOrigins(
"http://localhost:3000",
"https://localhost:3000",
"https://dannyscasino-abhqdhcwabdaccfg.westus3-01.azurewebsites.net"
)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials(); // Required for cookies
.AllowCredentials();
}
);
});
return services;
}

/// <summary>
/// Registers the database.
/// </summary>
public static IServiceCollection AddDatabase(
this IServiceCollection services,
IConfiguration configuration
Expand All @@ -127,9 +149,6 @@ IConfiguration configuration
return services;
}

/// <summary>
/// Registers the services and repositories used by the application.
/// </summary>
public static IServiceCollection AddApplicationServices(this IServiceCollection services)
{
// scoped services
Expand All @@ -138,32 +157,25 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
services.AddScoped<IRoomService, RoomService>();
services.AddScoped<IUserService, UserService>();

// singleton services
services.AddHttpClient<IDeckApiService, DeckApiService>();
services.AddSingleton<IRoomSSEService, RoomSSEService>();

// scoped repositories
services.AddScoped<IHandRepository, HandRepository>();
services.AddScoped<IRoomPlayerRepository, RoomPlayerRepository>();
services.AddScoped<IRoomRepository, RoomRepository>();
services.AddScoped<IUserRepository, UserRepository>();

// automapper!!!
services.AddAutoMapper(typeof(Program));

return services;
}

/// <summary>
/// Configures and registers the authentication services.
/// </summary>
public static IServiceCollection AddAuth(
this IServiceCollection services,
IConfiguration configuration,
IWebHostEnvironment environment
)
{
// sanity check for auth errors logging
if (!environment.IsEnvironment("Testing"))
{
var gid = configuration["Google:ClientId"];
Expand All @@ -180,24 +192,16 @@ IWebHostEnvironment environment
);
}

// Google OAuth
services
.AddAuthentication(options =>
{
// where the app reads identity from on each request
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// how the app prompts an unauthenticated user to log in
// you can set DefaultChallengeScheme to Cookies instead but it didnt work before
options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
.AddCookie(cookie =>
{
// cross-site cookie SPA on :3000 to API on :7069
cookie.Cookie.SameSite = SameSiteMode.None;
// browsers require Secure when SameSite=None
cookie.Cookie.SecurePolicy = CookieSecurePolicy.Always;

// for APIs return status codes instead of 302 redirects
cookie.Events = new CookieAuthenticationEvents
{
OnRedirectToLogin = ctx =>
Expand All @@ -219,16 +223,12 @@ IWebHostEnvironment environment
options.CallbackPath = "/auth/google/callback";
options.Scope.Add("email");
options.Scope.Add("profile");

// ensure we actually fetch profile claims from Google UserInfo endpoint
options.SaveTokens = true;
options.UserInformationEndpoint = "https://www.googleapis.com/oauth2/v3/userinfo";
options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
options.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
options.ClaimActions.MapJsonKey("picture", "picture");
options.ClaimActions.MapJsonKey("email_verified", "email_verified");

// Upsert user into DB when Google signs in successfully
options.Events = new OAuthEvents
{
OnCreatingTicket = async ctx =>
Expand All @@ -239,7 +239,7 @@ IWebHostEnvironment environment

// fetch the full user JSON from Google's userinfo endpoint
var request = new HttpRequestMessage(
System.Net.Http.HttpMethod.Get,
HttpMethod.Get,
ctx.Options.UserInformationEndpoint
);
request.Headers.Authorization =
Expand Down
1 change: 1 addition & 0 deletions Project.App/Project.Api/Project.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="DotNetEnv" Version="3.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="9.0.10" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.8" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
Expand Down
24 changes: 0 additions & 24 deletions Project.App/Project.Api/Project.Api.sln

This file was deleted.

Loading