Skip to content

Commit aa5d149

Browse files
committed
Throw different 401 error message
1 parent 74b15c9 commit aa5d149

File tree

8 files changed

+455
-349
lines changed

8 files changed

+455
-349
lines changed

src/Microsoft.Azure.SignalR.Common/Auth/MicrosoftEntra/MicrosoftEntraAccessKey.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ internal class MicrosoftEntraAccessKey : IAccessKey
3838

3939
private static readonly TimeSpan GetAccessKeyIntervalWhenUnauthorized = TimeSpan.FromMinutes(5);
4040

41-
private static readonly TimeSpan GetAccessKeyRetryInterval = TimeSpan.FromSeconds(3);
42-
4341
private readonly TaskCompletionSource<object?> _initializedTcs = new TaskCompletionSource<object?>(TaskCreationOptions.RunContinuationsAsynchronously);
4442

4543
private readonly IHttpClientFactory _httpClientFactory;
@@ -48,6 +46,10 @@ internal class MicrosoftEntraAccessKey : IAccessKey
4846

4947
private DateTime _lastUpdatedTime = DateTime.MinValue;
5048

49+
private volatile string? _kid;
50+
51+
private volatile byte[]? _keyBytes;
52+
5153
public bool IsAuthorized
5254
{
5355
get => _isAuthorized;
@@ -66,23 +68,21 @@ private set
6668

6769
public TokenCredential TokenCredential { get; }
6870

69-
internal Exception? LastException { get; private set; }
70-
71-
internal string GetAccessKeyUrl { get; }
71+
public string Kid => _kid ?? throw new ArgumentNullException(nameof(Kid));
7272

73-
internal bool HasExpired => DateTime.UtcNow - _lastUpdatedTime > TimeSpan.FromMinutes(GetAccessKeyIntervalInMinute * 2);
73+
public byte[] KeyBytes => _keyBytes ?? throw new ArgumentNullException(nameof(KeyBytes));
7474

75-
private Task<object?> InitializedTask => _initializedTcs.Task;
75+
public Uri Endpoint { get; }
7676

77-
private volatile string? _kid;
77+
internal Exception? LastException { get; private set; }
7878

79-
private volatile byte[]? _keyBytes;
79+
internal string GetAccessKeyUrl { get; }
8080

81-
public string Kid => _kid ?? throw new ArgumentNullException(nameof(Kid));
81+
internal bool HasExpired => DateTime.UtcNow - _lastUpdatedTime > TimeSpan.FromMinutes(GetAccessKeyIntervalInMinute * 2);
8282

83-
public byte[] KeyBytes => _keyBytes ?? throw new ArgumentNullException(nameof(KeyBytes));
83+
internal TimeSpan GetAccessKeyRetryInterval { get; set; } = TimeSpan.FromSeconds(3);
8484

85-
public Uri Endpoint { get; }
85+
private Task<object?> InitializedTask => _initializedTcs.Task;
8686

8787
public MicrosoftEntraAccessKey(Uri endpoint,
8888
TokenCredential credential,
@@ -189,7 +189,7 @@ internal async Task UpdateAccessKeyAsync(CancellationToken ctoken = default)
189189
IsAuthorized = false;
190190
}
191191

192-
private static async Task ThrowExceptionOnResponseFailureAsync(HttpResponseMessage response)
192+
private static async Task ThrowExceptionOnResponseFailureAsync(HttpRequestMessage request, HttpResponseMessage response)
193193
{
194194
if (response.IsSuccessStatusCode)
195195
{
@@ -208,11 +208,12 @@ private static async Task ThrowExceptionOnResponseFailureAsync(HttpResponseMessa
208208
$"Response status code does not indicate success: {(int)response.StatusCode} ({response.ReasonPhrase})");
209209
#endif
210210

211-
var requestUri = response.RequestMessage?.RequestUri?.ToString();
211+
var requestUri = request.RequestUri?.ToString();
212+
var jwtToken = request.Headers.Authorization?.Parameter ?? null;
212213
throw response.StatusCode switch
213214
{
214215
HttpStatusCode.BadRequest => new AzureSignalRInvalidArgumentException(requestUri, innerException, content),
215-
HttpStatusCode.Unauthorized => new AzureSignalRUnauthorizedException(requestUri, innerException),
216+
HttpStatusCode.Unauthorized => new AzureSignalRUnauthorizedException(requestUri, innerException, jwtToken),
216217
HttpStatusCode.NotFound => new AzureSignalRInaccessibleEndpointException(requestUri, innerException),
217218
_ => new AzureSignalRRuntimeException(requestUri, innerException, response.StatusCode, content),
218219
};
@@ -231,7 +232,7 @@ private async Task UpdateAccessKeyInternalAsync(CancellationToken ctoken)
231232

232233
await HandleHttpResponseAsync(response);
233234

234-
await ThrowExceptionOnResponseFailureAsync(response);
235+
await ThrowExceptionOnResponseFailureAsync(request, response);
235236
}
236237

237238
private async Task<bool> HandleHttpResponseAsync(HttpResponseMessage response)

src/Microsoft.Azure.SignalR.Common/Exceptions/AzureSignalRRuntimeException.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class AzureSignalRRuntimeException : AzureSignalRException
1414
{
1515
internal const string ErrorMessage = "Azure SignalR service runtime error.";
1616

17-
internal const string NetworkErrorMessage = "403 Forbidden, please check your service Networking settings.";
17+
internal const string NetworkErrorMessage = "please check your service Networking settings.";
1818

1919
internal HttpStatusCode StatusCode { get; private set; } = HttpStatusCode.InternalServerError;
2020

@@ -39,7 +39,8 @@ private static string GetExceptionMessage(HttpStatusCode statusCode, string? req
3939

4040
private static string GetForbiddenReason(string content)
4141
{
42-
return content.Contains("nginx") ? NetworkErrorMessage : content;
42+
var reason = content.Contains("nginx") ? NetworkErrorMessage : content;
43+
return $"403 Forbidden, {reason}";
4344
}
4445

4546
#if NET8_0_OR_GREATER

src/Microsoft.Azure.SignalR.Common/Exceptions/AzureSignalRUnauthorizedException.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,33 @@
66

77
namespace Microsoft.Azure.SignalR.Common;
88

9+
#nullable enable
10+
911
[Serializable]
1012
public class AzureSignalRUnauthorizedException : AzureSignalRException
1113
{
12-
/// <summary>
13-
/// TODO: Try parse token issuer to identify the possible cause of 401
14-
/// </summary>
14+
[Obsolete]
1515
internal const string ErrorMessage = $"401 Unauthorized. If you were using AccessKey, {ErrorMessageLocalAuth}. If you were using Microsoft Entra authentication, {ErrorMessageMicrosoftEntra}";
1616

1717
internal const string ErrorMessageLocalAuth = "please check the connection string and see if the AccessKey is correct.";
1818

1919
internal const string ErrorMessageMicrosoftEntra = "please check if you set the AzureAuthorityHosts properly in your TokenCredentialOptions, see \"https://learn.microsoft.com/en-us/dotnet/api/azure.identity.azureauthorityhosts\".";
2020

21-
public AzureSignalRUnauthorizedException(string requestUri, Exception innerException) : base(string.IsNullOrEmpty(requestUri) ? ErrorMessage : $"{ErrorMessage} Request Uri: {requestUri}", innerException)
21+
[Obsolete]
22+
public AzureSignalRUnauthorizedException(string? requestUri, Exception innerException) : base(string.IsNullOrEmpty(requestUri) ? ErrorMessage : $"{ErrorMessage} Request Uri: {requestUri}", innerException)
23+
{
24+
}
25+
26+
internal AzureSignalRUnauthorizedException(string? requestUri, Exception innerException, string? jwtToken) : base(GetExceptionMessage(jwtToken, requestUri), innerException)
2227
{
2328
}
2429

25-
internal AzureSignalRUnauthorizedException(Exception innerException) : base(ErrorMessage, innerException)
30+
private static string GetExceptionMessage(string? jwtToken, string? requestUri)
2631
{
32+
var message = TokenUtilities.TryParseIssuer(jwtToken, out var iss)
33+
? Constants.AsrsTokenIssuer.Equals(iss) ? ErrorMessageLocalAuth : ErrorMessageMicrosoftEntra
34+
: ErrorMessageLocalAuth;
35+
return string.IsNullOrEmpty(requestUri) ? $"401 Unauthorized, {message}" : $"401 Unauthorized, {message} Request Uri: {requestUri}";
2736
}
2837

2938
#if NET8_0_OR_GREATER

src/Microsoft.Azure.SignalR.Common/Exceptions/ExceptionExtensions.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@
44
using System;
55
using System.Net.WebSockets;
66

7-
namespace Microsoft.Azure.SignalR.Common
7+
namespace Microsoft.Azure.SignalR.Common;
8+
9+
#nullable enable
10+
11+
internal static class ExceptionExtensions
812
{
9-
internal static class ExceptionExtensions
13+
internal static Exception WrapAsAzureSignalRException(this Exception e, string? jwtToken)
1014
{
11-
internal static Exception WrapAsAzureSignalRException(this Exception e)
15+
switch (e)
1216
{
13-
switch (e)
14-
{
15-
case WebSocketException webSocketException:
16-
if (e.Message.StartsWith("The server returned status code \"401\""))
17-
{
18-
return new AzureSignalRUnauthorizedException(webSocketException);
19-
}
20-
return e;
21-
default: return e;
22-
}
17+
case WebSocketException webSocketException:
18+
if (e.Message.StartsWith("The server returned status code \"401\""))
19+
{
20+
return new AzureSignalRUnauthorizedException(null, webSocketException, jwtToken);
21+
}
22+
return e;
23+
default: return e;
2324
}
2425
}
2526
}

0 commit comments

Comments
 (0)