Skip to content

Commit 33446b7

Browse files
Merge pull request #16 from webexpress-framework/copilot/extend-identity-provider-interface
Extend IIdentityProvider with Logout and CreateForbiddenResponse
2 parents be40e5e + 6550409 commit 33446b7

5 files changed

Lines changed: 139 additions & 0 deletions

File tree

src/WebExpress.WebCore.Test/Data/MockIdentityProvider.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ public IIdentity Authenticate(IRequest request)
5656
return null; // not needed for this test
5757
}
5858

59+
/// <summary>
60+
/// Logs out the specified request by clearing any authentication state.
61+
/// </summary>
62+
/// <param name="request">
63+
/// The request whose authentication state should be cleared. Cannot be null.
64+
/// </param>
65+
public void Logout(IRequest request)
66+
{
67+
// not needed for this test
68+
}
69+
5970
/// <summary>
6071
/// Displays a login dialog using the specified request and identity information.
6172
/// </summary>
@@ -77,5 +88,27 @@ public IResponse CreateAuthenticationPrompt(IRequest request, IEndpointContext i
7788
{
7889
return null;
7990
}
91+
92+
/// <summary>
93+
/// Creates a forbidden response page for the specified request when the authenticated
94+
/// user lacks the required permissions to access the requested resource.
95+
/// </summary>
96+
/// <param name="request">
97+
/// The request for which access was denied. Cannot be null.
98+
/// </param>
99+
/// <param name="initiator">
100+
/// The endpoint that the user attempted to access.
101+
/// </param>
102+
/// <param name="identity">
103+
/// The authenticated identity that lacks sufficient permissions.
104+
/// </param>
105+
/// <returns>
106+
/// A response representing the forbidden page if this provider can handle the forbidden
107+
/// scenario; otherwise, <c>null</c>.
108+
/// </returns>
109+
public IResponse CreateForbiddenResponse(IRequest request, IEndpointContext initiator, IIdentity identity)
110+
{
111+
return null;
112+
}
80113
}
81114
}

src/WebExpress.WebCore/HttpServer.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,24 @@ public async Task ProcessRequestAsync(IHttpContext httpContext)
657657
// check again
658658
if (!_componentHub.IdentityManager.CheckAccess(identity, searchResult.EndpointContext))
659659
{
660+
// if the user is authenticated but lacks the required permissions, show the forbidden page
661+
if (identity is not null)
662+
{
663+
var forbiddenResponse = _componentHub.IdentityManager.CreateForbiddenResponse
664+
(
665+
httpContext.Request,
666+
searchResult.EndpointContext,
667+
identity
668+
);
669+
670+
if (forbiddenResponse is not null)
671+
{
672+
await responseSender.SendAsync(httpContext, forbiddenResponse);
673+
return;
674+
}
675+
}
676+
677+
// if the user is not authenticated, show the login prompt
660678
var loginResponse = _componentHub.IdentityManager.CreateAuthenticationPrompt
661679
(
662680
httpContext.Request,

src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,26 @@ public interface IIdentityManager : IComponentManager
4141
/// </returns>
4242
IResponse CreateAuthenticationPrompt(IRequest request, IEndpointContext initiator, IIdentity identity = null);
4343

44+
/// <summary>
45+
/// Creates a forbidden response page for the specified request when the authenticated
46+
/// user lacks the required permissions to access the requested resource.
47+
/// </summary>
48+
/// <param name="request">
49+
/// The request for which access was denied. Cannot be null.
50+
/// </param>
51+
/// <param name="initiator">
52+
/// The endpoint that the user attempted to access. Used to determine the origin and
53+
/// context of the authorization failure.
54+
/// </param>
55+
/// <param name="identity">
56+
/// The authenticated identity that lacks sufficient permissions. Cannot be null.
57+
/// </param>
58+
/// <returns>
59+
/// A response representing the forbidden page if a registered identity provider can handle the
60+
/// forbidden scenario; otherwise, <c>null</c>.
61+
/// </returns>
62+
IResponse CreateForbiddenResponse(IRequest request, IEndpointContext initiator, IIdentity identity);
63+
4464
/// <summary>
4565
/// Attempts to authenticate the specified request within the given application context.
4666
/// </summary>

src/WebExpress.WebCore/WebIdentity/IIdentityProvider.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ public interface IIdentityProvider
3131
/// </returns>
3232
IIdentity Authenticate(IRequest request);
3333

34+
/// <summary>
35+
/// Logs out the specified request by clearing any authentication state
36+
/// managed by this identity provider.
37+
/// </summary>
38+
/// <param name="request">
39+
/// The request whose authentication state should be cleared. Cannot be null.
40+
/// </param>
41+
void Logout(IRequest request);
42+
3443
/// <summary>
3544
/// Displays a login dialog using the specified request and identity information.
3645
/// </summary>
@@ -49,5 +58,25 @@ public interface IIdentityProvider
4958
/// relevant status information.
5059
/// </returns>
5160
IResponse CreateAuthenticationPrompt(IRequest request, IEndpointContext initiator, IIdentity identity);
61+
62+
/// <summary>
63+
/// Creates a forbidden response page for the specified request when the authenticated
64+
/// user lacks the required permissions to access the requested resource.
65+
/// </summary>
66+
/// <param name="request">
67+
/// The request for which access was denied. Cannot be null.
68+
/// </param>
69+
/// <param name="initiator">
70+
/// The endpoint that the user attempted to access. Used to determine the origin and
71+
/// context of the authorization failure.
72+
/// </param>
73+
/// <param name="identity">
74+
/// The authenticated identity that lacks sufficient permissions. Cannot be null.
75+
/// </param>
76+
/// <returns>
77+
/// A response representing the forbidden page if this provider can handle the forbidden
78+
/// scenario; otherwise, <c>null</c>.
79+
/// </returns>
80+
IResponse CreateForbiddenResponse(IRequest request, IEndpointContext initiator, IIdentity identity);
5281
}
5382
}

