From f8564480fc5d044695881012209d99b895955179 Mon Sep 17 00:00:00 2001 From: frederikprijck Date: Thu, 6 Oct 2022 14:22:02 +0200 Subject: [PATCH 1/3] Rework IdTokenValidator to be able to use a proxy --- .../AuthenticationApiClient.cs | 4 +- .../Tokens/IdTokenValidator.cs | 8 ++- .../Tokens/JsonWebKeyCache.cs | 7 ++- .../Tokens/JsonWebKeys.cs | 15 +++-- .../Tokens/OpenIdConnectDocumentRetriever.cs | 22 +++++++ .../IdTokenValidatorIntegrationTests.cs | 59 +++++++++++++++++++ 6 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 src/Auth0.AuthenticationApi/Tokens/OpenIdConnectDocumentRetriever.cs diff --git a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs index 9d0b66cf2..46cc6893a 100644 --- a/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs +++ b/src/Auth0.AuthenticationApi/AuthenticationApiClient.cs @@ -17,7 +17,7 @@ namespace Auth0.AuthenticationApi /// public class AuthenticationApiClient : IAuthenticationApiClient, IDisposable { - static readonly IdTokenValidator idTokenValidator = new IdTokenValidator(); + readonly IdTokenValidator idTokenValidator; readonly TimeSpan idTokenValidationLeeway = TimeSpan.FromMinutes(1); readonly Uri tokenUri; protected readonly IAuthenticationConnection connection; @@ -51,6 +51,8 @@ public AuthenticationApiClient(Uri baseUri, IAuthenticationConnection connection this.connection = connection; } + idTokenValidator = new IdTokenValidator(new OpenIdConnectDocumentRetriever(this.connection)); + tokenUri = BuildUri("oauth/token"); } diff --git a/src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs b/src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs index 87b7c2f74..70b138a26 100644 --- a/src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs +++ b/src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs @@ -1,4 +1,5 @@ using Auth0.AuthenticationApi.Tokens; +using Microsoft.IdentityModel.Protocols; using System; using System.IdentityModel.Tokens.Jwt; using System.Threading.Tasks; @@ -9,7 +10,12 @@ internal class IdTokenValidator { readonly TimeSpan maxJwksKeySetValidFor = TimeSpan.FromMinutes(10); readonly TimeSpan minJwksRefreshInterval = TimeSpan.FromSeconds(15); - readonly JsonWebKeyCache jsonWebKeyCache = new JsonWebKeyCache(); + readonly JsonWebKeyCache jsonWebKeyCache; + + public IdTokenValidator(IDocumentRetriever openIdConnectDcumentRetriever = null) + { + jsonWebKeyCache = new JsonWebKeyCache(new JsonWebKeys(openIdConnectDcumentRetriever)); + } public async Task Assert(IdTokenRequirements requirements, string idToken, string clientSecret, DateTime? pointInTime = null) { diff --git a/src/Auth0.AuthenticationApi/Tokens/JsonWebKeyCache.cs b/src/Auth0.AuthenticationApi/Tokens/JsonWebKeyCache.cs index ee1bb3bee..248f4a085 100644 --- a/src/Auth0.AuthenticationApi/Tokens/JsonWebKeyCache.cs +++ b/src/Auth0.AuthenticationApi/Tokens/JsonWebKeyCache.cs @@ -6,7 +6,12 @@ namespace Auth0.AuthenticationApi.Tokens { internal class JsonWebKeyCache { - readonly AsyncAgedCache cache = new AsyncAgedCache(JsonWebKeys.GetForIssuer); + readonly AsyncAgedCache cache; + + public JsonWebKeyCache(JsonWebKeys jsonWebKeys) + { + cache = new AsyncAgedCache(jsonWebKeys.GetForIssuer); + } public Task Get(string issuer, TimeSpan maxAge) { diff --git a/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs b/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs index 2a47cd37b..7e7c87d48 100644 --- a/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs +++ b/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs @@ -6,20 +6,27 @@ namespace Auth0.AuthenticationApi.Tokens { - static class JsonWebKeys + public class JsonWebKeys { - public static async Task GetForIssuer(string issuer) + private readonly IDocumentRetriever openIdConnectDcumentRetriever; + + public JsonWebKeys(IDocumentRetriever openIdConnectDcumentRetriever = null) + { + this.openIdConnectDcumentRetriever = openIdConnectDcumentRetriever; + } + + public async Task GetForIssuer(string issuer) { var metadataAddress = new UriBuilder(issuer) { Path = "/.well-known/openid-configuration" }.Uri.OriginalString; var openIdConfiguration = await GetOpenIdConfiguration(metadataAddress).ConfigureAwait(false); return openIdConfiguration.JsonWebKeySet; } - private static Task GetOpenIdConfiguration(string metadataAddress) + private Task GetOpenIdConfiguration(string metadataAddress) { try { - var configurationManager = new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever()); + var configurationManager = openIdConnectDcumentRetriever != null ? new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever(), openIdConnectDcumentRetriever) : new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever()); return configurationManager.GetConfigurationAsync(); } catch (Exception e) diff --git a/src/Auth0.AuthenticationApi/Tokens/OpenIdConnectDocumentRetriever.cs b/src/Auth0.AuthenticationApi/Tokens/OpenIdConnectDocumentRetriever.cs new file mode 100644 index 000000000..cd0789802 --- /dev/null +++ b/src/Auth0.AuthenticationApi/Tokens/OpenIdConnectDocumentRetriever.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Protocols; + +namespace Auth0.AuthenticationApi.Tokens +{ + public class OpenIdConnectDocumentRetriever : IDocumentRetriever + { + private readonly IAuthenticationConnection connection; + + public OpenIdConnectDocumentRetriever(IAuthenticationConnection connection) + { + this.connection = connection; + } + + public Task GetDocumentAsync(string address, CancellationToken cancel) + { + return connection.GetAsync(new Uri(address), cancellationToken: cancel); + } + } +} diff --git a/tests/Auth0.AuthenticationApi.IntegrationTests/Tokens/IdTokenValidatorIntegrationTests.cs b/tests/Auth0.AuthenticationApi.IntegrationTests/Tokens/IdTokenValidatorIntegrationTests.cs index 58641b173..4f04e9b54 100644 --- a/tests/Auth0.AuthenticationApi.IntegrationTests/Tokens/IdTokenValidatorIntegrationTests.cs +++ b/tests/Auth0.AuthenticationApi.IntegrationTests/Tokens/IdTokenValidatorIntegrationTests.cs @@ -88,6 +88,35 @@ public async Task Passes_Token_Validation_RS256() } } + [Fact] + public async Task Passes_Token_Validation_RS256_With_Document_Retreiver() + { + var authUrl = GetVariable("AUTH0_AUTHENTICATION_API_URL"); + var clientId = GetVariable("AUTH0_CLIENT_ID"); + var clientSecret = GetVariable("AUTH0_CLIENT_SECRET"); + + // Arrange + var connection = new TestHttpClientAuthenticationConnection(); + var documentRetreiver = new OpenIdConnectDocumentRetriever(connection); + using (var authenticationApiClient = new TestAuthenticationApiClient(authUrl, connection)) + { + // Act + var authenticationResponse = await authenticationApiClient.GetTokenAsync(new ResourceOwnerTokenRequest + { + ClientId = clientId, + ClientSecret = clientSecret, + Realm = _connection.Name, + Scope = "openid", + Username = _user.Email, + Password = Password + }); + + var issuer = $"https://{authUrl}/"; + var requirements = new IdTokenRequirements(JwtSignatureAlgorithm.RS256, issuer, clientId, TimeSpan.FromMinutes(1)); + await new IdTokenValidator(documentRetreiver).Assert(requirements, authenticationResponse.IdToken, clientSecret); + } + } + [Fact] public async Task Passes_Token_Validation_HS256() { @@ -116,6 +145,36 @@ public async Task Passes_Token_Validation_HS256() } } + [Fact] + public async Task Passes_Token_Validation_HS256_With_Document_Retreiver() + { + var authUrl = GetVariable("AUTH0_AUTHENTICATION_API_URL"); + var clientId = GetVariable("AUTH0_HS256_CLIENT_ID"); + var clientSecret = GetVariable("AUTH0_HS256_CLIENT_SECRET"); + + // Arrange + var connection = new TestHttpClientAuthenticationConnection(); + var documentRetreiver = new OpenIdConnectDocumentRetriever(connection); + using (var authenticationApiClient = new TestAuthenticationApiClient(authUrl, connection)) + { + // Act + var authenticationResponse = await authenticationApiClient.GetTokenAsync(new ResourceOwnerTokenRequest + { + ClientId = clientId, + ClientSecret = clientSecret, + Realm = _connection.Name, + SigningAlgorithm = JwtSignatureAlgorithm.HS256, + Scope = "openid", + Username = _user.Email, + Password = Password + }); + + var issuer = $"https://{authUrl}/"; + var requirements = new IdTokenRequirements(JwtSignatureAlgorithm.HS256, issuer, clientId, TimeSpan.FromMinutes(1)); + await new IdTokenValidator(documentRetreiver).Assert(requirements, authenticationResponse.IdToken, clientSecret); + } + } + [Fact] public async Task Passes_Token_Validation_With_CNAME() { From fbfac2b082fe4f1375a887eaad371bb4ed25d563 Mon Sep 17 00:00:00 2001 From: frederikprijck Date: Thu, 6 Oct 2022 14:26:19 +0200 Subject: [PATCH 2/3] Fix typo --- src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs | 4 ++-- src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs b/src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs index 70b138a26..cee7aa242 100644 --- a/src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs +++ b/src/Auth0.AuthenticationApi/Tokens/IdTokenValidator.cs @@ -12,9 +12,9 @@ internal class IdTokenValidator readonly TimeSpan minJwksRefreshInterval = TimeSpan.FromSeconds(15); readonly JsonWebKeyCache jsonWebKeyCache; - public IdTokenValidator(IDocumentRetriever openIdConnectDcumentRetriever = null) + public IdTokenValidator(IDocumentRetriever openIdConnectDocumentRetriever = null) { - jsonWebKeyCache = new JsonWebKeyCache(new JsonWebKeys(openIdConnectDcumentRetriever)); + jsonWebKeyCache = new JsonWebKeyCache(new JsonWebKeys(openIdConnectDocumentRetriever)); } public async Task Assert(IdTokenRequirements requirements, string idToken, string clientSecret, DateTime? pointInTime = null) diff --git a/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs b/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs index 7e7c87d48..9eb144a36 100644 --- a/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs +++ b/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs @@ -8,11 +8,11 @@ namespace Auth0.AuthenticationApi.Tokens { public class JsonWebKeys { - private readonly IDocumentRetriever openIdConnectDcumentRetriever; + private readonly IDocumentRetriever openIdConnectDocumentRetriever; - public JsonWebKeys(IDocumentRetriever openIdConnectDcumentRetriever = null) + public JsonWebKeys(IDocumentRetriever openIdConnectDocumentRetriever = null) { - this.openIdConnectDcumentRetriever = openIdConnectDcumentRetriever; + this.openIdConnectDocumentRetriever = openIdConnectDocumentRetriever; } public async Task GetForIssuer(string issuer) @@ -26,7 +26,7 @@ private Task GetOpenIdConfiguration(string metadataA { try { - var configurationManager = openIdConnectDcumentRetriever != null ? new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever(), openIdConnectDcumentRetriever) : new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever()); + var configurationManager = openIdConnectDocumentRetriever != null ? new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever(), openIdConnectDocumentRetriever) : new ConfigurationManager(metadataAddress, new OpenIdConnectConfigurationRetriever()); return configurationManager.GetConfigurationAsync(); } catch (Exception e) From c6eb023848d96a421326080500cfdfac3a214a55 Mon Sep 17 00:00:00 2001 From: frederikprijck Date: Thu, 6 Oct 2022 14:28:00 +0200 Subject: [PATCH 3/3] Fix access modifiers --- src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs | 2 +- .../Tokens/OpenIdConnectDocumentRetriever.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs b/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs index 9eb144a36..234799fad 100644 --- a/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs +++ b/src/Auth0.AuthenticationApi/Tokens/JsonWebKeys.cs @@ -6,7 +6,7 @@ namespace Auth0.AuthenticationApi.Tokens { - public class JsonWebKeys + internal class JsonWebKeys { private readonly IDocumentRetriever openIdConnectDocumentRetriever; diff --git a/src/Auth0.AuthenticationApi/Tokens/OpenIdConnectDocumentRetriever.cs b/src/Auth0.AuthenticationApi/Tokens/OpenIdConnectDocumentRetriever.cs index cd0789802..c0b99a310 100644 --- a/src/Auth0.AuthenticationApi/Tokens/OpenIdConnectDocumentRetriever.cs +++ b/src/Auth0.AuthenticationApi/Tokens/OpenIdConnectDocumentRetriever.cs @@ -5,7 +5,7 @@ namespace Auth0.AuthenticationApi.Tokens { - public class OpenIdConnectDocumentRetriever : IDocumentRetriever + internal class OpenIdConnectDocumentRetriever : IDocumentRetriever { private readonly IAuthenticationConnection connection;