-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathMessageReceivedHandler.cs
More file actions
181 lines (158 loc) · 7.13 KB
/
Copy pathMessageReceivedHandler.cs
File metadata and controls
181 lines (158 loc) · 7.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
using System.Net;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Auth0.AspNetCore.Authentication.Api.DPoP.EventHandlers;
internal class MessageReceivedHandler : DPoPEventHandlerBase, IDPoPEventHandler<MessageReceivedContext>
{
private readonly ILogger<MessageReceivedHandler> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="MessageReceivedHandler"/> class.
/// </summary>
/// <param name="logger">The logger instance for logging operations.</param>
public MessageReceivedHandler(ILogger<MessageReceivedHandler> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// Initializes a new instance of the <see cref="MessageReceivedHandler"/> class without a logger.
/// </summary>
internal MessageReceivedHandler() : this(Microsoft.Extensions.Logging.Abstractions.NullLogger<MessageReceivedHandler>.Instance)
{
}
public Task Handle(MessageReceivedContext context)
{
try
{
DPoPOptions dPoPOptions = context.HttpContext.RequestServices.GetRequiredService<DPoPOptions>();
return dPoPOptions.Mode switch
{
DPoPModes.Disabled => HandleDisabledMode(context),
DPoPModes.Allowed => HandleAllowedMode(context),
DPoPModes.Required => HandleRequiredMode(context),
_ => Task.CompletedTask
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to handle message received for DPoP mode");
return Task.CompletedTask;
}
}
/// <summary>
/// Handles the scenario when DPoP authentication is disabled.
/// Skips all DPoP validation and processing, allowing requests to proceed without DPoP checks.
/// </summary>
internal virtual Task HandleDisabledMode(MessageReceivedContext context)
{
// When DPoP is disabled, skip all DPoP validation and processing
return Task.CompletedTask;
}
/// <summary>
/// Handles the DPoP authentication scenario when the mode is set to Allowed.
/// In Allowed mode, the method processes the request to extract the DPoP token if present.
/// If any validation fails, authentication is not performed and the request proceeds without DPoP checks.
/// </summary>
/// <param name="context">
/// The JWT Bearer message received context containing the HTTP request.
/// </param>
/// <returns>
/// A completed <see cref="Task" /> after processing the Allowed DPoP authentication logic.
/// </returns>
internal virtual Task HandleAllowedMode(MessageReceivedContext context)
{
if (!IsAuthorizationTokenExists(context.Request))
{
_logger.LogError("Invalid authorization header in allowed mode - missing token");
HandleInvalidRequestInAllowedMode(context);
return Task.CompletedTask;
}
// In Allowed mode, process the request to extract DPoP token if present
if (!IsValidAuthorizationHeaderCount(context.Request))
{
_logger.LogError("Invalid authorization header count in allowed mode");
HandleInvalidRequestInAllowedMode(context);
return Task.CompletedTask;
}
// If not using DPoP scheme, check for conflicting DPoP proof headers
if (!IsDPoPScheme(context.Request))
{
if (IsDPoPProofHeaderExists(context.Request))
{
_logger.LogError("Bearer scheme used with DPoP proof header in allowed mode");
context.HttpContext.Items[Auth0Constants.DPoP.BearerErrorCode] =
Auth0Constants.DPoP.Error.Code.InvalidToken;
context.HttpContext.Items[Auth0Constants.DPoP.BearerErrorDescription] =
Auth0Constants.DPoP.Error.Description.BearerSchemeWithDPoPProof;
context.HttpContext.Items[Auth0Constants.DPoP.BearerStatusCode] = HttpStatusCode.Unauthorized;
context.Fail(Auth0Constants.DPoP.Error.Description.BearerSchemeWithDPoPProof);
return Task.CompletedTask;
}
return Task.CompletedTask;
}
if (!IsDPoPProofHeaderExists(context.Request))
{
_logger.LogError("DPoP scheme used without DPoP proof header in allowed mode");
HandleInvalidRequestInAllowedMode(context);
return Task.CompletedTask;
}
// If a valid DPoP scheme is used and DPoP header exists, extract the token
var accessToken = ExtractDPoPBoundAccessToken(context.Request);
if (accessToken is null)
{
_logger.LogError("Failed to extract DPoP-bound access token in allowed mode");
HandleInvalidRequestInAllowedMode(context);
return Task.CompletedTask;
}
context.Token = accessToken;
return Task.CompletedTask;
}
/// <summary>
/// Handles the scenario when DPoP authentication is required.
/// Validates that the request contains exactly one Authorization header using the DPoP scheme,
/// and exactly one valid DPoP proof header. If any validation fails, authentication is not performed.
/// If all checks pass, extracts the DPoP-bound access token for downstream authentication.
/// </summary>
/// <param name="context">
/// The JWT Bearer message received context containing the HTTP request.
/// </param>
/// <returns>
/// A completed <see cref="Task" /> after processing the required DPoP authentication logic.
/// </returns>
internal virtual Task HandleRequiredMode(MessageReceivedContext context)
{
if (!IsValidAuthorizationHeaderCount(context.Request))
{
_logger.LogError("Invalid authorization header count in required mode");
HandleInvalidRequestInRequiredMode(context);
return Task.CompletedTask;
}
if (!IsDPoPScheme(context.Request))
{
_logger.LogError("Non-DPoP authentication scheme used in required mode");
HandleInvalidRequestInRequiredMode(context);
return Task.CompletedTask;
}
if (!IsDPoPProofHeaderExists(context.Request))
{
_logger.LogError("Missing DPoP proof header in required mode");
HandleInvalidRequestInRequiredMode(context);
return Task.CompletedTask;
}
if (!IsAuthorizationTokenExists(context.Request))
{
_logger.LogError("Missing authorization token in required mode");
HandleInvalidRequestInRequiredMode(context);
return Task.CompletedTask;
}
var accessToken = ExtractDPoPBoundAccessToken(context.Request);
if (accessToken is null)
{
_logger.LogError("Failed to extract DPoP-bound access token in required mode");
HandleInvalidRequestInRequiredMode(context);
return Task.CompletedTask;
}
context.Token = accessToken;
return Task.CompletedTask;
}
}