src/WebExpress.WebCore/WebIdentity/IdentityManager.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,36 @@ public IResponse CreateAuthenticationPrompt(IRequest request, IEndpointContext i
404404
return null;
405405
}
406406

407+
/// <summary>
408+
/// Creates a forbidden response page for the specified request when the authenticated
409+
/// user lacks the required permissions to access the requested resource.
410+
/// </summary>
411+
/// <param name="request">The request for which access was denied. Cannot be null.</param>
412+
/// <param name="initiator">The endpoint that the user attempted to access.</param>
413+
/// <param name="identity">The authenticated identity that lacks sufficient permissions.</param>
414+
/// <returns>
415+
/// A response representing the forbidden page if a registered identity provider can handle the
416+
/// forbidden scenario; otherwise, <c>null</c>.
417+
/// </returns>
418+
public IResponse CreateForbiddenResponse(IRequest request, IEndpointContext initiator, IIdentity identity)
419+
{
420+
if (_identityProviders.TryGetValue(initiator?.ApplicationContext, out var list))
421+
{
422+
foreach (var provider in list)
423+
{
424+
var response = provider.CreateForbiddenResponse(request, initiator, identity);
425+
426+
if (response is not null)
427+
{
428+
// the first provider that can show a forbidden page wins
429+
return response;
430+
}
431+
}
432+
}
433+
434+
return null;
435+
}
436+
407437
/// <summary>
408438
/// Attempts to authenticate the specified request within the given application context.
409439
/// </summary>
@@ -454,6 +484,15 @@ public bool Login(IRequest request, IIdentity identity)
454484
/// <param name="request">The request.</param>
455485
public void Logout(IRequest request)
456486
{
487+
// notify all registered identity providers so they can clear their own state
488+
foreach (var list in _identityProviders.Values)
489+
{
490+
foreach (var provider in list)
491+
{
492+
provider.Logout(request);
493+
}
494+
}
495+
457496
var session = _componentHub.SessionManager.GetSession(request);
458497
session.RemoveProperty<SessionPropertyAuthentification>();
459498
}

0 commit comments

Comments
 (0)