Skip to content

Commit c8afe98

Browse files
committed
Added Integration Test Project & First Login Tests
1 parent 3307883 commit c8afe98

File tree

12 files changed

+177
-7
lines changed

12 files changed

+177
-7
lines changed

UltimateAuth.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<File Path="Roadmap.md" />
1717
</Folder>
1818
<Folder Name="/Tests/">
19+
<Project Path="tests/CodeBeam.UltimateAuth.Tests.Integration/CodeBeam.UltimateAuth.Tests.Integration.csproj" Id="fe34a0b1-8038-400d-b8ab-02dad7051f2d" />
1920
<Project Path="tests/CodeBeam.UltimateAuth.Tests.Unit/CodeBeam.UltimateAuth.Tests.Unit.csproj" Id="6f4b22da-849a-4a79-b5c5-aee7cb1429a6" />
2021
</Folder>
2122
<Project Path="src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj" Id="a8d758ad-052e-4331-9bf7-280ea9a55981" />
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
namespace CodeBeam.UltimateAuth.Core.Errors;
22

3-
public sealed class UAuthChallengeRequiredException : UAuthException
3+
public sealed class UAuthChallengeRequiredException : UAuthRuntimeException
44
{
5-
public UAuthChallengeRequiredException(string? reason = null)
6-
: base(code: "challenge_required", message: reason ?? "Additional authentication is required.")
5+
public override int StatusCode => 401;
6+
7+
public override string Title => "Reauthentication Required";
8+
9+
public override string TypePrefix => "https://docs.ultimateauth.com/errors/challenge";
10+
11+
public UAuthChallengeRequiredException(string? reason = null)
12+
: base("challenge_required", reason ?? "Additional authentication is required.")
713
{
814
}
915
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace CodeBeam.UltimateAuth.Core.Errors;
2+
3+
public sealed class UAuthAuthenticationException : UAuthRuntimeException
4+
{
5+
public override int StatusCode => 401;
6+
public override string Title => "Unauthorized";
7+
8+
public UAuthAuthenticationException(string code = "authentication_required")
9+
: base(code, code)
10+
{
11+
}
12+
}

src/CodeBeam.UltimateAuth.Server/Extensions/UAuthExceptionHandlingExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,14 @@ private static Task WriteProblemDetails(HttpContext context, UAuthRuntimeExcepti
4646
private static int MapStatusCode(UAuthRuntimeException ex) =>
4747
ex switch
4848
{
49+
UAuthAuthenticationException => StatusCodes.Status401Unauthorized,
50+
UAuthAuthorizationException => StatusCodes.Status403Forbidden,
4951
UAuthConflictException => StatusCodes.Status409Conflict,
5052
UAuthValidationException => StatusCodes.Status400BadRequest,
5153
UAuthUnauthorizedException => StatusCodes.Status401Unauthorized,
5254
UAuthForbiddenException => StatusCodes.Status403Forbidden,
5355
UAuthNotFoundException => StatusCodes.Status404NotFound,
56+
UAuthChallengeRequiredException => StatusCodes.Status401Unauthorized,
5457
_ => StatusCodes.Status400BadRequest
5558
};
5659
}

src/CodeBeam.UltimateAuth.Server/Infrastructure/Orchestrator/UAuthSessionOrchestrator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public async Task<TResult> ExecuteAsync<TResult>(AuthContext authContext, ISessi
2828
switch (decision.Decision)
2929
{
3030
case AuthorizationDecision.Deny:
31-
throw new UAuthAuthorizationException(decision.Reason ?? "authorization_denied");
31+
throw new UAuthAuthenticationException(decision.Reason ?? "authorization_denied");
3232

3333
case AuthorizationDecision.Challenge:
3434
throw new UAuthChallengeRequiredException(decision.Reason);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[assembly: CollectionBehavior(DisableTestParallelization = true)]
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Microsoft.AspNetCore.Hosting;
2+
using Microsoft.AspNetCore.Mvc.Testing;
3+
4+
namespace CodeBeam.UltimateAuth.Tests.Integration;
5+
6+
public class AuthServerFactory : WebApplicationFactory<Program>
7+
{
8+
protected override void ConfigureWebHost(IWebHostBuilder builder)
9+
{
10+
builder.UseEnvironment("Development");
11+
}
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using Microsoft.AspNetCore.Mvc.Testing;
2+
using Microsoft.VisualStudio.TestPlatform.TestHost;
3+
4+
namespace CodeBeam.UltimateAuth.Tests.Integration;
5+
6+
public class AuthServerTests : IClassFixture<WebApplicationFactory<Program>>
7+
{
8+
private readonly WebApplicationFactory<Program> _factory;
9+
10+
public AuthServerTests(WebApplicationFactory<Program> factory)
11+
{
12+
_factory = factory;
13+
}
14+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="coverlet.collector" Version="6.0.4" />
12+
<PackageReference Include="FluentAssertions" Version="8.9.0" />
13+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.5" />
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
15+
<PackageReference Include="xunit" Version="2.9.3" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<ProjectReference Include="..\..\samples\UAuthHub\CodeBeam.UltimateAuth.Sample.UAuthHub\CodeBeam.UltimateAuth.Sample.UAuthHub.csproj" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<Using Include="Xunit" />
25+
</ItemGroup>
26+
27+
</Project>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using FluentAssertions;
2+
using Microsoft.AspNetCore.Mvc.Testing;
3+
using System.Net;
4+
using System.Net.Http.Json;
5+
6+
namespace CodeBeam.UltimateAuth.Tests.Integration;
7+
8+
public class LoginTests : IClassFixture<AuthServerFactory>
9+
{
10+
private readonly HttpClient _client;
11+
12+
public LoginTests(AuthServerFactory factory)
13+
{
14+
_client = factory.CreateClient(new WebApplicationFactoryClientOptions
15+
{
16+
AllowAutoRedirect = false,
17+
HandleCookies = false
18+
});
19+
20+
_client.DefaultRequestHeaders.Add("Origin", "https://localhost:6130");
21+
_client.DefaultRequestHeaders.Add("X-UDID", "test-device-1234567890123456");
22+
}
23+
24+
[Fact]
25+
public async Task Login_Should_Return_Cookie()
26+
{
27+
var response = await _client.PostAsJsonAsync("/auth/login", new
28+
{
29+
identifier = "admin",
30+
secret = "admin"
31+
});
32+
33+
response.StatusCode.Should().Be(HttpStatusCode.Found);
34+
response.Headers.Location.Should().NotBeNull();
35+
response.Headers.TryGetValues("Set-Cookie", out var cookies).Should().BeTrue();
36+
cookies.Should().NotBeNull();
37+
}
38+
39+
[Fact]
40+
public async Task Session_Lifecycle_Should_Work_Correctly()
41+
{
42+
var loginResponse1 = await _client.PostAsJsonAsync("/auth/login", new
43+
{
44+
identifier = "admin",
45+
secret = "admin"
46+
});
47+
48+
loginResponse1.StatusCode.Should().Be(HttpStatusCode.Found);
49+
50+
var cookie1 = loginResponse1.Headers.GetValues("Set-Cookie").FirstOrDefault();
51+
cookie1.Should().NotBeNull();
52+
53+
_client.DefaultRequestHeaders.Add("Cookie", cookie1!);
54+
55+
var logoutResponse = await _client.PostAsync("/auth/logout", null);
56+
logoutResponse.StatusCode.Should().Be(HttpStatusCode.Found);
57+
58+
var logoutAgain = await _client.PostAsync("/auth/logout", null);
59+
logoutAgain.StatusCode.Should().BeOneOf(HttpStatusCode.Unauthorized, HttpStatusCode.Found);
60+
61+
_client.DefaultRequestHeaders.Remove("Cookie");
62+
63+
var loginResponse2 = await _client.PostAsJsonAsync("/auth/login", new
64+
{
65+
identifier = "admin",
66+
secret = "admin"
67+
});
68+
69+
loginResponse2.StatusCode.Should().Be(HttpStatusCode.Found);
70+
var cookie2 = loginResponse2.Headers.GetValues("Set-Cookie").FirstOrDefault();
71+
cookie2.Should().NotBeNull();
72+
cookie2.Should().NotBe(cookie1);
73+
}
74+
75+
[Fact]
76+
public async Task Authenticated_User_Should_Access_Me_Endpoint()
77+
{
78+
var loginResponse = await _client.PostAsJsonAsync("/auth/login", new
79+
{
80+
identifier = "admin",
81+
secret = "admin"
82+
});
83+
84+
var cookie = loginResponse.Headers.GetValues("Set-Cookie").First();
85+
_client.DefaultRequestHeaders.Add("Cookie", cookie);
86+
var response = await _client.PostAsync("/auth/me/get", null);
87+
response.StatusCode.Should().Be(HttpStatusCode.OK);
88+
}
89+
90+
[Fact]
91+
public async Task Anonymous_Should_Not_Access_Me()
92+
{
93+
var response = await _client.PostAsync("/auth/me/get", null);
94+
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
95+
}
96+
}

0 commit comments

Comments
 (0)