Skip to content
This repository was archived by the owner on Dec 24, 2020. It is now read-only.

Commit 85fb8c4

Browse files
committed
Update the non-interactive endpoints to return a 401 response for invalid client credentials when basic authentication is used
1 parent ffe788e commit 85fb8c4

File tree

7 files changed

+51
-20
lines changed

7 files changed

+51
-20
lines changed

build/dependencies.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup Label="Package Versions">
44
<AngleSharpVersion>0.9.9</AngleSharpVersion>
5-
<AspNetContribOpenIdExtensionsVersion>2.0.0-rc2-final</AspNetContribOpenIdExtensionsVersion>
5+
<AspNetContribOpenIdExtensionsVersion>2.0.0-rc3-final</AspNetContribOpenIdExtensionsVersion>
66
<AspNetCoreVersion>2.0.0</AspNetCoreVersion>
77
<CoreFxVersion>4.4.0</CoreFxVersion>
88
<EntityFrameworkVersion>6.1.3</EntityFrameworkVersion>

src/AspNet.Security.OpenIdConnect.Server/OpenIdConnectServerHandler.Userinfo.cs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,9 @@ private async Task<bool> InvokeUserinfoEndpointAsync()
191191
{
192192
Logger.LogError("The userinfo request was rejected because the access token was invalid.");
193193

194-
// Note: an invalid token should result in an unauthorized response
195-
// but returning a 401 status would invoke the previously registered
196-
// authentication middleware and potentially replace it by a 302 response.
197-
// To work around this limitation, a 400 error is returned instead.
198-
// See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError
199194
return await SendUserinfoResponseAsync(new OpenIdConnectResponse
200195
{
201-
Error = OpenIdConnectConstants.Errors.InvalidGrant,
196+
Error = OpenIdConnectConstants.Errors.InvalidToken,
202197
ErrorDescription = "The specified access token is not valid."
203198
});
204199
}
@@ -208,14 +203,9 @@ private async Task<bool> InvokeUserinfoEndpointAsync()
208203
{
209204
Logger.LogError("The userinfo request was rejected because the access token was expired.");
210205

211-
// Note: an invalid token should result in an unauthorized response
212-
// but returning a 401 status would invoke the previously registered
213-
// authentication middleware and potentially replace it by a 302 response.
214-
// To work around this limitation, a 400 error is returned instead.
215-
// See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError
216206
return await SendUserinfoResponseAsync(new OpenIdConnectResponse
217207
{
218-
Error = OpenIdConnectConstants.Errors.InvalidGrant,
208+
Error = OpenIdConnectConstants.Errors.InvalidToken,
219209
ErrorDescription = "The specified access token is no longer valid."
220210
});
221211
}

src/AspNet.Security.OpenIdConnect.Server/OpenIdConnectServerHandler.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Collections.Generic;
99
using System.IO;
1010
using System.Security.Claims;
11+
using System.Text;
1112
using System.Text.Encodings.Web;
1213
using System.Threading.Tasks;
1314
using AspNet.Security.OpenIdConnect.Extensions;
@@ -17,6 +18,7 @@
1718
using Microsoft.Extensions.DependencyInjection;
1819
using Microsoft.Extensions.Logging;
1920
using Microsoft.Extensions.Options;
21+
using Microsoft.Extensions.Primitives;
2022
using Microsoft.Net.Http.Headers;
2123
using Newtonsoft.Json;
2224
using Newtonsoft.Json.Linq;
@@ -736,7 +738,42 @@ private async Task<bool> SendPayloadAsync(OpenIdConnectResponse response)
736738

737739
if (!string.IsNullOrEmpty(response.Error))
738740
{
739-
Response.StatusCode = 400;
741+
// When client authentication is made using basic authentication, the authorization server MUST return
742+
// a 401 response with a valid WWW-Authenticate header containing the Basic scheme and a non-empty realm.
743+
// A similar error MAY be returned even when basic authentication is not used and MUST also be returned
744+
// when an invalid token is received by the userinfo endpoint using the Bearer authentication scheme.
745+
// To simplify the logic, a 401 response with the Bearer scheme is returned for invalid_token errors
746+
// and a 401 response with the Basic scheme is returned for invalid_client, even if the credentials
747+
// were specified in the request form instead of the HTTP headers, as allowed by the specification.
748+
string GetAuthenticationScheme()
749+
{
750+
switch (response.Error)
751+
{
752+
case OpenIdConnectConstants.Errors.InvalidClient: return OpenIdConnectConstants.Schemes.Basic;
753+
case OpenIdConnectConstants.Errors.InvalidToken: return OpenIdConnectConstants.Schemes.Bearer;
754+
default: return null;
755+
}
756+
}
757+
758+
var scheme = GetAuthenticationScheme();
759+
if (!string.IsNullOrEmpty(scheme))
760+
{
761+
Response.StatusCode = 401;
762+
763+
Response.Headers[HeaderNames.WWWAuthenticate] = new StringBuilder()
764+
.Append(scheme)
765+
.Append(' ')
766+
.Append(OpenIdConnectConstants.Parameters.Realm)
767+
.Append("=\"")
768+
.Append(Context.GetIssuer(Options))
769+
.Append('"')
770+
.ToString();
771+
}
772+
773+
else
774+
{
775+
Response.StatusCode = 400;
776+
}
740777
}
741778

