Skip to content

Commit 44485a5

Browse files
authored
Merge pull request #8 from valeriob/avoid_WWW-Authenticate_header_for_ajax_requests
avoid returning WWW-Authenticate header if challenged by an ajax request
2 parents 4f727e0 + c278d3f commit 44485a5

File tree

4 files changed

+71
-1
lines changed

4 files changed

+71
-1
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,21 @@ public void ConfigureServices(IServiceCollection services)
129129
});
130130
}
131131
```
132+
133+
you can suppress the response WWW-Authenticate header (avoiding the browser to show a popup) for ajax requests by using a switch
134+
135+
```c#
136+
public void ConfigureServices(IServiceCollection services)
137+
{
138+
services.AddScoped<AuthenticationEvents>();
139+
140+
services
141+
.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
142+
.AddBasicAuthentication(
143+
options =>
144+
{
145+
options.Realm = "My Application";
146+
options.SupressResponseHeaderWWWAuthenticateForAjaxRequests = true;
147+
});
148+
}
149+
```

src/ZNetCS.AspNetCore.Authentication.Basic/BasicAuthenticationHandler.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,19 @@ protected override Task HandleChallengeAsync(AuthenticationProperties context)
164164
{
165165
var realmHeader = new NameValueHeaderValue("realm", $"\"{this.Options.Realm}\"");
166166
this.Response.StatusCode = StatusCodes.Status401Unauthorized;
167-
this.Response.Headers.Append(HeaderNames.WWWAuthenticate, $"{Basic} {realmHeader}");
168167

168+
if (this.Options.SupressResponseHeaderWWWAuthenticateForAjaxRequests)
169+
{
170+
if (this.Request.Headers.TryGetValue(this.Options.AjaxRequestHeaderName, out var value))
171+
{
172+
if (value == this.Options.AjaxRequestHeaderValue)
173+
{
174+
return Task.CompletedTask;
175+
}
176+
}
177+
}
178+
179+
this.Response.Headers.Append(HeaderNames.WWWAuthenticate, $"{Basic} {realmHeader}");
169180
return Task.CompletedTask;
170181
}
171182

src/ZNetCS.AspNetCore.Authentication.Basic/BasicAuthenticationOptions.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ namespace ZNetCS.AspNetCore.Authentication.Basic
2424
/// </summary>
2525
public class BasicAuthenticationOptions : AuthenticationSchemeOptions
2626
{
27+
public const string DefaultAjaxRequestHeaderName = "X-Requested-With";
28+
public const string DefaultAjaxRequestHeaderValue = "XMLHttpRequest";
29+
2730
#region Constructors and Destructors
2831

2932
/// <summary>
@@ -33,6 +36,8 @@ public BasicAuthenticationOptions()
3336
{
3437
this.Realm = BasicAuthenticationDefaults.Realm;
3538
this.Events = new BasicAuthenticationEvents();
39+
AjaxRequestHeaderName = DefaultAjaxRequestHeaderName;
40+
AjaxRequestHeaderValue = DefaultAjaxRequestHeaderValue;
3641
}
3742

3843
#endregion
@@ -72,6 +77,14 @@ public BasicAuthenticationOptions()
7277
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "OK")]
7378
public string Realm { get; set; }
7479

80+
/// <summary>
81+
/// If enabled, it suppress sending the WWWAuthenticate response header when a request has the header (X-Requested-With,XMLHttpRequest)
82+
/// </summary>
83+
public bool SupressResponseHeaderWWWAuthenticateForAjaxRequests { get; set; }
84+
85+
public string AjaxRequestHeaderName { get; set; }
86+
87+
public string AjaxRequestHeaderValue { get; set; }
7588
#endregion
7689
}
7790
}

test/ZNetCS.AspNetCore.Authentication.BasicTests/AuthorizationTest.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,34 @@ public async Task UnauthorizedWrongHeaderTest()
274274
}
275275
}
276276

277+
/// <summary>
278+
/// The unauthorized basic realm via ajax
279+
/// </summary>
280+
[TestMethod]
281+
public async Task UnauthorizedMyRealmTestAjaxRequestSuppressed()
282+
{
283+
using (var server = new TestServer(WebHostBuilderHelper.CreateBuilder(o =>
284+
{
285+
o.Realm = "My realm";
286+
o.SupressResponseHeaderWWWAuthenticateForAjaxRequests = true;
287+
})))
288+
{
289+
using (HttpClient client = server.CreateClient())
290+
{
291+
client.DefaultRequestHeaders.Add(Basic.BasicAuthenticationOptions.DefaultAjaxRequestHeaderName, Basic.BasicAuthenticationOptions.DefaultAjaxRequestHeaderValue);
292+
293+
// Act
294+
HttpResponseMessage response = await client.GetAsync("api/test");
295+
296+
// Assert
297+
AuthenticationHeaderValue wwwAuth = response.Headers.WwwAuthenticate.SingleOrDefault();
298+
299+
Assert.AreEqual(HttpStatusCode.Unauthorized, response.StatusCode, "StatusCode != Unauthorized");
300+
Assert.IsNull(wwwAuth, "No header should be sent back on ajax request");
301+
}
302+
}
303+
}
304+
277305
#endregion
278306
}
279307
}

0 commit comments

Comments
 (0)