Skip to content

Commit 9d14300

Browse files
authored
Merge pull request #32 from Authress/support-alt-domain-for-auth-checks
Allow authress domains that match the api one can be used for authori…
2 parents b19991c + 6178e3c commit 9d14300

File tree

3 files changed

+103
-2
lines changed

3 files changed

+103
-2
lines changed

src/Authress.SDK/Client/TokenVerifier.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,11 @@ public async Task<VerifiedUserIdentity> VerifyToken(string authorizationHeaderVa
156156
throw new ArgumentNullException("The authress custom domain must be specified in the AuthressSettings.");
157157
}
158158

159-
var completeIssuerUrl = new Uri(Sanitizers.SanitizeUrl(authressCustomDomain));
159+
var completeIssuerUrl = new Uri(Sanitizers.SanitizeIssuerUrl(authressCustomDomain));
160+
var altIssuerUrl = new Uri(Sanitizers.SanitizeUrl(authressCustomDomain));
160161
try {
161-
if (new Uri(unverifiedJwtPayload.Issuer).GetLeftPart(UriPartial.Authority) != completeIssuerUrl.GetLeftPart(UriPartial.Authority)) {
162+
if (new Uri(unverifiedJwtPayload.Issuer).GetLeftPart(UriPartial.Authority) != completeIssuerUrl.GetLeftPart(UriPartial.Authority)
163+
&& new Uri(unverifiedJwtPayload.Issuer).GetLeftPart(UriPartial.Authority) != altIssuerUrl.GetLeftPart(UriPartial.Authority)) {
162164
throw new TokenVerificationException($"Unauthorized: Invalid Issuer: {unverifiedJwtPayload.Issuer}");
163165
}
164166
} catch (Exception) {

src/Authress.SDK/Utilities/Sanitizers.cs

+20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Text.RegularExpressions;
23

34
namespace Authress.SDK.Utilities {
@@ -18,5 +19,24 @@ internal static string SanitizeUrl(string urlString) {
1819

1920
return $"https://{urlString}";
2021
}
22+
23+
internal static string SanitizeIssuerUrl(string rawUrlString) {
24+
var sanitizedUrlString = rawUrlString;
25+
if (!sanitizedUrlString.StartsWith("http")) {
26+
sanitizedUrlString = Regex.IsMatch(sanitizedUrlString, @"^(localhost|authress.localhost.localstack.cloud:4566$)") ? $"http://{sanitizedUrlString}" : $"https://{sanitizedUrlString}";
27+
}
28+
29+
var sanitizedUrl = new Uri(sanitizedUrlString);
30+
var domainBaseUrlMatch = Regex.Match(sanitizedUrl.GetLeftPart(UriPartial.Authority), @"^https?://([a-z0-9-]+)[.][a-z0-9-]+[.]authress[.]io$");
31+
if (domainBaseUrlMatch.Success) {
32+
var newSanitizedUrl = new UriBuilder(sanitizedUrl)
33+
{
34+
Host = $"{domainBaseUrlMatch.Groups[1].Value}.login.authress.io"
35+
};
36+
sanitizedUrlString = newSanitizedUrl.Uri.ToString();
37+
}
38+
39+
return sanitizedUrlString.Replace(@"[/]+$", "");
40+
}
2141
}
2242
}

tests/Authress.SDK/Client/Tokenverifier/VerifyTokenTests.cs

+79
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,85 @@ public async Task ValidateEddsaToken() {
5858
mockHttpClient.VerifyAll();
5959
}
6060

61+
[Fact]
62+
public async Task ValidateTokenWithAltCustomDomain() {
63+
var testUserId = Guid.NewGuid().ToString();
64+
var testKeyId = Guid.NewGuid().ToString();
65+
var authressClientTokenProvider = new AuthressClientTokenProvider($"{testUserId}.{testKeyId}.account.{eddsaKeys.Item1}", "authress.login.authress.io");
66+
// setup
67+
var edDsaJwkResponse = new JwkResponse { Keys = new List<Jwk> { new Jwk { Alg = Alg.EdDSA, kid = testKeyId, x = eddsaKeys.Item2 } } };
68+
var jwtToken = await authressClientTokenProvider.GetBearerToken();
69+
70+
var mockHttpClient = new Mock<HttpClientHandler>(MockBehavior.Strict);
71+
mockHttpClient.Protected().SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
72+
.ReturnsAsync(() => new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = edDsaJwkResponse.ToHttpContent() });
73+
74+
var mockFactory = new Mock<IHttpClientHandlerFactory>(MockBehavior.Strict);
75+
mockFactory.Setup(factory => factory.Create()).Returns(mockHttpClient.Object);
76+
77+
var mockHttpClientProvider = new HttpClientProvider(null, null, mockFactory.Object);
78+
var tokenVerifier = new SDK.TokenVerifier("authress.api-eu-west.authress.io", mockHttpClientProvider);
79+
80+
var result = await tokenVerifier.VerifyToken(jwtToken);
81+
result.Should().BeEquivalentTo(new VerifiedUserIdentity { UserId = testUserId });
82+
83+
mockFactory.Verify(mockFactory => mockFactory.Create(), Times.Once());
84+
mockHttpClient.VerifyAll();
85+
}
86+
87+
[Fact]
88+
public async Task ValidateTokenWithAltCustomDomainForBoth() {
89+
var testUserId = Guid.NewGuid().ToString();
90+
var testKeyId = Guid.NewGuid().ToString();
91+
var authressClientTokenProvider = new AuthressClientTokenProvider($"{testUserId}.{testKeyId}.account.{eddsaKeys.Item1}", "authress.api-eu-west.authress.io");
92+
// setup
93+
var edDsaJwkResponse = new JwkResponse { Keys = new List<Jwk> { new Jwk { Alg = Alg.EdDSA, kid = testKeyId, x = eddsaKeys.Item2 } } };
94+
var jwtToken = await authressClientTokenProvider.GetBearerToken();
95+
96+
var mockHttpClient = new Mock<HttpClientHandler>(MockBehavior.Strict);
97+
mockHttpClient.Protected().SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
98+
.ReturnsAsync(() => new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = edDsaJwkResponse.ToHttpContent() });
99+
100+
var mockFactory = new Mock<IHttpClientHandlerFactory>(MockBehavior.Strict);
101+
mockFactory.Setup(factory => factory.Create()).Returns(mockHttpClient.Object);
102+
103+
var mockHttpClientProvider = new HttpClientProvider(null, null, mockFactory.Object);
104+
var tokenVerifier = new SDK.TokenVerifier("authress.api-eu-west.authress.io", mockHttpClientProvider);
105+
106+
var result = await tokenVerifier.VerifyToken(jwtToken);
107+
result.Should().BeEquivalentTo(new VerifiedUserIdentity { UserId = testUserId });
108+
109+
mockFactory.Verify(mockFactory => mockFactory.Create(), Times.Once());
110+
mockHttpClient.VerifyAll();
111+
}
112+
113+
114+
[Fact]
115+
public async Task ValidateTokenWithNoCustomDomain() {
116+
var testUserId = Guid.NewGuid().ToString();
117+
var testKeyId = Guid.NewGuid().ToString();
118+
var authressClientTokenProvider = new AuthressClientTokenProvider($"{testUserId}.{testKeyId}.account.{eddsaKeys.Item1}", "authress.login.authress.io");
119+
// setup
120+
var edDsaJwkResponse = new JwkResponse { Keys = new List<Jwk> { new Jwk { Alg = Alg.EdDSA, kid = testKeyId, x = eddsaKeys.Item2 } } };
121+
var jwtToken = await authressClientTokenProvider.GetBearerToken();
122+
123+
var mockHttpClient = new Mock<HttpClientHandler>(MockBehavior.Strict);
124+
mockHttpClient.Protected().SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
125+
.ReturnsAsync(() => new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = edDsaJwkResponse.ToHttpContent() });
126+
127+
var mockFactory = new Mock<IHttpClientHandlerFactory>(MockBehavior.Strict);
128+
mockFactory.Setup(factory => factory.Create()).Returns(mockHttpClient.Object);
129+
130+
var mockHttpClientProvider = new HttpClientProvider(null, null, mockFactory.Object);
131+
var tokenVerifier = new SDK.TokenVerifier("authress.login.authress.io", mockHttpClientProvider);
132+
133+
var result = await tokenVerifier.VerifyToken(jwtToken);
134+
result.Should().BeEquivalentTo(new VerifiedUserIdentity { UserId = testUserId });
135+
136+
mockFactory.Verify(mockFactory => mockFactory.Create(), Times.Once());
137+
mockHttpClient.VerifyAll();
138+
}
139+
61140
[Fact]
62141
public async Task ValidateEddsaTokenWithExtraSpaces() {
63142
var testUserId = Guid.NewGuid().ToString();

0 commit comments

Comments
 (0)