742779
Response.ContentLength = buffer.Length;

src/Owin.Security.OpenIdConnect.Server/OpenIdConnectServerHandler.Userinfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ private async Task<bool> InvokeUserinfoEndpointAsync()
189189
// See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError
190190
return await SendUserinfoResponseAsync(new OpenIdConnectResponse
191191
{
192-
Error = OpenIdConnectConstants.Errors.InvalidGrant,
192+
Error = OpenIdConnectConstants.Errors.InvalidToken,
193193
ErrorDescription = "The specified access token is not valid."
194194
});
195195
}
@@ -206,7 +206,7 @@ private async Task<bool> InvokeUserinfoEndpointAsync()
206206
// See http://openid.net/specs/openid-connect-core-1_0.html#UserInfoError
207207
return await SendUserinfoResponseAsync(new OpenIdConnectResponse
208208
{
209-
Error = OpenIdConnectConstants.Errors.InvalidGrant,
209+
Error = OpenIdConnectConstants.Errors.InvalidToken,
210210
ErrorDescription = "The specified access token is no longer valid."
211211
});
212212
}

src/Owin.Security.OpenIdConnect.Server/OpenIdConnectServerHandler.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,10 @@ private async Task<bool> SendPayloadAsync(OpenIdConnectResponse response)
745745

746746
writer.Flush();
747747

748+
// Note: when using basic authentication, returning an invalid_client error MUST result in
749+
// an unauthorized response but returning a 401 status code would invoke the previously
750+
// registered authentication middleware and potentially replace it by a 302 response.
751+
// To work around this OWIN/Katana limitation, a 400 response code is always returned.
748752
if (!string.IsNullOrEmpty(response.Error))
749753
{
750754
Response.StatusCode = 400;

test/AspNet.Security.OpenIdConnect.Server.Tests/OpenIdConnectServerHandlerTests.Userinfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ public async Task InvokeUserinfoEndpointAsync_InvalidTokenCausesAnError()
252252
});
253253

254254
// Assert
255-
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
255+
Assert.Equal(OpenIdConnectConstants.Errors.InvalidToken, response.Error);
256256
Assert.Equal("The specified access token is not valid.", response.ErrorDescription);
257257
}
258258

@@ -286,7 +286,7 @@ public async Task InvokeUserinfoEndpointAsync_ExpiredTokenCausesAnError()
286286
});
287287

288288
// Assert
289-
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
289+
Assert.Equal(OpenIdConnectConstants.Errors.InvalidToken, response.Error);
290290
Assert.Equal("The specified access token is no longer valid.", response.ErrorDescription);
291291
}
292292

test/Owin.Security.OpenIdConnect.Server.Tests/OpenIdConnectServerHandlerTests.Userinfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ public async Task InvokeUserinfoEndpointAsync_InvalidTokenCausesAnError()
250250
});
251251

252252
// Assert
253-
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
253+
Assert.Equal(OpenIdConnectConstants.Errors.InvalidToken, response.Error);
254254
Assert.Equal("The specified access token is not valid.", response.ErrorDescription);
255255
}
256256

@@ -283,7 +283,7 @@ public async Task InvokeUserinfoEndpointAsync_ExpiredTokenCausesAnError()
283283
});
284284

285285
// Assert
286-
Assert.Equal(OpenIdConnectConstants.Errors.InvalidGrant, response.Error);
286+
Assert.Equal(OpenIdConnectConstants.Errors.InvalidToken, response.Error);
287287
Assert.Equal("The specified access token is no longer valid.", response.ErrorDescription);
288288
}
289289

0 commit comments

Comments
 (0)