Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow authress domains that match the api one can be used for authori… #32

Merged
merged 1 commit into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/Authress.SDK/Client/TokenVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ public async Task<VerifiedUserIdentity> VerifyToken(string authorizationHeaderVa
throw new ArgumentNullException("The authress custom domain must be specified in the AuthressSettings.");
}

var completeIssuerUrl = new Uri(Sanitizers.SanitizeUrl(authressCustomDomain));
var completeIssuerUrl = new Uri(Sanitizers.SanitizeIssuerUrl(authressCustomDomain));
var altIssuerUrl = new Uri(Sanitizers.SanitizeUrl(authressCustomDomain));
try {
if (new Uri(unverifiedJwtPayload.Issuer).GetLeftPart(UriPartial.Authority) != completeIssuerUrl.GetLeftPart(UriPartial.Authority)) {
if (new Uri(unverifiedJwtPayload.Issuer).GetLeftPart(UriPartial.Authority) != completeIssuerUrl.GetLeftPart(UriPartial.Authority)
&& new Uri(unverifiedJwtPayload.Issuer).GetLeftPart(UriPartial.Authority) != altIssuerUrl.GetLeftPart(UriPartial.Authority)) {
throw new TokenVerificationException($"Unauthorized: Invalid Issuer: {unverifiedJwtPayload.Issuer}");
}
} catch (Exception) {
Expand Down
20 changes: 20 additions & 0 deletions src/Authress.SDK/Utilities/Sanitizers.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Text.RegularExpressions;

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

return $"https://{urlString}";
}

internal static string SanitizeIssuerUrl(string rawUrlString) {
var sanitizedUrlString = rawUrlString;
if (!sanitizedUrlString.StartsWith("http")) {
sanitizedUrlString = Regex.IsMatch(sanitizedUrlString, @"^(localhost|authress.localhost.localstack.cloud:4566$)") ? $"http://{sanitizedUrlString}" : $"https://{sanitizedUrlString}";
}

var sanitizedUrl = new Uri(sanitizedUrlString);
var domainBaseUrlMatch = Regex.Match(sanitizedUrl.GetLeftPart(UriPartial.Authority), @"^https?://([a-z0-9-]+)[.][a-z0-9-]+[.]authress[.]io$");
if (domainBaseUrlMatch.Success) {
var newSanitizedUrl = new UriBuilder(sanitizedUrl)
{
Host = $"{domainBaseUrlMatch.Groups[1].Value}.login.authress.io"
};
sanitizedUrlString = newSanitizedUrl.Uri.ToString();
}

return sanitizedUrlString.Replace(@"[/]+$", "");
}
}
}
79 changes: 79 additions & 0 deletions tests/Authress.SDK/Client/Tokenverifier/VerifyTokenTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,85 @@ public async Task ValidateEddsaToken() {
mockHttpClient.VerifyAll();
}

[Fact]
public async Task ValidateTokenWithAltCustomDomain() {
var testUserId = Guid.NewGuid().ToString();
var testKeyId = Guid.NewGuid().ToString();
var authressClientTokenProvider = new AuthressClientTokenProvider($"{testUserId}.{testKeyId}.account.{eddsaKeys.Item1}", "authress.login.authress.io");
// setup
var edDsaJwkResponse = new JwkResponse { Keys = new List<Jwk> { new Jwk { Alg = Alg.EdDSA, kid = testKeyId, x = eddsaKeys.Item2 } } };
var jwtToken = await authressClientTokenProvider.GetBearerToken();

var mockHttpClient = new Mock<HttpClientHandler>(MockBehavior.Strict);
mockHttpClient.Protected().SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() => new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = edDsaJwkResponse.ToHttpContent() });

var mockFactory = new Mock<IHttpClientHandlerFactory>(MockBehavior.Strict);
mockFactory.Setup(factory => factory.Create()).Returns(mockHttpClient.Object);

var mockHttpClientProvider = new HttpClientProvider(null, null, mockFactory.Object);
var tokenVerifier = new SDK.TokenVerifier("authress.api-eu-west.authress.io", mockHttpClientProvider);

var result = await tokenVerifier.VerifyToken(jwtToken);
result.Should().BeEquivalentTo(new VerifiedUserIdentity { UserId = testUserId });

mockFactory.Verify(mockFactory => mockFactory.Create(), Times.Once());
mockHttpClient.VerifyAll();
}

[Fact]
public async Task ValidateTokenWithAltCustomDomainForBoth() {
var testUserId = Guid.NewGuid().ToString();
var testKeyId = Guid.NewGuid().ToString();
var authressClientTokenProvider = new AuthressClientTokenProvider($"{testUserId}.{testKeyId}.account.{eddsaKeys.Item1}", "authress.api-eu-west.authress.io");
// setup
var edDsaJwkResponse = new JwkResponse { Keys = new List<Jwk> { new Jwk { Alg = Alg.EdDSA, kid = testKeyId, x = eddsaKeys.Item2 } } };
var jwtToken = await authressClientTokenProvider.GetBearerToken();

var mockHttpClient = new Mock<HttpClientHandler>(MockBehavior.Strict);
mockHttpClient.Protected().SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() => new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = edDsaJwkResponse.ToHttpContent() });

var mockFactory = new Mock<IHttpClientHandlerFactory>(MockBehavior.Strict);
mockFactory.Setup(factory => factory.Create()).Returns(mockHttpClient.Object);

var mockHttpClientProvider = new HttpClientProvider(null, null, mockFactory.Object);
var tokenVerifier = new SDK.TokenVerifier("authress.api-eu-west.authress.io", mockHttpClientProvider);

var result = await tokenVerifier.VerifyToken(jwtToken);
result.Should().BeEquivalentTo(new VerifiedUserIdentity { UserId = testUserId });

mockFactory.Verify(mockFactory => mockFactory.Create(), Times.Once());
mockHttpClient.VerifyAll();
}


[Fact]
public async Task ValidateTokenWithNoCustomDomain() {
var testUserId = Guid.NewGuid().ToString();
var testKeyId = Guid.NewGuid().ToString();
var authressClientTokenProvider = new AuthressClientTokenProvider($"{testUserId}.{testKeyId}.account.{eddsaKeys.Item1}", "authress.login.authress.io");
// setup
var edDsaJwkResponse = new JwkResponse { Keys = new List<Jwk> { new Jwk { Alg = Alg.EdDSA, kid = testKeyId, x = eddsaKeys.Item2 } } };
var jwtToken = await authressClientTokenProvider.GetBearerToken();

var mockHttpClient = new Mock<HttpClientHandler>(MockBehavior.Strict);
mockHttpClient.Protected().SetupSequence<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() => new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = edDsaJwkResponse.ToHttpContent() });

var mockFactory = new Mock<IHttpClientHandlerFactory>(MockBehavior.Strict);
mockFactory.Setup(factory => factory.Create()).Returns(mockHttpClient.Object);

var mockHttpClientProvider = new HttpClientProvider(null, null, mockFactory.Object);
var tokenVerifier = new SDK.TokenVerifier("authress.login.authress.io", mockHttpClientProvider);

var result = await tokenVerifier.VerifyToken(jwtToken);
result.Should().BeEquivalentTo(new VerifiedUserIdentity { UserId = testUserId });

mockFactory.Verify(mockFactory => mockFactory.Create(), Times.Once());
mockHttpClient.VerifyAll();
}

[Fact]
public async Task ValidateEddsaTokenWithExtraSpaces() {
var testUserId = Guid.NewGuid().ToString();
Expand Down
Loading