diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 439ebd93b2..2f29ac340c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,15 +1,25 @@ name: "CodeQL" on: - push: - branches: [ "dev", "dev6x" ] + push: + paths-ignore: + - 'test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/**' + - 'test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/**' + - '/src/Microsoft.IdentityModel.KeyVaultExtensions/**' + - '/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/**' + branches: [ "dev", "dev6x", "dev7x"] pull_request: + paths-ignore: + - 'test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/**' + - 'test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/**' + - '/src/Microsoft.IdentityModel.KeyVaultExtensions/**' + - '/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/**' types: - opened - synchronize - reopened - ready_for_review - branches: [ "dev", "dev6x" ] + branches: [ "dev", "dev6x", "dev7x"] jobs: analyze: diff --git a/CHANGELOG.md b/CHANGELOG.md index 79b39aa066..fa81e50d90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,23 @@ See the [releases](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) for details on bug fixes and added features. - -Pending Next Release +8.0.0-preview1 +==== +### Breaking changes: +- IdentityModel 8x no longer supports .net461, which has reached end of life and is no longer supported. See issue [#2544](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2544) for details. +- Two IdentityModel extension dlls `Microsoft.IdentityModel.KeyVaultExtensions` and `Microsoft.IdentityModel.ManagedKeyVaultSecurityKey` were using ADAL, which is no longer supported . The affected packages have been removed, as the replacement is to use [Microsoft.Identity.Web](https://github.com/AzureAD/microsoft-identity-web/wiki/Certificates). See issue [#2454](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2454) for details. +- `AppContext.SetSwitch` which were included in IdentityModel 7x, have been removed and are the default in IdentityModel 8x. The result is a more performant IdentityModel by default. See issue [#2629](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2629) and https://aka.ms/IdentityModel8x for details. + +7.6.1 ===== +### New Features: +- Add missing metadata parameters to OpenIdConnectConfiguration. See issue [#2498](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2498) for details. + ### Bug Fixes: -- Reduced allocations in `AadIssuerValidator` by not using `string.Replace` where appropriate. See issue [#2595](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2595) and PR [#2597](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2597) for more details. +- Fix over-reporting of `IDX14100`. See issue [#2058](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2058) and PR [#2618](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/2618) for details. +- `JwtRegisteredClaimNames` now contains previously missing Standard OpenIdConnect claims. See issue [#1598](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/1598) for details. + +### Performance Improvements: +- No longer for every string claim, calling DateTime.TryParse on each value, whether it is expected to be a DateTime or not. See issue [#2615](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2615) for details. 7.6.0 ===== diff --git a/Wilson.sln b/Wilson.sln index 9c671e6168..18274278e0 100644 --- a/Wilson.sln +++ b/Wilson.sln @@ -66,18 +66,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.Jso EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{EB14B99B-2255-45BC-BF14-E488DCD4A4BA}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{B961CF69-0DE6-4B9F-9473-9F669365BD62}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.KeyVaultExtensions.Tests", "test\Microsoft.IdentityModel.KeyVaultExtensions.Tests\Microsoft.IdentityModel.KeyVaultExtensions.Tests.csproj", "{987772FA-BA24-4EF4-9B58-3DA78FFD61DD}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests", "test\Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests\Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests.csproj", "{97315A25-B694-4BD0-8DF5-C339884A6D26}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.KeyVaultExtensions", "src\Microsoft.IdentityModel.KeyVaultExtensions\Microsoft.IdentityModel.KeyVaultExtensions.csproj", "{F5636C24-D6D5-4F6A-8A21-7C78FC1FC6C6}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.TestExtensions", "src\Microsoft.IdentityModel.TestExtensions\Microsoft.IdentityModel.TestExtensions.csproj", "{AF787AA8-DE6E-4B74-816E-E8F3203A2FA0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.ManagedKeyVaultSecurityKey", "src\Microsoft.IdentityModel.ManagedKeyVaultSecurityKey\Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.csproj", "{8DFF1DEA-F01F-4CE4-9471-5D2CEFB7E59F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.Protocols.SignedHttpRequest", "src\Microsoft.IdentityModel.Protocols.SignedHttpRequest\Microsoft.IdentityModel.Protocols.SignedHttpRequest.csproj", "{C768FBB5-DE0D-4970-918C-96B37485E34C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests", "test\Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests\Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests.csproj", "{15944563-F7DA-4150-B5F1-6144EBF2CE23}" @@ -85,6 +75,8 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4655DBB4-70C6-475D-8971-FE6619B85F70}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + buildPack.bat = buildPack.bat + buildTestPack.bat = buildTestPack.bat EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.IdentityModel.Validators", "src\Microsoft.IdentityModel.Validators\Microsoft.IdentityModel.Validators.csproj", "{DA585910-0E6C-45A5-AABD-30917130FD63}" @@ -198,26 +190,10 @@ Global {DBF58792-25DF-4B6E-866C-77A0BC5AB81B}.Debug|Any CPU.Build.0 = Debug|Any CPU {DBF58792-25DF-4B6E-866C-77A0BC5AB81B}.Release|Any CPU.ActiveCfg = Release|Any CPU {DBF58792-25DF-4B6E-866C-77A0BC5AB81B}.Release|Any CPU.Build.0 = Release|Any CPU - {987772FA-BA24-4EF4-9B58-3DA78FFD61DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {987772FA-BA24-4EF4-9B58-3DA78FFD61DD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {987772FA-BA24-4EF4-9B58-3DA78FFD61DD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {987772FA-BA24-4EF4-9B58-3DA78FFD61DD}.Release|Any CPU.Build.0 = Release|Any CPU - {97315A25-B694-4BD0-8DF5-C339884A6D26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {97315A25-B694-4BD0-8DF5-C339884A6D26}.Debug|Any CPU.Build.0 = Debug|Any CPU - {97315A25-B694-4BD0-8DF5-C339884A6D26}.Release|Any CPU.ActiveCfg = Release|Any CPU - {97315A25-B694-4BD0-8DF5-C339884A6D26}.Release|Any CPU.Build.0 = Release|Any CPU - {F5636C24-D6D5-4F6A-8A21-7C78FC1FC6C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5636C24-D6D5-4F6A-8A21-7C78FC1FC6C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5636C24-D6D5-4F6A-8A21-7C78FC1FC6C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5636C24-D6D5-4F6A-8A21-7C78FC1FC6C6}.Release|Any CPU.Build.0 = Release|Any CPU {AF787AA8-DE6E-4B74-816E-E8F3203A2FA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF787AA8-DE6E-4B74-816E-E8F3203A2FA0}.Debug|Any CPU.Build.0 = Debug|Any CPU {AF787AA8-DE6E-4B74-816E-E8F3203A2FA0}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF787AA8-DE6E-4B74-816E-E8F3203A2FA0}.Release|Any CPU.Build.0 = Release|Any CPU - {8DFF1DEA-F01F-4CE4-9471-5D2CEFB7E59F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8DFF1DEA-F01F-4CE4-9471-5D2CEFB7E59F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8DFF1DEA-F01F-4CE4-9471-5D2CEFB7E59F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8DFF1DEA-F01F-4CE4-9471-5D2CEFB7E59F}.Release|Any CPU.Build.0 = Release|Any CPU {C768FBB5-DE0D-4970-918C-96B37485E34C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C768FBB5-DE0D-4970-918C-96B37485E34C}.Debug|Any CPU.Build.0 = Debug|Any CPU {C768FBB5-DE0D-4970-918C-96B37485E34C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -287,12 +263,7 @@ Global {E4E6D0ED-12CB-4C01-A4C1-4F60D10E2304} = {BD2706C5-6C57-484D-89C8-A0CF5F8E3D19} {DBF58792-25DF-4B6E-866C-77A0BC5AB81B} = {BD2706C5-6C57-484D-89C8-A0CF5F8E3D19} {EB14B99B-2255-45BC-BF14-E488DCD4A4BA} = {BD2706C5-6C57-484D-89C8-A0CF5F8E3D19} - {B961CF69-0DE6-4B9F-9473-9F669365BD62} = {8905D2E3-4499-4A86-BF3E-F098F228DD59} - {987772FA-BA24-4EF4-9B58-3DA78FFD61DD} = {B961CF69-0DE6-4B9F-9473-9F669365BD62} - {97315A25-B694-4BD0-8DF5-C339884A6D26} = {B961CF69-0DE6-4B9F-9473-9F669365BD62} - {F5636C24-D6D5-4F6A-8A21-7C78FC1FC6C6} = {EB14B99B-2255-45BC-BF14-E488DCD4A4BA} {AF787AA8-DE6E-4B74-816E-E8F3203A2FA0} = {EB14B99B-2255-45BC-BF14-E488DCD4A4BA} - {8DFF1DEA-F01F-4CE4-9471-5D2CEFB7E59F} = {EB14B99B-2255-45BC-BF14-E488DCD4A4BA} {C768FBB5-DE0D-4970-918C-96B37485E34C} = {BD2706C5-6C57-484D-89C8-A0CF5F8E3D19} {15944563-F7DA-4150-B5F1-6144EBF2CE23} = {8905D2E3-4499-4A86-BF3E-F098F228DD59} {DA585910-0E6C-45A5-AABD-30917130FD63} = {BD2706C5-6C57-484D-89C8-A0CF5F8E3D19} diff --git a/benchmark/Microsoft.IdentityModel.Benchmarks/BenchmarkUtils.cs b/benchmark/Microsoft.IdentityModel.Benchmarks/BenchmarkUtils.cs index 8c14fdf03c..ecce92c3e4 100644 --- a/benchmark/Microsoft.IdentityModel.Benchmarks/BenchmarkUtils.cs +++ b/benchmark/Microsoft.IdentityModel.Benchmarks/BenchmarkUtils.cs @@ -60,6 +60,43 @@ public static Dictionary Claims } } + public static Dictionary ClaimsExtendedExample + { + get + { + DateTime now = DateTime.UtcNow; + return new Dictionary() + { + { "acct", 0 }, + { "aio", Guid.NewGuid().ToString() }, + { "amr", new List() { "pwd", "mfa" } }, + { "app_displayname", "MyApp" }, + { "appidacr", 0 }, + { "azpacr", 0 }, + { "azp", Guid.NewGuid().ToString() }, + { "groups", new List() { "group1", "group2" } }, + { "name", "Bob" }, + { "oid", Guid.NewGuid().ToString() }, + { "rh", Guid.NewGuid().ToString() }, + { "scp", "access_as_user" }, + { JwtRegisteredClaimNames.Sub, Guid.NewGuid().ToString() }, + { "tid", Guid.NewGuid().ToString() }, + { "family_name", "Smith" }, + { "ver", "2.0" }, + { "wids", new List() { Guid.NewGuid().ToString() } }, + { "xms_cc", Guid.NewGuid().ToString() }, + { "role", new List() { "role1", "Developer", "Sales"} }, + { JwtRegisteredClaimNames.Email, "Bob@contoso.com" }, + { JwtRegisteredClaimNames.Exp, EpochTime.GetIntDate(now + TimeSpan.FromDays(1)) }, + { JwtRegisteredClaimNames.Nbf, EpochTime.GetIntDate(now) }, + { JwtRegisteredClaimNames.Iat, EpochTime.GetIntDate(now) }, + { JwtRegisteredClaimNames.GivenName, "Bob" }, + { JwtRegisteredClaimNames.Iss, Issuer }, + { JwtRegisteredClaimNames.Aud, Audience } + }; + } + } + public static SigningCredentials SigningCredentialsRsaSha256 => new(RsaSecurityKey, SecurityAlgorithms.RsaSha256, SecurityAlgorithms.Sha256); public static EncryptingCredentials EncryptingCredentialsAes256Sha512 => new(SymmetricEncryptionKey512, "dir", SecurityAlgorithms.Aes256CbcHmacSha512); diff --git a/benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs b/benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs index 785f31b22b..64344834e6 100644 --- a/benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs +++ b/benchmark/Microsoft.IdentityModel.Benchmarks/Program.cs @@ -49,6 +49,7 @@ private static void DebugThroughTests() ValidateTokenAsyncTests validateTokenAsyncTests = new ValidateTokenAsyncTests(); validateTokenAsyncTests.Setup(); TokenValidationResult tokenValidationResult = validateTokenAsyncTests.JsonWebTokenHandler_ValidateTokenAsync().Result; + var claims = validateTokenAsyncTests.JsonWebTokenHandler_ValidateTokenAsync_CreateClaims(); ValidateSignedHttpRequestAsyncTests validateSignedHttpRequestAsyncTests = new ValidateSignedHttpRequestAsyncTests(); validateSignedHttpRequestAsyncTests.Setup(); diff --git a/benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncTests.cs b/benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncTests.cs index 71bba7b668..1bd6e9fedb 100644 --- a/benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncTests.cs +++ b/benchmark/Microsoft.IdentityModel.Benchmarks/ValidateTokenAsyncTests.cs @@ -1,7 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using Microsoft.IdentityModel.JsonWebTokens; @@ -16,7 +19,9 @@ public class ValidateTokenAsyncTests private JsonWebTokenHandler _jsonWebTokenHandler; private JwtSecurityTokenHandler _jwtSecurityTokenHandler; private SecurityTokenDescriptor _tokenDescriptor; + private SecurityTokenDescriptor _tokenDescriptorExtendedClaims; private string _jws; + private string _jwsExtendedClaims; private TokenValidationParameters _validationParameters; [GlobalSetup] @@ -28,8 +33,15 @@ public void Setup() SigningCredentials = BenchmarkUtils.SigningCredentialsRsaSha256, }; + _tokenDescriptorExtendedClaims = new SecurityTokenDescriptor + { + Claims = BenchmarkUtils.ClaimsExtendedExample, + SigningCredentials = BenchmarkUtils.SigningCredentialsRsaSha256, + }; + _jsonWebTokenHandler = new JsonWebTokenHandler(); _jws = _jsonWebTokenHandler.CreateToken(_tokenDescriptor); + _jwsExtendedClaims = _jsonWebTokenHandler.CreateToken(_tokenDescriptorExtendedClaims); _jwtSecurityTokenHandler = new JwtSecurityTokenHandler(); _jwtSecurityTokenHandler.SetDefaultTimesOnTokenCreation = false; @@ -43,6 +55,15 @@ public void Setup() }; } + [Benchmark] + public async Task> JsonWebTokenHandler_ValidateTokenAsync_CreateClaims() + { + var result = await _jsonWebTokenHandler.ValidateTokenAsync(_jwsExtendedClaims, _validationParameters).ConfigureAwait(false); + var claimsIdentity = result.ClaimsIdentity; + var claims = claimsIdentity.Claims; + return claims.ToList(); + } + [Benchmark] public async Task JsonWebTokenHandler_ValidateTokenAsync() => await _jsonWebTokenHandler.ValidateTokenAsync(_jws, _validationParameters).ConfigureAwait(false); diff --git a/build/common.props b/build/common.props index 0fbddb3d63..efd7e90e2f 100644 --- a/build/common.props +++ b/build/common.props @@ -32,7 +32,7 @@ - 7.6.1 + 8.0.0 preview-$([System.DateTime]::Now.AddYears(-2019).Year)$([System.DateTime]::Now.ToString("MMddHHmmss")) @@ -46,8 +46,8 @@ - true - 7.0.0 + false + 8.0.0 diff --git a/build/dependencies.props b/build/dependencies.props index 23e1b91065..8203da572c 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -2,8 +2,6 @@ 2.1.1 - 3.0.5 - 1.0.3 4.5.0 1.0.0 2.0.3 diff --git a/build/targets.props b/build/targets.props index b1af98f10a..6f92780d1f 100644 --- a/build/targets.props +++ b/build/targets.props @@ -1,7 +1,7 @@ - net461;net462;net472;netstandard2.0;net6.0;net8.0 - net461;netstandard2.0;net8.0 + net462;net472;netstandard2.0;net6.0;net8.0 + netstandard2.0;net8.0 $(SrcTargets);net9.0 netstandard2.0 diff --git a/build/targetsTest.props b/build/targetsTest.props index 3f26b8e2d9..e867c8b147 100644 --- a/build/targetsTest.props +++ b/build/targetsTest.props @@ -1,7 +1,7 @@ - net461;net462;net472;netcoreapp2.1;net6.0;net8.0 - net461;netcoreapp2.1;net8.0 + net462;net472;netcoreapp2.1;net6.0;net8.0 + netcoreapp2.1;net8.0 $(TestTargets);net9.0 netcoreapp2.1 diff --git a/buildConfiguration.xml b/buildConfiguration.xml index 06e064dafa..0abda703bf 100644 --- a/buildConfiguration.xml +++ b/buildConfiguration.xml @@ -14,8 +14,6 @@ - - @@ -33,8 +31,6 @@ - - diff --git a/buildPack.bat b/buildPack.bat index 7e782b647f..6a4c89623c 100644 --- a/buildPack.bat +++ b/buildPack.bat @@ -1,2 +1,3 @@ +dotnet clean Product.proj > clean.log dotnet build /r Product.proj dotnet pack --no-restore -o artifacts --no-build Product.proj diff --git a/buildTestPack.bat b/buildTestPack.bat index 40e59fda52..b5dba1b86f 100644 --- a/buildTestPack.bat +++ b/buildTestPack.bat @@ -1,3 +1,4 @@ +dotnet clean Product.proj > clean.log dotnet build /r Product.proj dotnet test --no-restore --no-build Product.proj dotnet pack --no-restore -o artifacts --no-build Product.proj diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs b/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs index a47e488129..aa6276a320 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/Json/JsonClaimSet.cs @@ -55,7 +55,7 @@ internal static void CreateClaimFromObject(List claims, string claimType, { // Json.net recognized DateTime by default. if (value is string str) - claims.Add(new Claim(claimType, str, JwtTokenUtilities.GetStringClaimValueType(str), issuer, issuer)); + claims.Add(new Claim(claimType, str, JwtTokenUtilities.GetStringClaimValueType(str, claimType), issuer, issuer)); else if (value is int i) claims.Add(new Claim(claimType, i.ToString(CultureInfo.InvariantCulture), ClaimValueTypes.Integer32, issuer, issuer)); else if (value is long l) @@ -107,7 +107,7 @@ internal static Claim CreateClaimFromJsonElement(string claimType, string issuer if (jsonElement.ValueKind == JsonValueKind.String) { string claimValue = jsonElement.ToString(); - return new Claim(claimType, claimValue, JwtTokenUtilities.GetStringClaimValueType(claimValue), issuer, issuer); + return new Claim(claimType, claimValue, JwtTokenUtilities.GetStringClaimValueType(claimValue, claimType), issuer, issuer); } else if (jsonElement.ValueKind == JsonValueKind.Null) return new Claim(claimType, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer); diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs new file mode 100644 index 0000000000..3242311e9a --- /dev/null +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.ValidateToken.cs @@ -0,0 +1,771 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Abstractions; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Tokens; +using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; + +namespace Microsoft.IdentityModel.JsonWebTokens +{ + /// + /// A designed for creating and validating Json Web Tokens. + /// See: https://datatracker.ietf.org/doc/html/rfc7519 and http://www.rfc-editor.org/info/rfc7515. + /// + public partial class JsonWebTokenHandler : TokenHandler + { + /// + /// Returns a value that indicates if this handler can validate a . + /// + /// 'true', indicating this instance can validate a . + public virtual bool CanValidateToken + { + get { return true; } + } + + internal async ValueTask ValidateJWEAsync( + JsonWebToken jwtToken, + TokenValidationParameters validationParameters, + BaseConfiguration configuration) + { + try + { + TokenValidationResult tokenValidationResult = ReadToken(DecryptToken(jwtToken, validationParameters), validationParameters); + if (!tokenValidationResult.IsValid) + return tokenValidationResult; + + + tokenValidationResult = await ValidateJWSAsync( + tokenValidationResult.SecurityToken as JsonWebToken, + validationParameters, + configuration).ConfigureAwait(false); + + if (!tokenValidationResult.IsValid) + return tokenValidationResult; + + jwtToken.InnerToken = tokenValidationResult.SecurityToken as JsonWebToken; + jwtToken.Payload = (tokenValidationResult.SecurityToken as JsonWebToken).Payload; + return new TokenValidationResult + { + SecurityToken = jwtToken, + ClaimsIdentityNoLocking = tokenValidationResult.ClaimsIdentityNoLocking, + IsValid = true, + TokenType = tokenValidationResult.TokenType + }; + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + return new TokenValidationResult + { + Exception = ex, + IsValid = false, + TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jwtToken : null + }; + } + } + + internal async ValueTask ValidateJWEAsync( + JsonWebToken jwtToken, + TokenValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + try + { + TokenValidationResult tokenValidationResult = ReadToken(DecryptToken(jwtToken, validationParameters), validationParameters); + if (!tokenValidationResult.IsValid) + return tokenValidationResult; + + tokenValidationResult = await ValidateJWSAsync( + tokenValidationResult.SecurityToken as JsonWebToken, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + + if (!tokenValidationResult.IsValid) + return tokenValidationResult; + + jwtToken.InnerToken = tokenValidationResult.SecurityToken as JsonWebToken; + jwtToken.Payload = (tokenValidationResult.SecurityToken as JsonWebToken).Payload; + return new TokenValidationResult + { + SecurityToken = jwtToken, + ClaimsIdentityNoLocking = tokenValidationResult.ClaimsIdentityNoLocking, + IsValid = true, + TokenType = tokenValidationResult.TokenType + }; + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + return new TokenValidationResult + { + Exception = ex, + IsValid = false, + TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jwtToken : null + }; + } + } + + internal async ValueTask ValidateJWSAsync( + JsonWebToken jsonWebToken, + TokenValidationParameters validationParameters, + BaseConfiguration configuration) + { + try + { + TokenValidationResult tokenValidationResult; + if (validationParameters.TransformBeforeSignatureValidation != null) + jsonWebToken = validationParameters.TransformBeforeSignatureValidation(jsonWebToken, validationParameters) as JsonWebToken; + + if (validationParameters.SignatureValidator != null || validationParameters.SignatureValidatorUsingConfiguration != null) + { + var validatedToken = ValidateSignatureUsingDelegates(jsonWebToken, validationParameters); + tokenValidationResult = await ValidateTokenPayloadAsync( + validatedToken, + validationParameters, + configuration).ConfigureAwait(false); + + Validators.ValidateIssuerSecurityKey(validatedToken.SigningKey, validatedToken, validationParameters); + } + else + { + if (validationParameters.ValidateSignatureLast) + { + tokenValidationResult = await ValidateTokenPayloadAsync( + jsonWebToken, + validationParameters, + configuration).ConfigureAwait(false); + + if (tokenValidationResult.IsValid) + tokenValidationResult.SecurityToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, configuration); + } + else + { + var validatedToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, configuration); + tokenValidationResult = await ValidateTokenPayloadAsync( + validatedToken, + validationParameters, + configuration).ConfigureAwait(false); + } + } + + return tokenValidationResult; + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + return new TokenValidationResult + { + Exception = ex, + IsValid = false, + TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jsonWebToken : null + }; + } + } + + internal async ValueTask ValidateJWSAsync( + JsonWebToken jsonWebToken, + TokenValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + try + { + BaseConfiguration currentConfiguration = null; + if (validationParameters.ConfigurationManager != null) + { + try + { + currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + // The exception is not re-thrown as the TokenValidationParameters may have the issuer and signing key set + // directly on them, allowing the library to continue with token validation. + if (LogHelper.IsEnabled(EventLogLevel.Warning)) + LogHelper.LogWarning(LogHelper.FormatInvariant(TokenLogMessages.IDX10261, validationParameters.ConfigurationManager.MetadataAddress, ex.ToString())); + } + } + + TokenValidationResult tokenValidationResult; + if (validationParameters.TransformBeforeSignatureValidation != null) + jsonWebToken = validationParameters.TransformBeforeSignatureValidation(jsonWebToken, validationParameters) as JsonWebToken; + + if (validationParameters.SignatureValidator != null || validationParameters.SignatureValidatorUsingConfiguration != null) + { + var validatedToken = ValidateSignatureUsingDelegates(jsonWebToken, validationParameters); + tokenValidationResult = await ValidateTokenPayloadAsync( + validatedToken, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + + Validators.ValidateIssuerSecurityKey(validatedToken.SigningKey, validatedToken, validationParameters); + } + else + { + if (validationParameters.ValidateSignatureLast) + { + tokenValidationResult = await ValidateTokenPayloadAsync( + jsonWebToken, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + + if (tokenValidationResult.IsValid) + tokenValidationResult.SecurityToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, currentConfiguration); + } + else + { + var validatedToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, currentConfiguration); + tokenValidationResult = await ValidateTokenPayloadAsync( + validatedToken, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + } + } + + return tokenValidationResult; + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + return new TokenValidationResult + { + Exception = ex, + IsValid = false, + TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jsonWebToken : null + }; + } + } + + private static JsonWebToken ValidateSignatureAndIssuerSecurityKey(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + { + JsonWebToken validatedToken = ValidateSignature(jsonWebToken, validationParameters, configuration); + Validators.ValidateIssuerSecurityKey(validatedToken.SigningKey, jsonWebToken, validationParameters, configuration); + return validatedToken; + } + + /// + /// Validates the JWT signature. + /// + private static JsonWebToken ValidateSignature(JsonWebToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + { + bool kidMatched = false; + IEnumerable keys = null; + + if (!jwtToken.IsSigned) + { + if (validationParameters.RequireSignedTokens) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10504, jwtToken))); + else + return jwtToken; + } + + if (validationParameters.IssuerSigningKeyResolverUsingConfiguration != null) + { + keys = validationParameters.IssuerSigningKeyResolverUsingConfiguration(jwtToken.EncodedToken, jwtToken, jwtToken.Kid, validationParameters, configuration); + } + else if (validationParameters.IssuerSigningKeyResolver != null) + { + keys = validationParameters.IssuerSigningKeyResolver(jwtToken.EncodedToken, jwtToken, jwtToken.Kid, validationParameters); + } + else + { + var key = JwtTokenUtilities.ResolveTokenSigningKey(jwtToken.Kid, jwtToken.X5t, validationParameters, configuration); + if (key != null) + { + kidMatched = true; + keys = [key]; + } + } + + if (validationParameters.TryAllIssuerSigningKeys && keys.IsNullOrEmpty()) + { + // control gets here if: + // 1. User specified delegate: IssuerSigningKeyResolver returned null + // 2. ResolveIssuerSigningKey returned null + // Try all the keys. This is the degenerate case, not concerned about perf. + keys = TokenUtilities.GetAllSigningKeys(configuration, validationParameters); + } + + // keep track of exceptions thrown, keys that were tried + StringBuilder exceptionStrings = null; + StringBuilder keysAttempted = null; + var kidExists = !string.IsNullOrEmpty(jwtToken.Kid); + + if (keys != null) + { + foreach (var key in keys) + { +#pragma warning disable CA1031 // Do not catch general exception types + try + { + if (ValidateSignature(jwtToken, key, validationParameters)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(TokenLogMessages.IDX10242, jwtToken); + + jwtToken.SigningKey = key; + return jwtToken; + } + } + catch (Exception ex) + { + (exceptionStrings ??= new StringBuilder()).AppendLine(ex.ToString()); + } +#pragma warning restore CA1031 // Do not catch general exception types + + if (key != null) + { + (keysAttempted ??= new StringBuilder()).Append(key.ToString()).Append(" , KeyId: ").AppendLine(key.KeyId); + if (kidExists && !kidMatched && key.KeyId != null) + kidMatched = jwtToken.Kid.Equals(key.KeyId, key is X509SecurityKey ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); + } + } + } + + // Get information on where keys used during token validation came from for debugging purposes. + var keysInTokenValidationParameters = TokenUtilities.GetAllSigningKeys(validationParameters: validationParameters); + + var keysInConfiguration = TokenUtilities.GetAllSigningKeys(configuration); + var numKeysInTokenValidationParameters = keysInTokenValidationParameters.Count(); + var numKeysInConfiguration = keysInConfiguration.Count(); + + if (kidExists) + { + if (kidMatched) + { + JsonWebToken localJwtToken = jwtToken; // avoid closure on non-exceptional path + var isKidInTVP = keysInTokenValidationParameters.Any(x => x.KeyId.Equals(localJwtToken.Kid)); + var keyLocation = isKidInTVP ? "TokenValidationParameters" : "Configuration"; + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10511, + LogHelper.MarkAsNonPII((object)keysAttempted ?? ""), + LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), + LogHelper.MarkAsNonPII(numKeysInConfiguration), + LogHelper.MarkAsNonPII(keyLocation), + LogHelper.MarkAsNonPII(jwtToken.Kid), + (object)exceptionStrings ?? "", + jwtToken))); + } + + if (!validationParameters.ValidateSignatureLast) + { + InternalValidators.ValidateAfterSignatureFailed( + jwtToken, + jwtToken.ValidFromNullable, + jwtToken.ValidToNullable, + jwtToken.Audiences, + validationParameters, + configuration); + } + } + + if (keysAttempted is not null) + { + if (kidExists) + { + throw LogHelper.LogExceptionMessage(new SecurityTokenSignatureKeyNotFoundException(LogHelper.FormatInvariant(TokenLogMessages.IDX10503, + LogHelper.MarkAsNonPII(jwtToken.Kid), + LogHelper.MarkAsNonPII((object)keysAttempted ?? ""), + LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), + LogHelper.MarkAsNonPII(numKeysInConfiguration), + (object)exceptionStrings ?? "", + jwtToken))); + } + else + { + throw LogHelper.LogExceptionMessage(new SecurityTokenSignatureKeyNotFoundException(LogHelper.FormatInvariant(TokenLogMessages.IDX10517, + LogHelper.MarkAsNonPII((object)keysAttempted ?? ""), + LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), + LogHelper.MarkAsNonPII(numKeysInConfiguration), + (object)exceptionStrings ?? "", + jwtToken))); + } + } + + throw LogHelper.LogExceptionMessage(new SecurityTokenSignatureKeyNotFoundException(TokenLogMessages.IDX10500)); + } + + internal static bool IsSignatureValid(byte[] signatureBytes, int signatureBytesLength, SignatureProvider signatureProvider, byte[] dataToVerify, int dataToVerifyLength) + { + if (signatureProvider is SymmetricSignatureProvider) + { + return signatureProvider.Verify(dataToVerify, 0, dataToVerifyLength, signatureBytes, 0, signatureBytesLength); + } + else + { + if (signatureBytes.Length == signatureBytesLength) + { + return signatureProvider.Verify(dataToVerify, 0, dataToVerifyLength, signatureBytes, 0, signatureBytesLength); + } + else + { + byte[] sigBytes = new byte[signatureBytesLength]; + Array.Copy(signatureBytes, 0, sigBytes, 0, signatureBytesLength); + return signatureProvider.Verify(dataToVerify, 0, dataToVerifyLength, sigBytes, 0, signatureBytesLength); + } + } + } + + internal static bool ValidateSignature(byte[] bytes, int len, string stringWithSignature, int signatureStartIndex, SignatureProvider signatureProvider) + { + return Base64UrlEncoding.Decode( + stringWithSignature, + signatureStartIndex + 1, + stringWithSignature.Length - signatureStartIndex - 1, + signatureProvider, + bytes, + len, + IsSignatureValid); + } + + internal static bool ValidateSignature(JsonWebToken jsonWebToken, SecurityKey key, TokenValidationParameters validationParameters) + { + var cryptoProviderFactory = validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory; + if (!cryptoProviderFactory.IsSupportedAlgorithm(jsonWebToken.Alg, key)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX14000, LogHelper.MarkAsNonPII(jsonWebToken.Alg), key); + + return false; + } + + Validators.ValidateAlgorithm(jsonWebToken.Alg, key, jsonWebToken, validationParameters); + var signatureProvider = cryptoProviderFactory.CreateForVerifying(key, jsonWebToken.Alg); + try + { + if (signatureProvider == null) + throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(TokenLogMessages.IDX10636, key == null ? "Null" : key.ToString(), LogHelper.MarkAsNonPII(jsonWebToken.Alg)))); + + return EncodingUtils.PerformEncodingDependentOperation( + jsonWebToken.EncodedToken, + 0, + jsonWebToken.Dot2, + Encoding.UTF8, + jsonWebToken.EncodedToken, + jsonWebToken.Dot2, + signatureProvider, + ValidateSignature); + } + finally + { + cryptoProviderFactory.ReleaseSignatureProvider(signatureProvider); + } + } + + private static JsonWebToken ValidateSignatureUsingDelegates(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters) + { + if (validationParameters.SignatureValidatorUsingConfiguration != null) + { + // TODO - get configuration from validationParameters + BaseConfiguration configuration = null; + var validatedToken = validationParameters.SignatureValidatorUsingConfiguration(jsonWebToken.EncodedToken, validationParameters, configuration); + if (validatedToken == null) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10505, jsonWebToken))); + + if (!(validatedToken is JsonWebToken validatedJsonWebToken)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10506, LogHelper.MarkAsNonPII(typeof(JsonWebToken)), LogHelper.MarkAsNonPII(validatedToken.GetType()), jsonWebToken))); + + return validatedJsonWebToken; + } + else if (validationParameters.SignatureValidator != null) + { + var validatedToken = validationParameters.SignatureValidator(jsonWebToken.EncodedToken, validationParameters); + if (validatedToken == null) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10505, jsonWebToken))); + + if (!(validatedToken is JsonWebToken validatedJsonWebToken)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10506, LogHelper.MarkAsNonPII(typeof(JsonWebToken)), LogHelper.MarkAsNonPII(validatedToken.GetType()), jsonWebToken))); + + return validatedJsonWebToken; + } + + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10505, jsonWebToken))); + } + + /// + /// Validates a JWS or a JWE. + /// + /// A 'JSON Web Token' (JWT) in JWS or JWE Compact Serialization Format. + /// A required for validation. + /// A + [Obsolete("`JsonWebTokens.ValidateToken(string, TokenValidationParameters)` has been deprecated and will be removed in a future release. Use `JsonWebTokens.ValidateTokenAsync(string, TokenValidationParameters)` instead. For more information, see https://aka.ms/IdentityModel/7-breaking-changes", false)] + public virtual TokenValidationResult ValidateToken(string token, TokenValidationParameters validationParameters) + { + return ValidateTokenAsync(token, validationParameters).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + /// + /// Validates a token. + /// On a validation failure, no exception will be thrown; instead, the exception will be set in the returned TokenValidationResult.Exception property. + /// Callers should always check the TokenValidationResult.IsValid property to verify the validity of the result. + /// + /// The token to be validated. + /// A required for validation. + /// A + /// + /// TokenValidationResult.Exception will be set to one of the following exceptions if the is invalid. + /// if is null or empty. + /// if is null. + /// 'token.Length' is greater than . + /// if is not a valid , + /// if the validationParameters.TokenReader delegate is not able to parse/read the token as a valid , + /// + public override async Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters) + { + if (string.IsNullOrEmpty(token)) + return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(token)), IsValid = false }; + + if (validationParameters == null) + return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(validationParameters)), IsValid = false }; + + if (token.Length > MaximumTokenSizeInBytes) + return new TokenValidationResult { Exception = LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(TokenLogMessages.IDX10209, LogHelper.MarkAsNonPII(token.Length), LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)))), IsValid = false }; + + try + { + TokenValidationResult result = ReadToken(token, validationParameters); + if (result.IsValid) + return await ValidateTokenAsync(result.SecurityToken, validationParameters).ConfigureAwait(false); + + return result; + } + catch (Exception ex) + { + return new TokenValidationResult + { + Exception = ex, + IsValid = false + }; + } + } + + /// + public override async Task ValidateTokenAsync(SecurityToken token, TokenValidationParameters validationParameters) + { + if (token == null) + throw LogHelper.LogArgumentNullException(nameof(token)); + + if (validationParameters == null) + return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(validationParameters)), IsValid = false }; + + var jwt = token as JsonWebToken; + if (jwt == null) + return new TokenValidationResult { Exception = LogHelper.LogArgumentException(nameof(token), $"{nameof(token)} must be a {nameof(JsonWebToken)}."), IsValid = false }; + + try + { + return await ValidateTokenAsync(jwt, validationParameters).ConfigureAwait(false); + } + catch (Exception ex) + { + return new TokenValidationResult + { + Exception = ex, + IsValid = false + }; + } + } + + /// + /// Internal method for token validation, responsible for: + /// (1) Obtaining a configuration from the . + /// (2) Revalidating using the Last Known Good Configuration (if present), and obtaining a refreshed configuration (if necessary) and revalidating using it. + /// + /// The JWT token + /// The to be used for validation. + /// + internal async ValueTask ValidateTokenAsync( + JsonWebToken jsonWebToken, + TokenValidationParameters validationParameters) + { + BaseConfiguration currentConfiguration = null; + if (validationParameters.ConfigurationManager != null) + { + try + { + currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + // The exception is not re-thrown as the TokenValidationParameters may have the issuer and signing key set + // directly on them, allowing the library to continue with token validation. + if (LogHelper.IsEnabled(EventLogLevel.Warning)) + LogHelper.LogWarning(LogHelper.FormatInvariant(TokenLogMessages.IDX10261, validationParameters.ConfigurationManager.MetadataAddress, ex.ToString())); + } + } + + TokenValidationResult tokenValidationResult = jsonWebToken.IsEncrypted ? + await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false) : + await ValidateJWSAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false); + + if (validationParameters.ConfigurationManager != null) + { + if (tokenValidationResult.IsValid) + { + // Set current configuration as LKG if it exists. + if (currentConfiguration != null) + validationParameters.ConfigurationManager.LastKnownGoodConfiguration = currentConfiguration; + + return tokenValidationResult; + } + else if (TokenUtilities.IsRecoverableException(tokenValidationResult.Exception)) + { + // If we were still unable to validate, attempt to refresh the configuration and validate using it + // but ONLY if the currentConfiguration is not null. We want to avoid refreshing the configuration on + // retrieval error as this case should have already been hit before. This refresh handles the case + // where a new valid configuration was somehow published during validation time. + if (currentConfiguration != null) + { + validationParameters.ConfigurationManager.RequestRefresh(); + validationParameters.RefreshBeforeValidation = true; + var lastConfig = currentConfiguration; + currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); + + // Only try to re-validate using the newly obtained config if it doesn't reference equal the previously used configuration. + if (lastConfig != currentConfiguration) + { + tokenValidationResult = jsonWebToken.IsEncrypted ? + await ValidateJWEAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false) : + await ValidateJWSAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false); + + if (tokenValidationResult.IsValid) + { + validationParameters.ConfigurationManager.LastKnownGoodConfiguration = currentConfiguration; + return tokenValidationResult; + } + } + } + + if (validationParameters.ConfigurationManager.UseLastKnownGoodConfiguration) + { + validationParameters.RefreshBeforeValidation = false; + validationParameters.ValidateWithLKG = true; + var recoverableException = tokenValidationResult.Exception; + + foreach (BaseConfiguration lkgConfiguration in validationParameters.ConfigurationManager.GetValidLkgConfigurations()) + { + if (!lkgConfiguration.Equals(currentConfiguration) && TokenUtilities.IsRecoverableConfiguration(jsonWebToken.Kid, currentConfiguration, lkgConfiguration, recoverableException)) + { + tokenValidationResult = jsonWebToken.IsEncrypted ? + await ValidateJWEAsync(jsonWebToken, validationParameters, lkgConfiguration).ConfigureAwait(false) : + await ValidateJWSAsync(jsonWebToken, validationParameters, lkgConfiguration).ConfigureAwait(false); + + if (tokenValidationResult.IsValid) + return tokenValidationResult; + } + } + } + } + } + + return tokenValidationResult; + } + + internal async ValueTask ValidateTokenPayloadAsync( + JsonWebToken jsonWebToken, + TokenValidationParameters validationParameters, + BaseConfiguration configuration) + { + var expires = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Exp) ? (DateTime?)jsonWebToken.ValidTo : null; + var notBefore = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Nbf) ? (DateTime?)jsonWebToken.ValidFrom : null; + + Validators.ValidateLifetime(notBefore, expires, jsonWebToken, validationParameters); + Validators.ValidateAudience(jsonWebToken.Audiences, jsonWebToken, validationParameters); + string issuer = await Validators.ValidateIssuerAsync(jsonWebToken.Issuer, jsonWebToken, validationParameters, configuration).ConfigureAwait(false); + + Validators.ValidateTokenReplay(expires, jsonWebToken.EncodedToken, validationParameters); + if (validationParameters.ValidateActor && !string.IsNullOrWhiteSpace(jsonWebToken.Actor)) + { + // Infinite recursion should not occur here, as the JsonWebToken passed into this method is (1) constructed from a string + // AND (2) the signature is successfully validated on it. (1) implies that even if there are nested actor tokens, + // they must end at some point since they cannot reference one another. (2) means that the token has a valid signature + // and (since issuer validation occurs first) came from a trusted authority. + // NOTE: More than one nested actor token should not be considered a valid token, but if we somehow encounter one, + // this code will still work properly. + TokenValidationResult tokenValidationResult = + await ValidateTokenAsync(jsonWebToken.Actor, validationParameters.ActorValidationParameters ?? validationParameters).ConfigureAwait(false); + + if (!tokenValidationResult.IsValid) + return tokenValidationResult; + } + + string tokenType = Validators.ValidateTokenType(jsonWebToken.Typ, jsonWebToken, validationParameters); + return new TokenValidationResult(jsonWebToken, this, validationParameters.Clone(), issuer) + { + IsValid = true, + TokenType = tokenType + }; + } + + internal async ValueTask ValidateTokenPayloadAsync( + JsonWebToken jsonWebToken, + TokenValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + var expires = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Exp) ? (DateTime?)jsonWebToken.ValidTo : null; + var notBefore = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Nbf) ? (DateTime?)jsonWebToken.ValidFrom : null; + + Validators.ValidateLifetime(notBefore, expires, jsonWebToken, validationParameters); + Validators.ValidateAudience(jsonWebToken.Audiences, jsonWebToken, validationParameters); + + IssuerValidationResult issuerValidationResult = await Validators.ValidateIssuerAsync( + jsonWebToken.Issuer, + jsonWebToken, + validationParameters, + callContext, + cancellationToken).ConfigureAwait(false); + + if (!issuerValidationResult.IsValid) + { + return new TokenValidationResult(jsonWebToken, this, validationParameters, issuerValidationResult.Issuer) + { + IsValid = false, + Exception = issuerValidationResult.Exception + }; + } + + Validators.ValidateTokenReplay(expires, jsonWebToken.EncodedToken, validationParameters); + if (validationParameters.ValidateActor && !string.IsNullOrWhiteSpace(jsonWebToken.Actor)) + { + // Infinite recursion should not occur here, as the JsonWebToken passed into this method is (1) constructed from a string + // AND (2) the signature is successfully validated on it. (1) implies that even if there are nested actor tokens, + // they must end at some point since they cannot reference one another. (2) means that the token has a valid signature + // and (since issuer validation occurs first) came from a trusted authority. + // NOTE: More than one nested actor token should not be considered a valid token, but if we somehow encounter one, + // this code will still work properly. + TokenValidationResult tokenValidationResult = + await ValidateTokenAsync(jsonWebToken.Actor, validationParameters.ActorValidationParameters ?? validationParameters).ConfigureAwait(false); + + if (!tokenValidationResult.IsValid) + return tokenValidationResult; + } + + string tokenType = Validators.ValidateTokenType(jsonWebToken.Typ, jsonWebToken, validationParameters); + return new TokenValidationResult(jsonWebToken, this, validationParameters.Clone(), issuerValidationResult.Issuer) + { + IsValid = true, + TokenType = tokenType + }; + } + } +} diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs index cf4ad2e264..43d1999a3c 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JsonWebTokenHandler.cs @@ -3,12 +3,8 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Security.Claims; -using System.Text; using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; @@ -174,15 +170,6 @@ public virtual bool CanReadToken(string token) } } - /// - /// Returns a value that indicates if this handler can validate a . - /// - /// 'true', indicating this instance can validate a . - public virtual bool CanValidateToken - { - get { return true; } - } - private static StringComparison GetStringComparisonRuleIf509(SecurityKey securityKey) => (securityKey is X509SecurityKey) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; @@ -498,90 +485,6 @@ public override SecurityToken ReadToken(string token) return ReadJsonWebToken(token); } - /// - /// Validates a JWS or a JWE. - /// - /// A 'JSON Web Token' (JWT) in JWS or JWE Compact Serialization Format. - /// A required for validation. - /// A - [Obsolete("`JsonWebTokens.ValidateToken(string, TokenValidationParameters)` has been deprecated and will be removed in a future release. Use `JsonWebTokens.ValidateTokenAsync(string, TokenValidationParameters)` instead. For more information, see https://aka.ms/IdentityModel/7-breaking-changes", false)] - public virtual TokenValidationResult ValidateToken(string token, TokenValidationParameters validationParameters) - { - return ValidateTokenAsync(token, validationParameters).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - /// - /// Validates a token. - /// On a validation failure, no exception will be thrown; instead, the exception will be set in the returned TokenValidationResult.Exception property. - /// Callers should always check the TokenValidationResult.IsValid property to verify the validity of the result. - /// - /// The token to be validated. - /// A required for validation. - /// A - /// - /// TokenValidationResult.Exception will be set to one of the following exceptions if the is invalid. - /// if is null or empty. - /// if is null. - /// 'token.Length' is greater than . - /// if is not a valid , - /// if the validationParameters.TokenReader delegate is not able to parse/read the token as a valid , - /// - public override async Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters) - { - if (string.IsNullOrEmpty(token)) - return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(token)), IsValid = false }; - - if (validationParameters == null) - return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(validationParameters)), IsValid = false }; - - if (token.Length > MaximumTokenSizeInBytes) - return new TokenValidationResult { Exception = LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(TokenLogMessages.IDX10209, LogHelper.MarkAsNonPII(token.Length), LogHelper.MarkAsNonPII(MaximumTokenSizeInBytes)))), IsValid = false }; - - try - { - TokenValidationResult result = ReadToken(token, validationParameters); - if (result.IsValid) - return await ValidateTokenAsync(result.SecurityToken, validationParameters).ConfigureAwait(false); - - return result; - } - catch (Exception ex) - { - return new TokenValidationResult - { - Exception = ex, - IsValid = false - }; - } - } - - /// - public override async Task ValidateTokenAsync(SecurityToken token, TokenValidationParameters validationParameters) - { - if (token == null) - throw LogHelper.LogArgumentNullException(nameof(token)); - - if (validationParameters == null) - return new TokenValidationResult { Exception = LogHelper.LogArgumentNullException(nameof(validationParameters)), IsValid = false }; - - var jwt = token as JsonWebToken; - if (jwt == null) - return new TokenValidationResult { Exception = LogHelper.LogArgumentException(nameof(token), $"{nameof(token)} must be a {nameof(JsonWebToken)}."), IsValid = false }; - - try - { - return await ValidateTokenAsync(jwt, validationParameters).ConfigureAwait(false); - } - catch (Exception ex) - { - return new TokenValidationResult - { - Exception = ex, - IsValid = false - }; - } - } - /// /// Converts a string into an instance of . /// @@ -627,453 +530,5 @@ private static TokenValidationResult ReadToken(string token, TokenValidationPara IsValid = true }; } - - /// - /// Private method for token validation, responsible for: - /// (1) Obtaining a configuration from the . - /// (2) Revalidating using the Last Known Good Configuration (if present), and obtaining a refreshed configuration (if necessary) and revalidating using it. - /// - /// The JWT token - /// The to be used for validation. - /// - private async ValueTask ValidateTokenAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters) - { - BaseConfiguration currentConfiguration = null; - if (validationParameters.ConfigurationManager != null) - { - try - { - currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - } -#pragma warning disable CA1031 // Do not catch general exception types - catch (Exception ex) -#pragma warning restore CA1031 // Do not catch general exception types - { - // The exception is not re-thrown as the TokenValidationParameters may have the issuer and signing key set - // directly on them, allowing the library to continue with token validation. - if (LogHelper.IsEnabled(EventLogLevel.Warning)) - LogHelper.LogWarning(LogHelper.FormatInvariant(TokenLogMessages.IDX10261, validationParameters.ConfigurationManager.MetadataAddress, ex.ToString())); - } - } - - TokenValidationResult tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false); - if (validationParameters.ConfigurationManager != null) - { - if (tokenValidationResult.IsValid) - { - // Set current configuration as LKG if it exists. - if (currentConfiguration != null) - validationParameters.ConfigurationManager.LastKnownGoodConfiguration = currentConfiguration; - - return tokenValidationResult; - } - else if (TokenUtilities.IsRecoverableException(tokenValidationResult.Exception)) - { - // If we were still unable to validate, attempt to refresh the configuration and validate using it - // but ONLY if the currentConfiguration is not null. We want to avoid refreshing the configuration on - // retrieval error as this case should have already been hit before. This refresh handles the case - // where a new valid configuration was somehow published during validation time. - if (currentConfiguration != null) - { - validationParameters.ConfigurationManager.RequestRefresh(); - validationParameters.RefreshBeforeValidation = true; - var lastConfig = currentConfiguration; - currentConfiguration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(CancellationToken.None).ConfigureAwait(false); - - // Only try to re-validate using the newly obtained config if it doesn't reference equal the previously used configuration. - if (lastConfig != currentConfiguration) - { - tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, currentConfiguration).ConfigureAwait(false); - - if (tokenValidationResult.IsValid) - { - validationParameters.ConfigurationManager.LastKnownGoodConfiguration = currentConfiguration; - return tokenValidationResult; - } - } - } - - if (validationParameters.ConfigurationManager.UseLastKnownGoodConfiguration) - { - validationParameters.RefreshBeforeValidation = false; - validationParameters.ValidateWithLKG = true; - var recoverableException = tokenValidationResult.Exception; - - foreach (BaseConfiguration lkgConfiguration in validationParameters.ConfigurationManager.GetValidLkgConfigurations()) - { - if (!lkgConfiguration.Equals(currentConfiguration) && TokenUtilities.IsRecoverableConfiguration(jsonWebToken.Kid, currentConfiguration, lkgConfiguration, recoverableException)) - { - tokenValidationResult = await ValidateTokenAsync(jsonWebToken, validationParameters, lkgConfiguration).ConfigureAwait(false); - - if (tokenValidationResult.IsValid) - return tokenValidationResult; - } - } - } - } - } - - return tokenValidationResult; - } - - private ValueTask ValidateTokenAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - return jsonWebToken.IsEncrypted ? - ValidateJWEAsync(jsonWebToken, validationParameters, configuration) : - ValidateJWSAsync(jsonWebToken, validationParameters, configuration); - } - - private async ValueTask ValidateJWSAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - try - { - TokenValidationResult tokenValidationResult; - if (validationParameters.TransformBeforeSignatureValidation != null) - jsonWebToken = validationParameters.TransformBeforeSignatureValidation(jsonWebToken, validationParameters) as JsonWebToken; - - if (validationParameters.SignatureValidator != null || validationParameters.SignatureValidatorUsingConfiguration != null) - { - var validatedToken = ValidateSignatureUsingDelegates(jsonWebToken, validationParameters, configuration); - tokenValidationResult = await ValidateTokenPayloadAsync(validatedToken, validationParameters, configuration).ConfigureAwait(false); - Validators.ValidateIssuerSecurityKey(validatedToken.SigningKey, validatedToken, validationParameters, configuration); - } - else - { - if (validationParameters.ValidateSignatureLast) - { - tokenValidationResult = await ValidateTokenPayloadAsync(jsonWebToken, validationParameters, configuration).ConfigureAwait(false); - if (tokenValidationResult.IsValid) - tokenValidationResult.SecurityToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, configuration); - } - else - { - var validatedToken = ValidateSignatureAndIssuerSecurityKey(jsonWebToken, validationParameters, configuration); - tokenValidationResult = await ValidateTokenPayloadAsync(validatedToken, validationParameters, configuration).ConfigureAwait(false); - } - } - - return tokenValidationResult; - } -#pragma warning disable CA1031 // Do not catch general exception types - catch (Exception ex) -#pragma warning restore CA1031 // Do not catch general exception types - { - return new TokenValidationResult - { - Exception = ex, - IsValid = false, - TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jsonWebToken : null - }; - } - } - - private async ValueTask ValidateJWEAsync(JsonWebToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - try - { - TokenValidationResult tokenValidationResult = ReadToken(DecryptToken(jwtToken, validationParameters, configuration), validationParameters); - if (!tokenValidationResult.IsValid) - return tokenValidationResult; - - tokenValidationResult = await ValidateJWSAsync(tokenValidationResult.SecurityToken as JsonWebToken, validationParameters, configuration).ConfigureAwait(false); - if (!tokenValidationResult.IsValid) - return tokenValidationResult; - - jwtToken.InnerToken = tokenValidationResult.SecurityToken as JsonWebToken; - jwtToken.Payload = (tokenValidationResult.SecurityToken as JsonWebToken).Payload; - return new TokenValidationResult - { - SecurityToken = jwtToken, - ClaimsIdentityNoLocking = tokenValidationResult.ClaimsIdentityNoLocking, - IsValid = true, - TokenType = tokenValidationResult.TokenType - }; - } -#pragma warning disable CA1031 // Do not catch general exception types - catch (Exception ex) -#pragma warning restore CA1031 // Do not catch general exception types - { - return new TokenValidationResult - { - Exception = ex, - IsValid = false, - TokenOnFailedValidation = validationParameters.IncludeTokenOnFailedValidation ? jwtToken : null - }; - } - } - - private static JsonWebToken ValidateSignatureUsingDelegates(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - if (validationParameters.SignatureValidatorUsingConfiguration != null) - { - var validatedToken = validationParameters.SignatureValidatorUsingConfiguration(jsonWebToken.EncodedToken, validationParameters, configuration); - if (validatedToken == null) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10505, jsonWebToken))); - - if (!(validatedToken is JsonWebToken validatedJsonWebToken)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10506, LogHelper.MarkAsNonPII(typeof(JsonWebToken)), LogHelper.MarkAsNonPII(validatedToken.GetType()), jsonWebToken))); - - return validatedJsonWebToken; - } - else if (validationParameters.SignatureValidator != null) - { - var validatedToken = validationParameters.SignatureValidator(jsonWebToken.EncodedToken, validationParameters); - if (validatedToken == null) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10505, jsonWebToken))); - - if (!(validatedToken is JsonWebToken validatedJsonWebToken)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10506, LogHelper.MarkAsNonPII(typeof(JsonWebToken)), LogHelper.MarkAsNonPII(validatedToken.GetType()), jsonWebToken))); - - return validatedJsonWebToken; - } - - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10505, jsonWebToken))); - } - - private static JsonWebToken ValidateSignatureAndIssuerSecurityKey(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - JsonWebToken validatedToken = ValidateSignature(jsonWebToken, validationParameters, configuration); - Validators.ValidateIssuerSecurityKey(validatedToken.SigningKey, jsonWebToken, validationParameters, configuration); - - return validatedToken; - } - - private async ValueTask ValidateTokenPayloadAsync(JsonWebToken jsonWebToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - var expires = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Exp) ? (DateTime?)jsonWebToken.ValidTo : null; - var notBefore = jsonWebToken.HasPayloadClaim(JwtRegisteredClaimNames.Nbf) ? (DateTime?)jsonWebToken.ValidFrom : null; - - Validators.ValidateLifetime(notBefore, expires, jsonWebToken, validationParameters); - Validators.ValidateAudience(jsonWebToken.Audiences, jsonWebToken, validationParameters); - string issuer = await Validators.ValidateIssuerAsync(jsonWebToken.Issuer, jsonWebToken, validationParameters, configuration).ConfigureAwait(false); - - Validators.ValidateTokenReplay(expires, jsonWebToken.EncodedToken, validationParameters); - if (validationParameters.ValidateActor && !string.IsNullOrWhiteSpace(jsonWebToken.Actor)) - { - // Infinite recursion should not occur here, as the JsonWebToken passed into this method is (1) constructed from a string - // AND (2) the signature is successfully validated on it. (1) implies that even if there are nested actor tokens, - // they must end at some point since they cannot reference one another. (2) means that the token has a valid signature - // and (since issuer validation occurs first) came from a trusted authority. - // NOTE: More than one nested actor token should not be considered a valid token, but if we somehow encounter one, - // this code will still work properly. - TokenValidationResult tokenValidationResult = - await ValidateTokenAsync(jsonWebToken.Actor, validationParameters.ActorValidationParameters ?? validationParameters).ConfigureAwait(false); - - if (!tokenValidationResult.IsValid) - return tokenValidationResult; - } - - string tokenType = Validators.ValidateTokenType(jsonWebToken.Typ, jsonWebToken, validationParameters); - return new TokenValidationResult(jsonWebToken, this, validationParameters.Clone(), issuer) - { - IsValid = true, - TokenType = tokenType - }; - } - - /// - /// Validates the JWT signature. - /// - private static JsonWebToken ValidateSignature(JsonWebToken jwtToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - bool kidMatched = false; - IEnumerable keys = null; - - if (!jwtToken.IsSigned) - { - if (validationParameters.RequireSignedTokens) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10504, jwtToken))); - else - return jwtToken; - } - - if (validationParameters.IssuerSigningKeyResolverUsingConfiguration != null) - { - keys = validationParameters.IssuerSigningKeyResolverUsingConfiguration(jwtToken.EncodedToken, jwtToken, jwtToken.Kid, validationParameters, configuration); - } - else if (validationParameters.IssuerSigningKeyResolver != null) - { - keys = validationParameters.IssuerSigningKeyResolver(jwtToken.EncodedToken, jwtToken, jwtToken.Kid, validationParameters); - } - else - { - var key = JwtTokenUtilities.ResolveTokenSigningKey(jwtToken.Kid, jwtToken.X5t, validationParameters, configuration); - if (key != null) - { - kidMatched = true; - keys = [key]; - } - } - - if (validationParameters.TryAllIssuerSigningKeys && keys.IsNullOrEmpty()) - { - // control gets here if: - // 1. User specified delegate: IssuerSigningKeyResolver returned null - // 2. ResolveIssuerSigningKey returned null - // Try all the keys. This is the degenerate case, not concerned about perf. - keys = TokenUtilities.GetAllSigningKeys(configuration, validationParameters); - } - - // keep track of exceptions thrown, keys that were tried - StringBuilder exceptionStrings = null; - StringBuilder keysAttempted = null; - var kidExists = !string.IsNullOrEmpty(jwtToken.Kid); - - if (keys != null) - { - foreach (var key in keys) - { - try - { - if (ValidateSignature(jwtToken, key, validationParameters)) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(TokenLogMessages.IDX10242, jwtToken); - - jwtToken.SigningKey = key; - return jwtToken; - } - } - catch (Exception ex) - { - (exceptionStrings ??= new StringBuilder()).AppendLine(ex.ToString()); - } - - if (key != null) - { - (keysAttempted ??= new StringBuilder()).Append(key.ToString()).Append(" , KeyId: ").AppendLine(key.KeyId); - if (kidExists && !kidMatched && key.KeyId != null) - kidMatched = jwtToken.Kid.Equals(key.KeyId, key is X509SecurityKey ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); - } - } - } - - // Get information on where keys used during token validation came from for debugging purposes. - var keysInTokenValidationParameters = TokenUtilities.GetAllSigningKeys(validationParameters: validationParameters); - var keysInConfiguration = TokenUtilities.GetAllSigningKeys(configuration); - var numKeysInTokenValidationParameters = keysInTokenValidationParameters.Count(); - var numKeysInConfiguration = keysInConfiguration.Count(); - - if (kidExists) - { - if (kidMatched) - { - JsonWebToken localJwtToken = jwtToken; // avoid closure on non-exceptional path - var isKidInTVP = keysInTokenValidationParameters.Any(x => x.KeyId.Equals(localJwtToken.Kid)); - var keyLocation = isKidInTVP ? "TokenValidationParameters" : "Configuration"; - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidSignatureException(LogHelper.FormatInvariant(TokenLogMessages.IDX10511, - LogHelper.MarkAsNonPII((object)keysAttempted ?? ""), - LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), - LogHelper.MarkAsNonPII(numKeysInConfiguration), - LogHelper.MarkAsNonPII(keyLocation), - LogHelper.MarkAsNonPII(jwtToken.Kid), - (object)exceptionStrings ?? "", - jwtToken))); - } - - if (!validationParameters.ValidateSignatureLast) - { - InternalValidators.ValidateAfterSignatureFailed( - jwtToken, - jwtToken.ValidFromNullable, - jwtToken.ValidToNullable, - jwtToken.Audiences, - validationParameters, - configuration); - } - } - - if (keysAttempted is not null) - { - if (kidExists) - { - throw LogHelper.LogExceptionMessage(new SecurityTokenSignatureKeyNotFoundException(LogHelper.FormatInvariant(TokenLogMessages.IDX10503, - LogHelper.MarkAsNonPII(jwtToken.Kid), - LogHelper.MarkAsNonPII((object)keysAttempted ?? ""), - LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), - LogHelper.MarkAsNonPII(numKeysInConfiguration), - (object)exceptionStrings ?? "", - jwtToken))); - } - else - { - throw LogHelper.LogExceptionMessage(new SecurityTokenSignatureKeyNotFoundException(LogHelper.FormatInvariant(TokenLogMessages.IDX10517, - LogHelper.MarkAsNonPII((object)keysAttempted ?? ""), - LogHelper.MarkAsNonPII(numKeysInTokenValidationParameters), - LogHelper.MarkAsNonPII(numKeysInConfiguration), - (object)exceptionStrings ?? "", - jwtToken))); - } - } - - throw LogHelper.LogExceptionMessage(new SecurityTokenSignatureKeyNotFoundException(TokenLogMessages.IDX10500)); - } - - internal static bool IsSignatureValid(byte[] signatureBytes, int signatureBytesLength, SignatureProvider signatureProvider, byte[] dataToVerify, int dataToVerifyLength) - { - if (signatureProvider is SymmetricSignatureProvider) - { - return signatureProvider.Verify(dataToVerify, 0, dataToVerifyLength, signatureBytes, 0, signatureBytesLength); - } - else - { - if (signatureBytes.Length == signatureBytesLength) - { - return signatureProvider.Verify(dataToVerify, 0, dataToVerifyLength, signatureBytes, 0, signatureBytesLength); - } - else - { - byte[] sigBytes = new byte[signatureBytesLength]; - Array.Copy(signatureBytes, 0, sigBytes, 0, signatureBytesLength); - return signatureProvider.Verify(dataToVerify, 0, dataToVerifyLength, sigBytes, 0, signatureBytesLength); - } - } - } - - internal static bool ValidateSignature(byte[] bytes, int len, string stringWithSignature, int signatureStartIndex, SignatureProvider signatureProvider) - { - return Base64UrlEncoding.Decode( - stringWithSignature, - signatureStartIndex + 1, - stringWithSignature.Length - signatureStartIndex - 1, - signatureProvider, - bytes, - len, - IsSignatureValid); - } - - internal static bool ValidateSignature(JsonWebToken jsonWebToken, SecurityKey key, TokenValidationParameters validationParameters) - { - var cryptoProviderFactory = validationParameters.CryptoProviderFactory ?? key.CryptoProviderFactory; - if (!cryptoProviderFactory.IsSupportedAlgorithm(jsonWebToken.Alg, key)) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX14000, LogHelper.MarkAsNonPII(jsonWebToken.Alg), key); - - return false; - } - - Validators.ValidateAlgorithm(jsonWebToken.Alg, key, jsonWebToken, validationParameters); - var signatureProvider = cryptoProviderFactory.CreateForVerifying(key, jsonWebToken.Alg); - try - { - if (signatureProvider == null) - throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(TokenLogMessages.IDX10636, key == null ? "Null" : key.ToString(), LogHelper.MarkAsNonPII(jsonWebToken.Alg)))); - - return EncodingUtils.PerformEncodingDependentOperation( - jsonWebToken.EncodedToken, - 0, - jsonWebToken.Dot2, - Encoding.UTF8, - jsonWebToken.EncodedToken, - jsonWebToken.Dot2, - signatureProvider, - ValidateSignature); - } - finally - { - cryptoProviderFactory.ReleaseSignatureProvider(signatureProvider); - } - } } } diff --git a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs index 9fb2db5350..52b84a7b37 100644 --- a/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs +++ b/src/Microsoft.IdentityModel.JsonWebTokens/JwtTokenUtilities.cs @@ -12,6 +12,7 @@ using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Json; using TokenLogMessages = Microsoft.IdentityModel.Tokens.LogMessages; @@ -599,8 +600,18 @@ internal static IEnumerable ConcatSigningKeys(TokenValidationParame } // If a string is in IS8061 format, assume a DateTime is in UTC + // Because this is a friend class, we can't remove this method without + // breaking compatibility. internal static string GetStringClaimValueType(string str) { + return GetStringClaimValueType(str, string.Empty); + } + + internal static string GetStringClaimValueType(string str, string claimType) + { + if (!string.IsNullOrEmpty(claimType) && !JsonSerializerPrimitives.TryAllStringClaimsAsDateTime() && JsonSerializerPrimitives.IsKnownToNotBeDateTime(claimType)) + return ClaimValueTypes.String; + if (DateTime.TryParse(str, out DateTime dateTimeValue)) { string dtUniversal = dateTimeValue.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture); diff --git a/src/Microsoft.IdentityModel.KeyVaultExtensions/InternalsVisibleTo.cs b/src/Microsoft.IdentityModel.KeyVaultExtensions/InternalsVisibleTo.cs deleted file mode 100644 index 5e625e7a84..0000000000 --- a/src/Microsoft.IdentityModel.KeyVaultExtensions/InternalsVisibleTo.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.KeyVaultExtensions.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultCryptoProvider.cs b/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultCryptoProvider.cs deleted file mode 100644 index 7eecfe8d82..0000000000 --- a/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultCryptoProvider.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Linq; -using Microsoft.Azure.KeyVault.WebKey; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.Tokens; - -namespace Microsoft.IdentityModel.KeyVaultExtensions -{ - /// - /// Provides cryptographic operators based on Azure Key Vault. - /// - public class KeyVaultCryptoProvider : ICryptoProvider - { - private readonly CryptoProviderCache _cache; - - /// - /// Initializes a new instance of the class. - /// - public KeyVaultCryptoProvider() - { - _cache = new InMemoryCryptoProviderCache(); - } - - /// - /// Gets the - /// - internal CryptoProviderCache CryptoProviderCache => _cache; - - /// - /// Returns a cryptographic operator that supports the algorithm. - /// - /// the algorithm that defines the cryptographic operator. - /// the arguments required by the cryptographic operator. May be null. - /// if is null or empty. - /// if is null. - /// if does not contain a . - /// call when finished with the object. - public object Create(string algorithm, params object[] args) - { - if (string.IsNullOrEmpty(algorithm)) - throw LogHelper.LogArgumentNullException(nameof(algorithm)); - - if (args == null) - throw LogHelper.LogArgumentNullException(nameof(args)); - - if (args.FirstOrDefault() is KeyVaultSecurityKey key) - { - if (JsonWebKeyEncryptionAlgorithm.AllAlgorithms.Contains(algorithm, StringComparer.Ordinal)) - return new KeyVaultKeyWrapProvider(key, algorithm); - else if (JsonWebKeySignatureAlgorithm.AllAlgorithms.Contains(algorithm, StringComparer.Ordinal)) - { - var willCreateSignatures = (bool)(args.Skip(1).FirstOrDefault() ?? false); - - if (_cache.TryGetSignatureProvider(key, algorithm, typeofProvider: key.GetType().ToString(), willCreateSignatures, out var cachedProvider)) - return cachedProvider; - - var signatureProvider = new KeyVaultSignatureProvider(key, algorithm, willCreateSignatures); - if (CryptoProviderFactory.ShouldCacheSignatureProvider(signatureProvider)) - _cache.TryAdd(signatureProvider); - - return signatureProvider; - } - } - - throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10652, LogHelper.MarkAsNonPII(algorithm)))); - } - - /// - /// Called to determine if a cryptographic operation is supported. - /// - /// the algorithm that defines the cryptographic operator. - /// the arguments required by the cryptographic operator. May be null. - /// true if supported - public bool IsSupportedAlgorithm(string algorithm, params object[] args) - { - if (string.IsNullOrEmpty(algorithm)) - throw LogHelper.LogArgumentNullException(nameof(algorithm)); - - if (args == null) - throw LogHelper.LogArgumentNullException(nameof(args)); - - return args.FirstOrDefault() is KeyVaultSecurityKey - && (JsonWebKeyEncryptionAlgorithm.AllAlgorithms.Contains(algorithm, StringComparer.Ordinal) || JsonWebKeySignatureAlgorithm.AllAlgorithms.Contains(algorithm, StringComparer.Ordinal)); - } - - /// - /// Called to release the object returned from - /// - /// the object returned from . - public void Release(object cryptoInstance) - { - if (cryptoInstance is SignatureProvider signatureProvider) - _cache.TryRemove(signatureProvider); - - if (cryptoInstance is IDisposable obj) - obj.Dispose(); - } - } -} diff --git a/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultKeyWrapProvider.cs b/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultKeyWrapProvider.cs deleted file mode 100644 index 63c625fe03..0000000000 --- a/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultKeyWrapProvider.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.KeyVault; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.Tokens; - -namespace Microsoft.IdentityModel.KeyVaultExtensions -{ - /// - /// Provides wrap and unwrap operations using Azure Key Vault. - /// - public class KeyVaultKeyWrapProvider : KeyWrapProvider - { - private readonly IKeyVaultClient _client; - private readonly KeyVaultSecurityKey _key; - private readonly string _algorithm; - private bool _disposed = false; - - /// - /// Initializes a new instance of the class. - /// - /// The that will be used for key wrap operations. - /// The key wrap algorithm to apply. - /// if is null. - /// if is not a . - /// if is null or empty. - public KeyVaultKeyWrapProvider(SecurityKey key, string algorithm) - : this(key, algorithm, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The that will be used for key wrap operations. - /// The key wrap algorithm to apply. - /// A mock used for testing purposes. - internal KeyVaultKeyWrapProvider(SecurityKey key, string algorithm, IKeyVaultClient? client) - { - _algorithm = string.IsNullOrEmpty(algorithm) ? throw LogHelper.LogArgumentNullException(nameof(algorithm)) : algorithm; - if (key == null) - throw LogHelper.LogArgumentNullException(nameof(key)); - - _key = key as KeyVaultSecurityKey ?? throw LogHelper.LogExceptionMessage(new NotSupportedException(key.GetType().ToString())); - _client = client ?? new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(_key.Callback!)); - } - - /// - /// Gets the KeyWrap algorithm that is being used. - /// - public override string Algorithm => _algorithm; - - /// - /// Gets or sets a user context for a . - /// - /// This is null by default. This can be used by runtimes or for extensibility scenarios. - public override string? Context { get; set; } - - /// - /// Gets the that is being used. - /// - public override SecurityKey Key => _key; - - /// - /// Unwrap a key. - /// - /// key to unwrap. - /// if is null. - /// if .Length == 0. - /// Unwrapped key. - public override byte[] UnwrapKey(byte[] keyBytes) - { - return UnwrapKeyAsync(keyBytes, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - /// - /// Wrap a key. - /// - /// the key to be wrapped - /// if is null. - /// if .Length == 0. - /// wrapped key. - public override byte[] WrapKey(byte[] keyBytes) - { - return WrapKeyAsync(keyBytes, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - /// true, if called from Dispose(), false, if invoked inside a finalizer - protected override void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - _disposed = true; - _client.Dispose(); - } - } - } - - /// - /// Unwraps a symmetric key using Azure Key Vault. - /// - /// key to unwrap. - /// Propagates notification that operations should be canceled. - /// if is null. - /// if .Length == 0. - /// Unwrapped key. - private async Task UnwrapKeyAsync(byte[] keyBytes, CancellationToken cancellation) - { - if (keyBytes == null || keyBytes.Length == 0) - throw LogHelper.LogArgumentNullException(nameof(keyBytes)); - - return (await _client.UnwrapKeyAsync(_key.KeyId, Algorithm, keyBytes, cancellation).ConfigureAwait(false)).Result; - } - - /// - /// Wraps a symmetric key using Azure Key Vault. - /// - /// the key to be wrapped - /// Propagates notification that operations should be canceled. - /// if is null. - /// if .Length == 0. - /// wrapped key. - private async Task WrapKeyAsync(byte[] keyBytes, CancellationToken cancellation) - { - if (keyBytes == null || keyBytes.Length == 0) - throw LogHelper.LogArgumentNullException(nameof(keyBytes)); - - return (await _client.WrapKeyAsync(_key.KeyId, Algorithm, keyBytes, cancellation).ConfigureAwait(false)).Result; - } - } -} diff --git a/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultSecurityKey.cs b/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultSecurityKey.cs deleted file mode 100644 index 9a56ce27e8..0000000000 --- a/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultSecurityKey.cs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.KeyVault; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.Tokens; - -namespace Microsoft.IdentityModel.KeyVaultExtensions -{ - /// - /// Provides signing and verifying operations using Azure Key Vault. - /// - public class KeyVaultSecurityKey : SecurityKey - { - private int? _keySize; - private string? _keyId; - - /// - /// The authentication callback delegate which is to be implemented by the client code. - /// - /// Identifier of the authority, a URL. - /// Identifier of the target resource that is the recipient of the requested token, a URL. - /// The scope of the authentication request. - /// An access token for Azure Key Vault. - public delegate Task AuthenticationCallback(string authority, string resource, string scope); - - /// - /// Initializes a new instance of the class. - /// - protected KeyVaultSecurityKey() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// The key identifier that is recognized by KeyVault. - /// The authentication callback that will obtain the access_token for KeyVault. - /// if is null or empty. - /// if is null. - public KeyVaultSecurityKey(string keyIdentifier, AuthenticationCallback callback) - { - Callback = callback ?? throw LogHelper.LogArgumentNullException(nameof(callback)); - KeyId = keyIdentifier; - } - - internal KeyVaultSecurityKey(string keyIdentifier, int keySize) - { - _keyId = keyIdentifier; - _keySize = keySize; - } - - /// - /// The authentication callback delegate that retrieves an access token for the KeyVault. - /// - public AuthenticationCallback? Callback { get; protected set; } - - /// - /// The uniform resource identifier of the security key. - /// - public override string KeyId - { - get => _keyId!; - set - { - if (string.IsNullOrEmpty(value)) - throw LogHelper.LogArgumentNullException(nameof(value)); - else if (StringComparer.Ordinal.Equals(_keyId, value)) - return; - - _keyId = value; - - // Reset the properties so they can be retrieved from Azure KeyVault the next time they are accessed. - _keySize = null; - } - } - - /// - /// The size of the security key. - /// - public override int KeySize - { - get - { - if (!_keySize.HasValue) - Initialize(); - - return _keySize!.Value; - } - } - - /// - /// Retrieve the properties from Azure Key Vault. - /// - private void Initialize() - { - using (var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(Callback!))) - { - var bundle = client.GetKeyAsync(_keyId, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); - _keySize = new BitArray(bundle.Key.N).Length; - } - } - } -} diff --git a/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultSignatureProvider.cs b/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultSignatureProvider.cs deleted file mode 100644 index 38478482fd..0000000000 --- a/src/Microsoft.IdentityModel.KeyVaultExtensions/KeyVaultSignatureProvider.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.KeyVault; -using Microsoft.IdentityModel.Logging; -using Microsoft.IdentityModel.Tokens; - -namespace Microsoft.IdentityModel.KeyVaultExtensions -{ - /// - /// Provides signing and verifying operations using Azure Key Vault. - /// - public class KeyVaultSignatureProvider : SignatureProvider - { - private readonly HashAlgorithm _hash; - private readonly IKeyVaultClient _client; - private readonly KeyVaultSecurityKey _key; - private bool _disposed = false; - - /// - /// Initializes a new instance of the class. - /// - /// The that will be used for signature operations. - /// The signature algorithm to apply. - /// Whether this is required to create signatures then set this to true. - /// is null. - /// is null or empty. - public KeyVaultSignatureProvider(SecurityKey key, string algorithm, bool willCreateSignatures) - : this(key, algorithm, willCreateSignatures, null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The that will be used for signature operations. - /// The signature algorithm to apply. - /// Whether this is required to create signatures then set this to true. - /// A mock used for testing purposes. - internal KeyVaultSignatureProvider(SecurityKey key, string algorithm, bool willCreateSignatures, IKeyVaultClient? client) - : base(key, algorithm) - { - _key = key as KeyVaultSecurityKey ?? throw LogHelper.LogArgumentNullException(nameof(key)); - _client = client ?? new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(_key.Callback!)); - WillCreateSignatures = willCreateSignatures; - - switch (algorithm) - { - case SecurityAlgorithms.RsaSha256: - _hash = SHA256.Create(); - break; - case SecurityAlgorithms.RsaSha384: - _hash = SHA384.Create(); - break; - case SecurityAlgorithms.RsaSha512: - _hash = SHA512.Create(); - break; - default: - throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10652, LogHelper.MarkAsNonPII(algorithm)), nameof(algorithm))); - } - } - - /// - /// Produces a signature over the 'input' using Azure Key Vault. - /// - /// The bytes to sign. - /// A signature over the input. - /// if is null. - /// if .Length == 0. - /// If Dispose has been called. - public override byte[] Sign(byte[] input) - { - return SignAsync(input, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - /// - /// Verifies that the is over using Azure Key Vault. - /// - /// bytes to verify. - /// signature to compare against. - /// true if the computed signature matches the signature parameter, false otherwise. - /// is null or has length == 0. - /// is null or has length == 0. - /// If Dispose has been called. - public override bool Verify(byte[] input, byte[] signature) - { - return VerifyAsync(input, signature, CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - /// - public override bool Verify(byte[] input, int inputOffset, int lengthOffset, byte[] signature, int signatureOffset, int signatureLength) => throw new NotImplementedException(); - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - /// true, if called from Dispose(), false, if invoked inside a finalizer - protected override void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - _disposed = true; - _hash.Dispose(); - _client.Dispose(); - } - } - } - - /// - /// Creates a digital signature using Azure Key Vault. - /// - /// bytes to sign. - /// Propagates notification that operations should be canceled. - /// A signature over the input. - /// is null or has length == 0. - /// If Dispose has been called. - private async Task SignAsync(byte[] input, CancellationToken cancellation) - { - if (input == null || input.Length == 0) - throw LogHelper.LogArgumentNullException(nameof(input)); - - if (_disposed) - throw LogHelper.LogExceptionMessage(new ObjectDisposedException(GetType().ToString())); - - return (await _client.SignAsync(_key.KeyId, Algorithm, _hash.ComputeHash(input), cancellation).ConfigureAwait(false)).Result; - } - - /// - /// Verifies a digital signature using Azure Key Vault. - /// - /// bytes to verify. - /// signature to compare against. - /// Propagates notification that operations should be canceled. - /// true if the computed signature matches the signature parameter, false otherwise. - /// is null or has length == 0. - /// is null or has length == 0. - /// If Dispose has been called. - private async Task VerifyAsync(byte[] input, byte[] signature, CancellationToken cancellation) - { - if (input == null || input.Length == 0) - throw LogHelper.LogArgumentNullException(nameof(input)); - - if (signature == null || signature.Length == 0) - throw LogHelper.LogArgumentNullException(nameof(signature)); - - if (_disposed) - throw LogHelper.LogExceptionMessage(new ObjectDisposedException(GetType().ToString())); - - return await _client.VerifyAsync(_key.KeyId, Algorithm, _hash.ComputeHash(input), signature, cancellation).ConfigureAwait(false); - } - } -} diff --git a/src/Microsoft.IdentityModel.KeyVaultExtensions/Microsoft.IdentityModel.KeyVaultExtensions.csproj b/src/Microsoft.IdentityModel.KeyVaultExtensions/Microsoft.IdentityModel.KeyVaultExtensions.csproj deleted file mode 100644 index 2825f85730..0000000000 --- a/src/Microsoft.IdentityModel.KeyVaultExtensions/Microsoft.IdentityModel.KeyVaultExtensions.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - Microsoft.IdentityModel.KeyVaultExtensions - Includes types that provide support for signing and encrypting tokens with Azure Key Vault. - true - Microsoft.IdentityModel.KeyVaultExtensions - netstandard2.0;net6.0;net8.0 - $(TargetFrameworks);net9.0 - .NET;Windows;Authentication;Identity;Azure;Key;Vault;Extensions - enable - - - - full - true - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - diff --git a/src/Microsoft.IdentityModel.KeyVaultExtensions/Properties/AssemblyInfo.cs b/src/Microsoft.IdentityModel.KeyVaultExtensions/Properties/AssemblyInfo.cs deleted file mode 100644 index 1f06ec3782..0000000000 --- a/src/Microsoft.IdentityModel.KeyVaultExtensions/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyMetadata("Serviceable", "True")] -[assembly: CLSCompliant(true)] -[assembly: ComVisible(false)] diff --git a/src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs b/src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs index 0a29c91991..74a8a0e7ac 100644 --- a/src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs +++ b/src/Microsoft.IdentityModel.Logging/IdentityModelTelemetryUtil.cs @@ -27,9 +27,7 @@ public static class IdentityModelTelemetryUtil /// Get the string that represents the client SKU. /// public static string ClientSku => -#if NET461 - "ID_NET461"; -#elif NET462 +#if NET462 "ID_NET462"; #elif NET472 "ID_NET472"; diff --git a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/GlobalSuppression.cs b/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/GlobalSuppression.cs deleted file mode 100644 index 04498d0dc3..0000000000 --- a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/GlobalSuppression.cs +++ /dev/null @@ -1,8 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Naming", "CA1724:Type names should not match namespaces", Justification = "Previously released with this name", Scope ="Type", Target = "Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.ManagedKeyVaultSecurityKey")] diff --git a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/InternalsVisibleTo.cs b/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/InternalsVisibleTo.cs deleted file mode 100644 index cae70d8a10..0000000000 --- a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/InternalsVisibleTo.cs +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.Tokens.Extensions.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/ManagedKeyVaultSecurityKey.cs b/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/ManagedKeyVaultSecurityKey.cs deleted file mode 100644 index 78ce01f9ee..0000000000 --- a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/ManagedKeyVaultSecurityKey.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Azure.Services.AppAuthentication; -using Microsoft.IdentityModel.Clients.ActiveDirectory; -using Microsoft.IdentityModel.KeyVaultExtensions; -using Microsoft.IdentityModel.Logging; -using System; - -namespace Microsoft.IdentityModel.ManagedKeyVaultSecurityKey -{ - /// - /// Provides signing and verifying operations using Azure Key Vault - /// for resources that are using Managed identities for Azure resources. - /// - public class ManagedKeyVaultSecurityKey : KeyVaultSecurityKey - { - /// - /// Initializes a new instance of the class. - /// - /// The key identifier that is recognized by KeyVault. - /// if is null or empty. - public ManagedKeyVaultSecurityKey(string keyIdentifier) - : base(keyIdentifier, new AuthenticationCallback((new AzureServiceTokenProvider()).KeyVaultTokenCallback)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The key identifier that is recognized by KeyVault. - /// The authentication callback. - /// if is null or empty. - /// if is null. - public ManagedKeyVaultSecurityKey(string keyIdentifier, AuthenticationCallback callback) - : base(keyIdentifier, callback) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The key identifier. - /// Identifier of the client. - /// Secret of the client identity. - /// if is null or empty. - /// if is null or empty. - /// if is null or clientSecret. - public ManagedKeyVaultSecurityKey(string keyIdentifier, string clientId, string clientSecret) - { - if (string.IsNullOrEmpty(keyIdentifier)) - throw LogHelper.LogArgumentNullException(nameof(keyIdentifier)); - - if (string.IsNullOrEmpty(clientId)) - throw LogHelper.LogArgumentNullException(nameof(clientId)); - - if (string.IsNullOrEmpty(clientSecret)) - throw LogHelper.LogArgumentNullException(nameof(clientSecret)); - - KeyId = keyIdentifier; - Callback = new AuthenticationCallback(async (string authority, string resource, string scope) => - (await (new AuthenticationContext(authority, TokenCache.DefaultShared)).AcquireTokenAsync(resource, new ClientCredential(clientId, clientSecret)).ConfigureAwait(false)).AccessToken); - } - } -} diff --git a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.csproj b/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.csproj deleted file mode 100644 index aa342135ca..0000000000 --- a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - Microsoft.IdentityModel.ManagedKeyVaultSecurityKey - Includes types that provide support for signing and encrypting tokens with Azure Key Vault for - Applications that are using Managed identities for Azure resources. - true - Microsoft.IdentityModel.ManagedKeyVaultSecurityKey - netstandard2.0 - .NET;Windows;Authentication;Identity;Azure;Key;Vault;Extensions - enable - - - - full - true - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - diff --git a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/Properties/AssemblyInfo.cs b/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/Properties/AssemblyInfo.cs deleted file mode 100644 index 1f06ec3782..0000000000 --- a/src/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyMetadata("Serviceable", "True")] -[assembly: CLSCompliant(true)] -[assembly: ComVisible(false)] diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs index 0d3555ad69..c513056b7d 100644 --- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs +++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfiguration.cs @@ -23,21 +23,31 @@ public class OpenIdConnectConfiguration : BaseConfiguration // these are used to lazy create private Dictionary _additionalData; private ICollection _acrValuesSupported; + private ICollection _authorizationEncryptionAlgValuesSupported; + private ICollection _authorizationEncryptionEncValuesSupported; + private ICollection _authorizationSigningAlgValuesSupported; + private ICollection _backchannelAuthenticationRequestSigningAlgValuesSupported; + private ICollection _backchannelTokenDeliveryModesSupported; private ICollection _claimsSupported; private ICollection _claimsLocalesSupported; private ICollection _claimTypesSupported; + private ICollection _codeChallengeMethodsSupported; private ICollection _displayValuesSupported; + private ICollection _dPoPSigningAlgValuesSupported; private ICollection _grantTypesSupported; private ICollection _idTokenEncryptionAlgValuesSupported; private ICollection _idTokenEncryptionEncValuesSupported; private ICollection _idTokenSigningAlgValuesSupported; private ICollection _introspectionEndpointAuthMethodsSupported; private ICollection _introspectionEndpointAuthSigningAlgValuesSupported; + private ICollection _promptValuesSupported; private ICollection _requestObjectEncryptionAlgValuesSupported; private ICollection _requestObjectEncryptionEncValuesSupported; private ICollection _requestObjectSigningAlgValuesSupported; private ICollection _responseModesSupported; private ICollection _responseTypesSupported; + private ICollection _revocationEndpointAuthMethodsSupported; + private ICollection _revocationEndpointAuthSigningAlgValuesSupported; private ICollection _scopesSupported; private ICollection _subjectTypesSupported; private ICollection _tokenEndpointAuthMethodsSupported; @@ -139,6 +149,78 @@ public OpenIdConnectConfiguration(string json) #endif public string AuthorizationEndpoint { get; set; } + /// + /// Gets the collection of 'authorization_encryption_alg_values_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.AuthorizationEncryptionAlgValuesSupported)] + public ICollection AuthorizationEncryptionAlgValuesSupported => + _authorizationEncryptionAlgValuesSupported ?? + Interlocked.CompareExchange(ref _authorizationEncryptionAlgValuesSupported, new Collection(), null) ?? + _authorizationEncryptionAlgValuesSupported; + + /// + /// Gets the collection of 'authorization_encryption_enc_values_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.AuthorizationEncryptionEncValuesSupported)] + public ICollection AuthorizationEncryptionEncValuesSupported => + _authorizationEncryptionEncValuesSupported ?? + Interlocked.CompareExchange(ref _authorizationEncryptionEncValuesSupported, new Collection(), null) ?? + _authorizationEncryptionEncValuesSupported; + + /// + /// Gets or sets the 'authorization_response_iss_parameter_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.AuthorizationResponseIssParameterSupported)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +#endif + public bool AuthorizationResponseIssParameterSupported { get; set; } + + /// + /// Gets the collection of 'authorization_signing_alg_values_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.AuthorizationSigningAlgValuesSupported)] + public ICollection AuthorizationSigningAlgValuesSupported => + _authorizationSigningAlgValuesSupported ?? + Interlocked.CompareExchange(ref _authorizationSigningAlgValuesSupported, new Collection(), null) ?? + _authorizationSigningAlgValuesSupported; + + /// + /// Gets or sets the 'backchannel_authentication_endpoint'. + /// + [JsonPropertyName(OpenIdProviderMetadataNames.BackchannelAuthenticationEndpoint)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif + public string BackchannelAuthenticationEndpoint { get; set; } + + /// + /// Gets the collection of 'backchannel_authentication_request_signing_alg_values_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.BackchannelAuthenticationRequestSigningAlgValuesSupported)] + public ICollection BackchannelAuthenticationRequestSigningAlgValuesSupported => + _backchannelAuthenticationRequestSigningAlgValuesSupported ?? + Interlocked.CompareExchange(ref _backchannelAuthenticationRequestSigningAlgValuesSupported, new Collection(), null) ?? + _backchannelAuthenticationRequestSigningAlgValuesSupported; + + /// + /// Gets the collection of 'backchannel_token_delivery_modes_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.BackchannelTokenDeliveryModesSupported)] + public ICollection BackchannelTokenDeliveryModesSupported => + _backchannelTokenDeliveryModesSupported ?? + Interlocked.CompareExchange(ref _backchannelTokenDeliveryModesSupported, new Collection(), null) ?? + _backchannelTokenDeliveryModesSupported; + + /// + /// Gets or sets the 'backchannel_user_code_parameter_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.BackchannelUserCodeParameterSupported)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +#endif + public bool BackchannelUserCodeParameterSupported { get; set; } + /// /// Gets or sets the 'check_session_iframe'. /// @@ -184,6 +266,24 @@ public OpenIdConnectConfiguration(string json) Interlocked.CompareExchange(ref _claimTypesSupported, new Collection(), null) ?? _claimTypesSupported; + /// + /// Gets the collection of 'code_challenge_methods_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.CodeChallengeMethodsSupported)] + public ICollection CodeChallengeMethodsSupported => + _codeChallengeMethodsSupported ?? + Interlocked.CompareExchange(ref _codeChallengeMethodsSupported, new Collection(), null) ?? + _codeChallengeMethodsSupported; + + /// + /// Gets or sets the 'device_authorization_endpoint'. + /// + [JsonPropertyName(OpenIdProviderMetadataNames.DeviceAuthorizationEndpoint)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif + public string DeviceAuthorizationEndpoint { get; set; } + /// /// Gets the collection of 'display_values_supported' /// @@ -193,6 +293,15 @@ public OpenIdConnectConfiguration(string json) Interlocked.CompareExchange(ref _displayValuesSupported, new Collection(), null) ?? _displayValuesSupported; + /// + /// Gets the collection of 'dpop_signing_alg_values_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.DPoPSigningAlgValuesSupported)] + public ICollection DPoPSigningAlgValuesSupported => + _dPoPSigningAlgValuesSupported ?? + Interlocked.CompareExchange(ref _dPoPSigningAlgValuesSupported, new Collection(), null) ?? + _dPoPSigningAlgValuesSupported; + /// /// Gets or sets the 'end_session_endpoint'. /// @@ -316,7 +425,7 @@ public OpenIdConnectConfiguration(string json) /// Gets or sets the /// [JsonIgnore] - public JsonWebKeySet JsonWebKeySet {get; set;} + public JsonWebKeySet JsonWebKeySet { get; set; } /// /// Boolean value specifying whether the OP can pass a sid (session ID) query parameter to identify the RP session at the OP when the logout_uri is used. Dafault Value is false. @@ -342,6 +451,24 @@ public OpenIdConnectConfiguration(string json) #endif public string OpTosUri { get; set; } + /// + /// Gets the collection of 'prompt_values_supported' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.PromptValuesSupported)] + public ICollection PromptValuesSupported => + _promptValuesSupported ?? + Interlocked.CompareExchange(ref _promptValuesSupported, new Collection(), null) ?? + _promptValuesSupported; + + /// + /// Gets or sets the 'pushed_authorization_request_endpoint'. + /// + [JsonPropertyName(OpenIdProviderMetadataNames.PushedAuthorizationRequestEndpoint)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif + public string PushedAuthorizationRequestEndpoint { get; set; } + /// /// Gets or sets the 'registration_endpoint' /// @@ -396,6 +523,15 @@ public OpenIdConnectConfiguration(string json) #endif public bool RequestUriParameterSupported { get; set; } + /// + /// Gets or sets the 'require_pushed_authorization_requests' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.RequirePushedAuthorizationRequests)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +#endif + public bool RequirePushedAuthorizationRequests { get; set; } + /// /// Gets or sets the 'require_request_uri_registration' /// @@ -423,6 +559,33 @@ public OpenIdConnectConfiguration(string json) Interlocked.CompareExchange(ref _responseTypesSupported, new Collection(), null) ?? _responseTypesSupported; + /// + /// Gets or sets the 'revocation_endpoint' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.RevocationEndpoint)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] +#endif + public string RevocationEndpoint { get; set; } + + /// + /// Gets the collection of 'revocation_endpoint_auth_methods_supported'. + /// + [JsonPropertyName(OpenIdProviderMetadataNames.RevocationEndpointAuthMethodsSupported)] + public ICollection RevocationEndpointAuthMethodsSupported => + _revocationEndpointAuthMethodsSupported ?? + Interlocked.CompareExchange(ref _revocationEndpointAuthMethodsSupported, new Collection(), null) ?? + _revocationEndpointAuthMethodsSupported; + + /// + /// Gets the collection of 'revocation_endpoint_auth_signing_alg_values_supported'. + /// + [JsonPropertyName(OpenIdProviderMetadataNames.RevocationEndpointAuthSigningAlgValuesSupported)] + public ICollection RevocationEndpointAuthSigningAlgValuesSupported => + _revocationEndpointAuthSigningAlgValuesSupported ?? + Interlocked.CompareExchange(ref _revocationEndpointAuthSigningAlgValuesSupported, new Collection(), null) ?? + _revocationEndpointAuthSigningAlgValuesSupported; + /// /// Gets or sets the 'service_documentation' /// @@ -466,7 +629,7 @@ public OpenIdConnectConfiguration(string json) public override string TokenEndpoint { get; set; } /// - /// This base class property is not used in OpenIdConnect. + /// This base class property is not used in OpenIdConnect. /// [JsonIgnore] public override string ActiveTokenEndpoint { get; set; } @@ -489,6 +652,15 @@ public OpenIdConnectConfiguration(string json) Interlocked.CompareExchange(ref _tokenEndpointAuthSigningAlgValuesSupported, new Collection(), null) ?? _tokenEndpointAuthSigningAlgValuesSupported; + /// + /// Gets or sets the 'tls_client_certificate_bound_access_tokens' + /// + [JsonPropertyName(OpenIdProviderMetadataNames.TlsClientCertificateBoundAccessTokens)] +#if NET6_0_OR_GREATER + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] +#endif + public bool TlsClientCertificateBoundAccessTokens { get; set; } + /// /// Gets the collection of 'ui_locales_supported' /// @@ -548,6 +720,61 @@ public bool ShouldSerializeAcrValuesSupported() return AcrValuesSupported.Count > 0; } + /// + /// Gets a bool that determines if the 'authorization_encryption_alg_values_supported' (AuthorizationEncryptionAlgValuesSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'authorization_encryption_alg_values_supported' (AuthorizationEncryptionAlgValuesSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeAuthorizationEncryptionAlgValuesSupported() + { + return AuthorizationEncryptionAlgValuesSupported.Count > 0; + } + + /// + /// Gets a bool that determines if the 'authorization_encryption_enc_values_supported' (AuthorizationEncryptionEncValuesSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'authorization_encryption_enc_values_supported' (AuthorizationEncryptionEncValuesSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeAuthorizationEncryptionEncValuesSupported() + { + return AuthorizationEncryptionEncValuesSupported.Count > 0; + } + + /// + /// Gets a bool that determines if the 'authorization_signing_alg_values_supported' (AuthorizationSigningAlgValuesSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'authorization_signing_alg_values_supported' (AuthorizationSigningAlgValuesSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeAuthorizationSigningAlgValuesSupported() + { + return AuthorizationSigningAlgValuesSupported.Count > 0; + } + + /// + /// Gets a bool that determines if the 'backchannel_token_delivery_modes_supported' (BackchannelTokenDeliveryModesSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'backchannel_token_delivery_modes_supported' (BackchannelTokenDeliveryModesSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeBackchannelTokenDeliveryModesSupported() + { + return BackchannelTokenDeliveryModesSupported.Count > 0; + } + + /// + /// Gets a bool that determines if the 'backchannel_authentication_request_signing_alg_values_supported' (BackchannelAuthenticationRequestSigningAlgValuesSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'backchannel_authentication_request_signing_alg_values_supported' (BackchannelAuthenticationRequestSigningAlgValuesSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeBackchannelAuthenticationRequestSigningAlgValuesSupported() + { + return BackchannelAuthenticationRequestSigningAlgValuesSupported.Count > 0; + } + /// /// Gets a bool that determines if the 'claims_supported' (ClaimsSupported) property should be serialized. /// This is used by Json.NET in order to conditionally serialize properties. @@ -581,6 +808,17 @@ public bool ShouldSerializeClaimTypesSupported() return ClaimTypesSupported.Count > 0; } + /// + /// Gets a bool that determines if the 'code_challenge_methods_supported' (CodeChallengeMethodsSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'code_challenge_methods_supported' (CodeChallengeMethodsSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeCodeChallengeMethodsSupported() + { + return CodeChallengeMethodsSupported.Count > 0; + } + /// /// Gets a bool that determines if the 'display_values_supported' (DisplayValuesSupported) property should be serialized. /// This is used by Json.NET in order to conditionally serialize properties. @@ -592,6 +830,17 @@ public bool ShouldSerializeDisplayValuesSupported() return DisplayValuesSupported.Count > 0; } + /// + /// Gets a bool that determines if the 'dpop_signing_alg_values_supported' (DPoPSigningAlgValuesSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'dpop_signing_alg_values_supported' (DPoPSigningAlgValuesSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeDPoPSigningAlgValuesSupported() + { + return DPoPSigningAlgValuesSupported.Count > 0; + } + /// /// Gets a bool that determines if the 'grant_types_supported' (GrantTypesSupported) property should be serialized. /// This is used by Json.NET in order to conditionally serialize properties. @@ -658,6 +907,17 @@ public bool ShouldSerializeIntrospectionEndpointAuthSigningAlgValuesSupported() return IntrospectionEndpointAuthSigningAlgValuesSupported.Count > 0; } + /// + /// Gets a bool that determines if the 'prompt_values_supported' (PromptValuesSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'prompt_values_supported' (PromptValuesSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializePromptValuesSupported() + { + return PromptValuesSupported.Count > 0; + } + /// /// Gets a bool that determines if the 'request_object_encryption_alg_values_supported' (RequestObjectEncryptionAlgValuesSupported) property should be serialized. /// This is used by Json.NET in order to conditionally serialize properties. @@ -713,6 +973,28 @@ public bool ShouldSerializeResponseTypesSupported() return ResponseTypesSupported.Count > 0; } + /// + /// Gets a bool that determines if the 'revocation_endpoint_auth_methods_supported' (RevocationEndpointAuthMethodsSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'revocation_endpoint_auth_methods_supported' (RevocationEndpointAuthMethodsSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeRevocationEndpointAuthMethodsSupported() + { + return RevocationEndpointAuthMethodsSupported.Count > 0; + } + + /// + /// Gets a bool that determines if the 'revocation_endpoint_auth_signing_alg_values_supported' (RevocationEndpointAuthSigningAlgValuesSupported) property should be serialized. + /// This is used by Json.NET in order to conditionally serialize properties. + /// + /// true if 'revocation_endpoint_auth_signing_alg_values_supported' (RevocationEndpointAuthSigningAlgValuesSupported) is not empty; otherwise, false. + [EditorBrowsable(EditorBrowsableState.Never)] + public bool ShouldSerializeRevocationEndpointAuthSigningAlgValuesSupported() + { + return RevocationEndpointAuthSigningAlgValuesSupported.Count > 0; + } + /// /// Gets a bool that determines if the 'SigningKeys' property should be serialized. /// This is used by Json.NET in order to conditionally serialize properties. @@ -811,7 +1093,6 @@ public bool ShouldSerializeUserInfoEndpointSigningAlgValuesSupported() { return UserInfoEndpointSigningAlgValuesSupported.Count > 0; } - #endregion shouldserialize } } diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs index 17b354b390..d8d0c9d37b 100644 --- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs +++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Configuration/OpenIdConnectConfigurationRetriever.cs @@ -10,13 +10,11 @@ namespace Microsoft.IdentityModel.Protocols.OpenIdConnect { - /// /// Retrieves a populated given an address. /// public class OpenIdConnectConfigurationRetriever : IConfigurationRetriever { - /// /// Retrieves a populated given an address. /// diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Exceptions/OpenIdConnectProtocolException.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Exceptions/OpenIdConnectProtocolException.cs index 1cc301ed01..91585f9573 100644 --- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Exceptions/OpenIdConnectProtocolException.cs +++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Exceptions/OpenIdConnectProtocolException.cs @@ -6,7 +6,6 @@ namespace Microsoft.IdentityModel.Protocols.OpenIdConnect { - /// /// This exception is thrown when an OpenIdConnect protocol handler encounters a protocol error. /// diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Json/OpenIdConnectConfigurationSerializer.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Json/OpenIdConnectConfigurationSerializer.cs index f90a324d82..cffe63744f 100644 --- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Json/OpenIdConnectConfigurationSerializer.cs +++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/Json/OpenIdConnectConfigurationSerializer.cs @@ -3,6 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +#if NET8_0_OR_GREATER +using System.Collections.Frozen; +#endif using System.Collections.Generic; using System.IO; using System.Text; @@ -26,17 +29,34 @@ internal static class OpenIdConnectConfigurationSerializer // If not found, then we assume we should put the value into AdditionalData. // If we didn't do that, we would pay a performance penalty for those cases where there is AdditionalData // but otherwise the JSON properties are all lower case. - public static HashSet OpenIdProviderMetadataNamesUpperCase = new HashSet + public static readonly +#if NET8_0_OR_GREATER + FrozenSet +#else + HashSet +#endif + OpenIdProviderMetadataNamesUpperCase = new HashSet { "ACR_VALUES_SUPPORTED", "AUTHORIZATION_ENDPOINT", + "AUTHORIZATION_ENCRYPTION_ALG_VALUES_SUPPORTED", + "AUTHORIZATION_ENCRYPTION_ENC_VALUES_SUPPORTED", + "AUTHORIZATION_RESPONSE_ISS_PARAMETER_SUPPORTED", + "AUTHORIZATION_SIGNING_ALG_VALUES_SUPPORTED", + "BACKCHANNEL_AUTHENTICATION_ENDPOINT", + "BACKCHANNEL_AUTHENTICATION_REQUEST_SIGNING_ALG_VALUES_SUPPORTED", + "BACKCHANNEL_TOKEN_DELIVERY_MODES_SUPPORTED", + "BACKCHANNEL_USER_CODE_PARAMETER_SUPPORTED", "CHECK_SESSION_IFRAME", "CLAIMS_LOCALES_SUPPORTED", "CLAIMS_PARAMETER_SUPPORTED", "CLAIMS_SUPPORTED", "CLAIM_TYPES_SUPPORTED", + "CODE_CHALLENGE_METHODS_SUPPORTED", ".WELL-KNOWN/OPENID-CONFIGURATION", + "DEVICE_AUTHORIZATION_ENDPOINT", "DISPLAY_VALUES_SUPPORTED", + "DPOP_SIGNING_ALG_VALUES_SUPPORTED", "END_SESSION_ENDPOINT", "FRONTCHANNEL_LOGOUT_SESSION_SUPPORTED", "FRONTCHANNEL_LOGOUT_SUPPORTED", @@ -53,27 +73,38 @@ internal static class OpenIdConnectConfigurationSerializer "LOGOUT_SESSION_SUPPORTED", "OP_POLICY_URI", "OP_TOS_URI", + "PROMPT_VALUES_SUPPORTED", + "PUSHED_AUTHORIZATION_REQUEST_ENDPOINT", "REGISTRATION_ENDPOINT", "REQUEST_OBJECT_ENCRYPTION_ALG_VALUES_SUPPORTED", "REQUEST_OBJECT_ENCRYPTION_ENC_VALUES_SUPPORTED", "REQUEST_OBJECT_SIGNING_ALG_VALUES_SUPPORTED", "REQUEST_PARAMETER_SUPPORTED", "REQUEST_URI_PARAMETER_SUPPORTED", + "REQUIRE_PUSHED_AUTHORIZATION_REQUESTS", "REQUIRE_REQUEST_URI_REGISTRATION", "RESPONSE_MODES_SUPPORTED", "RESPONSE_TYPES_SUPPORTED", + "REVOCATION_ENDPOINT", + "REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED", + "REVOCATION_ENDPOINT_AUTH_SIGNING_ALG_VALUES_SUPPORTED", "SERVICE_DOCUMENTATION", "SCOPES_SUPPORTED", "SUBJECT_TYPES_SUPPORTED", "TOKEN_ENDPOINT", "TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED", "TOKEN_ENDPOINT_AUTH_SIGNING_ALG_VALUES_SUPPORTED", + "TLS_CLIENT_CERTIFICATE_BOUND_ACCESS_TOKENS", "UI_LOCALES_SUPPORTED", "USERINFO_ENDPOINT", "USERINFO_ENCRYPTION_ALG_VALUES_SUPPORTED", "USERINFO_ENCRYPTION_ENC_VALUES_SUPPORTED", "USERINFO_SIGNING_ALG_VALUES_SUPPORTED", - }; + } +#if NET8_0_OR_GREATER + .ToFrozenSet() +#endif + ; #region Read public static OpenIdConnectConfiguration Read(string json) @@ -88,7 +119,7 @@ public static OpenIdConnectConfiguration Read(string json, OpenIdConnectConfigur { return Read(ref reader, config); } - catch(JsonException ex) + catch (JsonException ex) { if (ex.GetType() == typeof(JsonException)) throw; @@ -122,7 +153,7 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC LogHelper.MarkAsNonPII(reader.CurrentDepth), LogHelper.MarkAsNonPII(reader.BytesConsumed)))); - while(true) + while (true) { #region Check property name using ValueTextEquals // https://datatracker.ietf.org/doc/html/rfc7517#section-4, does not require that we reject JSON with duplicate member names. @@ -135,6 +166,30 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (reader.ValueTextEquals(Utf8Bytes.AuthorizationEndpoint)) config.AuthorizationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.AuthorizationEndpoint, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.AuthorizationEncryptionAlgValuesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.AuthorizationEncryptionAlgValuesSupported, MetadataName.AuthorizationEncryptionAlgValuesSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.AuthorizationEncryptionEncValuesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.AuthorizationEncryptionEncValuesSupported, MetadataName.AuthorizationEncryptionEncValuesSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.AuthorizationResponseIssParameterSupported)) + config.AuthorizationResponseIssParameterSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.AuthorizationResponseIssParameterSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.AuthorizationSigningAlgValuesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.AuthorizationSigningAlgValuesSupported, MetadataName.AuthorizationSigningAlgValuesSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.BackchannelAuthenticationEndpoint)) + config.BackchannelAuthenticationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.BackchannelAuthenticationEndpoint, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.BackchannelAuthenticationRequestSigningAlgValuesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.BackchannelAuthenticationRequestSigningAlgValuesSupported, MetadataName.BackchannelAuthenticationRequestSigningAlgValuesSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.BackchannelTokenDeliveryModesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.BackchannelTokenDeliveryModesSupported, MetadataName.BackchannelTokenDeliveryModesSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.BackchannelUserCodeParameterSupported)) + config.BackchannelUserCodeParameterSupported = JsonPrimitives.ReadBoolean(ref reader, MetadataName.BackchannelUserCodeParameterSupported, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.CheckSessionIframe)) config.CheckSessionIframe = JsonPrimitives.ReadString(ref reader, MetadataName.CheckSessionIframe, ClassName, true); @@ -150,9 +205,18 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (reader.ValueTextEquals(Utf8Bytes.ClaimTypesSupported)) JsonPrimitives.ReadStrings(ref reader, config.ClaimTypesSupported, MetadataName.ClaimTypesSupported, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.CodeChallengeMethodsSupported)) + JsonPrimitives.ReadStrings(ref reader, config.CodeChallengeMethodsSupported, MetadataName.CodeChallengeMethodsSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.DeviceAuthorizationEndpoint)) + config.DeviceAuthorizationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.DeviceAuthorizationEndpoint, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.DisplayValuesSupported)) JsonPrimitives.ReadStrings(ref reader, config.DisplayValuesSupported, MetadataName.DisplayValuesSupported, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.DPoPSigningAlgValuesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.DPoPSigningAlgValuesSupported, MetadataName.DPoPSigningAlgValuesSupported, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.EndSessionEndpoint)) config.EndSessionEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.EndSessionEndpoint, ClassName, true); @@ -217,6 +281,12 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (reader.ValueTextEquals(Utf8Bytes.OpTosUri)) config.OpTosUri = JsonPrimitives.ReadString(ref reader, MetadataName.OpTosUri, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.PromptValuesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.PromptValuesSupported, MetadataName.PromptValuesSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.PushedAuthorizationRequestEndpoint)) + config.PushedAuthorizationRequestEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.PushedAuthorizationRequestEndpoint, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.RegistrationEndpoint)) config.RegistrationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.RegistrationEndpoint, ClassName, true); @@ -244,14 +314,20 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (reader.ValueTextEquals(Utf8Bytes.ResponseTypesSupported)) JsonPrimitives.ReadStrings(ref reader, config.ResponseTypesSupported, MetadataName.ResponseTypesSupported, ClassName, true); - else if (reader.ValueTextEquals(Utf8Bytes.ScopesSupported)) - JsonPrimitives.ReadStrings(ref reader, config.ScopesSupported, MetadataName.ScopesSupported, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.RevocationEndpoint)) + config.RevocationEndpoint = JsonPrimitives.ReadString(ref reader, MetadataName.RevocationEndpoint, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.RevocationEndpointAuthMethodsSupported)) + JsonPrimitives.ReadStrings(ref reader, config.RevocationEndpointAuthMethodsSupported, MetadataName.RevocationEndpointAuthMethodsSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.RevocationEndpointAuthSigningAlgValuesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.RevocationEndpointAuthSigningAlgValuesSupported, MetadataName.RevocationEndpointAuthSigningAlgValuesSupported, ClassName, true); else if (reader.ValueTextEquals(Utf8Bytes.ServiceDocumentation)) - config.ServiceDocumentation = JsonPrimitives.ReadString(ref reader, MetadataName.ScopesSupported, ClassName, true); + config.ServiceDocumentation = JsonPrimitives.ReadString(ref reader, MetadataName.ServiceDocumentation, ClassName, true); - else if (reader.ValueTextEquals(Utf8Bytes.SubjectTypesSupported)) - JsonPrimitives.ReadStrings(ref reader, config.SubjectTypesSupported, MetadataName.SubjectTypesSupported, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.ScopesSupported)) + JsonPrimitives.ReadStrings(ref reader, config.ScopesSupported, MetadataName.ScopesSupported, ClassName, true); else if (reader.ValueTextEquals(Utf8Bytes.SubjectTypesSupported)) JsonPrimitives.ReadStrings(ref reader, config.SubjectTypesSupported, MetadataName.SubjectTypesSupported, ClassName, true); @@ -265,6 +341,9 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (reader.ValueTextEquals(Utf8Bytes.TokenEndpointAuthSigningAlgValuesSupported)) JsonPrimitives.ReadStrings(ref reader, config.TokenEndpointAuthSigningAlgValuesSupported, MetadataName.TokenEndpointAuthSigningAlgValuesSupported, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.TlsClientCertificateBoundAccessTokens)) + config.TlsClientCertificateBoundAccessTokens = JsonPrimitives.ReadBoolean(ref reader, MetadataName.TlsClientCertificateBoundAccessTokens, ClassName, true); + else if (reader.ValueTextEquals(Utf8Bytes.UILocalesSupported)) JsonPrimitives.ReadStrings(ref reader, config.UILocalesSupported, MetadataName.UILocalesSupported, ClassName, true); @@ -279,6 +358,9 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (reader.ValueTextEquals(Utf8Bytes.UserInfoSigningAlgValuesSupported)) JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointSigningAlgValuesSupported, MetadataName.UserInfoSigningAlgValuesSupported, ClassName, true); + + else if (reader.ValueTextEquals(Utf8Bytes.RequirePushedAuthorizationRequests)) + config.RequirePushedAuthorizationRequests = JsonPrimitives.ReadBoolean(ref reader, MetadataName.RequirePushedAuthorizationRequests, ClassName, true); #endregion else { @@ -300,6 +382,30 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (propertyName.Equals(MetadataName.AuthorizationEndpoint, StringComparison.OrdinalIgnoreCase)) config.AuthorizationEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); + else if (propertyName.Equals(MetadataName.AuthorizationEncryptionAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.AuthorizationEncryptionAlgValuesSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.AuthorizationEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.AuthorizationEncryptionEncValuesSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.AuthorizationResponseIssParameterSupported, StringComparison.OrdinalIgnoreCase)) + config.AuthorizationResponseIssParameterSupported = JsonPrimitives.ReadBoolean(ref reader, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.AuthorizationSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.AuthorizationSigningAlgValuesSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.BackchannelAuthenticationEndpoint, StringComparison.OrdinalIgnoreCase)) + config.BackchannelAuthenticationEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.BackchannelAuthenticationRequestSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.BackchannelAuthenticationRequestSigningAlgValuesSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.BackchannelTokenDeliveryModesSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.BackchannelTokenDeliveryModesSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.BackchannelUserCodeParameterSupported, StringComparison.OrdinalIgnoreCase)) + config.BackchannelUserCodeParameterSupported = JsonPrimitives.ReadBoolean(ref reader, propertyName, ClassName); + else if (propertyName.Equals(MetadataName.CheckSessionIframe, StringComparison.OrdinalIgnoreCase)) config.CheckSessionIframe = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); @@ -315,9 +421,18 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (propertyName.Equals(MetadataName.ClaimTypesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.ClaimTypesSupported, propertyName, ClassName); + else if (propertyName.Equals(MetadataName.CodeChallengeMethodsSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.CodeChallengeMethodsSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.DeviceAuthorizationEndpoint, StringComparison.OrdinalIgnoreCase)) + config.DeviceAuthorizationEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); + else if (propertyName.Equals(MetadataName.DisplayValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.DisplayValuesSupported, propertyName, ClassName); + else if (propertyName.Equals(MetadataName.DPoPSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.DPoPSigningAlgValuesSupported, propertyName, ClassName); + else if (propertyName.Equals(MetadataName.EndSessionEndpoint, StringComparison.OrdinalIgnoreCase)) config.EndSessionEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); @@ -353,93 +468,113 @@ public static OpenIdConnectConfiguration Read(ref Utf8JsonReader reader, OpenIdC else if (propertyName.Equals(MetadataName.IdTokenEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.IdTokenEncryptionEncValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName.IdTokenSigningAlgValuesSupported , StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.IdTokenSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.IdTokenSigningAlgValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. IntrospectionEndpoint, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.IntrospectionEndpoint, StringComparison.OrdinalIgnoreCase)) config.IntrospectionEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. IntrospectionEndpointAuthMethodsSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.IntrospectionEndpointAuthMethodsSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.IntrospectionEndpointAuthMethodsSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. IntrospectionEndpointAuthSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.IntrospectionEndpointAuthSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.IntrospectionEndpointAuthSigningAlgValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. Issuer, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.Issuer, StringComparison.OrdinalIgnoreCase)) config.Issuer = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. JwksUri, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.JwksUri, StringComparison.OrdinalIgnoreCase)) config.JwksUri = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. LogoutSessionSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.LogoutSessionSupported, StringComparison.OrdinalIgnoreCase)) config.LogoutSessionSupported = JsonPrimitives.ReadBoolean(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. OpPolicyUri, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.OpPolicyUri, StringComparison.OrdinalIgnoreCase)) config.OpPolicyUri = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. OpTosUri, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.OpTosUri, StringComparison.OrdinalIgnoreCase)) config.OpTosUri = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. RegistrationEndpoint, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.PromptValuesSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.PromptValuesSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.PushedAuthorizationRequestEndpoint, StringComparison.OrdinalIgnoreCase)) + config.PushedAuthorizationRequestEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.RegistrationEndpoint, StringComparison.OrdinalIgnoreCase)) config.RegistrationEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. RequestObjectEncryptionAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.RequestObjectEncryptionAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.RequestObjectEncryptionAlgValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. RequestObjectEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.RequestObjectEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.RequestObjectEncryptionEncValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. RequestObjectSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.RequestObjectSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.RequestObjectSigningAlgValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. RequestParameterSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.RequestParameterSupported, StringComparison.OrdinalIgnoreCase)) config.RequestParameterSupported = JsonPrimitives.ReadBoolean(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. RequestUriParameterSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.RequestUriParameterSupported, StringComparison.OrdinalIgnoreCase)) config.RequestUriParameterSupported = JsonPrimitives.ReadBoolean(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. RequireRequestUriRegistration, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.RequirePushedAuthorizationRequests, StringComparison.OrdinalIgnoreCase)) + config.RequirePushedAuthorizationRequests = JsonPrimitives.ReadBoolean(ref reader, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.RequireRequestUriRegistration, StringComparison.OrdinalIgnoreCase)) config.RequireRequestUriRegistration = JsonPrimitives.ReadBoolean(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. ResponseModesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.ResponseModesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.ResponseModesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. ResponseTypesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.ResponseTypesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.ResponseTypesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. ScopesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.RevocationEndpoint, StringComparison.OrdinalIgnoreCase)) + config.RevocationEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.RevocationEndpointAuthMethodsSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.RevocationEndpointAuthMethodsSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.RevocationEndpointAuthSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + JsonPrimitives.ReadStrings(ref reader, config.RevocationEndpointAuthSigningAlgValuesSupported, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.ScopesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.ScopesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. ServiceDocumentation, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.ServiceDocumentation, StringComparison.OrdinalIgnoreCase)) config.ServiceDocumentation = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. SubjectTypesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.SubjectTypesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.SubjectTypesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. TokenEndpoint, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.TokenEndpoint, StringComparison.OrdinalIgnoreCase)) config.TokenEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. TokenEndpointAuthMethodsSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.TokenEndpointAuthMethodsSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.TokenEndpointAuthMethodsSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. TokenEndpointAuthSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.TokenEndpointAuthSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.TokenEndpointAuthSigningAlgValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. UILocalesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.TlsClientCertificateBoundAccessTokens, StringComparison.OrdinalIgnoreCase)) + config.TlsClientCertificateBoundAccessTokens = JsonPrimitives.ReadBoolean(ref reader, propertyName, ClassName); + + else if (propertyName.Equals(MetadataName.UILocalesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.UILocalesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. UserInfoEncryptionAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.UserInfoEncryptionAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointEncryptionAlgValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. UserInfoEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.UserInfoEncryptionEncValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointEncryptionEncValuesSupported, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. UserInfoEndpoint, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.UserInfoEndpoint, StringComparison.OrdinalIgnoreCase)) config.UserInfoEndpoint = JsonPrimitives.ReadString(ref reader, propertyName, ClassName); - else if (propertyName.Equals(MetadataName. UserInfoSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) + else if (propertyName.Equals(MetadataName.UserInfoSigningAlgValuesSupported, StringComparison.OrdinalIgnoreCase)) JsonPrimitives.ReadStrings(ref reader, config.UserInfoEndpointSigningAlgValuesSupported, propertyName, ClassName); - } #endregion case-insensitive } @@ -485,6 +620,30 @@ public static void Write(ref Utf8JsonWriter writer, OpenIdConnectConfiguration c if (!string.IsNullOrEmpty(config.AuthorizationEndpoint)) writer.WriteString(Utf8Bytes.AuthorizationEndpoint, config.AuthorizationEndpoint); + if (config.AuthorizationEncryptionAlgValuesSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.AuthorizationEncryptionAlgValuesSupported, config.AuthorizationEncryptionAlgValuesSupported); + + if (config.AuthorizationEncryptionEncValuesSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.AuthorizationEncryptionEncValuesSupported, config.AuthorizationEncryptionEncValuesSupported); + + if (config.AuthorizationResponseIssParameterSupported) + writer.WriteBoolean(Utf8Bytes.AuthorizationResponseIssParameterSupported, config.AuthorizationResponseIssParameterSupported); + + if (config.AuthorizationSigningAlgValuesSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.AuthorizationSigningAlgValuesSupported, config.AuthorizationSigningAlgValuesSupported); + + if (!string.IsNullOrEmpty(config.BackchannelAuthenticationEndpoint)) + writer.WriteString(Utf8Bytes.BackchannelAuthenticationEndpoint, config.BackchannelAuthenticationEndpoint); + + if (config.BackchannelAuthenticationRequestSigningAlgValuesSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.BackchannelAuthenticationRequestSigningAlgValuesSupported, config.BackchannelAuthenticationRequestSigningAlgValuesSupported); + + if (config.BackchannelTokenDeliveryModesSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.BackchannelTokenDeliveryModesSupported, config.BackchannelTokenDeliveryModesSupported); + + if (config.BackchannelUserCodeParameterSupported) + writer.WriteBoolean(Utf8Bytes.BackchannelUserCodeParameterSupported, config.BackchannelUserCodeParameterSupported); + if (!string.IsNullOrEmpty(config.CheckSessionIframe)) writer.WriteString(Utf8Bytes.CheckSessionIframe, config.CheckSessionIframe); @@ -500,9 +659,18 @@ public static void Write(ref Utf8JsonWriter writer, OpenIdConnectConfiguration c if (config.ClaimTypesSupported.Count > 0) JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.ClaimTypesSupported, config.ClaimTypesSupported); + if (config.CodeChallengeMethodsSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.CodeChallengeMethodsSupported, config.CodeChallengeMethodsSupported); + + if (!string.IsNullOrEmpty(config.DeviceAuthorizationEndpoint)) + writer.WriteString(Utf8Bytes.DeviceAuthorizationEndpoint, config.DeviceAuthorizationEndpoint); + if (config.DisplayValuesSupported.Count > 0) JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.DisplayValuesSupported, config.DisplayValuesSupported); + if (config.DPoPSigningAlgValuesSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.DPoPSigningAlgValuesSupported, config.DPoPSigningAlgValuesSupported); + if (!string.IsNullOrEmpty(config.EndSessionEndpoint)) writer.WriteString(Utf8Bytes.EndSessionEndpoint, config.EndSessionEndpoint); @@ -551,8 +719,14 @@ public static void Write(ref Utf8JsonWriter writer, OpenIdConnectConfiguration c if (!string.IsNullOrEmpty(config.OpTosUri)) writer.WriteString(Utf8Bytes.OpTosUri, config.OpTosUri); + if (config.PromptValuesSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.PromptValuesSupported, config.PromptValuesSupported); + + if (!string.IsNullOrEmpty(config.PushedAuthorizationRequestEndpoint)) + writer.WriteString(Utf8Bytes.PushedAuthorizationRequestEndpoint, config.PushedAuthorizationRequestEndpoint); + if (!string.IsNullOrEmpty(config.RegistrationEndpoint)) - writer.WriteString(Utf8Bytes.RegistrationEndpoint, config.RegistrationEndpoint); + writer.WriteString(Utf8Bytes.RegistrationEndpoint, config.RegistrationEndpoint); if (config.RequestObjectEncryptionAlgValuesSupported.Count > 0) JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.RequestObjectEncryptionAlgValuesSupported, config.RequestObjectEncryptionAlgValuesSupported); @@ -569,6 +743,9 @@ public static void Write(ref Utf8JsonWriter writer, OpenIdConnectConfiguration c if (config.RequestUriParameterSupported) writer.WriteBoolean(Utf8Bytes.RequestUriParameterSupported, config.RequestUriParameterSupported); + if (config.RequirePushedAuthorizationRequests) + writer.WriteBoolean(Utf8Bytes.RequirePushedAuthorizationRequests, config.RequirePushedAuthorizationRequests); + if (config.RequireRequestUriRegistration) writer.WriteBoolean(Utf8Bytes.RequireRequestUriRegistration, config.RequireRequestUriRegistration); @@ -581,6 +758,15 @@ public static void Write(ref Utf8JsonWriter writer, OpenIdConnectConfiguration c if (config.ScopesSupported.Count > 0) JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.ScopesSupported, config.ScopesSupported); + if (!string.IsNullOrEmpty(config.RevocationEndpoint)) + writer.WriteString(Utf8Bytes.RevocationEndpoint, config.RevocationEndpoint); + + if (config.RevocationEndpointAuthMethodsSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.RevocationEndpointAuthMethodsSupported, config.RevocationEndpointAuthMethodsSupported); + + if (config.RevocationEndpointAuthSigningAlgValuesSupported.Count > 0) + JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.RevocationEndpointAuthSigningAlgValuesSupported, config.RevocationEndpointAuthSigningAlgValuesSupported); + if (!string.IsNullOrEmpty(config.ServiceDocumentation)) writer.WriteString(Utf8Bytes.ServiceDocumentation, config.ServiceDocumentation); @@ -596,6 +782,9 @@ public static void Write(ref Utf8JsonWriter writer, OpenIdConnectConfiguration c if (config.TokenEndpointAuthSigningAlgValuesSupported.Count > 0) JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.TokenEndpointAuthSigningAlgValuesSupported, config.TokenEndpointAuthSigningAlgValuesSupported); + if (config.TlsClientCertificateBoundAccessTokens) + writer.WriteBoolean(Utf8Bytes.TlsClientCertificateBoundAccessTokens, config.TlsClientCertificateBoundAccessTokens); + if (config.UILocalesSupported.Count > 0) JsonPrimitives.WriteStrings(ref writer, Utf8Bytes.UILocalesSupported, config.UILocalesSupported); @@ -619,4 +808,3 @@ public static void Write(ref Utf8JsonWriter writer, OpenIdConnectConfiguration c #endregion } } - diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectScope.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectScope.cs index 6a3c753e77..c32880eaaf 100644 --- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectScope.cs +++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdConnectScope.cs @@ -37,10 +37,15 @@ public static class OpenIdConnectScope public const string OpenIdProfile = "openid profile"; /// - /// Indicates phone profile scope see: https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims. + /// Indicates phone scope see: https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims. /// public const string Phone = "phone"; + /// + /// Indicates profile scope see: https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims. + /// + public const string Profile = "profile"; + /// /// Indicates user_impersonation scope for Azure Active Directory. /// diff --git a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdProviderMetadataNames.cs b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdProviderMetadataNames.cs index eaef4e2319..b5c802d3dd 100644 --- a/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdProviderMetadataNames.cs +++ b/src/Microsoft.IdentityModel.Protocols.OpenIdConnect/OpenIdProviderMetadataNames.cs @@ -14,13 +14,24 @@ public static class OpenIdProviderMetadataNames #pragma warning disable 1591 public const string AcrValuesSupported = "acr_values_supported"; public const string AuthorizationEndpoint = "authorization_endpoint"; + public const string AuthorizationEncryptionAlgValuesSupported = "authorization_encryption_alg_values_supported"; + public const string AuthorizationEncryptionEncValuesSupported = "authorization_encryption_enc_values_supported"; + public const string AuthorizationResponseIssParameterSupported = "authorization_response_iss_parameter_supported"; + public const string AuthorizationSigningAlgValuesSupported = "authorization_signing_alg_values_supported"; + public const string BackchannelAuthenticationEndpoint = "backchannel_authentication_endpoint"; + public const string BackchannelAuthenticationRequestSigningAlgValuesSupported = "backchannel_authentication_request_signing_alg_values_supported"; + public const string BackchannelTokenDeliveryModesSupported = "backchannel_token_delivery_modes_supported"; + public const string BackchannelUserCodeParameterSupported = "backchannel_user_code_parameter_supported"; public const string CheckSessionIframe = "check_session_iframe"; public const string ClaimsLocalesSupported = "claims_locales_supported"; public const string ClaimsParameterSupported = "claims_parameter_supported"; public const string ClaimsSupported = "claims_supported"; public const string ClaimTypesSupported = "claim_types_supported"; + public const string CodeChallengeMethodsSupported = "code_challenge_methods_supported"; + public const string DeviceAuthorizationEndpoint = "device_authorization_endpoint"; public const string Discovery = ".well-known/openid-configuration"; public const string DisplayValuesSupported = "display_values_supported"; + public const string DPoPSigningAlgValuesSupported = "dpop_signing_alg_values_supported"; public const string EndSessionEndpoint = "end_session_endpoint"; public const string FrontchannelLogoutSessionSupported = "frontchannel_logout_session_supported"; public const string FrontchannelLogoutSupported = "frontchannel_logout_supported"; @@ -38,15 +49,21 @@ public static class OpenIdProviderMetadataNames public const string MicrosoftMultiRefreshToken = "microsoft_multi_refresh_token"; public const string OpPolicyUri = "op_policy_uri"; public const string OpTosUri = "op_tos_uri"; + public const string PromptValuesSupported = "prompt_values_supported"; + public const string PushedAuthorizationRequestEndpoint = "pushed_authorization_request_endpoint"; public const string RegistrationEndpoint = "registration_endpoint"; public const string RequestObjectEncryptionAlgValuesSupported = "request_object_encryption_alg_values_supported"; public const string RequestObjectEncryptionEncValuesSupported = "request_object_encryption_enc_values_supported"; public const string RequestObjectSigningAlgValuesSupported = "request_object_signing_alg_values_supported"; public const string RequestParameterSupported = "request_parameter_supported"; public const string RequestUriParameterSupported = "request_uri_parameter_supported"; + public const string RequirePushedAuthorizationRequests = "require_pushed_authorization_requests"; public const string RequireRequestUriRegistration = "require_request_uri_registration"; public const string ResponseModesSupported = "response_modes_supported"; public const string ResponseTypesSupported = "response_types_supported"; + public const string RevocationEndpoint = "revocation_endpoint"; + public const string RevocationEndpointAuthMethodsSupported = "revocation_endpoint_auth_methods_supported"; + public const string RevocationEndpointAuthSigningAlgValuesSupported = "revocation_endpoint_auth_signing_alg_values_supported"; public const string ServiceDocumentation = "service_documentation"; public const string ScopesSupported = "scopes_supported"; public const string SubjectTypesSupported = "subject_types_supported"; @@ -54,6 +71,7 @@ public static class OpenIdProviderMetadataNames public const string TokenEndpointAuthMethodsSupported = "token_endpoint_auth_methods_supported"; public const string TokenEndpointAuthSigningAlgValuesSupported = "token_endpoint_auth_signing_alg_values_supported"; public const string UILocalesSupported = "ui_locales_supported"; + public const string TlsClientCertificateBoundAccessTokens = "tls_client_certificate_bound_access_tokens"; public const string UserInfoEndpoint = "userinfo_endpoint"; public const string UserInfoEncryptionAlgValuesSupported = "userinfo_encryption_alg_values_supported"; public const string UserInfoEncryptionEncValuesSupported = "userinfo_encryption_enc_values_supported"; @@ -70,13 +88,24 @@ internal static class OpenIdProviderMetadataUtf8Bytes { public static ReadOnlySpan AcrValuesSupported => "acr_values_supported"u8; public static ReadOnlySpan AuthorizationEndpoint => "authorization_endpoint"u8; + public static ReadOnlySpan AuthorizationEncryptionAlgValuesSupported => "authorization_encryption_alg_values_supported"u8; + public static ReadOnlySpan AuthorizationEncryptionEncValuesSupported => "authorization_encryption_enc_values_supported"u8; + public static ReadOnlySpan AuthorizationResponseIssParameterSupported => "authorization_response_iss_parameter_supported"u8; + public static ReadOnlySpan AuthorizationSigningAlgValuesSupported => "authorization_signing_alg_values_supported"u8; + public static ReadOnlySpan BackchannelAuthenticationEndpoint => "backchannel_authentication_endpoint"u8; + public static ReadOnlySpan BackchannelAuthenticationRequestSigningAlgValuesSupported => "backchannel_authentication_request_signing_alg_values_supported"u8; + public static ReadOnlySpan BackchannelTokenDeliveryModesSupported => "backchannel_token_delivery_modes_supported"u8; + public static ReadOnlySpan BackchannelUserCodeParameterSupported => "backchannel_user_code_parameter_supported"u8; public static ReadOnlySpan CheckSessionIframe => "check_session_iframe"u8; public static ReadOnlySpan ClaimsLocalesSupported => "claims_locales_supported"u8; public static ReadOnlySpan ClaimsParameterSupported => "claims_parameter_supported"u8; public static ReadOnlySpan ClaimsSupported => "claims_supported"u8; public static ReadOnlySpan ClaimTypesSupported => "claim_types_supported"u8; + public static ReadOnlySpan CodeChallengeMethodsSupported => "code_challenge_methods_supported"u8; + public static ReadOnlySpan DeviceAuthorizationEndpoint => "device_authorization_endpoint"u8; public static ReadOnlySpan Discovery => ".well-known/openid-configuration"u8; public static ReadOnlySpan DisplayValuesSupported => "display_values_supported"u8; + public static ReadOnlySpan DPoPSigningAlgValuesSupported => "dpop_signing_alg_values_supported"u8; public static ReadOnlySpan EndSessionEndpoint => "end_session_endpoint"u8; public static ReadOnlySpan FrontchannelLogoutSessionSupported => "frontchannel_logout_session_supported"u8; public static ReadOnlySpan FrontchannelLogoutSupported => "frontchannel_logout_supported"u8; @@ -94,21 +123,28 @@ internal static class OpenIdProviderMetadataUtf8Bytes public static ReadOnlySpan MicrosoftMultiRefreshToken => "microsoft_multi_refresh_token"u8; public static ReadOnlySpan OpPolicyUri => "op_policy_uri"u8; public static ReadOnlySpan OpTosUri => "op_tos_uri"u8; + public static ReadOnlySpan PromptValuesSupported => "prompt_values_supported"u8; + public static ReadOnlySpan PushedAuthorizationRequestEndpoint => "pushed_authorization_request_endpoint"u8; public static ReadOnlySpan RegistrationEndpoint => "registration_endpoint"u8; public static ReadOnlySpan RequestObjectEncryptionAlgValuesSupported => "request_object_encryption_alg_values_supported"u8; public static ReadOnlySpan RequestObjectEncryptionEncValuesSupported => "request_object_encryption_enc_values_supported"u8; public static ReadOnlySpan RequestObjectSigningAlgValuesSupported => "request_object_signing_alg_values_supported"u8; public static ReadOnlySpan RequestParameterSupported => "request_parameter_supported"u8; public static ReadOnlySpan RequestUriParameterSupported => "request_uri_parameter_supported"u8; + public static ReadOnlySpan RequirePushedAuthorizationRequests => "require_pushed_authorization_requests"u8; public static ReadOnlySpan RequireRequestUriRegistration => "require_request_uri_registration"u8; public static ReadOnlySpan ResponseModesSupported => "response_modes_supported"u8; public static ReadOnlySpan ResponseTypesSupported => "response_types_supported"u8; + public static ReadOnlySpan RevocationEndpoint => "revocation_endpoint"u8; + public static ReadOnlySpan RevocationEndpointAuthMethodsSupported => "revocation_endpoint_auth_methods_supported"u8; + public static ReadOnlySpan RevocationEndpointAuthSigningAlgValuesSupported => "revocation_endpoint_auth_signing_alg_values_supported"u8; public static ReadOnlySpan ServiceDocumentation => "service_documentation"u8; public static ReadOnlySpan ScopesSupported => "scopes_supported"u8; public static ReadOnlySpan SubjectTypesSupported => "subject_types_supported"u8; public static ReadOnlySpan TokenEndpoint => "token_endpoint"u8; public static ReadOnlySpan TokenEndpointAuthMethodsSupported => "token_endpoint_auth_methods_supported"u8; public static ReadOnlySpan TokenEndpointAuthSigningAlgValuesSupported => "token_endpoint_auth_signing_alg_values_supported"u8; + public static ReadOnlySpan TlsClientCertificateBoundAccessTokens => "tls_client_certificate_bound_access_tokens"u8; public static ReadOnlySpan UILocalesSupported => "ui_locales_supported"u8; public static ReadOnlySpan UserInfoEndpoint => "userinfo_endpoint"u8; public static ReadOnlySpan UserInfoEncryptionAlgValuesSupported => "userinfo_encryption_alg_values_supported"u8; diff --git a/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs b/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs index 593bd33a19..3ce56064ad 100644 --- a/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs +++ b/src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs @@ -318,7 +318,6 @@ public virtual SignatureProvider CreateForVerifying(SecurityKey key, string algo return CreateSignatureProvider(key, algorithm, false, cacheProvider); } -#if NET461 || NET462 || NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER /// /// Creates a for a specific algorithm. /// @@ -354,7 +353,6 @@ public virtual HashAlgorithm CreateHashAlgorithm(HashAlgorithmName algorithm) throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10640, LogHelper.MarkAsNonPII(algorithm)))); } -#endif /// /// Creates a for a specific algorithm. diff --git a/src/Microsoft.IdentityModel.Tokens/ECDsaAdapter.cs b/src/Microsoft.IdentityModel.Tokens/ECDsaAdapter.cs index 003d4e18ab..6986a9b0ee 100644 --- a/src/Microsoft.IdentityModel.Tokens/ECDsaAdapter.cs +++ b/src/Microsoft.IdentityModel.Tokens/ECDsaAdapter.cs @@ -32,14 +32,12 @@ internal ECDsaAdapter() CreateECDsaFunction = CreateECDsaUsingECParams; #elif NETSTANDARD2_0 // Although NETSTANDARD2_0 specifies that ECParameters are supported, we still need to call SupportsECParameters() - // as NET461 is listed as supporting NETSTANDARD2_0, but DOES NOT support ECParameters. + // as NET462 is listed as supporting NETSTANDARD2_0, but DOES NOT support ECParameters. // See: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.ecparameters?view=netstandard-2.0 - if (SupportsECParameters()) - CreateECDsaFunction = CreateECDsaUsingECParams; - else - CreateECDsaFunction = CreateECDsaUsingCNGKey; + if (SupportsECParameters()) CreateECDsaFunction = CreateECDsaUsingECParams; + else CreateECDsaFunction = CreateECDsaUsingCNGKey; #else - CreateECDsaFunction = CreateECDsaUsingCNGKey; + CreateECDsaFunction = CreateECDsaUsingCNGKey; #endif } @@ -51,7 +49,7 @@ internal ECDsa CreateECDsa(JsonWebKey jsonWebKey, bool usePrivateKey) return CreateECDsaFunction(jsonWebKey, usePrivateKey); } -#if NET461 || NET462 || NETSTANDARD2_0 +#if NET462 || NETSTANDARD2_0 /// /// Creates an ECDsa object using the and . /// 'ECParameters' structure is available in .NET Framework 4.7+, .NET Standard 1.6+, and .NET Core 1.0+. @@ -230,32 +228,12 @@ private static uint GetMagicValue(string curveId, bool willCreateSignatures) return (uint)magicNumber; } - /// - /// Tests if user's runtime platform supports operations using . - /// - /// True if operations using are supported on user's runtime platform, false otherwise. - [MethodImpl(MethodImplOptions.NoOptimization)] - private static bool SupportsCNGKey() - { - try - { -#pragma warning disable CA1416 // Validate platform compatibility - _ = CngKeyBlobFormat.EccPrivateBlob; -#pragma warning restore CA1416 // Validate platform compatibility - return true; - } - catch - { - return false; - } - } - #if NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER /// /// Creates an ECDsa object using the and . /// 'ECParameters' structure is available in .NET Framework 4.7+, .NET Standard 1.6+, and .NET Core 1.0+. /// - private ECDsa CreateECDsaUsingECParams(JsonWebKey jsonWebKey, bool usePrivateKey) + private static ECDsa CreateECDsaUsingECParams(JsonWebKey jsonWebKey, bool usePrivateKey) { if (jsonWebKey == null) throw LogHelper.LogArgumentNullException(nameof(jsonWebKey)); diff --git a/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs b/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs index b6b063936d..b9459a1af8 100644 --- a/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs +++ b/src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs @@ -33,7 +33,6 @@ private struct AuthenticatedKeys private DecryptionDelegate DecryptFunction; private EncryptionDelegate EncryptFunction; private const string _className = "Microsoft.IdentityModel.Tokens.AuthenticatedEncryptionProvider"; - internal const string _skipValidationOfAuthenticationTagLength = "Switch.Microsoft.IdentityModel.SkipAuthenticationTagLengthValidation"; /// /// Initializes a new instance of the class used for encryption and decryption. @@ -167,8 +166,7 @@ private AuthenticatedEncryptionResult EncryptWithAesCbc(byte[] plaintext, byte[] private byte[] DecryptWithAesCbc(byte[] ciphertext, byte[] authenticatedData, byte[] iv, byte[] authenticationTag) { // Verify authentication Tag - if (ShouldValidateAuthenticationTagLength() - && SymmetricSignatureProvider.ExpectedSignatureSizeInBytes.TryGetValue(Algorithm, out int expectedTagLength) + if (SymmetricSignatureProvider.ExpectedSignatureSizeInBytes.TryGetValue(Algorithm, out int expectedTagLength) && expectedTagLength != authenticationTag.Length) throw LogHelper.LogExceptionMessage(new SecurityTokenDecryptionFailedException( LogHelper.FormatInvariant(LogMessages.IDX10625, authenticationTag.Length, expectedTagLength, Base64UrlEncoder.Encode(authenticationTag), Algorithm))); @@ -197,11 +195,6 @@ private byte[] DecryptWithAesCbc(byte[] ciphertext, byte[] authenticatedData, by } } - private static bool ShouldValidateAuthenticationTagLength() - { - return !(AppContext.TryGetSwitch(_skipValidationOfAuthenticationTagLength, out bool skipValidation) && skipValidation); - } - private AuthenticatedKeys CreateAuthenticatedKeys() { ValidateKeySize(Key, Algorithm); diff --git a/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs index f544c99e93..e346c7cf73 100644 --- a/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs +++ b/src/Microsoft.IdentityModel.Tokens/Exceptions/SecurityTokenException.cs @@ -2,18 +2,22 @@ // Licensed under the MIT License. using System; +using System.Diagnostics; using System.Runtime.Serialization; +using System.Text; using Microsoft.IdentityModel.Logging; namespace Microsoft.IdentityModel.Tokens { - /// /// Represents a security token exception. /// [Serializable] public class SecurityTokenException : Exception { + [NonSerialized] + private string _stackTrace; + /// /// Initializes a new instance of the class. /// @@ -55,6 +59,49 @@ protected SecurityTokenException(SerializationInfo info, StreamingContext contex { } + /// + /// Gets the stack trace that is captured when the exception is created. + /// + public override string StackTrace + { + get + { + if (_stackTrace == null) + { + if (ExceptionDetail == null) + return base.StackTrace; +#if NET8_0_OR_GREATER + _stackTrace = new StackTrace(ExceptionDetail.StackFrames).ToString(); +#else + StringBuilder sb = new(); + foreach (StackFrame frame in ExceptionDetail.StackFrames) + { + sb.Append(frame.ToString()); + sb.Append(Environment.NewLine); + } + + _stackTrace = sb.ToString(); +#endif + } + + return _stackTrace; + } + } + + /// + /// Gets or sets the source of the exception. + /// + public override string Source + { + get => base.Source; + set => base.Source = value; + } + + internal ExceptionDetail ExceptionDetail + { + get; set; + } + #if NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER /// /// When overridden in a derived class, sets the System.Runtime.Serialization.SerializationInfo diff --git a/src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs b/src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs index f8c5c81d5b..27c7f4e0c6 100644 --- a/src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs +++ b/src/Microsoft.IdentityModel.Tokens/GlobalSuppressions.cs @@ -21,8 +21,6 @@ [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Breaking change", Scope = "member", Target = "~P:Microsoft.IdentityModel.Tokens.AuthenticatedEncryptionResult.AuthenticationTag")] [assembly: SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Breaking change", Scope = "member", Target = "~P:Microsoft.IdentityModel.Tokens.SymmetricSecurityKey.Key")] [assembly: SuppressMessage("Usage", "CA2211:Non-constant fields should not be visible", Justification = "Breaking change", Scope = "member", Target = "~F:Microsoft.IdentityModel.Tokens.JsonWebKeySet.DefaultSkipUnresolvedJsonWebKeys")] -[assembly: SuppressMessage("Usage", "CA1801:Review unused parameters", Justification = "Used in net461", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.SupportedAlgorithms.IsSupportedRsaPss(Microsoft.IdentityModel.Tokens.SecurityKey)~System.Boolean")] -[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used as platform test", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.ECDsaAdapter.SupportsCNGKey~System.Boolean")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used for try pattern", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.JsonWebKeyConverter.ConvertFromRSASecurityKey(Microsoft.IdentityModel.Tokens.RsaSecurityKey)~Microsoft.IdentityModel.Tokens.JsonWebKey")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used for try pattern", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.JsonWebKeyConverter.TryConvertToECDsaSecurityKey(Microsoft.IdentityModel.Tokens.JsonWebKey,Microsoft.IdentityModel.Tokens.SecurityKey@)~System.Boolean")] [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Used for try pattern", Scope = "member", Target = "~M:Microsoft.IdentityModel.Tokens.JsonWebKeyConverter.TryConvertToSecurityKey(Microsoft.IdentityModel.Tokens.JsonWebKey,Microsoft.IdentityModel.Tokens.SecurityKey@)~System.Boolean")] diff --git a/src/Microsoft.IdentityModel.Tokens/InternalsVisibleTo.cs b/src/Microsoft.IdentityModel.Tokens/InternalsVisibleTo.cs index ba34f3462b..dd03b90595 100644 --- a/src/Microsoft.IdentityModel.Tokens/InternalsVisibleTo.cs +++ b/src/Microsoft.IdentityModel.Tokens/InternalsVisibleTo.cs @@ -1,14 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.KeyVaultExtensions, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.TestUtils, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("CrossVersionTokenValidation.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.Tokens.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.JsonWebTokens, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.JsonWebTokens.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.Protocols.OpenIdConnect, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.TestUtils, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("System.IdentityModel.Tokens.Jwt, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("System.IdentityModel.Tokens.Jwt.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.IdentityModel.S2S, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs index cd99a503ca..f539555a8b 100644 --- a/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs +++ b/src/Microsoft.IdentityModel.Tokens/Json/JsonSerializerPrimitives.cs @@ -129,6 +129,14 @@ public static JsonElement CreateJsonElement(string json) } internal static object CreateObjectFromJsonElement(JsonElement jsonElement, int currentDepth) + { + return CreateObjectFromJsonElement(jsonElement, currentDepth, string.Empty); + } + + /// + /// is not considered on recursive calls. + /// + internal static object CreateObjectFromJsonElement(JsonElement jsonElement, int currentDepth, string claimType) { if (currentDepth >= MaxDepth) throw new InvalidOperationException(LogHelper.FormatInvariant( @@ -138,6 +146,9 @@ internal static object CreateObjectFromJsonElement(JsonElement jsonElement, int if (jsonElement.ValueKind == JsonValueKind.String) { + if (!string.IsNullOrEmpty(claimType) && !TryAllStringClaimsAsDateTime() && IsKnownToNotBeDateTime(claimType)) + return jsonElement.GetString(); + if (DateTime.TryParse(jsonElement.GetString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out DateTime dateTime)) return dateTime; @@ -175,7 +186,7 @@ internal static object CreateObjectFromJsonElement(JsonElement jsonElement, int int index = 0; foreach (JsonElement j in jsonElement.EnumerateArray()) { - items[index++] = CreateObjectFromJsonElement(j, currentDepth + 1); + items[index++] = CreateObjectFromJsonElement(j, currentDepth + 1, string.Empty); } return items; @@ -190,7 +201,7 @@ internal static object CreateObjectFromJsonElement(JsonElement jsonElement, int KeyValuePair[] kvps = new KeyValuePair[numItems]; foreach (JsonProperty property in jsonElement.EnumerateObject()) { - kvps[index++] = new KeyValuePair(property.Name, CreateObjectFromJsonElement(property.Value, currentDepth + 1)); + kvps[index++] = new KeyValuePair(property.Name, CreateObjectFromJsonElement(property.Value, currentDepth + 1, string.Empty)); } return kvps; @@ -295,7 +306,7 @@ public static bool TryCreateTypeFromJsonElement(JsonElement jsonElement, out Dictionary dictionary = new(); foreach (JsonProperty property in jsonElement.EnumerateObject()) { - dictionary[property.Name] = CreateObjectFromJsonElement(property.Value, currentDepth + 1); + dictionary[property.Name] = CreateObjectFromJsonElement(property.Value, currentDepth + 1, string.Empty); } t = (T)(object)dictionary; @@ -386,7 +397,7 @@ public static bool TryCreateTypeFromJsonElement(JsonElement jsonElement, out numItems = 0; foreach (JsonElement j in jsonElement.EnumerateArray()) { - items[numItems++] = CreateObjectFromJsonElement(j, currentDepth + 1); + items[numItems++] = CreateObjectFromJsonElement(j, currentDepth + 1, string.Empty); } t = (T)(object)items; @@ -397,7 +408,7 @@ public static bool TryCreateTypeFromJsonElement(JsonElement jsonElement, out List items = new(); foreach (JsonElement j in jsonElement.EnumerateArray()) { - items.Add(CreateObjectFromJsonElement(j, currentDepth + 1)); + items.Add(CreateObjectFromJsonElement(j, currentDepth + 1, string.Empty)); } t = (T)(object)items; @@ -408,7 +419,7 @@ public static bool TryCreateTypeFromJsonElement(JsonElement jsonElement, out Collection items = new(); foreach (JsonElement j in jsonElement.EnumerateArray()) { - items.Add(CreateObjectFromJsonElement(j, currentDepth + 1)); + items.Add(CreateObjectFromJsonElement(j, currentDepth + 1, string.Empty)); } t = (T)(object)items; @@ -695,6 +706,122 @@ internal static string ReadStringOrNumberAsString(ref Utf8JsonReader reader, str return retVal; } + internal const string TryToCreateDateTimeClaimsSwitch = "Switch.Microsoft.IdentityModel.TryAllStringClaimsAsDateTime"; + + public static bool TryAllStringClaimsAsDateTime() + { + return (AppContext.TryGetSwitch(TryToCreateDateTimeClaimsSwitch, out bool tryAsDateTime) && tryAsDateTime); + } + + /// + /// This is a non-exhaustive list of claim types that are not expected to be DateTime values + /// sourced from expected Entra V1 and V2 claims, OpenID Connect claims, and a selection of + /// restricted claim names. + /// + private static readonly HashSet s_knownNonDateTimeClaimTypes = new(StringComparer.Ordinal) + { + // Header Values. + "alg", + "cty", + "crit", + "enc", + "jku", + "jwk", + "kid", + "typ", + "x5c", + "x5t", + "x5t#S256", + "x5u", + "zip", + // JWT claims. + "acr", + "acrs", + "access_token", + "account_type", + "acct", + "actor", + "actort", + "actortoken", + "aio", + "altsecid", + "amr", + "app_displayname", + "appid", + "appidacr", + "at_hash", + "aud", + "authorization_code", + "azp", + "azpacr", + "c_hash", + "cnf", + "capolids", + "ctry", + "email", + "family_name", + "fwd", + "gender", + "given_name", + "groups", + "hasgroups", + "idp", + "idtyp", + "in_corp", + "ipaddr", + "iss", + "jti", + "login_hint", + "name", + "nameid", + "nickname", + "nonce", + "oid", + "onprem_sid", + "phone_number", + "phone_number_verified", + "pop_jwk", + "preferred_username", + "prn", + "puid", + "pwd_url", + "rh", + "role", + "roles", + "secaud", + "sid", + "sub", + "tenant_ctry", + "tenant_region_scope", + "tid", + "unique_name", + "upn", + "uti", + "ver", + "verified_primary_email", + "verified_secondary_email", + "vnet", + "website", + "wids", + "xms_cc", + "xms_edov", + "xms_pdl", + "xms_pl", + "xms_tpl", + "ztdid" + }; + + internal static bool IsKnownToNotBeDateTime(string claimType) + { + if (string.IsNullOrEmpty(claimType)) + return true; + + if (s_knownNonDateTimeClaimTypes.Contains(claimType)) + return true; + + return false; + } + internal static object ReadStringAsObject(ref Utf8JsonReader reader, string propertyName, string className, bool read = false) { // returning null keeps the same logic as JsonSerialization.ReadObject @@ -706,6 +833,13 @@ internal static object ReadStringAsObject(ref Utf8JsonReader reader, string prop CreateJsonReaderException(ref reader, "JsonTokenType.String", className, propertyName)); string originalString = reader.GetString(); + + if (!TryAllStringClaimsAsDateTime() && IsKnownToNotBeDateTime(propertyName)) + { + reader.Read(); + return originalString; + } + #pragma warning disable CA1031 // Do not catch general exception types try { diff --git a/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs b/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs index ef28e1cf5c..89defe3734 100644 --- a/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs +++ b/src/Microsoft.IdentityModel.Tokens/Json/JsonWebKeySerializer.cs @@ -3,6 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +#if NET8_0_OR_GREATER +using System.Collections.Frozen; +#endif using System.Collections.Generic; using System.IO; using System.Text; @@ -21,7 +24,13 @@ internal static class JsonWebKeySerializer // If not found, then we assume we should put the value into AdditionalData. // If we didn't do that, we would pay a performance penalty for those cases where there is AdditionalData // but otherwise the JSON properties are all lower case. - public static HashSet JsonWebKeyParameterNamesUpperCase = new HashSet + public static readonly +#if NET8_0_OR_GREATER + FrozenSet +#else + HashSet +# endif + JsonWebKeyParameterNamesUpperCase = new HashSet { "ALG", "CRV", @@ -46,7 +55,11 @@ internal static class JsonWebKeySerializer "X5T#S256", "X5U", "Y" - }; + } +#if NET8_0_OR_GREATER + .ToFrozenSet() +#endif + ; #region Read public static JsonWebKey Read(string json) @@ -399,4 +412,3 @@ public static void Write(ref Utf8JsonWriter writer, JsonWebKey jsonWebKey) #endregion } } - diff --git a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs index b9fe6c2996..689b2ccb9d 100644 --- a/src/Microsoft.IdentityModel.Tokens/LogMessages.cs +++ b/src/Microsoft.IdentityModel.Tokens/LogMessages.cs @@ -13,7 +13,7 @@ internal static class LogMessages { #pragma warning disable 1591 // general - // public const string IDX10000 = "IDX10000:"; + public const string IDX10000 = "IDX10000: The parameter '{0}' cannot be a 'null' or an empty object. "; // properties, configuration public const string IDX10101 = "IDX10101: MaximumTokenSizeInBytes must be greater than zero. value: '{0}'"; @@ -35,7 +35,7 @@ internal static class LogMessages public const string IDX10207 = "IDX10207: Unable to validate audience. The 'audiences' parameter is null."; public const string IDX10208 = "IDX10208: Unable to validate audience. validationParameters.ValidAudience is null or whitespace and validationParameters.ValidAudiences is null."; public const string IDX10209 = "IDX10209: Token has length: '{0}' which is larger than the MaximumTokenSizeInBytes: '{1}'."; - public const string IDX10211 = "IDX10211: Unable to validate issuer. The 'issuer' parameter is null or whitespace"; + public const string IDX10211 = "IDX10211: Unable to validate issuer. The 'issuer' parameter is null or whitespace."; public const string IDX10214 = "IDX10214: Audience validation failed. Audiences: '{0}'. Did not match: validationParameters.ValidAudience: '{1}' or validationParameters.ValidAudiences: '{2}'."; public const string IDX10222 = "IDX10222: Lifetime validation failed. The token is not yet valid. ValidFrom (UTC): '{0}', Current time (UTC): '{1}'."; public const string IDX10223 = "IDX10223: Lifetime validation failed. The token is expired. ValidTo (UTC): '{0}', Current time (UTC): '{1}'."; @@ -79,6 +79,8 @@ internal static class LogMessages //public const string IDX10263 = "IDX10263: Unable to re-validate with ConfigurationManager.LastKnownGoodConfiguration as it is expired."; public const string IDX10264 = "IDX10264: Reading issuer signing keys from validation parameters and configuration."; public const string IDX10265 = "IDX10265: Reading issuer signing keys from configuration."; + //public const string IDX10266 = "IDX10266: Unable to validate issuer. validationParameters.ValidIssuer is null or whitespace, validationParameters.ValidIssuers is null or empty and ConfigurationManager is null."; + // 10500 - SignatureValidation public const string IDX10500 = "IDX10500: Signature validation failed. No security keys were provided to validate the signature."; @@ -134,7 +136,7 @@ internal static class LogMessages // public const string IDX10622 = "IDX10622:"; // public const string IDX10623 = "IDX10623:"; // public const string IDX10624 = "IDX10624:"; - public const string IDX10625 = "IDX10625: Failed to verify the authenticationTag length, the actual tag length '{0}' does not match the expected tag length '{1}'. authenticationTag: '{2}', algorithm: '{3}' See: https://aka.ms/IdentityModel/SkipAuthenticationTagLengthValidation"; + public const string IDX10625 = "IDX10625: Failed to verify the authenticationTag length, the actual tag length '{0}' does not match the expected tag length '{1}'. authenticationTag: '{2}', algorithm: '{3}'."; // public const string IDX10627 = "IDX10627:"; public const string IDX10628 = "IDX10628: Cannot set the MinimumSymmetricKeySizeInBits to less than '{0}'."; public const string IDX10630 = "IDX10630: The '{0}' for signing cannot be smaller than '{1}' bits. KeySize: '{2}'."; diff --git a/src/Microsoft.IdentityModel.Tokens/Properties/AssemblyInfo.cs b/src/Microsoft.IdentityModel.Tokens/Properties/AssemblyInfo.cs index f22a0f4b5e..12d7bcb55f 100644 --- a/src/Microsoft.IdentityModel.Tokens/Properties/AssemblyInfo.cs +++ b/src/Microsoft.IdentityModel.Tokens/Properties/AssemblyInfo.cs @@ -17,7 +17,6 @@ [assembly: InternalsVisibleTo("Microsoft.IdentityModel.Tokens.Saml, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.Xml, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.AzureAD.SmartSessionEvaluator, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo("Microsoft.IdentityModel.KeyVaultExtensions.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.Protocols, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.Protocols.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo("Microsoft.IdentityModel.Validators, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/Microsoft.IdentityModel.Tokens/RsaCryptoServiceProviderProxy.cs b/src/Microsoft.IdentityModel.Tokens/RsaCryptoServiceProviderProxy.cs index ea2adbee28..07b21c14d0 100644 --- a/src/Microsoft.IdentityModel.Tokens/RsaCryptoServiceProviderProxy.cs +++ b/src/Microsoft.IdentityModel.Tokens/RsaCryptoServiceProviderProxy.cs @@ -205,7 +205,7 @@ public bool VerifyData(byte[] input, object hash, byte[] signature) return _rsa.VerifyData(input, hash, signature); } -#if NET461 || NET462 || NET472 || NETSTANDARD2_0 +#if NET462 || NET472 || NETSTANDARD2_0 /// /// Verifies that a digital signature is valid by determining the hash value in the signature using the provided public key and comparing it to the hash value of the provided data. /// diff --git a/src/Microsoft.IdentityModel.Tokens/RsaSecurityKey.cs b/src/Microsoft.IdentityModel.Tokens/RsaSecurityKey.cs index 248f5cc947..7b3fa3ef05 100644 --- a/src/Microsoft.IdentityModel.Tokens/RsaSecurityKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/RsaSecurityKey.cs @@ -76,7 +76,7 @@ public override bool HasPrivateKey { // imitate signing byte[] hash = new byte[20]; -#if NET461 || NET462 || NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER +#if NET462 || NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER Rsa.SignData(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); #else if (Rsa is RSACryptoServiceProvider rsaCryptoServiceProvider) diff --git a/src/Microsoft.IdentityModel.Tokens/SupportedAlgorithms.cs b/src/Microsoft.IdentityModel.Tokens/SupportedAlgorithms.cs index 086374360a..7be591da2c 100644 --- a/src/Microsoft.IdentityModel.Tokens/SupportedAlgorithms.cs +++ b/src/Microsoft.IdentityModel.Tokens/SupportedAlgorithms.cs @@ -104,7 +104,6 @@ internal static class SupportedAlgorithms SecurityAlgorithms.EcdhEsA256kw }; -#if NET461 || NET462 || NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER /// /// Creating a Signature requires the use of a . /// This method returns the @@ -148,7 +147,6 @@ internal static HashAlgorithmName GetHashAlgorithmName(string algorithm) throw LogHelper.LogExceptionMessage(new ArgumentOutOfRangeException(nameof(algorithm), LogHelper.FormatInvariant(LogMessages.IDX10652, LogHelper.MarkAsNonPII(algorithm)))); } -#endif /// /// Creating a Signature requires the use of a . diff --git a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs index bb68764a6d..2112772699 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs +++ b/src/Microsoft.IdentityModel.Tokens/TokenValidationParameters.cs @@ -13,7 +13,7 @@ namespace Microsoft.IdentityModel.Tokens /// /// Contains a set of parameters that are used by a when validating a . /// - public class TokenValidationParameters + public partial class TokenValidationParameters { private string _authenticationType; private TimeSpan _clockSkew = DefaultClockSkew; @@ -38,7 +38,7 @@ public class TokenValidationParameters /// Default for the maximum token size. /// /// 250 KB (kilobytes). - public const Int32 DefaultMaximumTokenSizeInBytes = 1024 * 250; + public const int DefaultMaximumTokenSizeInBytes = 1024 * 250; /// /// Copy constructor for . @@ -66,6 +66,7 @@ protected TokenValidationParameters(TokenValidationParameters other) IssuerSigningKeyValidatorUsingConfiguration = other.IssuerSigningKeyValidatorUsingConfiguration; IssuerValidator = other.IssuerValidator; IssuerValidatorAsync = other.IssuerValidatorAsync; + IssuerValidationDelegateAsync = other.IssuerValidationDelegateAsync; IssuerValidatorUsingConfiguration = other.IssuerValidatorUsingConfiguration; LifetimeValidator = other.LifetimeValidator; LogTokenId = other.LogTokenId; @@ -172,22 +173,6 @@ public string AuthenticationType } } - ///// - ///// Gets or sets the for validating X509Certificate2(s). - ///// - //public X509CertificateValidator CertificateValidator - //{ - // get - // { - // return _certificateValidator; - // } - - // set - // { - // _certificateValidator = value; - // } - //} - /// /// Gets or sets the clock skew to apply when validating a time. /// @@ -368,7 +353,6 @@ public virtual ClaimsIdentity CreateClaimsIdentity(SecurityToken securityToken, /// public IssuerValidator IssuerValidator { get; set; } - /// /// Gets or sets a delegate that will be used to validate the issuer of the token. /// diff --git a/src/Microsoft.IdentityModel.Tokens/Utility.cs b/src/Microsoft.IdentityModel.Tokens/Utility.cs index 49e2a3d059..4990981417 100644 --- a/src/Microsoft.IdentityModel.Tokens/Utility.cs +++ b/src/Microsoft.IdentityModel.Tokens/Utility.cs @@ -113,7 +113,7 @@ public static bool IsHttps(Uri uri) { return false; } -#if NET461 || NET462 +#if NET462 return uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase); //Uri.UriSchemeHttps is internal in dnxcore #else return uri.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase); diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/AsyncValidate.cd b/src/Microsoft.IdentityModel.Tokens/Validation/AsyncValidate.cd new file mode 100644 index 0000000000..ebefb90859 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/AsyncValidate.cd @@ -0,0 +1,81 @@ + + + + + + ABEAIAABEEAAEAIAAAAAAAABEQAAAEEACABAAAAkIoA= + Validation\TokenValidationResult.cs + + + + + + AAEAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAA= + Validation\IssuerValidationResult.cs + + + + + + AAAEAAAAAAAAAAAAAAAAEAAEAAAAAAAAAEAABAAAAAA= + Validation\ExceptionDetail.cs + + + + + + + + + AIAAAAJAAAAAAAAAAAgAIAABAAgAAAAABEBBAAAAAAA= + Validation\ValidationResult.cs + + + + + + + + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAA= + Validation\LogDetail.cs + + + + + + + + + + + + AAAIAAAAAAAAAAAAAAIAAAQAAABAQAAAAAAAAAAAAAA= + Validation\ValidationFailureType.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAEIAAIAAAAAA= + Validation\MessageDetail.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CallContext.cs + + + + + + + + \ No newline at end of file diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ExceptionDetail.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ExceptionDetail.cs new file mode 100644 index 0000000000..1ab311c9e8 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ExceptionDetail.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Contains information so that Exceptions can be logged or thrown written as required. + /// + internal class ExceptionDetail + { + /// + /// Creates an instance of + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of exception that occurred. + /// contains information about the stack frame where the exception occurred. + public ExceptionDetail(MessageDetail messageDetail, Type exceptionType, StackFrame stackFrame) + : this(messageDetail, exceptionType, stackFrame, null) + { + } + + /// + /// Creates an instance of + /// + /// contains information about the exception that is used to generate the exception message. + /// is the type of exception that occurred. + /// contains information about the stack frame where the exception occurred. + /// is the inner exception that occurred. + public ExceptionDetail(MessageDetail messageDetail, Type exceptionType, StackFrame stackFrame, Exception innerException) + { + ExceptionType = exceptionType; + InnerException = innerException; + MessageDetail = messageDetail; + StackFrames.Add(stackFrame); + } + + /// + /// Creates an instance of an using + /// + /// An instantance of an Exception. + public Exception GetException() + { + if (InnerException != null) + return Activator.CreateInstance(ExceptionType, MessageDetail.Message, InnerException) as Exception; + + return Activator.CreateInstance(ExceptionType, MessageDetail.Message) as Exception; + } + + /// + /// Gets the type of exception that occurred. + /// + public Type ExceptionType { get; } + + /// + /// Gets the inner exception that occurred. + /// + public Exception InnerException { get; } + + /// + /// Gets the message details that are used to generate the exception message. + /// + public MessageDetail MessageDetail { get; } + + /// + /// Gets the stack frames where the exception occurred. + /// + public IList StackFrames { get; } = []; + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Exceptions.cd b/src/Microsoft.IdentityModel.Tokens/Validation/Exceptions.cd new file mode 100644 index 0000000000..cd69bb66db --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Exceptions.cd @@ -0,0 +1,35 @@ + + + + + + AIAAAAAAAgAAAgAAAAQAAAAAAAAAAAAAAAAAAAAAAAA= + Exceptions\SecurityTokenException.cs + + + + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Exceptions\SecurityTokenValidationException.cs + + + + + + AAgAAEAAAAAAAAAAAAACAAAgAAAAAAAAAAAAAAAAAAA= + Exceptions\SecurityTokenInvalidIssuerException.cs + + + + + + AAAEAAAAAAAAAAAAAAAAEAAEAAAAAAAAAEAABAAAAAA= + Validation\ExceptionDetail.cs + + + + \ No newline at end of file diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/IssuerValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/IssuerValidationResult.cs new file mode 100644 index 0000000000..292eacfab1 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/IssuerValidationResult.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Contains the result of validating a issuer. + /// The contains a collection of for each step in the token validation. + /// + internal class IssuerValidationResult : ValidationResult + { + internal enum ValidationSource + { + NotValidated = 0, + IssuerIsConfigurationIssuer, + IssuerIsValidIssuer, + IssuerIsAmongValidIssuers + } + + private Exception _exception; + + /// + /// Creates an instance of + /// + /// is the issuer that was validated successfully. + /// is the indicating how this issuer was validated. + public IssuerValidationResult(string issuer, ValidationSource source = ValidationSource.NotValidated) + : base(ValidationFailureType.ValidationSucceeded) + { + Issuer = issuer; + IsValid = true; + Source = source; + } + + /// + /// Creates an instance of + /// + /// is the issuer that was intended to be validated. + /// is the that occurred during validation. + /// is the that occurred during validation. + /// is the indicating how this issuer was validated. + public IssuerValidationResult(string issuer, ValidationFailureType validationFailure, ExceptionDetail exceptionDetail, ValidationSource source = ValidationSource.NotValidated) + : base(validationFailure, exceptionDetail) + { + Issuer = issuer; + IsValid = false; + Source = source; + } + + /// + /// Gets the that occurred during validation. + /// + public override Exception Exception + { + get + { + if (_exception != null || ExceptionDetail == null) + return _exception; + + HasValidOrExceptionWasRead = true; + _exception = ExceptionDetail.GetException(); + SecurityTokenInvalidIssuerException securityTokenInvalidIssuerException = _exception as SecurityTokenInvalidIssuerException; + if (securityTokenInvalidIssuerException != null) + { + securityTokenInvalidIssuerException.InvalidIssuer = Issuer; + securityTokenInvalidIssuerException.ExceptionDetail = ExceptionDetail; + securityTokenInvalidIssuerException.Source = "Microsoft.IdentityModel.Tokens"; + } + + return _exception; + } + } + + /// + /// Gets the issuer that was validated or intended to be validated. + /// + public string Issuer { get; } + + public ValidationSource Source { get; } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/LogDetail.cs b/src/Microsoft.IdentityModel.Tokens/Validation/LogDetail.cs new file mode 100644 index 0000000000..6b91a67c1e --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/LogDetail.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.IdentityModel.Abstractions; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Contains information so that logs can be written when needed. + /// + internal class LogDetail + { + /// + /// Creates an instance of + /// + /// contains information about the exception that is used to generate the exception message. + /// is the level of the event log. + public LogDetail(MessageDetail messageDetail, EventLogLevel eventLogLevel) + { + EventLogLevel = eventLogLevel; + MessageDetail = messageDetail; + } + + /// + /// Gets the level of the event log. + /// + public EventLogLevel EventLogLevel { get; } + + /// + /// Gets the message detail. + /// + public MessageDetail MessageDetail { get; } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/MessageDetail.cs b/src/Microsoft.IdentityModel.Tokens/Validation/MessageDetail.cs new file mode 100644 index 0000000000..800126a725 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/MessageDetail.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Contains information about a message that is used to generate a message for logging or exceptions. + /// + internal class MessageDetail + { + private string _message; + + // TODO - remove the need to create NonPII objects, we could use tuples where bool == true => object is PII. + // TODO - does this need to be ReadOnlyMemory? + /// + /// Creates an instance of + /// + /// The message to be formated. + /// The parameters for formatting. + public MessageDetail(string formatString, params object[] parameters) + { + // TODO - paramter validation. + FormatString = formatString; + Parameters = parameters; + } + + /// + /// Gets the formatted message. + /// + public string Message + { + get + { + _message ??= LogHelper.FormatInvariant(FormatString, Parameters); + return _message; + } + } + + private string FormatString { get; } + + private object[] Parameters { get; } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/TokenValidationParameters.IssuerValidationDelegate.cs b/src/Microsoft.IdentityModel.Tokens/Validation/TokenValidationParameters.IssuerValidationDelegate.cs new file mode 100644 index 0000000000..cab11a6bbc --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/TokenValidationParameters.IssuerValidationDelegate.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// partial class for the IssuerValidation delegate. + /// + public partial class TokenValidationParameters + { + /// + /// Gets or sets a delegate that will be used to validate the issuer of a . + /// + internal IssuerValidationDelegateAsync IssuerValidationDelegateAsync { get; set; } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/TokenValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/TokenValidationResult.cs similarity index 85% rename from src/Microsoft.IdentityModel.Tokens/TokenValidationResult.cs rename to src/Microsoft.IdentityModel.Tokens/Validation/TokenValidationResult.cs index 6714c71b9e..46c90738e0 100644 --- a/src/Microsoft.IdentityModel.Tokens/TokenValidationResult.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validation/TokenValidationResult.cs @@ -19,10 +19,6 @@ public class TokenValidationResult private readonly TokenValidationParameters _validationParameters; private readonly TokenHandler _tokenHandler; - private Exception _exception; - private bool _hasIsValidOrExceptionBeenRead = false; - private bool _isValid = false; - // Fields lazily initialized in a thread-safe manner. _claimsIdentity is protected by the _claimsIdentitySyncObj // lock, and since null is a valid initialized value, _claimsIdentityInitialized tracks whether or not it's valid. // _claims is constructed by reading the data from the ClaimsIdentity and is synchronized using Interlockeds @@ -37,6 +33,11 @@ public class TokenValidationResult private ClaimsIdentity _claimsIdentity; private Dictionary _claims; private Dictionary _propertyBag; + // TODO - lazy creation of _validationResults + private List _validationResults = []; + + private Exception _exception; + private bool _isValid; /// /// Creates an instance of @@ -60,6 +61,18 @@ internal TokenValidationResult(SecurityToken securityToken, TokenHandler tokenHa SecurityToken = securityToken; } + /// + /// Adds a to the list of . + /// + /// the associated with one of the validation steps. For example . + internal void AddValidationResult(ValidationResult validationResult) + { + if (validationResult is null) + throw LogHelper.LogArgumentNullException(nameof(validationResult)); + + _validationResults.Add(validationResult); + } + /// /// The created from the validated security token. /// @@ -67,7 +80,7 @@ public IDictionary Claims { get { - if (!_hasIsValidOrExceptionBeenRead) + if (!HasValidOrExceptionWasRead) LogHelper.LogWarning(LogMessages.IDX10109); if (_claims is null && ClaimsIdentity is { } ci) @@ -162,7 +175,7 @@ public Exception Exception { get { - _hasIsValidOrExceptionBeenRead = true; + HasValidOrExceptionWasRead = true; return _exception; } set @@ -171,6 +184,8 @@ public Exception Exception } } + internal bool HasValidOrExceptionWasRead { get; set; } + /// /// Gets or sets the issuer that was found in the token. /// @@ -183,7 +198,7 @@ public bool IsValid { get { - _hasIsValidOrExceptionBeenRead = true; + HasValidOrExceptionWasRead = true; return _isValid; } set @@ -228,5 +243,19 @@ public bool IsValid /// (e.g for a JSON Web Token, from the "typ" header). /// public string TokenType { get; set; } + + /// + /// Gets the list of that contains the result of validating the token. + /// + internal IReadOnlyList ValidationResults + { + get + { + if (_validationResults is null) + Interlocked.CompareExchange(ref _validationResults, new List(), null); + + return _validationResults; + } + } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationDelegates.cd b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationDelegates.cd new file mode 100644 index 0000000000..a6af5bfb82 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationDelegates.cd @@ -0,0 +1,21 @@ + + + + + + YAgEEAQAKMQck0AAi5R6AACRWgBkBQIAAQgYQsaIkxA= + Validation\TokenValidationParameters.IssuerValidationDelegate.cs + + + + + + + + + AAAAAAAAAACAAAAAACAQBAAAAAAAAAAAgAAAAAAAAAA= + Validation\Validators.Issuer.cs + + + + \ No newline at end of file diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs new file mode 100644 index 0000000000..3d872bb63a --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationFailureType.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// The type of the failure that occurred when validating a . + /// + internal abstract class ValidationFailureType + { + /// + /// Creates an instance of + /// + protected ValidationFailureType(string name) + { + Name = name; + } + + /// + /// Gets the name of the . + /// + public string Name { get; } + + /// + /// Defines a type that represents a required parameter was null. + /// + public static readonly ValidationFailureType NullArgument = new NullArgumentFailure("NullArgument"); + private class NullArgumentFailure : ValidationFailureType { internal NullArgumentFailure(string name) : base(name) { } } + + /// + /// Defines a type that represents that issuer validation failed. + /// + public static readonly ValidationFailureType IssuerValidationFailed = new IssuerValidationFailure("IssuerValidationFailed"); + private class IssuerValidationFailure : ValidationFailureType { internal IssuerValidationFailure(string name) : base(name) { } } + + /// + /// Defines a type that represents that no evaluation has taken place. + /// + public static readonly ValidationFailureType ValidationNotEvaluated = new NotEvaluated("NotEvaluated"); + private class NotEvaluated : ValidationFailureType { internal NotEvaluated(string name) : base(name) { } } + + /// + /// Defines a type that represents that no evaluation has taken place. + /// + public static readonly ValidationFailureType ValidationSucceeded = new Succeeded("Succeeded"); + private class Succeeded : ValidationFailureType { internal Succeeded(string name) : base(name) { } } + + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/ValidationResult.cs b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationResult.cs new file mode 100644 index 0000000000..f4d8ce0dc0 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/ValidationResult.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Contains results of a single step in validating a . + /// A maintains a list of for each step in the token validation. + /// + internal abstract class ValidationResult + { + private bool _isValid = false; + + /// + /// Creates an instance of + /// + protected ValidationResult() + { + ValidationFailureType = ValidationFailureType.ValidationNotEvaluated; + } + + /// + /// Creates an instance of + /// + /// The that occurred during validation. + protected ValidationResult(ValidationFailureType validationFailureType) + { + ValidationFailureType = validationFailureType; + } + + /// + /// Creates an instance of + /// + /// The that occurred during validation. + /// The representing the that occurred during validation. + protected ValidationResult(ValidationFailureType validationFailureType, ExceptionDetail exceptionDetail) + { + ValidationFailureType = validationFailureType; + ExceptionDetail = exceptionDetail; + } + + /// + /// Adds a new stack frame to the exception details. + /// + /// + public void AddStackFrame(StackFrame stackFrame) + { + ExceptionDetail.StackFrames.Add(stackFrame); + } + + /// + /// Gets the that occurred during validation. + /// + public abstract Exception Exception { get; } + + /// + /// Gets the that occurred during validation. + /// + public ExceptionDetail ExceptionDetail { get; } + + /// + /// True if the token was successfully validated, false otherwise. + /// + public bool IsValid + { + get + { + HasValidOrExceptionWasRead = true; + return _isValid; + } + set + { + _isValid = value; + } + } + + // TODO - HasValidOrExceptionWasRead, IsValid, Exception are temporary and will be removed when TokenValidationResult derives from ValidationResult. + /// + /// Gets or sets a boolean recording if IsValid or Exception was called. + /// + protected bool HasValidOrExceptionWasRead { get; set; } + + /// + /// Logs the validation result. + /// +#pragma warning disable CA1822 // Mark members as static + public void Log() +#pragma warning restore CA1822 // Mark members as static + { + // TODO - Do we need this, how will it work? + } + + /// + /// Contains any logs that would have been written. + /// + public IList LogDetails { get; } = new List(); + + /// + /// Gets the indicating why the validation was not satisfied. + /// + public ValidationFailureType ValidationFailureType + { + get; + } = ValidationFailureType.ValidationNotEvaluated; + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs new file mode 100644 index 0000000000..9c41082c53 --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Audience.cs @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.IdentityModel.Abstractions; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Partial class for Audience Validation. + /// + public static partial class Validators + { + /// + /// Determines if the audiences found in a are valid. + /// + /// The audiences found in the . + /// The being validated. + /// required for validation. + /// If 'validationParameters' is null. + /// If 'audiences' is null and is true. + /// If is null or whitespace and is null. + /// If none of the 'audiences' matched either or one of . + /// An EXACT match is required. + public static void ValidateAudience(IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + if (validationParameters.AudienceValidator != null) + { + if (!validationParameters.AudienceValidator(audiences, securityToken, validationParameters)) + throw LogHelper.LogExceptionMessage( + new SecurityTokenInvalidAudienceException( + LogHelper.FormatInvariant( + LogMessages.IDX10231, + LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString()))) + { + InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) + }); + + return; + } + + if (!validationParameters.ValidateAudience) + { + LogHelper.LogWarning(LogMessages.IDX10233); + return; + } + + if (audiences == null) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAudienceException(LogMessages.IDX10207) { InvalidAudience = null }); + + if (string.IsNullOrWhiteSpace(validationParameters.ValidAudience) && (validationParameters.ValidAudiences == null)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAudienceException(LogMessages.IDX10208) { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) }); + + if (!audiences.Any()) + throw LogHelper.LogExceptionMessage( + new SecurityTokenInvalidAudienceException(LogHelper.FormatInvariant(LogMessages.IDX10206)) + { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) }); + + // create enumeration of all valid audiences from validationParameters + IEnumerable validationParametersAudiences; + + if (validationParameters.ValidAudiences == null) + validationParametersAudiences = new[] { validationParameters.ValidAudience }; + else if (string.IsNullOrWhiteSpace(validationParameters.ValidAudience)) + validationParametersAudiences = validationParameters.ValidAudiences; + else + validationParametersAudiences = validationParameters.ValidAudiences.Concat(new[] { validationParameters.ValidAudience }); + + if (AudienceIsValid(audiences, validationParameters, validationParametersAudiences)) + return; + + SecurityTokenInvalidAudienceException ex = new SecurityTokenInvalidAudienceException( + LogHelper.FormatInvariant(LogMessages.IDX10214, + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(audiences)), + LogHelper.MarkAsNonPII(validationParameters.ValidAudience ?? "null"), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidAudiences)))) + { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) }; + + if (!validationParameters.LogValidationExceptions) + throw ex; + + throw LogHelper.LogExceptionMessage(ex); + } + + private static bool AudienceIsValid(IEnumerable audiences, TokenValidationParameters validationParameters, IEnumerable validationParametersAudiences) + { + foreach (string tokenAudience in audiences) + { + if (string.IsNullOrWhiteSpace(tokenAudience)) + continue; + + foreach (string validAudience in validationParametersAudiences) + { + if (string.IsNullOrWhiteSpace(validAudience)) + continue; + + if (AudiencesMatch(validationParameters, tokenAudience, validAudience)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); + + return true; + } + } + } + + return false; + } + + private static bool AudiencesMatch(TokenValidationParameters validationParameters, string tokenAudience, string validAudience) + { + if (validAudience.Length == tokenAudience.Length) + { + if (string.Equals(validAudience, tokenAudience)) + return true; + } + else if (validationParameters.IgnoreTrailingSlashWhenValidatingAudience && AudiencesMatchIgnoringTrailingSlash(tokenAudience, validAudience)) + return true; + + return false; + } + + private static bool AudiencesMatchIgnoringTrailingSlash(string tokenAudience, string validAudience) + { + int length = -1; + + if (validAudience.Length == tokenAudience.Length + 1 && validAudience.EndsWith("/", StringComparison.InvariantCulture)) + length = validAudience.Length - 1; + else if (tokenAudience.Length == validAudience.Length + 1 && tokenAudience.EndsWith("/", StringComparison.InvariantCulture)) + length = tokenAudience.Length - 1; + + // the length of the audiences is different by more than 1 and neither ends in a "/" + if (length == -1) + return false; + + if (string.CompareOrdinal(validAudience, 0, tokenAudience, 0, length) == 0) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); + + return true; + } + + return false; + } + + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs new file mode 100644 index 0000000000..f4d580e1ef --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Issuer.cs @@ -0,0 +1,313 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.Abstractions; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// Definition for delegate that will validate the issuer value in a token. + /// + /// The issuer to validate. + /// The that is being validated. + /// required for validation. + /// + /// + /// A that contains the results of validating the issuer. + /// This delegate is not expected to throw. + internal delegate Task IssuerValidationDelegateAsync( + string issuer, + SecurityToken securityToken, + TokenValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken); + + /// + /// IssuerValidation + /// + public static partial class Validators + { + /// + /// Determines if an issuer found in a is valid. + /// + /// The issuer to validate + /// The that is being validated. + /// required for validation. + /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". + /// If 'validationParameters' is null. + /// If 'issuer' is null or whitespace and is true. + /// If is null or whitespace and is null. + /// If 'issuer' failed to matched either or one of . + /// An EXACT match is required. + public static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + return ValidateIssuer(issuer, securityToken, validationParameters, null); + } + + /// + /// Determines if an issuer found in a is valid. + /// + /// The issuer to validate + /// The that is being validated. + /// required for validation. + /// The required for issuer and signing key validation. + /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". + /// If 'validationParameters' is null. + /// If 'issuer' is null or whitespace and is true. + /// If ' configuration' is null. + /// If is null or whitespace and is null and is null. + /// If 'issuer' failed to matched either or one of or . + /// An EXACT match is required. + internal static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) + { + ValueTask vt = ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration); + return vt.IsCompletedSuccessfully ? + vt.Result : + vt.AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + /// + /// Determines if an issuer found in a is valid. + /// + /// The issuer to validate + /// The that is being validated. + /// required for validation. + /// The required for issuer and signing key validation. + /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". + /// If 'validationParameters' is null. + /// If 'issuer' is null or whitespace and is true. + /// If ' configuration' is null. + /// If is null or whitespace and is null and is null. + /// If 'issuer' failed to matched either or one of or . + /// An EXACT match is required. + internal static async ValueTask ValidateIssuerAsync( + string issuer, + SecurityToken securityToken, + TokenValidationParameters validationParameters, + BaseConfiguration configuration) + { + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + if (validationParameters.IssuerValidatorAsync != null) + return await validationParameters.IssuerValidatorAsync(issuer, securityToken, validationParameters).ConfigureAwait(false); + + if (validationParameters.IssuerValidatorUsingConfiguration != null) + return validationParameters.IssuerValidatorUsingConfiguration(issuer, securityToken, validationParameters, configuration); + + if (validationParameters.IssuerValidator != null) + return validationParameters.IssuerValidator(issuer, securityToken, validationParameters); + + if (!validationParameters.ValidateIssuer) + { + LogHelper.LogWarning(LogMessages.IDX10235); + return issuer; + } + + if (string.IsNullOrWhiteSpace(issuer)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10211) + { InvalidIssuer = issuer }); + + // Throw if all possible places to validate against are null or empty + if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer) + && validationParameters.ValidIssuers.IsNullOrEmpty() + && string.IsNullOrWhiteSpace(configuration?.Issuer)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10204) + { InvalidIssuer = issuer }); + + if (configuration != null) + { + if (string.Equals(configuration.Issuer, issuer)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); + + return issuer; + } + } + + if (string.Equals(validationParameters.ValidIssuer, issuer)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); + + return issuer; + } + + if (validationParameters.ValidIssuers != null) + { + foreach (string str in validationParameters.ValidIssuers) + { + if (string.IsNullOrEmpty(str)) + { + LogHelper.LogInformation(LogMessages.IDX10262); + continue; + } + + if (string.Equals(str, issuer)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); + + return issuer; + } + } + } + + SecurityTokenInvalidIssuerException ex = new SecurityTokenInvalidIssuerException( + LogHelper.FormatInvariant(LogMessages.IDX10205, + LogHelper.MarkAsNonPII(issuer), + LogHelper.MarkAsNonPII(validationParameters.ValidIssuer ?? "null"), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)), + LogHelper.MarkAsNonPII(configuration?.Issuer))) + { InvalidIssuer = issuer }; + + if (!validationParameters.LogValidationExceptions) + throw ex; + + throw LogHelper.LogExceptionMessage(ex); + } + + /// + /// Determines if an issuer found in a is valid. + /// + /// The issuer to validate + /// The that is being validated. + /// required for validation. + /// + /// + /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". + /// If 'validationParameters' is null. + /// If 'issuer' is null or whitespace and is true. + /// If is null or whitespace and is null. + /// If 'issuer' failed to matched either or one of . + /// An EXACT match is required. + internal static async Task ValidateIssuerAsync( + string issuer, + SecurityToken securityToken, + TokenValidationParameters validationParameters, + CallContext callContext, + CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(issuer)) + { + return new IssuerValidationResult( + issuer, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10211, + null), + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true), + null)); + } + + if (validationParameters == null) + return new IssuerValidationResult( + issuer, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII(nameof(validationParameters))), + typeof(ArgumentNullException), + new StackFrame(true), + null)); + + if (securityToken == null) + return new IssuerValidationResult( + issuer, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII(nameof(securityToken))), + typeof(ArgumentNullException), + new StackFrame(true), + null)); + + BaseConfiguration configuration = null; + if (validationParameters.ConfigurationManager != null) + configuration = await validationParameters.ConfigurationManager.GetBaseConfigurationAsync(cancellationToken).ConfigureAwait(false); + + // Throw if all possible places to validate against are null or empty + if (string.IsNullOrWhiteSpace(validationParameters.ValidIssuer) + && validationParameters.ValidIssuers.IsNullOrEmpty() + && string.IsNullOrWhiteSpace(configuration?.Issuer)) + { + return new IssuerValidationResult( + issuer, + ValidationFailureType.IssuerValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10211, + null), + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true))); + } + + if (configuration != null) + { + if (string.Equals(configuration.Issuer, issuer)) + { + // TODO - how and when to log + // Logs will have to be passed back to Wilson + // so that they can be written to the correct place and in the correct format respecting PII. + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer), callContext); + + return new IssuerValidationResult(issuer, + IssuerValidationResult.ValidationSource.IssuerIsConfigurationIssuer); + } + } + + if (string.Equals(validationParameters.ValidIssuer, issuer)) + { + return new IssuerValidationResult(issuer, + IssuerValidationResult.ValidationSource.IssuerIsValidIssuer); + } + + if (validationParameters.ValidIssuers != null) + { + foreach (string str in validationParameters.ValidIssuers) + { + if (string.IsNullOrEmpty(str)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10262); + + continue; + } + + if (string.Equals(str, issuer)) + { + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); + + return new IssuerValidationResult(issuer, + IssuerValidationResult.ValidationSource.IssuerIsAmongValidIssuers); + } + } + } + + return new IssuerValidationResult( + issuer, + ValidationFailureType.IssuerValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10205, + LogHelper.MarkAsNonPII(issuer), + LogHelper.MarkAsNonPII(validationParameters.ValidIssuer ?? "null"), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)), + LogHelper.MarkAsNonPII(configuration?.Issuer)), + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true))); + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs new file mode 100644 index 0000000000..77c553c4db --- /dev/null +++ b/src/Microsoft.IdentityModel.Tokens/Validation/Validators.Lifetime.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Microsoft.IdentityModel.Logging; + +namespace Microsoft.IdentityModel.Tokens +{ + /// + /// IssuerValidation + /// + public static partial class Validators + { + /// + /// Validates the lifetime of a . + /// + /// The 'notBefore' time found in the . + /// The 'expiration' time found in the . + /// The being validated. + /// required for validation. + /// If 'validationParameters' is null. + /// If 'expires.HasValue' is false and is true. + /// If 'notBefore' is > 'expires'. + /// If 'notBefore' is > DateTime.UtcNow. + /// If 'expires' is < DateTime.UtcNow. + /// All time comparisons apply . + public static void ValidateLifetime(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters) + { + if (validationParameters == null) + throw LogHelper.LogArgumentNullException(nameof(validationParameters)); + + if (validationParameters.LifetimeValidator != null) + { + if (!validationParameters.LifetimeValidator(notBefore, expires, securityToken, validationParameters)) + throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidLifetimeException(LogHelper.FormatInvariant(LogMessages.IDX10230, securityToken)) + { NotBefore = notBefore, Expires = expires }); + + return; + } + + if (!validationParameters.ValidateLifetime) + { + LogHelper.LogInformation(LogMessages.IDX10238); + return; + } + + ValidatorUtilities.ValidateLifetime(notBefore, expires, securityToken, validationParameters); + } + } +} diff --git a/src/Microsoft.IdentityModel.Tokens/ValidatorUtilities.cs b/src/Microsoft.IdentityModel.Tokens/ValidatorUtilities.cs index 482f2f3d37..6c652f6a3b 100644 --- a/src/Microsoft.IdentityModel.Tokens/ValidatorUtilities.cs +++ b/src/Microsoft.IdentityModel.Tokens/ValidatorUtilities.cs @@ -3,6 +3,7 @@ using System; using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.Abstractions; namespace Microsoft.IdentityModel.Tokens { @@ -49,7 +50,8 @@ internal static void ValidateLifetime(DateTime? notBefore, DateTime? expires, Se }); // if it reaches here, that means lifetime of the token is valid - LogHelper.LogInformation(LogMessages.IDX10239); + if (LogHelper.IsEnabled(EventLogLevel.Informational)) + LogHelper.LogInformation(LogMessages.IDX10239); } } } diff --git a/src/Microsoft.IdentityModel.Tokens/Validators.cs b/src/Microsoft.IdentityModel.Tokens/Validators.cs index 2c9f9e6ca1..000ba4c617 100644 --- a/src/Microsoft.IdentityModel.Tokens/Validators.cs +++ b/src/Microsoft.IdentityModel.Tokens/Validators.cs @@ -2,10 +2,8 @@ // Licensed under the MIT License. using System; -using System.Collections.Generic; using System.Linq; using System.Security.Cryptography.X509Certificates; -using System.Threading.Tasks; using Microsoft.IdentityModel.Abstractions; using Microsoft.IdentityModel.Logging; @@ -14,7 +12,7 @@ namespace Microsoft.IdentityModel.Tokens /// /// AudienceValidator /// - public static class Validators + public static partial class Validators { /// /// Validates if a given algorithm for a is valid. @@ -50,283 +48,6 @@ public static void ValidateAlgorithm(string algorithm, SecurityKey securityKey, } } - /// - /// Determines if the audiences found in a are valid. - /// - /// The audiences found in the . - /// The being validated. - /// required for validation. - /// If 'validationParameters' is null. - /// If 'audiences' is null and is true. - /// If is null or whitespace and is null. - /// If none of the 'audiences' matched either or one of . - /// An EXACT match is required. - public static void ValidateAudience(IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) - { - if (validationParameters == null) - throw LogHelper.LogArgumentNullException(nameof(validationParameters)); - - if (validationParameters.AudienceValidator != null) - { - if (!validationParameters.AudienceValidator(audiences, securityToken, validationParameters)) - throw LogHelper.LogExceptionMessage( - new SecurityTokenInvalidAudienceException( - LogHelper.FormatInvariant( - LogMessages.IDX10231, - LogHelper.MarkAsUnsafeSecurityArtifact(securityToken, t => t.ToString()))) - { - InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) - }); - - return; - } - - if (!validationParameters.ValidateAudience) - { - LogHelper.LogWarning(LogMessages.IDX10233); - return; - } - - if (audiences == null) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAudienceException(LogMessages.IDX10207) { InvalidAudience = null }); - - if (string.IsNullOrWhiteSpace(validationParameters.ValidAudience) && (validationParameters.ValidAudiences == null)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidAudienceException(LogMessages.IDX10208) { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) }); - - if (!audiences.Any()) - throw LogHelper.LogExceptionMessage( - new SecurityTokenInvalidAudienceException(LogHelper.FormatInvariant(LogMessages.IDX10206)) - { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) }); - - // create enumeration of all valid audiences from validationParameters - IEnumerable validationParametersAudiences; - - if (validationParameters.ValidAudiences == null) - validationParametersAudiences = new[] { validationParameters.ValidAudience }; - else if (string.IsNullOrWhiteSpace(validationParameters.ValidAudience)) - validationParametersAudiences = validationParameters.ValidAudiences; - else - validationParametersAudiences = validationParameters.ValidAudiences.Concat(new[] { validationParameters.ValidAudience }); - - if (AudienceIsValid(audiences, validationParameters, validationParametersAudiences)) - return; - - SecurityTokenInvalidAudienceException ex = new SecurityTokenInvalidAudienceException( - LogHelper.FormatInvariant(LogMessages.IDX10214, - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(audiences)), - LogHelper.MarkAsNonPII(validationParameters.ValidAudience ?? "null"), - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidAudiences)))) - { InvalidAudience = Utility.SerializeAsSingleCommaDelimitedString(audiences) }; - - if (!validationParameters.LogValidationExceptions) - throw ex; - - throw LogHelper.LogExceptionMessage(ex); - } - - private static bool AudienceIsValid(IEnumerable audiences, TokenValidationParameters validationParameters, IEnumerable validationParametersAudiences) - { - foreach (string tokenAudience in audiences) - { - if (string.IsNullOrWhiteSpace(tokenAudience)) - continue; - - foreach (string validAudience in validationParametersAudiences) - { - if (string.IsNullOrWhiteSpace(validAudience)) - continue; - - if (AudiencesMatch(validationParameters, tokenAudience, validAudience)) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); - - return true; - } - } - } - - return false; - } - - private static bool AudiencesMatch(TokenValidationParameters validationParameters, string tokenAudience, string validAudience) - { - if (validAudience.Length == tokenAudience.Length) - { - if (string.Equals(validAudience, tokenAudience)) - return true; - } - else if (validationParameters.IgnoreTrailingSlashWhenValidatingAudience && AudiencesMatchIgnoringTrailingSlash(tokenAudience, validAudience)) - return true; - - return false; - } - - private static bool AudiencesMatchIgnoringTrailingSlash(string tokenAudience, string validAudience) - { - int length = -1; - - if (validAudience.Length == tokenAudience.Length + 1 && validAudience.EndsWith("/", StringComparison.InvariantCulture)) - length = validAudience.Length - 1; - else if (tokenAudience.Length == validAudience.Length + 1 && tokenAudience.EndsWith("/", StringComparison.InvariantCulture)) - length = tokenAudience.Length - 1; - - // the length of the audiences is different by more than 1 and neither ends in a "/" - if (length == -1) - return false; - - if (string.CompareOrdinal(validAudience, 0, tokenAudience, 0, length) == 0) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10234, LogHelper.MarkAsNonPII(tokenAudience)); - - return true; - } - - return false; - } - - /// - /// Determines if an issuer found in a is valid. - /// - /// The issuer to validate - /// The that is being validated. - /// required for validation. - /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". - /// If 'validationParameters' is null. - /// If 'issuer' is null or whitespace and is true. - /// If is null or whitespace and is null. - /// If 'issuer' failed to matched either or one of . - /// An EXACT match is required. - public static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters) - { - return ValidateIssuer(issuer, securityToken, validationParameters, null); - } - - /// - /// Determines if an issuer found in a is valid. - /// - /// The issuer to validate - /// The that is being validated. - /// required for validation. - /// The required for issuer and signing key validation. - /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". - /// If 'validationParameters' is null. - /// If 'issuer' is null or whitespace and is true. - /// If ' configuration' is null. - /// If is null or whitespace and is null and is null. - /// If 'issuer' failed to matched either or one of or . - /// An EXACT match is required. - internal static string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters, BaseConfiguration configuration) - { - ValueTask vt = ValidateIssuerAsync(issuer, securityToken, validationParameters, configuration); - return vt.IsCompletedSuccessfully ? - vt.Result : - vt.AsTask().ConfigureAwait(false).GetAwaiter().GetResult(); - } - - /// - /// Determines if an issuer found in a is valid. - /// - /// The issuer to validate - /// The that is being validated. - /// required for validation. - /// The required for issuer and signing key validation. - /// The issuer to use when creating the "Claim"(s) in a "ClaimsIdentity". - /// If 'validationParameters' is null. - /// If 'issuer' is null or whitespace and is true. - /// If ' configuration' is null. - /// If is null or whitespace and is null and is null. - /// If 'issuer' failed to matched either or one of or . - /// An EXACT match is required. - internal static async ValueTask ValidateIssuerAsync( - string issuer, - SecurityToken securityToken, - TokenValidationParameters validationParameters, - BaseConfiguration configuration) - { - if (validationParameters == null) - throw LogHelper.LogArgumentNullException(nameof(validationParameters)); - - if (validationParameters.IssuerValidatorAsync != null) - return await validationParameters.IssuerValidatorAsync(issuer, securityToken, validationParameters).ConfigureAwait(false); - - if (validationParameters.IssuerValidatorUsingConfiguration != null) - return validationParameters.IssuerValidatorUsingConfiguration(issuer, securityToken, validationParameters, configuration); - - if (validationParameters.IssuerValidator != null) - return validationParameters.IssuerValidator(issuer, securityToken, validationParameters); - - if (!validationParameters.ValidateIssuer) - { - LogHelper.LogWarning(LogMessages.IDX10235); - return issuer; - } - - if (string.IsNullOrWhiteSpace(issuer)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10211) - { InvalidIssuer = issuer }); - - // Throw if all possible places to validate against are null or empty - if ( string.IsNullOrWhiteSpace(validationParameters.ValidIssuer) - && validationParameters.ValidIssuers.IsNullOrEmpty() - && string.IsNullOrWhiteSpace(configuration?.Issuer)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogMessages.IDX10204) - { InvalidIssuer = issuer }); - - if (configuration != null) - { - if (string.Equals(configuration.Issuer, issuer)) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); - - return issuer; - } - } - - if (string.Equals(validationParameters.ValidIssuer, issuer)) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); - - return issuer; - } - - if (validationParameters.ValidIssuers != null) - { - foreach (string str in validationParameters.ValidIssuers) - { - if (string.IsNullOrEmpty(str)) - { - LogHelper.LogInformation(LogMessages.IDX10262); - continue; - } - - if (string.Equals(str, issuer)) - { - if (LogHelper.IsEnabled(EventLogLevel.Informational)) - LogHelper.LogInformation(LogMessages.IDX10236, LogHelper.MarkAsNonPII(issuer)); - - return issuer; - } - } - } - - SecurityTokenInvalidIssuerException ex = new SecurityTokenInvalidIssuerException( - LogHelper.FormatInvariant(LogMessages.IDX10205, - LogHelper.MarkAsNonPII(issuer), - LogHelper.MarkAsNonPII(validationParameters.ValidIssuer ?? "null"), - LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(validationParameters.ValidIssuers)), - LogHelper.MarkAsNonPII(configuration?.Issuer))) - { InvalidIssuer = issuer }; - - if (!validationParameters.LogValidationExceptions) - throw ex; - - throw LogHelper.LogExceptionMessage(ex); - } - /// /// Validates the that signed a . /// @@ -422,42 +143,6 @@ internal static void ValidateIssuerSigningKeyLifeTime(SecurityKey securityKey, T } } - /// - /// Validates the lifetime of a . - /// - /// The 'notBefore' time found in the . - /// The 'expiration' time found in the . - /// The being validated. - /// required for validation. - /// If 'validationParameters' is null. - /// If 'expires.HasValue' is false and is true. - /// If 'notBefore' is > 'expires'. - /// If 'notBefore' is > DateTime.UtcNow. - /// If 'expires' is < DateTime.UtcNow. - /// All time comparisons apply . - public static void ValidateLifetime(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters) - { - if (validationParameters == null) - throw LogHelper.LogArgumentNullException(nameof(validationParameters)); - - if (validationParameters.LifetimeValidator != null) - { - if (!validationParameters.LifetimeValidator(notBefore, expires, securityToken, validationParameters)) - throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidLifetimeException(LogHelper.FormatInvariant(LogMessages.IDX10230, securityToken)) - { NotBefore = notBefore, Expires = expires }); - - return; - } - - if (!validationParameters.ValidateLifetime) - { - LogHelper.LogInformation(LogMessages.IDX10238); - return; - } - - ValidatorUtilities.ValidateLifetime(notBefore, expires, securityToken, validationParameters); - } - /// /// Validates if a token has been replayed. /// diff --git a/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs b/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs index ec0f483dd8..6a92eaedaa 100644 --- a/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs +++ b/src/Microsoft.IdentityModel.Tokens/X509EncryptingCredentials.cs @@ -11,8 +11,6 @@ namespace Microsoft.IdentityModel.Tokens /// public class X509EncryptingCredentials : EncryptingCredentials { - internal const string _useShortNameForRsaOaepKey = "Switch.Microsoft.IdentityModel.UseShortNameForRsaOaepKey"; - /// /// Designed to construct based on a x509 certificate. /// @@ -23,7 +21,7 @@ public class X509EncryptingCredentials : EncryptingCredentials /// /// if 'certificate' is null. public X509EncryptingCredentials(X509Certificate2 certificate) - : this(certificate, GetEncryptionAlgorithm(), SecurityAlgorithms.DefaultSymmetricEncryptionAlgorithm) + : this(certificate, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.DefaultSymmetricEncryptionAlgorithm) { } @@ -50,15 +48,5 @@ public X509Certificate2 Certificate get; private set; } - - private static string GetEncryptionAlgorithm() - { - return ShouldUseShortNameForRsaOaepKey() ? SecurityAlgorithms.RsaOAEP : SecurityAlgorithms.DefaultAsymmetricKeyWrapAlgorithm; - } - - private static bool ShouldUseShortNameForRsaOaepKey() - { - return AppContext.TryGetSwitch(_useShortNameForRsaOaepKey, out var useKeyWrap) && useKeyWrap; - } } } diff --git a/src/Microsoft.IdentityModel.Tokens/X509SecurityKey.cs b/src/Microsoft.IdentityModel.Tokens/X509SecurityKey.cs index 5b7687ec10..2c45122707 100644 --- a/src/Microsoft.IdentityModel.Tokens/X509SecurityKey.cs +++ b/src/Microsoft.IdentityModel.Tokens/X509SecurityKey.cs @@ -78,11 +78,8 @@ public AsymmetricAlgorithm PrivateKey { if (!_privateKeyAvailabilityDetermined) { -#if NET461 || NET462 || NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER _privateKey = RSACertificateExtensions.GetRSAPrivateKey(Certificate); -#else - _privateKey = Certificate.PrivateKey; -#endif + _privateKeyAvailabilityDetermined = true; } } @@ -105,11 +102,7 @@ public AsymmetricAlgorithm PublicKey { if (_publicKey == null) { -#if NET461 || NET462 || NET472 || NETSTANDARD2_0 || NET6_0_OR_GREATER _publicKey = RSACertificateExtensions.GetRSAPublicKey(Certificate); -#else - _publicKey = Certificate.PublicKey.Key; -#endif } } } diff --git a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs index 445671d134..323718cc84 100644 --- a/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs +++ b/src/Microsoft.IdentityModel.Validators/AadIssuerValidator/AadIssuerValidator.cs @@ -13,6 +13,7 @@ using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; +using static Microsoft.IdentityModel.Validators.AadIssuerValidator; namespace Microsoft.IdentityModel.Validators { @@ -382,31 +383,18 @@ private ConfigurationManager CreateConfigManager( } } - internal static bool IsValidIssuer(string validIssuerTemplate, string tenantId, string actualIssuer) + private static bool IsValidIssuer(string validIssuerTemplate, string tenantId, string actualIssuer) { - if (string.IsNullOrEmpty(validIssuerTemplate) || string.IsNullOrEmpty(actualIssuer) || string.IsNullOrEmpty(tenantId)) + if (string.IsNullOrEmpty(validIssuerTemplate)) return false; - ReadOnlySpan validIssuerTemplateSpan = validIssuerTemplate.AsSpan(); - ReadOnlySpan actualIssuerSpan = actualIssuer.AsSpan(); - int indexOfTenantIdTemplate = validIssuerTemplate.IndexOf(TenantIdTemplate, StringComparison.Ordinal); - - if (indexOfTenantIdTemplate >= 0 && actualIssuer.Length > indexOfTenantIdTemplate) + if (validIssuerTemplate.Contains(TenantIdTemplate)) { - // ensure the first part of the validIssuerTemplate matches the first part of actualIssuer - if (!validIssuerTemplateSpan.Slice(0, indexOfTenantIdTemplate).SequenceEqual(actualIssuerSpan.Slice(0, indexOfTenantIdTemplate))) - return false; - - // ensure that actualIssuer contains the tenantId from indexOfTenantIdTemplate for the length of tenantId.Length - if (!actualIssuerSpan.Slice(indexOfTenantIdTemplate, tenantId.Length).SequenceEqual(tenantId.AsSpan())) - return false; - - // ensure the second halves are equal - return validIssuerTemplateSpan.Slice(indexOfTenantIdTemplate + TenantIdTemplate.Length).SequenceEqual(actualIssuerSpan.Slice(indexOfTenantIdTemplate + tenantId.Length)); + return validIssuerTemplate.Replace(TenantIdTemplate, tenantId) == actualIssuer; } else { - return validIssuerTemplateSpan.SequenceEqual(actualIssuerSpan); + return validIssuerTemplate == actualIssuer; } } diff --git a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs index 5335284168..d745bf124c 100644 --- a/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs +++ b/src/Microsoft.IdentityModel.Validators/AadTokenValidationParametersExtension.cs @@ -76,36 +76,31 @@ internal static bool ValidateIssuerSigningKey(SecurityKey securityKey, SecurityT #if NET6_0_OR_GREATER if (!string.IsNullOrEmpty(tokenIssuer) && !tokenIssuer.Contains(tenantIdFromToken, StringComparison.Ordinal)) throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogHelper.FormatInvariant(LogMessages.IDX40004, LogHelper.MarkAsNonPII(tokenIssuer), LogHelper.MarkAsNonPII(tenantIdFromToken)))); + + // creating an effectiveSigningKeyIssuer is required as signingKeyIssuer might contain {tenantid} + var effectiveSigningKeyIssuer = signingKeyIssuer.Replace(AadIssuerValidator.TenantIdTemplate, tenantIdFromToken, StringComparison.Ordinal); + var v2TokenIssuer = openIdConnectConfiguration.Issuer?.Replace(AadIssuerValidator.TenantIdTemplate, tenantIdFromToken, StringComparison.Ordinal); #else if (!string.IsNullOrEmpty(tokenIssuer) && !tokenIssuer.Contains(tenantIdFromToken)) throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogHelper.FormatInvariant(LogMessages.IDX40004, LogHelper.MarkAsNonPII(tokenIssuer), LogHelper.MarkAsNonPII(tenantIdFromToken)))); + + // creating an effectiveSigningKeyIssuer is required as signingKeyIssuer might contain {tenantid} + var effectiveSigningKeyIssuer = signingKeyIssuer.Replace(AadIssuerValidator.TenantIdTemplate, tenantIdFromToken); + var v2TokenIssuer = openIdConnectConfiguration.Issuer?.Replace(AadIssuerValidator.TenantIdTemplate, tenantIdFromToken); #endif - // comparing effectiveSigningKeyIssuer with v2TokenIssuer is required because of the following scenario: + + // comparing effectiveSigningKeyIssuer with v2TokenIssuer is required as well because of the following scenario: // 1. service trusts /common/v2.0 endpoint // 2. service receieves a v1 token that has issuer like sts.windows.net // 3. signing key issuers will never match sts.windows.net as v1 endpoint doesn't have issuers attached to keys // v2TokenIssuer is the representation of Token.Issuer (if it was a v2 issuer) - if (!AadIssuerValidator.IsValidIssuer(signingKeyIssuer, tenantIdFromToken, tokenIssuer) - && !AadIssuerValidator.IsValidIssuer(signingKeyIssuer, tenantIdFromToken, openIdConnectConfiguration.Issuer)) - { - int templateStartIndex = signingKeyIssuer.IndexOf(AadIssuerValidator.TenantIdTemplate, StringComparison.Ordinal); - string effectiveSigningKeyIssuer = templateStartIndex > -1 ? CreateIssuer(signingKeyIssuer, AadIssuerValidator.TenantIdTemplate, tenantIdFromToken, templateStartIndex) : signingKeyIssuer; + if (effectiveSigningKeyIssuer != tokenIssuer && effectiveSigningKeyIssuer != v2TokenIssuer) throw LogHelper.LogExceptionMessage(new SecurityTokenInvalidIssuerException(LogHelper.FormatInvariant(LogMessages.IDX40005, LogHelper.MarkAsNonPII(tokenIssuer), LogHelper.MarkAsNonPII(effectiveSigningKeyIssuer)))); - } } return true; } - internal static string CreateIssuer(string issuer, string tenantIdTemplate, string tenantId, int templateStartIndex) - { -#if NET6_0_OR_GREATER - return string.Concat(issuer.AsSpan(0, templateStartIndex), tenantId, issuer.AsSpan(templateStartIndex + tenantIdTemplate.Length, issuer.Length - tenantIdTemplate.Length - templateStartIndex)); -#else - return string.Concat(issuer.Substring(0, templateStartIndex), tenantId, issuer.Substring(templateStartIndex + tenantIdTemplate.Length)); -#endif - } - /// /// Validates the issuer signing key certificate. /// diff --git a/src/System.IdentityModel.Tokens.Jwt/JwtPayload.cs b/src/System.IdentityModel.Tokens.Jwt/JwtPayload.cs index 7a36f83672..506bd31983 100644 --- a/src/System.IdentityModel.Tokens.Jwt/JwtPayload.cs +++ b/src/System.IdentityModel.Tokens.Jwt/JwtPayload.cs @@ -508,7 +508,7 @@ public virtual IEnumerable Claims claims.Add(new Claim(keyValuePair.Key, string.Empty, JsonClaimValueTypes.JsonNull, issuer, issuer)); else if (keyValuePair.Value is string str) - claims.Add(new Claim(keyValuePair.Key, str, GetClaimValueType(str), issuer, issuer)); + claims.Add(new Claim(keyValuePair.Key, str, GetClaimValueType(keyValuePair.Key, str), issuer, issuer)); else if (keyValuePair.Value is JsonElement j) AddClaimsFromJsonElement(keyValuePair.Key, issuer, j, claims); @@ -521,7 +521,7 @@ public virtual IEnumerable Claims { foreach (var item in dictionary) if (item.Value != null) - claims.Add(new Claim(keyValuePair.Key, "{" + item.Key + ":" + item.Value.ToString() + "}", GetClaimValueType(item.Value), issuer, issuer)); + claims.Add(new Claim(keyValuePair.Key, "{" + item.Key + ":" + item.Value.ToString() + "}", GetClaimValueType(item.Key, item.Value), issuer, issuer)); } else if (keyValuePair.Value is DateTime dateTime) claims.Add(new Claim(keyValuePair.Key, dateTime.ToString("o", CultureInfo.InvariantCulture), ClaimValueTypes.DateTime, issuer, issuer)); @@ -536,7 +536,7 @@ public virtual IEnumerable Claims else if (keyValuePair.Value != null) { var value = keyValuePair.Value; - var claimValueType = GetClaimValueType(value); + var claimValueType = GetClaimValueType(keyValuePair.Key, value); if (value is IFormattable formattable) claims.Add(new Claim(keyValuePair.Key, formattable.ToString(null, CultureInfo.InvariantCulture), claimValueType, issuer, issuer)); else @@ -569,7 +569,7 @@ private void AddListofObjects(string key, IEnumerable objects, List claimsCollection) this[kvp.Key] = kvp.Value; } - internal static string GetClaimValueType(object value) + private static string GetClaimValueType(string claimType, object value) { if (value == null) return JsonClaimValueTypes.JsonNull; @@ -672,7 +672,7 @@ internal static string GetClaimValueType(object value) Type objType = value.GetType(); if (value is string str) - return JwtTokenUtilities.GetStringClaimValueType(str); + return JwtTokenUtilities.GetStringClaimValueType(str, claimType); else if (objType == typeof(int)) return ClaimValueTypes.Integer32; else if (objType == typeof(long)) diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs index f7b1d7b843..6b67a4f4e3 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/JsonWebTokenHandlerTests.cs @@ -3523,7 +3523,7 @@ public static TheoryData ValidateJwsWithConfigTheoryData incorrectSigningKeysConfig.SigningKeys.Add(KeyingMaterial.X509SecurityKey2); theoryData.Add(new JwtTheoryData { - TestId = nameof(Default.AsymmetricJws) + "_" + "TVPInvalid" + "_" + "ConfigSigningKeysInvalid" + "_SignatureValidatorReturnsValidToken", + TestId = nameof(Default.AsymmetricJws) + "_TVPInvalid_ConfigSigningKeysInvalid_SignatureValidatorReturnsValidToken", Token = Default.AsymmetricJws, ValidationParameters = new TokenValidationParameters { @@ -4190,10 +4190,9 @@ public static TheoryData IncludeSecurityTokenOnFailureTes } [Theory, MemberData(nameof(ValidateAuthenticationTagLengthTheoryData))] - public void ValidateTokenAsync_ModifiedAuthNTag(CreateTokenTheoryData theoryData) + public async Task ValidateTokenAsync_ModifiedAuthNTag(CreateTokenTheoryData theoryData) { // arrange - AppContext.SetSwitch(AuthenticatedEncryptionProvider._skipValidationOfAuthenticationTagLength, theoryData.EnableAppContextSwitch); var payload = new JObject() { { JwtRegisteredClaimNames.Email, "Bob@contoso.com" }, @@ -4217,9 +4216,7 @@ public void ValidateTokenAsync_ModifiedAuthNTag(CreateTokenTheoryData theoryData var jweWithExtraCharacters = jwe + "_cannoli_hunts_truffles_"; // act - // calling ValidateTokenAsync.Result to prevent tests from sharing app context switch property - // normally, we would want to await ValidateTokenAsync().ConfigureAwait(false) - var tokenValidationResult = jsonWebTokenHandler.ValidateTokenAsync(jweWithExtraCharacters, theoryData.ValidationParameters).Result; + var tokenValidationResult = await jsonWebTokenHandler.ValidateTokenAsync(jweWithExtraCharacters, theoryData.ValidationParameters).ConfigureAwait(false); // assert Assert.Equal(theoryData.IsValid, tokenValidationResult.IsValid); @@ -4281,47 +4278,6 @@ public static TheoryData ValidateAuthenticationTagLengthT ValidIssuer = "http://Default.Issuer.com", }, IsValid = false - }, - new("A128CBC-HS256_SkipTagLengthValidationAppContextSwitchOn_IsValid") - { - EnableAppContextSwitch = true, - Algorithm = SecurityAlgorithms.Aes128CbcHmacSha256, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes128CbcHmacSha256), - ValidationParameters = new TokenValidationParameters - { - TokenDecryptionKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, - IssuerSigningKey = Default.SymmetricSigningKey256, - ValidAudience = "http://Default.Audience.com", - ValidIssuer = "http://Default.Issuer.com", - }, - IsValid = true - }, - new("A192CBC-HS384_SkipTagLengthValidationAppContextSwitchOn_IsValid") - { - EnableAppContextSwitch = true, - Algorithm = SecurityAlgorithms.Aes192CbcHmacSha384, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes192CbcHmacSha384), - ValidationParameters = new TokenValidationParameters - { - TokenDecryptionKey = KeyingMaterial.JsonWebKeyRsa256SigningCredentials.Key, - IssuerSigningKey = Default.SymmetricSigningKey256, - ValidAudience = "http://Default.Audience.com", - ValidIssuer = "http://Default.Issuer.com", - }, - IsValid = true - }, - new("A256CBC-HS512_SkipTagLengthValidationAppContextSwitchOn_IsValid") - { - EnableAppContextSwitch = true, - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaPKCS1, SecurityAlgorithms.Aes256CbcHmacSha512), - ValidationParameters = new TokenValidationParameters - { - TokenDecryptionKey = signingCredentials512.Key, - IssuerSigningKey = Default.SymmetricSigningKey256, - ValidAudience = "http://Default.Audience.com", - ValidIssuer = "http://Default.Issuer.com", - }, - IsValid = true } }; } @@ -4370,8 +4326,6 @@ public CreateTokenTheoryData(string testId) : base(testId) public IEnumerable ExpectedDecryptionKeys { get; set; } public Dictionary ExpectedClaims { get; set; } - - public bool EnableAppContextSwitch { get; set; } = false; } // Overrides CryptoProviderFactory.CreateAuthenticatedEncryptionProvider to create AuthenticatedEncryptionProviderMock that provides AesGcm encryption. diff --git a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/Microsoft.IdentityModel.JsonWebTokens.Tests.csproj b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/Microsoft.IdentityModel.JsonWebTokens.Tests.csproj index e333ac1f95..523c78d729 100644 --- a/test/Microsoft.IdentityModel.JsonWebTokens.Tests/Microsoft.IdentityModel.JsonWebTokens.Tests.csproj +++ b/test/Microsoft.IdentityModel.JsonWebTokens.Tests/Microsoft.IdentityModel.JsonWebTokens.Tests.csproj @@ -20,6 +20,10 @@ + + + + diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultCryptoProviderTests.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultCryptoProviderTests.cs deleted file mode 100644 index 39e83e852c..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultCryptoProviderTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading.Tasks; -using Microsoft.Azure.KeyVault.WebKey; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Xunit; -using static Microsoft.IdentityModel.KeyVaultExtensions.KeyVaultSecurityKey; - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class KeyVaultCryptoProviderTests - { - [Fact] - public void ShouldCacheSignatureProvider() - { - TestUtilities.WriteHeader($"{this}.ShouldCacheSignatureProvider"); - var context = new CompareContext($"{this}.ShouldCacheSignatureProvider"); - var keyVaultKeyWithEmptyKid = new CustomKeyVaultSecurityKey("test", new AuthenticationCallback((string authority, string resource, string scope) => Task.FromResult(string.Empty))); - var keyVaultCryptoProvider = new KeyVaultCryptoProvider(); - var signatureProvider = keyVaultCryptoProvider.Create(JsonWebKeySignatureAlgorithm.RS256, keyVaultKeyWithEmptyKid, true); - if (keyVaultCryptoProvider.CryptoProviderCache.TryGetSignatureProvider(keyVaultKeyWithEmptyKid, SecurityAlgorithms.RsaSha256Signature, typeof(KeyVaultSignatureProvider).ToString(), true, out var _)) - context.Diffs.Add("A SignatureProvider was added to keyVaultCryptoProvider.CryptoProviderCache.CryptoProviderCache, but ShouldCacheSignatureProvider() should return false as the key has an empty key id."); - - CryptoProviderFactory.Default.ReleaseSignatureProvider(signatureProvider as KeyVaultSignatureProvider); - - TestUtilities.AssertFailIfErrors(context); - } - - public class CustomKeyVaultSecurityKey : KeyVaultSecurityKey - { - /// - /// Initializes a new instance of the class. - /// - public CustomKeyVaultSecurityKey(string keyIdentifier, AuthenticationCallback callback) : base(keyIdentifier, callback) - { - } - - internal override string InternalId => ""; - } - } -} diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultKeyWrapProviderTests.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultKeyWrapProviderTests.cs deleted file mode 100644 index c16f6cdefa..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultKeyWrapProviderTests.cs +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Microsoft.Azure.KeyVault; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Xunit; - -#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class KeyVaultKeyWrapProviderTests - { - private readonly MockKeyVaultClient _client; - private readonly SecurityKey _key; - - public KeyVaultKeyWrapProviderTests() - { - _client = new MockKeyVaultClient(); - _key = new KeyVaultSecurityKey(KeyVaultUtilities.CreateKeyIdentifier(), keySize: default); - } - - [Theory, MemberData(nameof(DisposeProviderTheoryData))] - public void DisposeProviderTest(KeyWrapProviderTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.DisposeProviderTest", theoryData); - - try - { - var provider = new KeyVaultKeyWrapProvider(_key, theoryData.Algorithm, _client); - _key.CryptoProviderFactory.ReleaseKeyWrapProvider(provider); - - theoryData.ExpectedException.ProcessNoException(context); - } - catch (Exception exception) - { - theoryData.ExpectedException.ProcessException(exception, context); - } - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData DisposeProviderTheoryData - { - get => new TheoryData - { - new KeyWrapProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaPKCS1, - ExpectedException = ExpectedException.NoExceptionExpected, - First = true, - TestId = nameof(SecurityAlgorithms.RsaPKCS1), - }, - new KeyWrapProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaOAEP, - ExpectedException = ExpectedException.NoExceptionExpected, - TestId = nameof(SecurityAlgorithms.RsaOAEP), - }, - }; - } - - [Theory, MemberData(nameof(KeyWrapProviderTheoryData))] - public void WrapUnwrapKeyTest(KeyWrapProviderTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.WrapUnwrapKeyTest", theoryData); - - try - { - var provider = new KeyVaultKeyWrapProvider(_key, theoryData.Algorithm, _client); - if (provider == null) - context.AddDiff("(provider == null)"); - - var keyBytes = Guid.NewGuid().ToByteArray(); - var wrappedKey = provider.WrapKey(keyBytes); - if (wrappedKey == null) - context.AddDiff("(wrappedKey == null)"); - - if (_client.ExpectedKeyWrapLength != wrappedKey.Length) - context.AddDiff($"_client.ExpectedKeyWrapLength != wrappedKey.Length. {_client.ExpectedKeyWrapLength} != {wrappedKey.Length}"); - - if (Utility.AreEqual(keyBytes, wrappedKey)) - context.AddDiff("Utility.AreEqual(keyBytes, wrappedKey)"); - - var unwrappedKey = provider.UnwrapKey(wrappedKey); - if (unwrappedKey == null) - context.AddDiff("(unwrappedKey == null)"); - - IdentityComparer.AreBytesEqual(keyBytes, unwrappedKey, context); - - theoryData.ExpectedException.ProcessNoException(context); - } - catch (Exception exception) - { - theoryData.ExpectedException.ProcessException(exception, context); - } - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData KeyWrapProviderTheoryData - { - get => new TheoryData - { - new KeyWrapProviderTheoryData - { - Algorithm = null, - ExpectedException = ExpectedException.ArgumentNullException(), - First = true, - TestId = "NullAlgorithm", - }, - new KeyWrapProviderTheoryData - { - Algorithm = string.Empty, - ExpectedException = ExpectedException.ArgumentNullException(), - TestId = "EmptyAlgorithm", - }, - new KeyWrapProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaPKCS1, - TestId = nameof(SecurityAlgorithms.RsaPKCS1), - }, - new KeyWrapProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaOAEP, - TestId = nameof(SecurityAlgorithms.RsaOAEP), - }, - }; - } - } -} - -#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyAuthenticationCallbackTheoryData.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyAuthenticationCallbackTheoryData.cs deleted file mode 100644 index 39747206c6..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyAuthenticationCallbackTheoryData.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading.Tasks; - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class KeyVaultSecurityKeyAuthenticationCallbackTheoryData : KeyVaultSecurityKeyTheoryData - { - public KeyVaultSecurityKey.AuthenticationCallback Callback { get; set; } = new KeyVaultSecurityKey.AuthenticationCallback((string authority, string resource, string scope) => Task.FromResult(string.Empty)); - } -} diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyConfidentialClientTheoryData.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyConfidentialClientTheoryData.cs deleted file mode 100644 index 75e3f1bca9..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyConfidentialClientTheoryData.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class KeyVaultSecurityKeyConfidentialClientTheoryData : KeyVaultSecurityKeyTheoryData - { - public string ClientId { get; set; } = $"{Guid.NewGuid():D}"; - public string ClientSecret { get; set; } = Guid.NewGuid().ToString(); - } -} diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyManagedServiceIdentityTheoryData.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyManagedServiceIdentityTheoryData.cs deleted file mode 100644 index f5c096fa16..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyManagedServiceIdentityTheoryData.cs +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class KeyVaultSecurityKeyManagedServiceIdentityTheoryData : KeyVaultSecurityKeyTheoryData - { - } -} diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyTests.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyTests.cs deleted file mode 100644 index 9badfe1f52..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Reflection; -using Microsoft.Azure.KeyVault.Models; -using Microsoft.IdentityModel.TestUtils; -using Xunit; - -#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class KeyVaultSecurityKeyTests - { - private static ExpectedException ArgumentNullExceptionExpected = new ExpectedException(typeExpected: typeof(TargetInvocationException), substringExpected: "Exception has been thrown by the target of an invocation.", innerTypeExpected: typeof(ArgumentNullException)); - private static ExpectedException KeyVaultErrorExceptionExpected = new ExpectedException(typeExpected: typeof(TargetInvocationException), substringExpected: "Exception has been thrown by the target of an invocation.", innerTypeExpected: typeof(KeyVaultErrorException)); - - [Theory, MemberData(nameof(KeyVaultSecurityKeyAuthenticationCallbackTheoryData))] - public void AuthenticationCallbackConstructorParams(KeyVaultSecurityKeyAuthenticationCallbackTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.AuthenticationCallbackConstructorParams", theoryData); - - try - { - _ = Activator.CreateInstance(theoryData.Type, new object[] { theoryData.KeyIdentifier, theoryData.Callback }); - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); - } - } - - public static TheoryData KeyVaultSecurityKeyAuthenticationCallbackTheoryData - { - get => new TheoryData - { - new KeyVaultSecurityKeyAuthenticationCallbackTheoryData - { - // Callback = default, - ExpectedException = ExpectedException.ArgumentNullException(), - First = true, - KeyIdentifier = null, - TestId = typeof(KeyVaultSecurityKey).FullName, - Type = typeof(KeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyAuthenticationCallbackTheoryData - { - // Callback = default, - ExpectedException = ExpectedException.ArgumentNullException(), - KeyIdentifier = string.Empty, - TestId = typeof(KeyVaultSecurityKey).FullName, - Type = typeof(KeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyAuthenticationCallbackTheoryData - { - Callback = null, - ExpectedException = ExpectedException.ArgumentNullException(), - // KeyIdentifier = default, - TestId = typeof(KeyVaultSecurityKey).FullName, - Type = typeof(KeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyAuthenticationCallbackTheoryData - { - // Callback = default, - ExpectedException = KeyVaultErrorExceptionExpected, - // KeyIdentifier = default, - TestId = typeof(KeyVaultSecurityKey).FullName, - Type = typeof(KeyVaultSecurityKey), - }, - }; - } - } -} - -#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyTheoryData.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyTheoryData.cs deleted file mode 100644 index 73dd0a20c8..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSecurityKeyTheoryData.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Microsoft.IdentityModel.TestUtils; - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public abstract class KeyVaultSecurityKeyTheoryData : TheoryDataBase - { - public string KeyIdentifier { get; set; } = KeyVaultUtilities.CreateKeyIdentifier(); - public Type Type { get; set; } - } -} diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSignatureProviderTests.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSignatureProviderTests.cs deleted file mode 100644 index 7ab512625c..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultSignatureProviderTests.cs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Linq; -using Microsoft.Azure.KeyVault; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Microsoft.IdentityModel.KeyVaultExtensions; -using Xunit; - -#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class KeyVaultSignatureProviderTests - { - private readonly MockKeyVaultClient _client; - private readonly SecurityKey _key; - - public KeyVaultSignatureProviderTests() - { - _client = new MockKeyVaultClient(); - _key = new KeyVaultSecurityKey(KeyVaultUtilities.CreateKeyIdentifier(), keySize: default); - } - - [Theory, MemberData(nameof(DisposeProviderTheoryData))] - public void DisposeProviderTest(SignatureProviderTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.DisposeProviderTest", theoryData); - - try - { - var provider = new KeyVaultSignatureProvider(_key, theoryData.Algorithm, willCreateSignatures: true, _client); - _key.CryptoProviderFactory.ReleaseSignatureProvider(provider); - - theoryData.ExpectedException.ProcessNoException(context); - } - catch (Exception exception) - { - theoryData.ExpectedException.ProcessException(exception, context); - } - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData DisposeProviderTheoryData - { - get => new TheoryData - { - new SignatureProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaSha256, - ExpectedException = ExpectedException.NoExceptionExpected, - First = true, - TestId = nameof(SecurityAlgorithms.RsaSha256), - }, - new SignatureProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaSha384, - ExpectedException = ExpectedException.NoExceptionExpected, - TestId = nameof(SecurityAlgorithms.RsaSha384), - }, - new SignatureProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaSha512, - ExpectedException = ExpectedException.NoExceptionExpected, - TestId = nameof(SecurityAlgorithms.RsaSha512), - }, - }; - } - - [Theory, MemberData(nameof(SignatureProviderTheoryData))] - public void SignatureTest(SignatureProviderTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.SignatureTest", theoryData); - - try - { - var provider = new KeyVaultSignatureProvider(_key, theoryData.Algorithm, willCreateSignatures: true, _client); - if (provider == null) - context.AddDiff("(provider == null)"); - - var input = Guid.NewGuid().ToByteArray(); - var signature = provider.Sign(input); - - if (signature == null) - context.AddDiff("(signature == null)"); - - if (_client.ExpectedSignatureLength != signature.Length) - context.AddDiff($"_client.ExpectedSignatureLength != signature.Length. == {_client.ExpectedSignatureLength}, {signature.Length}."); - - if (!provider.Verify(input, signature)) - context.AddDiff("!provider.Verify(input, signature)"); - - var tamperedInput = new byte[input.Length]; - input.CopyTo(tamperedInput, 0); - if (tamperedInput[0] == byte.MaxValue) - tamperedInput[0]--; - else - tamperedInput[0]++; - - if (provider.Verify(tamperedInput, signature)) - context.AddDiff("provider.Verify(tamperedInput, signature)"); - - foreach (var data in SignatureProviderTheoryData) - { - var newAlgorithm = (data.Single() as SignatureProviderTheoryData)?.Algorithm; - if (string.IsNullOrEmpty(newAlgorithm)) - continue; // Skip invalid input - - // Check that a given Security Key will only validate a signature using the same hash algorithm. - var isValidSignature = new KeyVaultSignatureProvider(_key, newAlgorithm, willCreateSignatures: false, _client).Verify(input, signature); - if (StringComparer.Ordinal.Equals(theoryData.Algorithm, newAlgorithm)) - { - if (!isValidSignature) - context.AddDiff("Signature should have been valid, isValidSignature == false"); - } - else if (isValidSignature) - context.AddDiff("Signature should NOT have been valid, isValidSignature == true"); - } - - theoryData.ExpectedException.ProcessNoException(context); - } - catch (Exception exception) - { - theoryData.ExpectedException.ProcessException(exception, context); - } - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData SignatureProviderTheoryData - { - get => new TheoryData - { - new SignatureProviderTheoryData - { - Algorithm = null, - ExpectedException = ExpectedException.ArgumentNullException(), - First = true, - TestId = "NullAlgorithm", - }, - new SignatureProviderTheoryData - { - Algorithm = string.Empty, - ExpectedException = ExpectedException.ArgumentNullException(), - TestId = "EmptyAlgorithm", - }, - new SignatureProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaSha256, - TestId = nameof(SecurityAlgorithms.RsaSha256), - }, - new SignatureProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaSha384, - TestId = nameof(SecurityAlgorithms.RsaSha384), - }, - new SignatureProviderTheoryData - { - Algorithm = SecurityAlgorithms.RsaSha512, - TestId = nameof(SecurityAlgorithms.RsaSha512), - }, - }; - } - } -} - -#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultUtilities.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultUtilities.cs deleted file mode 100644 index 072a868068..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyVaultUtilities.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public static class KeyVaultUtilities - { - public static string CreateKeyIdentifier() => CreateKeyIdentifier("contoso.vault.azure.net", nameof(KeyVaultUtilities), $"{Guid.NewGuid():N}"); - - public static string CreateKeyIdentifier(string vaultBaseUrl, string vaultKeyName, string vaultKeyVersion) - { - return new UriBuilder(Uri.UriSchemeHttps, vaultBaseUrl, -1, $"/keys/{vaultKeyName}/{vaultKeyVersion}").Uri.ToString(); - } - } -} diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyWrapProviderTheoryData.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyWrapProviderTheoryData.cs deleted file mode 100644 index 5e8ab0f194..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/KeyWrapProviderTheoryData.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.IdentityModel.TestUtils; - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class KeyWrapProviderTheoryData : TheoryDataBase - { - public string Algorithm { get; set; } - } -} diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/Microsoft.IdentityModel.KeyVaultExtensions.Tests.csproj b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/Microsoft.IdentityModel.KeyVaultExtensions.Tests.csproj deleted file mode 100644 index 3c797af7a1..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/Microsoft.IdentityModel.KeyVaultExtensions.Tests.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - Microsoft.IdentityModel.KeyVaultExtensions.Tests - $(MSBuildThisFileDirectory)..\..\build\35MSSharedLib1024.snk - true - Tests for Microsoft.IdentityModel.KeyVaultExtensions - true - Microsoft.IdentityModel.KeyVaultExtensions.Tests - true - - - - - - - - - - - - diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/MockKeyVaultClient.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/MockKeyVaultClient.cs deleted file mode 100644 index 8fcbc31691..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/MockKeyVaultClient.cs +++ /dev/null @@ -1,622 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.KeyVault; -using Microsoft.Azure.KeyVault.Models; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Microsoft.Rest; -using Microsoft.Rest.Azure; -using Newtonsoft.Json; - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - [CLSCompliant(false)] - public class MockKeyVaultClient : IKeyVaultClient - { - private readonly Microsoft.Azure.KeyVault.WebKey.JsonWebKey _key; - private readonly RSACryptoServiceProvider _rsa; - private bool _disposed = false; - - public MockKeyVaultClient() - { - _rsa = new RSACryptoServiceProvider(); - _rsa.ImportParameters(KeyingMaterial.RsaParameters_2048); - _key = new Microsoft.Azure.KeyVault.WebKey.JsonWebKey(_rsa, includePrivateParameters: false); - } - - public int ExpectedKeyWrapLength => 256; - - public int ExpectedSignatureLength => 256; - - public JsonSerializerSettings SerializationSettings => throw new NotImplementedException(); - - public JsonSerializerSettings DeserializationSettings => throw new NotImplementedException(); - - public ServiceClientCredentials Credentials => throw new NotImplementedException(); - - public string ApiVersion => throw new NotImplementedException(); - - public string AcceptLanguage { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public int? LongRunningOperationRetryTimeout { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public bool? GenerateClientRequestId { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public Task> BackupCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> BackupKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> BackupSecretWithHttpMessagesAsync(string vaultBaseUrl, string secretName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> BackupStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> CreateCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, CertificatePolicy certificatePolicy = null, CertificateAttributes certificateAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> CreateKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string kty, int? keySize = null, IList keyOps = null, KeyAttributes keyAttributes = null, IDictionary tags = null, string curve = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DecryptWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string keyVersion, string algorithm, byte[] value, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DeleteCertificateContactsWithHttpMessagesAsync(string vaultBaseUrl, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DeleteCertificateIssuerWithHttpMessagesAsync(string vaultBaseUrl, string issuerName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DeleteCertificateOperationWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DeleteCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DeleteKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DeleteSasDefinitionWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string sasDefinitionName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DeleteSecretWithHttpMessagesAsync(string vaultBaseUrl, string secretName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> DeleteStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - /// - /// Calls and - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (!_disposed) - { - _disposed = true; - if (disposing) - { - _rsa.Dispose(); - } - } - } - - public Task> EncryptWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string keyVersion, string algorithm, byte[] value, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetCertificateContactsWithHttpMessagesAsync(string vaultBaseUrl, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetCertificateIssuersNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetCertificateIssuersWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetCertificateIssuerWithHttpMessagesAsync(string vaultBaseUrl, string issuerName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetCertificateOperationWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetCertificatePolicyWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetCertificatesNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetCertificatesWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, bool? includePending = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetCertificateVersionsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetCertificateVersionsWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, string certificateVersion, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedCertificatesNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedCertificatesWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, bool? includePending = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetDeletedCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedKeysNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedKeysWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetDeletedKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedSasDefinitionsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedSasDefinitionsWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetDeletedSasDefinitionWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string sasDefinitionName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedSecretsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedSecretsWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetDeletedSecretWithHttpMessagesAsync(string vaultBaseUrl, string secretName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedStorageAccountsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetDeletedStorageAccountsWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetDeletedStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetKeysNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetKeysWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetKeyVersionsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetKeyVersionsWithHttpMessagesAsync(string vaultBaseUrl, string keyName, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string keyVersion, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - _key.Kid = GetKeyIdentifier(vaultBaseUrl, keyName, keyVersion); - KeyAttributes attributes = new KeyAttributes(enabled: true); - var response = new AzureOperationResponse - { - Body = new KeyBundle(_key, attributes), - }; - - return Task.FromResult(response); - } - - public Task> GetPendingCertificateSigningRequestWithHttpMessagesAsync(string vault, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetSasDefinitionsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetSasDefinitionsWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetSasDefinitionWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string sasDefinitionName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetSecretsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetSecretsWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetSecretVersionsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetSecretVersionsWithHttpMessagesAsync(string vaultBaseUrl, string secretName, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetSecretWithHttpMessagesAsync(string vaultBaseUrl, string secretName, string secretVersion, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetStorageAccountsNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task>> GetStorageAccountsWithHttpMessagesAsync(string vaultBaseUrl, int? maxresults = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> GetStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> ImportCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, string base64EncodedCertificate, string password = null, CertificatePolicy certificatePolicy = null, CertificateAttributes certificateAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> ImportKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, Microsoft.Azure.KeyVault.WebKey.JsonWebKey key, bool? hsm = null, KeyAttributes keyAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> MergeCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, IList x509Certificates, CertificateAttributes certificateAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task PurgeDeletedCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task PurgeDeletedKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task PurgeDeletedSecretWithHttpMessagesAsync(string vaultBaseUrl, string secretName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task PurgeDeletedStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RecoverDeletedCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RecoverDeletedKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RecoverDeletedSasDefinitionWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string sasDefinitionName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RecoverDeletedSecretWithHttpMessagesAsync(string vaultBaseUrl, string secretName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RecoverDeletedStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RegenerateStorageAccountKeyWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string keyName, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RestoreCertificateWithHttpMessagesAsync(string vaultBaseUrl, byte[] certificateBundleBackup, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RestoreKeyWithHttpMessagesAsync(string vaultBaseUrl, byte[] keyBundleBackup, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RestoreSecretWithHttpMessagesAsync(string vaultBaseUrl, byte[] secretBundleBackup, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> RestoreStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, byte[] storageBundleBackup, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> SetCertificateContactsWithHttpMessagesAsync(string vaultBaseUrl, Contacts contacts, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> SetCertificateIssuerWithHttpMessagesAsync(string vaultBaseUrl, string issuerName, string provider, IssuerCredentials credentials = null, OrganizationDetails organizationDetails = null, IssuerAttributes attributes = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> SetSasDefinitionWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string sasDefinitionName, string templateUri, string sasType, string validityPeriod, SasDefinitionAttributes sasDefinitionAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> SetSecretWithHttpMessagesAsync(string vaultBaseUrl, string secretName, string value, IDictionary tags = null, string contentType = null, SecretAttributes secretAttributes = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> SetStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string resourceId, string activeKeyName, bool autoRegenerateKey, string regenerationPeriod = null, StorageAccountAttributes storageAccountAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> SignWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string keyVersion, string algorithm, byte[] value, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - string digestAlgorithm; - switch (algorithm) - { - case SecurityAlgorithms.RsaSha256: - digestAlgorithm = SecurityAlgorithms.Sha256; - break; - case SecurityAlgorithms.RsaSha384: - digestAlgorithm = SecurityAlgorithms.Sha384; - break; - case SecurityAlgorithms.RsaSha512: - digestAlgorithm = SecurityAlgorithms.Sha512; - break; - default: - throw new NotImplementedException(); - } - - var result = _rsa.SignHash(value, digestAlgorithm); - var response = new AzureOperationResponse - { - Body = new KeyOperationResult(GetKeyIdentifier(vaultBaseUrl, keyName, keyVersion), result), - }; - - return Task.FromResult(response); - } - - public Task> UnwrapKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string keyVersion, string algorithm, byte[] value, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - bool fOAEP; - if (StringComparer.OrdinalIgnoreCase.Equals(algorithm, SecurityAlgorithms.RsaOAEP)) - fOAEP = true; - else if (StringComparer.OrdinalIgnoreCase.Equals(algorithm, SecurityAlgorithms.RsaPKCS1)) - fOAEP = false; - else - throw new NotImplementedException($"The mock key vault is not configured to unwrap keys using the {algorithm} security algorithm."); - - var result = _rsa.Decrypt(value, fOAEP); - var response = new AzureOperationResponse - { - Body = new KeyOperationResult(GetKeyIdentifier(vaultBaseUrl, keyName, keyVersion), result), - }; - - return Task.FromResult(response); - } - - public Task> UpdateCertificateIssuerWithHttpMessagesAsync(string vaultBaseUrl, string issuerName, string provider = null, IssuerCredentials credentials = null, OrganizationDetails organizationDetails = null, IssuerAttributes attributes = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> UpdateCertificateOperationWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, bool cancellationRequested, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> UpdateCertificatePolicyWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, CertificatePolicy certificatePolicy, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> UpdateCertificateWithHttpMessagesAsync(string vaultBaseUrl, string certificateName, string certificateVersion, CertificatePolicy certificatePolicy = null, CertificateAttributes certificateAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> UpdateKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string keyVersion, IList keyOps = null, KeyAttributes keyAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> UpdateSasDefinitionWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string sasDefinitionName, string templateUri = null, string sasType = null, string validityPeriod = null, SasDefinitionAttributes sasDefinitionAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> UpdateSecretWithHttpMessagesAsync(string vaultBaseUrl, string secretName, string secretVersion, string contentType = null, SecretAttributes secretAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> UpdateStorageAccountWithHttpMessagesAsync(string vaultBaseUrl, string storageAccountName, string activeKeyName = null, bool? autoRegenerateKey = null, string regenerationPeriod = null, StorageAccountAttributes storageAccountAttributes = null, IDictionary tags = null, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public Task> VerifyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string keyVersion, string algorithm, byte[] digest, byte[] signature, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - string digestAlgorithm; - switch (algorithm) - { - case SecurityAlgorithms.RsaSha256: - digestAlgorithm = SecurityAlgorithms.Sha256; - break; - case SecurityAlgorithms.RsaSha384: - digestAlgorithm = SecurityAlgorithms.Sha384; - break; - case SecurityAlgorithms.RsaSha512: - digestAlgorithm = SecurityAlgorithms.Sha512; - break; - default: - digestAlgorithm = null; - break; - } - - var result = string.IsNullOrEmpty(digestAlgorithm) ? null : (bool?)_rsa.VerifyHash(digest, digestAlgorithm, signature); - var response = new AzureOperationResponse - { - Body = new KeyVerifyResult(result), - }; - - return Task.FromResult(response); - } - - public Task> WrapKeyWithHttpMessagesAsync(string vaultBaseUrl, string keyName, string keyVersion, string algorithm, byte[] value, Dictionary> customHeaders = null, CancellationToken cancellationToken = default) - { - bool fOAEP; - if (StringComparer.OrdinalIgnoreCase.Equals(algorithm, SecurityAlgorithms.RsaOAEP)) - fOAEP = true; - else if (StringComparer.OrdinalIgnoreCase.Equals(algorithm, SecurityAlgorithms.RsaPKCS1)) - fOAEP = false; - else - throw new NotImplementedException($"The mock key vault is not configured to wrap keys using the {algorithm} security algorithm."); - - var response = new AzureOperationResponse - { - Body = new KeyOperationResult(GetKeyIdentifier(vaultBaseUrl, keyName, keyVersion), _rsa.Encrypt(value, fOAEP)), - }; - - return Task.FromResult(response); - } - - private string GetKeyIdentifier(string vaultBaseUrl, string keyName, string keyVersion) - { - return new Uri(new Uri(vaultBaseUrl), $"/keys/{keyName}/{keyVersion}").ToString(); - } - } -} - diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/Properties/AssemblyInfo.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index b6a9bad9cd..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Runtime.InteropServices; -using Xunit; - -[assembly: CLSCompliant(false)] -[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)] -[assembly: ComVisible(false)] - diff --git a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/SignatureProviderTheoryData.cs b/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/SignatureProviderTheoryData.cs deleted file mode 100644 index 212a8656ed..0000000000 --- a/test/Microsoft.IdentityModel.KeyVaultExtensions.Tests/SignatureProviderTheoryData.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.IdentityModel.TestUtils; - -namespace Microsoft.IdentityModel.KeyVaultExtensions.Tests -{ - public class SignatureProviderTheoryData : TheoryDataBase - { - public string Algorithm { get; set; } - } -} diff --git a/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/KeyVaultSecurityKeyTests.cs b/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/KeyVaultSecurityKeyTests.cs deleted file mode 100644 index d7c3d41dc6..0000000000 --- a/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/KeyVaultSecurityKeyTests.cs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Azure.KeyVault.Models; -using Microsoft.IdentityModel.Clients.ActiveDirectory; -using Microsoft.IdentityModel.KeyVaultExtensions; -using Microsoft.IdentityModel.KeyVaultExtensions.Tests; -using Microsoft.IdentityModel.TestUtils; -using System; -using System.Reflection; -using Xunit; - -#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant - -namespace Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests -{ - public class KeyVaultSecurityKeyTests - { - private static ExpectedException AdalServiceExceptionExpected = new ExpectedException(typeExpected: typeof(TargetInvocationException), substringExpected: "Exception has been thrown by the target of an invocation.", innerTypeExpected: typeof(AdalServiceException)); - private static ExpectedException ArgumentNullExceptionExpected = new ExpectedException(typeExpected: typeof(TargetInvocationException), substringExpected: "Exception has been thrown by the target of an invocation.", innerTypeExpected: typeof(ArgumentNullException)); - private static ExpectedException KeyVaultErrorExceptionExpected = new ExpectedException(typeExpected: typeof(TargetInvocationException), substringExpected: "Exception has been thrown by the target of an invocation.", innerTypeExpected: typeof(KeyVaultErrorException)); - - [Theory, MemberData(nameof(KeyVaultSecurityKeyConfidentialClientTheoryData))] - public void ConfidentialClientConstructorParams(KeyVaultSecurityKeyConfidentialClientTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.ConfidentialClientConstructorParams", theoryData); - - try - { - _ = Activator.CreateInstance(theoryData.Type, new object[] { theoryData.KeyIdentifier, theoryData.ClientId, theoryData.ClientSecret }); - theoryData.ExpectedException.ProcessNoException(context); - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); - } - - TestUtilities.AssertFailIfErrors(context); - } - - public static TheoryData KeyVaultSecurityKeyConfidentialClientTheoryData - { - get => new TheoryData - { - new KeyVaultSecurityKeyConfidentialClientTheoryData - { - // ClientId = default, - // ClientSecret = default, - ExpectedException = ArgumentNullExceptionExpected, - First = true, - KeyIdentifier = null, - TestId = "Test1", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyConfidentialClientTheoryData - { - // ClientId = default, - // ClientSecret = default, - ExpectedException = ArgumentNullExceptionExpected, - KeyIdentifier = string.Empty, - TestId = "Test2", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyConfidentialClientTheoryData - { - ClientId = null, - // ClientSecret = default, - ExpectedException = ArgumentNullExceptionExpected, - // KeyIdentifier = default, - TestId = "Test3", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyConfidentialClientTheoryData - { - ClientId = string.Empty, - /* - ClientSecret = default, - */ - ExpectedException = ArgumentNullExceptionExpected, - // KeyIdentifier = default, - TestId = "Test4", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyConfidentialClientTheoryData - { - // ClientId = default, - ClientSecret = null, - ExpectedException = ArgumentNullExceptionExpected, - // KeyIdentifier = default, - TestId = "Test5", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyConfidentialClientTheoryData - { - // ClientId = default, - ClientSecret = string.Empty, - ExpectedException = ArgumentNullExceptionExpected, - // KeyIdentifier = default, - TestId = "Test6", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyConfidentialClientTheoryData - { - // ClientId = default, - // ClientSecret = default, - // KeyIdentifier = default, - TestId = "Test7", - Type = typeof(ManagedKeyVaultSecurityKey), - } - }; - } - - [Theory, MemberData(nameof(KeyVaultSecurityKeyManagedServiceIdentityTheoryData))] - public void ManagedServiceIdentityConstructorParams(KeyVaultSecurityKeyTheoryData theoryData) - { - var context = TestUtilities.WriteHeader($"{this}.ManagedServiceIdentityConstructorParams", theoryData); - - try - { - _ = Activator.CreateInstance(theoryData.Type, new object[] { theoryData.KeyIdentifier }); - theoryData.ExpectedException.ProcessNoException(context); - } - catch (Exception ex) - { - theoryData.ExpectedException.ProcessException(ex, context); - } - } - - public static TheoryData KeyVaultSecurityKeyManagedServiceIdentityTheoryData - { - get => new TheoryData - { - new KeyVaultSecurityKeyManagedServiceIdentityTheoryData - { - ExpectedException = ExpectedException.ArgumentNullException(), - First = true, - KeyIdentifier = null, - TestId = "Test1", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyManagedServiceIdentityTheoryData - { - ExpectedException = ExpectedException.ArgumentNullException(), - KeyIdentifier = string.Empty, - TestId = "Test2", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - new KeyVaultSecurityKeyManagedServiceIdentityTheoryData - { - ExpectedException = KeyVaultErrorExceptionExpected, - // KeyIdentifier = default, - TestId = "Test3", - Type = typeof(ManagedKeyVaultSecurityKey), - }, - }; - } - } -} - -#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests.csproj b/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests.csproj deleted file mode 100644 index d372f245e9..0000000000 --- a/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests - $(MSBuildThisFileDirectory)..\..\build\35MSSharedLib1024.snk - true - Tests for Microsoft.IdentityModel.ManagedKeyVaultSecurityKey - true - Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests - true - - - - - - - - - - - - - diff --git a/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/Properties/AssemblyInfo.cs b/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index b6a9bad9cd..0000000000 --- a/test/Microsoft.IdentityModel.ManagedKeyVaultSecurityKey.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Runtime.InteropServices; -using Xunit; - -[assembly: CLSCompliant(false)] -[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)] -[assembly: ComVisible(false)] - diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs index f887eccadd..75a62b1539 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConfigData.cs @@ -42,114 +42,132 @@ public static OpenIdConnectConfiguration FullyPopulatedWithKeys public static string HttpsBadUri = "https://_____NoSuchfile____"; #region Configuration Strings - public static string OpenIdConnectMetadataPingString = @"{""authorization_endpoint"":""https:\/\/connect-interop.pinglabs.org:9031\/as\/authorization.oauth2"", - ""issuer"":""https:\/\/connect-interop.pinglabs.org:9031"", - ""id_token_signing_alg_values_supported"":[""none"",""HS256"",""HS384"",""HS512"",""RS256"",""RS384"",""RS512"",""ES256"",""ES384"",""ES512""], - ""claim_types_supported"":[""normal""], - ""claims_parameter_supported"":false, - ""ping_end_session_endpoint"":""https:\/\/connect-interop.pinglabs.org:9031\/idp\/startSLO.ping"", - ""ping_revoked_sris_endpoint"":""https:\/\/connect-interop.pinglabs.org:9031\/pf-ws\/rest\/sessionMgmt\/revokedSris"", - ""request_parameter_supported"":false, - ""request_uri_parameter_supported"":false, - ""response_modes_supported"":[""fragment"",""query"",""form_post""], - ""response_types_supported"":[""code"",""token"",""id_token"",""code token"",""code id_token"",""token id_token"",""code token id_token""], - ""revocation_endpoint"":""https:\/\/connect-interop.pinglabs.org:9031\/as\/revoke_token.oauth2"", - ""scopes_supported"":[""phone"",""address"",""email"",""openid"",""profile""], - ""subject_types_supported"":[""public""], - ""token_endpoint"":""https:\/\/connect-interop.pinglabs.org:9031\/as\/token.oauth2"", - ""token_endpoint_auth_methods_supported"":[""client_secret_basic"",""client_secret_post""], - ""userinfo_endpoint"":""https:\/\/connect-interop.pinglabs.org:9031\/idp\/userinfo.openid"", - ""version"":""3.0""}"; + public static string OpenIdConnectMetadataPingString = @"{""authorization_endpoint"": ""https:\/\/connect-interop.pinglabs.org:9031\/as\/authorization.oauth2"", + ""issuer"": ""https:\/\/connect-interop.pinglabs.org:9031"", + ""id_token_signing_alg_values_supported"": [""none"", ""HS256"", ""HS384"", ""HS512"", ""RS256"", ""RS384"", ""RS512"", ""ES256"", ""ES384"", ""ES512""], + ""claim_types_supported"": [""normal""], + ""claims_parameter_supported"": false, + ""ping_end_session_endpoint"": ""https:\/\/connect-interop.pinglabs.org:9031\/idp\/startSLO.ping"", + ""ping_revoked_sris_endpoint"": ""https:\/\/connect-interop.pinglabs.org:9031\/pf-ws\/rest\/sessionMgmt\/revokedSris"", + ""request_parameter_supported"": false, + ""request_uri_parameter_supported"": false, + ""response_modes_supported"": [""fragment"", ""query"", ""form_post""], + ""response_types_supported"": [""code"", ""token"", ""id_token"", ""code token"", ""code id_token"", ""token id_token"", ""code token id_token""], + ""revocation_endpoint"": ""https:\/\/connect-interop.pinglabs.org:9031\/as\/revoke_token.oauth2"", + ""scopes_supported"": [""phone"", ""address"", ""email"", ""openid"", ""profile""], + ""subject_types_supported"": [""public""], + ""token_endpoint"": ""https:\/\/connect-interop.pinglabs.org:9031\/as\/token.oauth2"", + ""token_endpoint_auth_methods_supported"": [""client_secret_basic"", ""client_secret_post""], + ""userinfo_endpoint"": ""https:\/\/connect-interop.pinglabs.org:9031\/idp\/userinfo.openid"", + ""version"": ""3.0""}"; public static string JsonFile = @"OpenIdConnectMetadata.json"; public static string OpenIdConnectMetadataFileEnd2End = @"OpenIdConnectMetadataEnd2End.json"; public static string OpenIdConnectMetadataFileEnd2EndEC = @"OpenIdConnectMetadataEnd2EndEC.json"; public static string JsonWebKeySetBadUriFile = @"OpenIdConnectMetadataJsonWebKeySetBadUri.json"; public static string JsonAllValues = - @"{ ""acr_values_supported"" : [""acr_value1"", ""acr_value2"", ""acr_value3""], - ""authorization_endpoint"" : ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize"", + @"{ ""acr_values_supported"": [""acr_value1"", ""acr_value2"", ""acr_value3""], + ""authorization_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize"", + ""authorization_encryption_alg_values_supported"": [""A192KW"", ""A256KW""], + ""authorization_encryption_enc_values_supported"": [""A128CBC-HS256"", ""A256CBC-HS512""], + ""authorization_response_iss_parameter_supported"": false, + ""authorization_signing_alg_values_supported"": [""ES384"", ""ES512""], + ""backchannel_authentication_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/bc-authorize"", + ""backchannel_authentication_request_signing_alg_values_supported"": [""ES384"", ""ES512""], + ""backchannel_token_delivery_modes_supported"": [""poll"", ""ping""], + ""backchannel_user_code_parameter_supported"": false, + ""code_challenge_methods_supported"": [""plain"", ""S256""], + ""device_authorization_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/devicecode"", ""frontchannel_logout_session_supported"": ""true"", ""frontchannel_logout_supported"": ""true"", - ""check_session_iframe"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/checksession"", - ""claims_locales_supported"" : [ ""claim_local1"", ""claim_local2"", ""claim_local3"" ], - ""claims_parameter_supported"" : true, - ""claims_supported"": [ ""sub"", ""iss"", ""aud"", ""exp"", ""iat"", ""auth_time"", ""acr"", ""amr"", ""nonce"", ""email"", ""given_name"", ""family_name"", ""nickname"" ], - ""claim_types_supported"" : [ ""Normal Claims"", ""Aggregated Claims"", ""Distributed Claims"" ], - ""display_values_supported"" : [ ""displayValue1"", ""displayValue2"", ""displayValue3"" ], - ""end_session_endpoint"" : ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/logout"", - ""grant_types_supported"" : [""authorization_code"",""implicit""], - ""http_logout_supported"" : true, - ""id_token_encryption_alg_values_supported"" : [""RSA1_5"", ""A256KW""], - ""id_token_encryption_enc_values_supported"" : [""A128CBC-HS256"",""A256CBC-HS512""], - ""id_token_signing_alg_values_supported"" : [""RS256""], - ""introspection_endpoint"" : ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/introspect"", - ""introspection_endpoint_auth_methods_supported"" : [""client_secret_post"",""private_key_jwt""], - ""introspection_endpoint_auth_signing_alg_values_supported"" : [""ES192"", ""ES256""], - ""issuer"" : ""https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/"", - ""jwks_uri"" : ""JsonWebKeySet.json"", - ""logout_session_supported"" : true, - ""microsoft_multi_refresh_token"" : true, - ""op_policy_uri"" : ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_policy_uri"", - ""op_tos_uri"" : ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_tos_uri"", - ""request_object_encryption_alg_values_supported"" : [""A192KW"", ""A256KW""], - ""request_object_encryption_enc_values_supported"" : [""A192GCM"",""A256GCM""], - ""request_object_signing_alg_values_supported"" : [""PS256"", ""PS512""], - ""request_parameter_supported"" : true, - ""request_uri_parameter_supported"" : true, - ""require_request_uri_registration"" : true, - ""response_modes_supported"" : [""query"", ""fragment"",""form_post""], - ""response_types_supported"" : [""code"",""id_token"",""code id_token""], - ""service_documentation"" : ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/service_documentation"", - ""scopes_supported"" : [""openid""], - ""subject_types_supported"" : [""pairwise""], - ""token_endpoint"" : ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token"", - ""token_endpoint_auth_methods_supported"" : [""client_secret_post"",""private_key_jwt""], - ""token_endpoint_auth_signing_alg_values_supported"" : [""ES192"", ""ES256""], - ""ui_locales_supported"" : [""hak-CN"", ""en-us""], - ""userinfo_endpoint"" : ""https://login.microsoftonline.com/add29489-7269-41f4-8841-b63c95564420/openid/userinfo"", - ""userinfo_encryption_alg_values_supported"" : [""ECDH-ES+A128KW"",""ECDH-ES+A192KW""], - ""userinfo_encryption_enc_values_supported"" : [""A256CBC-HS512"", ""A128CBC-HS256""], - ""userinfo_signing_alg_values_supported"" : [""ES384"", ""ES512""] + ""check_session_iframe"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/checksession"", + ""claims_locales_supported"": [""claim_local1"", ""claim_local2"", ""claim_local3"" ], + ""claims_parameter_supported"": true, + ""claims_supported"": [""sub"", ""iss"", ""aud"", ""exp"", ""iat"", ""auth_time"", ""acr"", ""amr"", ""nonce"", ""email"", ""given_name"", ""family_name"", ""nickname"" ], + ""claim_types_supported"": [""Normal Claims"", ""Aggregated Claims"", ""Distributed Claims"" ], + ""display_values_supported"": [""displayValue1"", ""displayValue2"", ""displayValue3"" ], + ""dpop_signing_alg_values_supported"": [""ES384"", ""ES512""], + ""end_session_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/logout"", + ""grant_types_supported"": [""authorization_code"", ""implicit""], + ""http_logout_supported"": true, + ""id_token_encryption_alg_values_supported"": [""RSA1_5"", ""A256KW""], + ""id_token_encryption_enc_values_supported"": [""A128CBC-HS256"", ""A256CBC-HS512""], + ""id_token_signing_alg_values_supported"": [""RS256""], + ""introspection_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/introspect"", + ""introspection_endpoint_auth_methods_supported"": [""client_secret_post"", ""private_key_jwt""], + ""introspection_endpoint_auth_signing_alg_values_supported"": [""ES192"", ""ES256""], + ""issuer"": ""https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/"", + ""jwks_uri"": ""JsonWebKeySet.json"", + ""logout_session_supported"": true, + ""microsoft_multi_refresh_token"": true, + ""op_policy_uri"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_policy_uri"", + ""op_tos_uri"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_tos_uri"", + ""prompt_values_supported"": [""none"", ""login"", ""consent""], + ""pushed_authorization_request_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/par"", + ""request_object_encryption_alg_values_supported"": [""A192KW"", ""A256KW""], + ""request_object_encryption_enc_values_supported"": [""A192GCM"", ""A256GCM""], + ""request_object_signing_alg_values_supported"": [""PS256"", ""PS512""], + ""request_parameter_supported"": true, + ""request_uri_parameter_supported"": true, + ""require_pushed_authorization_requests"": false, + ""require_request_uri_registration"": true, + ""response_modes_supported"": [""query"", ""fragment"", ""form_post""], + ""response_types_supported"": [""code"", ""id_token"", ""code id_token""], + ""revocation_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/revocation"", + ""revocation_endpoint_auth_methods_supported"": [""client_secret_post"", ""client_secret_basic""], + ""revocation_endpoint_auth_signing_alg_values_supported"": [""ES192"", ""ES256""], + ""service_documentation"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/service_documentation"", + ""scopes_supported"": [""openid""], + ""subject_types_supported"": [""pairwise""], + ""token_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token"", + ""token_endpoint_auth_methods_supported"": [""client_secret_post"", ""private_key_jwt""], + ""token_endpoint_auth_signing_alg_values_supported"": [""ES192"", ""ES256""], + ""tls_client_certificate_bound_access_tokens"": true, + ""ui_locales_supported"": [""hak-CN"", ""en-us""], + ""userinfo_endpoint"": ""https://login.microsoftonline.com/add29489-7269-41f4-8841-b63c95564420/openid/userinfo"", + ""userinfo_encryption_alg_values_supported"": [""ECDH-ES+A128KW"", ""ECDH-ES+A192KW""], + ""userinfo_encryption_enc_values_supported"": [""A256CBC-HS512"", ""A128CBC-HS256""], + ""userinfo_signing_alg_values_supported"": [""ES384"", ""ES512""] }"; public static string OpenIdConnectMetadataSingleX509DataString = - @"{ ""authorization_endpoint"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize"", - ""check_session_iframe"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/checksession"", - ""end_session_endpoint"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/logout"", - ""id_token_signing_alg_values_supported"":[""RS256""], - ""issuer"":""https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/"", - ""jwks_uri"":""JsonWebKeySetSingleX509Data.json"", + @"{ ""authorization_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize"", + ""check_session_iframe"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/checksession"", + ""end_session_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/logout"", + ""id_token_signing_alg_values_supported"": [""RS256""], + ""issuer"": ""https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/"", + ""jwks_uri"": ""JsonWebKeySetSingleX509Data.json"", ""microsoft_multi_refresh_token"":true, - ""response_types_supported"":[""code"",""id_token"",""code id_token""], - ""response_modes_supported"":[""query"",""fragment"",""form_post""], - ""scopes_supported"":[""openid""], - ""subject_types_supported"":[""pairwise""], - ""token_endpoint"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token"", - ""token_endpoint_auth_methods_supported"":[""client_secret_post"",""private_key_jwt""] + ""response_types_supported"": [""code"", ""id_token"", ""code id_token""], + ""response_modes_supported"": [""query"", ""fragment"", ""form_post""], + ""scopes_supported"": [""openid""], + ""subject_types_supported"": [""pairwise""], + ""token_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token"", + ""token_endpoint_auth_methods_supported"": [""client_secret_post"", ""private_key_jwt""] }"; public static string JsonWithSigningKeys = - @"{ ""authorization_endpoint"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize"", - ""check_session_iframe"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/checksession"", - ""end_session_endpoint"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/logout"", - ""id_token_signing_alg_values_supported"":[""RS256""], - ""issuer"":""https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/"", - ""jwks_uri"":""JsonWebKeySetSingleX509Data.json"", + @"{ ""authorization_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize"", + ""check_session_iframe"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/checksession"", + ""end_session_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/logout"", + ""id_token_signing_alg_values_supported"": [""RS256""], + ""issuer"": ""https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/"", + ""jwks_uri"": ""JsonWebKeySetSingleX509Data.json"", ""microsoft_multi_refresh_token"":true, - ""response_types_supported"":[""code"",""id_token"",""code id_token""], - ""response_modes_supported"":[""query"",""fragment"",""form_post""], - ""scopes_supported"":[""openid""], - ""subject_types_supported"":[""pairwise""], - ""token_endpoint"":""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token"", - ""token_endpoint_auth_methods_supported"":[""client_secret_post"",""private_key_jwt""], - ""SigningKeys"":[""key1"",""key2""] + ""response_types_supported"": [""code"", ""id_token"", ""code id_token""], + ""response_modes_supported"": [""query"", ""fragment"", ""form_post""], + ""scopes_supported"": [""openid""], + ""subject_types_supported"": [""pairwise""], + ""token_endpoint"": ""https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token"", + ""token_endpoint_auth_methods_supported"": [""client_secret_post"", ""private_key_jwt""], + ""SigningKeys"": [""key1"", ""key2""] }"; - public static string OpenIdConnectMetadataBadX509DataString = @"{""jwks_uri"":""JsonWebKeySetBadX509Data.json""}"; - public static string OpenIdConnectMetadataBadBase64DataString = @"{""jwks_uri"":""JsonWebKeySetBadBase64Data.json""}"; - public static string OpenIdConnectMetadataBadUriKeysString = @"{""jwks_uri"":""___NoSuchFile___""}"; + public static string OpenIdConnectMetadataBadX509DataString = @"{""jwks_uri"": ""JsonWebKeySetBadX509Data.json""}"; + public static string OpenIdConnectMetadataBadBase64DataString = @"{""jwks_uri"": ""JsonWebKeySetBadBase64Data.json""}"; + public static string OpenIdConnectMetadataBadUriKeysString = @"{""jwks_uri"": ""___NoSuchFile___""}"; public static string OpenIdConnectMetadataBadFormatString = @"{""issuer""::""https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/""}"; - public static string OpenIdConnectMetadataPingLabsJWKSString = @"{""jwks_uri"":""PingLabsJWKS.json""}"; + public static string OpenIdConnectMetadataPingLabsJWKSString = @"{""jwks_uri"": ""PingLabsJWKS.json""}"; public static string OpenIdConnectMetatadataBadJson = @"{..."; #endregion @@ -182,14 +200,14 @@ public static OpenIdConnectConfiguration FullyPopulatedWithKeys "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo", "revocation_endpoint": "https://oauth2.googleapis.com/revoke", "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs", - "response_types_supported": ["code","id_token","code id_token"], + "response_types_supported": ["code", "id_token", "code id_token"], "subject_types_supported": ["public"], "id_token_signing_alg_values_supported": ["RS256"], - "scopes_supported": ["openid","email","profile"], - "token_endpoint_auth_methods_supported": ["client_secret_post","client_secret_basic"], - "claims_supported": ["aud","email","email_verified","exp","family_name","given_name","iat","iss","locale","name","picture","sub"], - "code_challenge_methods_supported": ["plain","S256"], - "grant_types_supported": ["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code","urn:ietf:params:oauth:grant-type:jwt-bearer"] + "scopes_supported": ["openid", "email", "profile"], + "token_endpoint_auth_methods_supported": ["client_secret_post", "client_secret_basic"], + "claims_supported": ["aud", "email", "email_verified", "exp", "family_name", "given_name", "iat", "iss", "locale", "name", "picture", "sub"], + "code_challenge_methods_supported": ["plain", "S256"], + "grant_types_supported": ["authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code", "urn:ietf:params:oauth:grant-type:jwt-bearer"] } """; public static OpenIdConnectConfiguration AccountsGoogleComConfig @@ -200,12 +218,15 @@ public static OpenIdConnectConfiguration AccountsGoogleComConfig OpenIdConnectConfiguration config = new OpenIdConnectConfiguration { AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth", + DeviceAuthorizationEndpoint = "https://oauth2.googleapis.com/device/code", Issuer = "https://accounts.google.com", JwksUri = "https://www.googleapis.com/oauth2/v3/certs", + RevocationEndpoint = "https://oauth2.googleapis.com/revoke", TokenEndpoint = "https://oauth2.googleapis.com/token", UserInfoEndpoint = "https://openidconnect.googleapis.com/v1/userinfo", }; + AddToCollection(config.CodeChallengeMethodsSupported, "plain", "S256"); AddToCollection(config.ResponseTypesSupported, "code", "id_token", "code id_token"); config.SubjectTypesSupported.Add("public"); config.IdTokenSigningAlgValuesSupported.Add("RS256"); @@ -214,27 +235,22 @@ public static OpenIdConnectConfiguration AccountsGoogleComConfig AddToCollection(config.ClaimsSupported, "aud", "email", "email_verified", "exp", "family_name", "given_name", "iat", "iss", "locale", "name", "picture", "sub"); AddToCollection(config.GrantTypesSupported, "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code", "urn:ietf:params:oauth:grant-type:jwt-bearer"); - // Adjust if Google changes their config or https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/2456 is implemented. - config.AdditionalData.Add("device_authorization_endpoint", "https://oauth2.googleapis.com/device/code"); - config.AdditionalData.Add("code_challenge_methods_supported", JsonUtilities.CreateJsonElement(""" ["plain","S256"] """)); - config.AdditionalData.Add("revocation_endpoint", "https://oauth2.googleapis.com/revoke"); - return config; } } #endregion - #region AADCommonV1 2/2/2024 https://login.microsoftonline.com/common/.well-known/openid-configuration + #region AADCommonV1 2/2/2024 https://login.microsoftonline.com/common/.well-known/openid-configuration public static string AADCommonV1Json => """ { "token_endpoint": "https://login.microsoftonline.com/common/oauth2/token", - "token_endpoint_auth_methods_supported": ["client_secret_post","private_key_jwt","client_secret_basic"], + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/common/discovery/keys", - "response_modes_supported": ["query","fragment","form_post"], + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], - "response_types_supported": ["code","id_token","code id_token","token id_token","token"], + "response_types_supported": ["code", "id_token", "code id_token", "token id_token", "token"], "scopes_supported": ["openid"], "issuer": "https://sts.windows.net/{tenantid}/", "microsoft_multi_refresh_token": true, @@ -243,7 +259,7 @@ public static OpenIdConnectConfiguration AccountsGoogleComConfig "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/common/oauth2/logout", - "claims_supported": ["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","amr","nonce","email","given_name","family_name","nickname"], + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "amr", "nonce", "email", "given_name", "family_name", "nickname"], "check_session_iframe": "https://login.microsoftonline.com/common/oauth2/checksession", "userinfo_endpoint": "https://login.microsoftonline.com/common/openid/userinfo", "kerberos_endpoint": "https://login.microsoftonline.com/common/kerberos", @@ -262,6 +278,7 @@ public static OpenIdConnectConfiguration AADCommonV1Config OpenIdConnectConfiguration config = new OpenIdConnectConfiguration { AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/authorize", + DeviceAuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/devicecode", CheckSessionIframe = "https://login.microsoftonline.com/common/oauth2/checksession", HttpLogoutSupported = true, Issuer = "https://sts.windows.net/{tenantid}/", @@ -280,7 +297,6 @@ public static OpenIdConnectConfiguration AADCommonV1Config AddToCollection(config.TokenEndpointAuthMethodsSupported, "client_secret_post", "private_key_jwt", "client_secret_basic"); AddToCollection(config.ClaimsSupported, "sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "amr", "nonce", "email", "given_name", "family_name", "nickname"); config.AdditionalData.Add("microsoft_multi_refresh_token", true); - config.AdditionalData.Add("device_authorization_endpoint", "https://login.microsoftonline.com/common/oauth2/devicecode"); config.AdditionalData.Add("kerberos_endpoint", "https://login.microsoftonline.com/common/kerberos"); config.AdditionalData.Add("tenant_region_scope", null); config.AdditionalData.Add("cloud_instance_name", "microsoftonline.com"); @@ -298,13 +314,13 @@ public static OpenIdConnectConfiguration AADCommonV1Config """ { "token_endpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/token", - "token_endpoint_auth_methods_supported": ["client_secret_post","private_key_jwt","client_secret_basic"], + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/common/discovery/v2.0/keys", - "response_modes_supported": ["query","fragment","form_post"], + "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], - "response_types_supported": ["code","id_token","code id_token","id_token token"], - "scopes_supported": ["openid","profile","email","offline_access"], + "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], + "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", @@ -313,7 +329,7 @@ public static OpenIdConnectConfiguration AADCommonV1Config "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/common/oauth2/v2.0/logout", - "claims_supported": ["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"], + "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "kerberos_endpoint": "https://login.microsoftonline.com/common/kerberos", "tenant_region_scope": null, "cloud_instance_name": "microsoftonline.com", @@ -330,6 +346,7 @@ public static OpenIdConnectConfiguration AADCommonV2Config OpenIdConnectConfiguration config = new OpenIdConnectConfiguration { AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", + DeviceAuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode", HttpLogoutSupported = true, Issuer = "https://login.microsoftonline.com/{tenantid}/v2.0", JwksUri = "https://login.microsoftonline.com/common/discovery/v2.0/keys", @@ -346,7 +363,6 @@ public static OpenIdConnectConfiguration AADCommonV2Config AddToCollection(config.ScopesSupported, "openid", "profile", "email", "offline_access"); AddToCollection(config.TokenEndpointAuthMethodsSupported, "client_secret_post", "private_key_jwt", "client_secret_basic"); AddToCollection(config.ClaimsSupported, "sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"); - config.AdditionalData.Add("device_authorization_endpoint", "https://login.microsoftonline.com/common/oauth2/v2.0/devicecode"); config.AdditionalData.Add("kerberos_endpoint", "https://login.microsoftonline.com/common/kerberos"); config.AdditionalData.Add("tenant_region_scope", null); config.AdditionalData.Add("cloud_instance_name", "microsoftonline.com"); @@ -598,12 +614,23 @@ private static OpenIdConnectConfiguration SetDefaultConfiguration(OpenIdConnectC { AddToCollection(config.AcrValuesSupported, "acr_value1", "acr_value2", "acr_value3"); config.AuthorizationEndpoint = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize"; + AddToCollection(config.AuthorizationEncryptionAlgValuesSupported, "A192KW", "A256KW"); + AddToCollection(config.AuthorizationEncryptionEncValuesSupported, "A128CBC-HS256", "A256CBC-HS512"); + config.AuthorizationResponseIssParameterSupported = false; + AddToCollection(config.AuthorizationSigningAlgValuesSupported, "ES384", "ES512"); + config.BackchannelAuthenticationEndpoint = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/bc-authorize"; + AddToCollection(config.BackchannelAuthenticationRequestSigningAlgValuesSupported, "ES384", "ES512"); + AddToCollection(config.BackchannelTokenDeliveryModesSupported, "poll", "ping"); + config.BackchannelUserCodeParameterSupported = false; config.CheckSessionIframe = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/checksession"; AddToCollection(config.ClaimsLocalesSupported, "claim_local1", "claim_local2", "claim_local3"); config.ClaimsParameterSupported = true; AddToCollection(config.ClaimsSupported, "sub", "iss", "aud", "exp", "iat", "auth_time", "acr", "amr", "nonce", "email", "given_name", "family_name", "nickname"); AddToCollection(config.ClaimTypesSupported, "Normal Claims", "Aggregated Claims", "Distributed Claims"); + AddToCollection(config.CodeChallengeMethodsSupported, "plain", "S256"); + config.DeviceAuthorizationEndpoint = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/devicecode"; AddToCollection(config.DisplayValuesSupported, "displayValue1", "displayValue2", "displayValue3"); + AddToCollection(config.DPoPSigningAlgValuesSupported, "ES384", "ES512"); config.EndSessionEndpoint = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/logout"; config.FrontchannelLogoutSessionSupported = "true"; config.FrontchannelLogoutSupported = "true"; @@ -620,20 +647,27 @@ private static OpenIdConnectConfiguration SetDefaultConfiguration(OpenIdConnectC config.LogoutSessionSupported = true; config.OpPolicyUri = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_policy_uri"; config.OpTosUri = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_tos_uri"; + AddToCollection(config.PromptValuesSupported, "none", "login", "consent"); + config.PushedAuthorizationRequestEndpoint = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/par"; AddToCollection(config.RequestObjectEncryptionAlgValuesSupported, "A192KW", "A256KW"); AddToCollection(config.RequestObjectEncryptionEncValuesSupported, "A192GCM", "A256GCM"); AddToCollection(config.RequestObjectSigningAlgValuesSupported, "PS256", "PS512"); config.RequestParameterSupported = true; config.RequestUriParameterSupported = true; + config.RequirePushedAuthorizationRequests = false; config.RequireRequestUriRegistration = true; AddToCollection(config.ResponseModesSupported, "query", "fragment", "form_post"); AddToCollection(config.ResponseTypesSupported, "code", "id_token", "code id_token"); - config.ScopesSupported.Add("openid"); + config.RevocationEndpoint = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/revocation"; + AddToCollection(config.RevocationEndpointAuthMethodsSupported, "client_secret_post", "client_secret_basic"); + AddToCollection(config.RevocationEndpointAuthSigningAlgValuesSupported, "ES192", "ES256"); config.ServiceDocumentation = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/service_documentation"; + config.ScopesSupported.Add("openid"); config.SubjectTypesSupported.Add("pairwise"); config.TokenEndpoint = "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token"; AddToCollection(config.TokenEndpointAuthMethodsSupported, "client_secret_post", "private_key_jwt"); AddToCollection(config.TokenEndpointAuthSigningAlgValuesSupported, "ES192", "ES256"); + config.TlsClientCertificateBoundAccessTokens = true; AddToCollection(config.UILocalesSupported, "hak-CN", "en-us"); config.UserInfoEndpoint = "https://login.microsoftonline.com/add29489-7269-41f4-8841-b63c95564420/openid/userinfo"; AddToCollection(config.UserInfoEndpointEncryptionAlgValuesSupported, "ECDH-ES+A128KW", "ECDH-ES+A192KW"); diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectConfigurationTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectConfigurationTests.cs index fc6690b7ce..c31a60b085 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectConfigurationTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectConfigurationTests.cs @@ -75,11 +75,20 @@ public void Defaults() { OpenIdConnectConfiguration configuration = new OpenIdConnectConfiguration(); Assert.NotNull(configuration.AcrValuesSupported); + Assert.NotNull(configuration.AuthorizationEncryptionAlgValuesSupported); + Assert.NotNull(configuration.AuthorizationEncryptionEncValuesSupported); + Assert.NotNull(configuration.AuthorizationSigningAlgValuesSupported); + Assert.False(configuration.AuthorizationResponseIssParameterSupported); + Assert.NotNull(configuration.BackchannelAuthenticationRequestSigningAlgValuesSupported); + Assert.NotNull(configuration.BackchannelTokenDeliveryModesSupported); + Assert.False(configuration.BackchannelUserCodeParameterSupported); Assert.NotNull(configuration.ClaimsSupported); Assert.NotNull(configuration.ClaimsLocalesSupported); Assert.False(configuration.ClaimsParameterSupported); Assert.NotNull(configuration.ClaimTypesSupported); + Assert.NotNull(configuration.CodeChallengeMethodsSupported); Assert.NotNull(configuration.DisplayValuesSupported); + Assert.NotNull(configuration.DPoPSigningAlgValuesSupported); Assert.NotNull(configuration.GrantTypesSupported); Assert.False(configuration.HttpLogoutSupported); Assert.NotNull(configuration.IdTokenEncryptionAlgValuesSupported); @@ -87,19 +96,24 @@ public void Defaults() Assert.NotNull(configuration.IdTokenSigningAlgValuesSupported); Assert.NotNull(configuration.IntrospectionEndpointAuthMethodsSupported); Assert.NotNull(configuration.IntrospectionEndpointAuthSigningAlgValuesSupported); + Assert.NotNull(configuration.PromptValuesSupported); Assert.NotNull(configuration.RequestObjectEncryptionAlgValuesSupported); Assert.NotNull(configuration.RequestObjectEncryptionEncValuesSupported); Assert.NotNull(configuration.RequestObjectSigningAlgValuesSupported); Assert.False(configuration.RequestParameterSupported); - Assert.NotNull(configuration.ResponseModesSupported); - Assert.NotNull(configuration.ResponseTypesSupported); + Assert.False(configuration.RequirePushedAuthorizationRequests); Assert.False(configuration.RequestUriParameterSupported); Assert.False(configuration.RequireRequestUriRegistration); + Assert.NotNull(configuration.ResponseModesSupported); + Assert.NotNull(configuration.ResponseTypesSupported); + Assert.NotNull(configuration.RevocationEndpointAuthMethodsSupported); + Assert.NotNull(configuration.RevocationEndpointAuthSigningAlgValuesSupported); Assert.NotNull(configuration.ScopesSupported); Assert.NotNull(configuration.SigningKeys); Assert.NotNull(configuration.SubjectTypesSupported); Assert.NotNull(configuration.TokenEndpointAuthMethodsSupported); Assert.NotNull(configuration.TokenEndpointAuthSigningAlgValuesSupported); + Assert.False(configuration.TlsClientCertificateBoundAccessTokens); Assert.NotNull(configuration.UILocalesSupported); Assert.NotNull(configuration.UserInfoEndpointEncryptionAlgValuesSupported); Assert.NotNull(configuration.UserInfoEndpointEncryptionEncValuesSupported); @@ -131,8 +145,8 @@ public void GetSets() OpenIdConnectConfiguration configuration = new OpenIdConnectConfiguration(); Type type = typeof(OpenIdConnectConfiguration); PropertyInfo[] properties = type.GetProperties(); - if (properties.Length != 49) - Assert.True(false, "Number of properties has changed from 49 to: " + properties.Length + ", adjust tests"); + if (properties.Length != 67) + Assert.True(false, "Number of properties has changed from 67 to: " + properties.Length + ", adjust tests"); TestUtilities.CallAllPublicInstanceAndStaticPropertyGets(configuration, "OpenIdConnectConfiguration_GetSets"); @@ -141,25 +155,39 @@ public void GetSets() { PropertyNamesAndSetGetValue = new List>> { - new KeyValuePair>("AuthorizationEndpoint", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("CheckSessionIframe", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("ClaimsParameterSupported", new List{false, true, false}), - new KeyValuePair>("EndSessionEndpoint", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("HttpLogoutSupported", new List{false, true, true}), - new KeyValuePair>("IntrospectionEndpoint", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("Issuer", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("JwksUri", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("JsonWebKeySet", new List{null, new JsonWebKeySet()}), - new KeyValuePair>("LogoutSessionSupported", new List{false, true, true}), - new KeyValuePair>("OpPolicyUri", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("OpTosUri", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("RegistrationEndpoint", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("RequireRequestUriRegistration", new List{false, true, true}), - new KeyValuePair>("RequestParameterSupported", new List{false, true, false}), - new KeyValuePair>("RequestUriParameterSupported", new List{false, true, true}), - new KeyValuePair>("ServiceDocumentation", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("TokenEndpoint", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), - new KeyValuePair>("UserInfoEndpoint", new List{(string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString()}), + new KeyValuePair>("AuthorizationEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("AuthorizationEncryptionAlgValuesSupported", new List{ false, true, true }), + new KeyValuePair>("AuthorizationEncryptionEncValuesSupported", new List{ false, true, true }), + new KeyValuePair>("AuthorizationResponseIssParameterSupported", new List{ false, true, true }), + new KeyValuePair>("AuthorizationSigningAlgValuesSupported", new List{ false, true, true }), + new KeyValuePair>("BackchannelAuthenticationEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("BackchannelUserCodeParameterSupported", new List{ false, true, true }), + new KeyValuePair>("CheckSessionIframe", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("ClaimsParameterSupported", new List{ false, true, false }), + new KeyValuePair>("CodeChallengeMethodsSupported", new List{ false, true, true }), + new KeyValuePair>("DeviceAuthorizationEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("EndSessionEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("HttpLogoutSupported", new List{ false, true, true }), + new KeyValuePair>("IntrospectionEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("Issuer", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("JwksUri", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("JsonWebKeySet", new List{ null, new JsonWebKeySet() }), + new KeyValuePair>("LogoutSessionSupported", new List{ false, true, true }), + new KeyValuePair>("OpPolicyUri", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("OpTosUri", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("PushedAuthorizationRequestEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("RegistrationEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("RequestParameterSupported", new List{ false, true, false }), + new KeyValuePair>("RequestUriParameterSupported", new List{ false, true, true }), + new KeyValuePair>("RequirePushedAuthorizationRequests", new List{ false, true, true }), + new KeyValuePair>("RequireRequestUriRegistration", new List{ false, true, true }), + new KeyValuePair>("RevocationEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("RevocationEndpointAuthMethodsSupported", new List{ false, true, true }), + new KeyValuePair>("RevocationEndpointAuthSigningAlgValuesSupported", new List{ false, true, true }), + new KeyValuePair>("ServiceDocumentation", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("TlsClientCertificateBoundAccessTokens", new List{ false, true, false }), + new KeyValuePair>("TokenEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), + new KeyValuePair>("UserInfoEndpoint", new List{ (string)null, Guid.NewGuid().ToString(), Guid.NewGuid().ToString() }), }, Object = configuration, @@ -269,21 +297,33 @@ public void NonemptyCollectionSerialization() var collectionNames = new List { "acr_values_supported", + "authorization_encryption_alg_values_supported", + "authorization_encryption_enc_values_supported", + "authorization_signing_alg_values_supported", + "backchannel_authentication_request_signing_alg_values_supported", + "backchannel_token_delivery_modes_supported", "claims_supported", "claims_locales_supported", "claim_types_supported", + "code_challenge_methods_supported", + "device_authorization_endpoint", "display_values_supported", + "dpop_signing_alg_values_supported", "grant_types_supported", "id_token_encryption_alg_values_supported", "id_token_encryption_enc_values_supported", "id_token_signing_alg_values_supported", "introspection_endpoint_auth_methods_supported", "introspection_endpoint_auth_signing_alg_values_supported", + "prompt_values_supported", "request_object_encryption_alg_values_supported", "request_object_encryption_enc_values_supported", "request_object_signing_alg_values_supported", "response_modes_supported", "response_types_supported", + "revocation_endpoint", + "revocation_endpoint_auth_methods_supported", + "revocation_endpoint_auth_signing_alg_values_supported", "scopes_supported", "subject_types_supported", "token_endpoint_auth_methods_supported", diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs index 5406e03d54..d1c4254365 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMessageTests.cs @@ -278,10 +278,7 @@ public void OidcCreateAuthenticationRequestUrl(string testId, OpenIdConnectMessa { TestUtilities.WriteHeader(testId, "OidcCreateAuthenticationRequestUrl", true); var context = new CompareContext(); -#if NET461 - if (!message.SkuTelemetryValue.Equals("ID_NET461")) - context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET461"); -#elif NET462 +#if NET462 if (!message.SkuTelemetryValue.Equals("ID_NET462")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET462"); #elif NET472 @@ -556,10 +553,7 @@ public void OidcCreateLogoutRequestUrl(string testId, OpenIdConnectMessage messa TestUtilities.WriteHeader("OidcCreateLogoutRequestUrl - " + testId, true); var context = new CompareContext(); -#if NET461 - if (!message.SkuTelemetryValue.Equals("ID_NET461")) - context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET461"); -#elif NET472 +#if NET472 if (!message.SkuTelemetryValue.Equals("ID_NET472")) context.Diffs.Add($"{message.SkuTelemetryValue} != ID_NET472"); #elif NET6_0 diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadata.json b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadata.json index 65a0a0183f..90af72ef9f 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadata.json +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectMetadata.json @@ -1,46 +1,64 @@ { - "acr_values_supported" : ["acr_value1", "acr_value2", "acr_value3"], + "acr_values_supported": ["acr_value1", "acr_value2", "acr_value3"], "authorization_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/authorize", - "frontchannel_logout_session_supported": "true", - "frontchannel_logout_supported": "true", + "authorization_encryption_alg_values_supported": ["A192KW", "A256KW"], + "authorization_encryption_enc_values_supported": ["A128CBC-HS256", "A256CBC-HS512"], + "authorization_response_iss_parameter_supported": false, + "authorization_signing_alg_values_supported": ["ES384", "ES512"], + "backchannel_authentication_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/bc-authorize", + "backchannel_authentication_request_signing_alg_values_supported": ["ES384", "ES512"], + "backchannel_token_delivery_modes_supported": ["poll", "ping"], + "backchannel_user_code_parameter_supported": false, + "dpop_signing_alg_values_supported": ["ES384", "ES512"], + "frontchannel_logout_session_supported": "true", + "frontchannel_logout_supported": "true", "check_session_iframe": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/checksession", - "claims_locales_supported": [ "claim_local1", "claim_local2", "claim_local3" ], + "claims_locales_supported": ["claim_local1", "claim_local2", "claim_local3"], "claims_parameter_supported": true, - "claims_supported": [ "sub", "iss", "aud", "exp", "iat", "auth_time", "acr", "amr", "nonce", "email", "given_name", "family_name", "nickname" ], - "claim_types_supported": [ "Normal Claims", "Aggregated Claims", "Distributed Claims" ], - "display_values_supported": [ "displayValue1", "displayValue2", "displayValue3" ], + "claims_supported": ["sub", "iss", "aud", "exp", "iat", "auth_time", "acr", "amr", "nonce", "email", "given_name", "family_name", "nickname"], + "claim_types_supported": ["Normal Claims", "Aggregated Claims", "Distributed Claims"], + "code_challenge_methods_supported": ["plain", "S256"], + "device_authorization_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/devicecode", + "display_values_supported": ["displayValue1", "displayValue2", "displayValue3"], "end_session_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/logout", - "grant_types_supported" : ["authorization_code","implicit"], + "grant_types_supported": ["authorization_code", "implicit"], "http_logout_supported": true, - "id_token_encryption_alg_values_supported" : ["RSA1_5", "A256KW"], - "id_token_encryption_enc_values_supported" : ["A128CBC-HS256","A256CBC-HS512"], - "id_token_signing_alg_values_supported": [ "RS256" ], - "introspection_endpoint" : "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/introspect", - "introspection_endpoint_auth_methods_supported" : ["client_secret_post","private_key_jwt"], - "introspection_endpoint_auth_signing_alg_values_supported" : ["ES192", "ES256"], + "id_token_encryption_alg_values_supported": ["RSA1_5", "A256KW"], + "id_token_encryption_enc_values_supported": ["A128CBC-HS256", "A256CBC-HS512"], + "id_token_signing_alg_values_supported": ["RS256"], + "introspection_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/introspect", + "introspection_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt"], + "introspection_endpoint_auth_signing_alg_values_supported": ["ES192", "ES256"], "issuer": "https://sts.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/", "jwks_uri": "JsonWebKeySet.json", - "logout_session_supported" : true, - "microsoft_multi_refresh_token" : true, - "op_policy_uri" : "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_policy_uri", - "op_tos_uri" : "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_tos_uri", - "request_object_encryption_alg_values_supported" : ["A192KW", "A256KW"], - "request_object_encryption_enc_values_supported" : ["A192GCM","A256GCM"], - "request_object_signing_alg_values_supported" : ["PS256", "PS512"], - "request_parameter_supported" : true, - "request_uri_parameter_supported" : true, - "require_request_uri_registration" : true, - "response_types_supported": [ "code", "id_token", "code id_token" ], - "response_modes_supported": [ "query", "fragment", "form_post" ], - "service_documentation" : "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/service_documentation", - "scopes_supported" : ["openid"], - "subject_types_supported": [ "pairwise" ], + "logout_session_supported": true, + "microsoft_multi_refresh_token": true, + "op_policy_uri": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_policy_uri", + "op_tos_uri": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/op_tos_uri", + "prompt_values_supported": ["none", "login", "consent"], + "pushed_authorization_request_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/par", + "request_object_encryption_alg_values_supported": ["A192KW", "A256KW"], + "request_object_encryption_enc_values_supported": ["A192GCM", "A256GCM"], + "request_object_signing_alg_values_supported": ["PS256", "PS512"], + "request_parameter_supported": true, + "request_uri_parameter_supported": true, + "require_pushed_authorization_requests": false, + "require_request_uri_registration": true, + "response_types_supported": ["code", "id_token", "code id_token"], + "response_modes_supported": ["query", "fragment", "form_post"], + "revocation_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/revocation", + "revocation_endpoint_auth_methods_supported": ["client_secret_post", "client_secret_basic"], + "revocation_endpoint_auth_signing_alg_values_supported": ["ES192", "ES256"], + "service_documentation": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/service_documentation", + "scopes_supported": ["openid"], + "subject_types_supported": ["pairwise"], "token_endpoint": "https://login.windows.net/d062b2b0-9aca-4ff7-b32a-ba47231a4002/oauth2/token", - "token_endpoint_auth_methods_supported": [ "client_secret_post", "private_key_jwt" ], - "token_endpoint_auth_signing_alg_values_supported" : ["ES192", "ES256"], - "ui_locales_supported" : ["hak-CN", "en-us"], - "userinfo_endpoint" : "https://login.microsoftonline.com/add29489-7269-41f4-8841-b63c95564420/openid/userinfo", - "userinfo_encryption_alg_values_supported" : ["ECDH-ES+A128KW","ECDH-ES+A192KW"], - "userinfo_encryption_enc_values_supported" : ["A256CBC-HS512", "A128CBC-HS256"], - "userinfo_signing_alg_values_supported" : ["ES384", "ES512"] -} \ No newline at end of file + "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt"], + "token_endpoint_auth_signing_alg_values_supported": ["ES192", "ES256"], + "tls_client_certificate_bound_access_tokens": true, + "ui_locales_supported": ["hak-CN", "en-us"], + "userinfo_endpoint": "https://login.microsoftonline.com/add29489-7269-41f4-8841-b63c95564420/openid/userinfo", + "userinfo_encryption_alg_values_supported": ["ECDH-ES+A128KW", "ECDH-ES+A192KW"], + "userinfo_encryption_enc_values_supported": ["A256CBC-HS512", "A128CBC-HS256"], + "userinfo_signing_alg_values_supported": ["ES384", "ES512"] +} diff --git a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectSerializationTests.cs b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectSerializationTests.cs index 9d28137c02..22454614f5 100644 --- a/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectSerializationTests.cs +++ b/test/Microsoft.IdentityModel.Protocols.OpenIdConnect.Tests/OpenIdConnectSerializationTests.cs @@ -57,8 +57,8 @@ public static TheoryData DesrializeTheoryData theoryData.Add(new OpenIdConnectTheoryData("AccountsGoogleCom") { - CompareTo = JsonUtilities.SetAdditionalDataKeysToUpperCase(OpenIdConfigData.AccountsGoogleComConfig), - Json = JsonUtilities.SetAdditionalDataKeysToUpperCase(OpenIdConfigData.AccountsGoogleComJson, OpenIdConfigData.AccountsGoogleComConfig) + CompareTo = OpenIdConfigData.AccountsGoogleComConfig, + Json = OpenIdConfigData.AccountsGoogleComJson }); theoryData.Add(new OpenIdConnectTheoryData("FrontChannelFalse") diff --git a/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/SignedHttpRequestTestUtils.cs b/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/SignedHttpRequestTestUtils.cs index 4bf1148703..f86bade886 100644 --- a/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/SignedHttpRequestTestUtils.cs +++ b/test/Microsoft.IdentityModel.Protocols.SignedHttpRequest.Tests/SignedHttpRequestTestUtils.cs @@ -103,7 +103,7 @@ public static class SignedHttpRequestTestUtils Kid = Base64UrlEncoder.Encode(new JsonWebKey(DefaultJwkEcdsa.ToString(Formatting.None)).ComputeJwkThumbprint()) }; -#if NET461 || NET462 +#if NET462 internal static JObject DefaultJwkEcdsa => new JObject { { "kty", "EC" }, diff --git a/test/Microsoft.IdentityModel.TestUtils/DerivedTypes.cs b/test/Microsoft.IdentityModel.TestUtils/DerivedTypes.cs index f4580dba31..1590981f36 100644 --- a/test/Microsoft.IdentityModel.TestUtils/DerivedTypes.cs +++ b/test/Microsoft.IdentityModel.TestUtils/DerivedTypes.cs @@ -303,7 +303,7 @@ public DerivedSecurityKey(string keyId, int keySize) _keySize = keySize; } - internal override string InternalId { get =>_keyId; } + internal override string InternalId { get => _keyId; } public Exception ThrowOnGetKeyId { get; set; } diff --git a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs index 753b97efad..7ef5e4fa3e 100644 --- a/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs +++ b/test/Microsoft.IdentityModel.TestUtils/IdentityComparer.cs @@ -19,6 +19,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text.Json; +using System.Xml.Linq; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; @@ -60,6 +61,7 @@ public class IdentityComparer { typeof(IEnumerable).ToString(), AreX509DataEnumsEqual }, { typeof(int).ToString(), AreIntsEqual }, { typeof(IssuerSerial).ToString(), CompareAllPublicProperties }, + { typeof(IssuerValidationResult).ToString(), AreIssuerValidationResultsEqual }, { typeof(JArray).ToString(), AreJArraysEqual }, { typeof(JObject).ToString(), AreJObjectsEqual }, { typeof(JsonElement).ToString(), AreJsonElementsEqual }, @@ -542,6 +544,69 @@ public static bool AreEqual(object object1, object object2, CompareContext conte return context.Merge(localContext); } + public static bool AreIssuerValidationResultsEqual(object object1, object object2, CompareContext context) + { + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(object1, object2, context)) + return context.Merge(localContext); + + return AreIssuerValidationResultsEqual( + object1 as IssuerValidationResult, + object2 as IssuerValidationResult, + "IssuerValidationResult1", + "IssuerValidationResult2", + null, + context); + } + + internal static bool AreIssuerValidationResultsEqual( + IssuerValidationResult issuerValidationResult1, + IssuerValidationResult issuerValidationResult2, + string name1, + string name2, + string stackPrefix, + CompareContext context) + { + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(issuerValidationResult1, issuerValidationResult2, localContext)) + return context.Merge(localContext); + + if (issuerValidationResult1.Issuer != issuerValidationResult2.Issuer) + localContext.Diffs.Add($"IssuerValidationResult1.Issuer: {issuerValidationResult1.Issuer} != IssuerValidationResult2.Issuer: {issuerValidationResult2.Issuer}"); + + if (issuerValidationResult1.Source != issuerValidationResult2.Source) + localContext.Diffs.Add($"IssuerValidationResult1.Source: {issuerValidationResult1.Source} != IssuerValidationResult2.Source: {issuerValidationResult2.Source}"); + + // true => both are not null. + if (ContinueCheckingEquality(issuerValidationResult1.Exception, issuerValidationResult2.Exception, localContext)) + { + AreStringsEqual( + issuerValidationResult1.Exception.Message, + issuerValidationResult2.Exception.Message, + $"({name1})issuerValidationResult1.Exception.Message", + $"({name2})issuerValidationResult1.Exception.Message", + localContext); + + AreStringsEqual( + issuerValidationResult1.Exception.Source, + issuerValidationResult2.Exception.Source, + $"({name1})issuerValidationResult1.Exception.Source", + $"({name2})issuerValidationResult2.Exception.Source", + localContext); + + if (!string.IsNullOrEmpty(stackPrefix)) + AreStringPrefixesEqual( + issuerValidationResult1.Exception.StackTrace.Trim(), + issuerValidationResult2.Exception.StackTrace.Trim(), + $"({name1})issuerValidationResult1.Exception.StackTrace", + $"({name2})issuerValidationResult2.Exception.StackTrace", + stackPrefix.Trim(), + localContext); + } + + return context.Merge(localContext); + } + public static bool AreJArraysEqual(object object1, object object2, CompareContext context) { var localContext = new CompareContext(context); @@ -1089,15 +1154,43 @@ public static bool AreStringsEqual(object object1, object object2, string name1, if (!string.Equals(str1, str2, context.StringComparison)) { - localContext.Diffs.Add($"{name1} != {name2}, StringComparison: '{context.StringComparison}'"); - localContext.Diffs.Add(str1); + localContext.Diffs.Add($"'{name1}' != '{name2}', StringComparison: '{context.StringComparison}'"); + localContext.Diffs.Add($"'{str1}'"); localContext.Diffs.Add($"!="); - localContext.Diffs.Add(str2); + localContext.Diffs.Add($"'{str2}'"); } return context.Merge(localContext); } + public static bool AreStringPrefixesEqual( + string string1, + string string2, + string name1, + string name2, + string prefix, + CompareContext context) + { + var localContext = new CompareContext(context); + if (!ContinueCheckingEquality(string1, string2, localContext)) + return context.Merge(localContext); + + if (!string1.StartsWith(prefix, context.StringComparison)) + { + localContext.Diffs.Add($"'{name1}': does not start with prefix: '{prefix}', StringComparison: '{context.StringComparison}'"); + localContext.Diffs.Add($"'{string1}'"); + } + + if (!string2.StartsWith(prefix, context.StringComparison)) + { + localContext.Diffs.Add($"'{name2}': does not start with prefix: '{prefix}', StringComparison: '{context.StringComparison}'"); + localContext.Diffs.Add($"'{string2}'"); + } + + return context.Merge(localContext); + } + + public static bool AreStringEnumDictionariesEqual(IDictionary> dictionary1, IDictionary> dictionary2, CompareContext context) { var localContext = new CompareContext(context); @@ -1316,6 +1409,26 @@ public static bool CompareAllPublicProperties(object obj1, object obj2, CompareC return context.Merge($"CompareAllPublicProperties: {type}", localContext); } + public static bool IsOnlyOneObjectNull(object object1, object object2, CompareContext context) + { + if (object1 == null && object2 == null) + return false; + + if (object1 == null) + { + context.Diffs.Add(BuildStringDiff(object2.GetType().ToString(), object1, object2)); + return true; + } + + if (object2 == null) + { + context.Diffs.Add(BuildStringDiff(object1.GetType().ToString(), object1, object2)); + return true; + } + + return false; + } + public static bool ContinueCheckingEquality(object obj1, object obj2, CompareContext context) { if (obj1 == null && obj2 == null) diff --git a/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs b/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs index e867a8d028..9251fc487a 100644 --- a/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs +++ b/test/Microsoft.IdentityModel.TestUtils/KeyingMaterial.cs @@ -11,7 +11,7 @@ namespace Microsoft.IdentityModel.TestUtils { static public class KeyingMaterial { -#if NET461 || NET462 || NET472 +#if NET462 || NET472 static Type _rsaCngType = typeof(CngKey).Assembly.GetType("System.Security.Cryptography.RSACng", false); private static Lazy _rsaCng = new Lazy(CreateRSACng2048); #endif @@ -460,7 +460,7 @@ static KeyingMaterial() RsaSigningCreds_4096 = new SigningCredentials(RsaSecurityKey_2048, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256); RsaSigningCreds_4096_Public = new SigningCredentials(RsaSecurityKey_2048_Public, SecurityAlgorithms.RsaSha256Signature); -#if NET461 || NET462 +#if NET462 //ecdsa byte[] ecdsa256KeyBlob = TestUtilities.HexToByteArray("454353322000000096e476f7473cb17c5b38684daae437277ae1efadceb380fad3d7072be2ffe5f0b54a94c2d6951f073bfc25e7b81ac2a4c41317904929d167c3dfc99122175a9438e5fb3e7625493138d4149c9438f91a2fecc7f48f804a92b6363776892ee134"); byte[] ecdsa384KeyBlob = TestUtilities.HexToByteArray("45435334300000009dc6bb9cdc8dac31e3db6e6b5f58f8e3a304e5c08e632705ca9a236f1134646dca526b89f7ea98653962f4a781f2fc9bf479a2d627561b1269548050e6d2c388018b837f4ceba8ee7fe2eefea67c8418ad1e84f60c1309385e573ea5183e9ae8b6d5308a78da207c6e556af2053983321a5f8ac057b787089ee783c99093b9f2afb2f9a1e9a560ad3095b9667aa699fa"); @@ -506,7 +506,7 @@ static KeyingMaterial() } -#if NET461 || NET462 +#if NET462 public static RsaSecurityKey RsaSecurityKeyWithCspProvider_2048 { get @@ -548,7 +548,7 @@ public static RsaSecurityKey RsaSecurityKey_2048_FromRsa_Public } #endif -#if NET461 || NET462 +#if NET462 public static RsaSecurityKey RsaSecurityKeyWithCngProvider_2048 { get @@ -596,7 +596,7 @@ public static RsaSecurityKey RsaSecurityKey_2048_FromRsa_Public public static RsaSecurityKey RsaSecurityKey_2048 => new RsaSecurityKey(RsaParameters_2048) { KeyId = "RsaSecurityKey_2048" }; -#if NET461 || NET462 || NET472 +#if NET462 || NET472 public static RsaSecurityKey RsaSecurityKeyCng_2048 => new RsaSecurityKey(_rsaCng.Value as RSA) { KeyId = "RsaSecurityKeyRsaCng_2048" }; private static object CreateRSACng2048() diff --git a/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs b/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs index f0663cc997..1170d40822 100644 --- a/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs +++ b/test/Microsoft.IdentityModel.TestUtils/ValidationDelegates.cs @@ -87,7 +87,7 @@ public static string IssuerValidatorUsingConfigEcho(string issuer, SecurityToken return issuer; } - public static ValueTask IssuerValidatorAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters) + public static ValueTask IssuerValidatorInternalAsync(string issuer, SecurityToken token, TokenValidationParameters validationParameters) { return new ValueTask(issuer); } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricAdapterTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricAdapterTests.cs index a0d2570872..fd7c37f0e3 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricAdapterTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricAdapterTests.cs @@ -24,11 +24,8 @@ public void AsymmetricAdapterUsageTests(AsymmetricAdapterTheoryData theoryData) try { -#if NET461 || NET462 || NET472 || NETCOREAPP2_1 || NET6_0_OR_GREATER AsymmetricAdapter asymmetricdapter = new AsymmetricAdapter(theoryData.SecurityKey, theoryData.Algorithm, hashAlgorithm, SupportedAlgorithms.GetHashAlgorithmName(theoryData.Algorithm), true); -#else - AsymmetricAdapter asymmetricdapter = new AsymmetricAdapter(theoryData.SecurityKey, theoryData.Algorithm, hashAlgorithm, true); -#endif + byte[] signature = asymmetricdapter.Sign(bytes); if (!asymmetricdapter.Verify(bytes, signature)) context.AddDiff($"Verify failed for test: {theoryData.TestId}"); @@ -59,7 +56,6 @@ public static TheoryData AsymmetricAdapterUsageTest // RSA // RSACertificateExtensions.GetRSAPrivateKey - this results in - #if NET461 || NET462 || NET472 || NETCOREAPP2_1 || NET6_0_OR_GREATER new AsymmetricAdapterTheoryData { Algorithm = SecurityAlgorithms.RsaSha256, @@ -67,7 +63,6 @@ public static TheoryData AsymmetricAdapterUsageTest SecurityKey = new RsaSecurityKey(RSACertificateExtensions.GetRSAPrivateKey(KeyingMaterial.CertSelfSigned2048_SHA256) as RSA), TestId = "RSACertificateExtensions_GetRSAPrivateKey" }, - #endif // X509Certificte2.PrivateKey - this results in the RSA being of type RSACryptoServiceProviderProxy new AsymmetricAdapterTheoryData diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricSignatureTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricSignatureTests.cs index 768d3011d6..d7e5bb50f3 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricSignatureTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/AsymmetricSignatureTests.cs @@ -21,7 +21,7 @@ public void UnsupportedRSATypes() var context = new CompareContext("UnsupportedRSATypes"); TestUtilities.WriteHeader($"{this}.UnsupportedRSATypes"); -#if NET461 || NET462 || NET472 || NET_CORE +#if NET462 || NET472 || NET_CORE var expectedException = ExpectedException.NoExceptionExpected; #endif try @@ -34,7 +34,7 @@ public void UnsupportedRSATypes() expectedException.ProcessException(ex, context); } -#if NET461 || NET462 || NET472 || NET_CORE +#if NET462 || NET472 || NET_CORE expectedException = ExpectedException.NoExceptionExpected; #endif @@ -107,7 +107,7 @@ public static TheoryData SignVerifyTheoryData }, theoryData); -#if NET461 || NET462 || NET472 || NET_CORE +#if NET462 || NET472 || NET_CORE theoryData.Add(new SignatureProviderTheoryData() { SigningAlgorithm = SecurityAlgorithms.RsaSsaPssSha512, @@ -151,7 +151,7 @@ public static TheoryData SignVerifyTheoryData SigningKey = new RsaSecurityKey(certTuple.Item1.PrivateKey as RSA), TestId = "CapiCapi" + certTuple.Item3, VerifyKey = new RsaSecurityKey(certTuple.Item2.PublicKey.Key as RSA), -#if NET461 || NET462 || NET472 +#if NET462 || NET472 ExpectedException = ExpectedException.NotSupportedException("IDX10634:"), #elif NET_CORE ExpectedException = ExpectedException.NoExceptionExpected, @@ -165,7 +165,7 @@ public static TheoryData SignVerifyTheoryData SigningKey = new RsaSecurityKey(certTuple.Item1.PrivateKey as RSA), TestId = "CapiCng" + certTuple.Item3, VerifyKey = new RsaSecurityKey(certTuple.Item2.GetRSAPublicKey()), -#if NET461 || NET462 || NET472 +#if NET462 || NET472 ExpectedException = ExpectedException.NotSupportedException("IDX10634:"), #elif NET_CORE ExpectedException = ExpectedException.NoExceptionExpected, @@ -179,7 +179,7 @@ public static TheoryData SignVerifyTheoryData SigningKey = new RsaSecurityKey(certTuple.Item1.GetRSAPrivateKey()), TestId = "CngCapi" + certTuple.Item3, VerifyKey = new RsaSecurityKey(certTuple.Item2.PublicKey.Key as RSA), -#if NET461 || NET462 || NET472 +#if NET462 || NET472 ExpectedException = ExpectedException.NotSupportedException("IDX10634:"), #elif NET_CORE ExpectedException = ExpectedException.NoExceptionExpected, diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/CryptoProviderCacheTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/CryptoProviderCacheTests.cs index 43b99908b3..aa8a580535 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/CryptoProviderCacheTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/CryptoProviderCacheTests.cs @@ -602,7 +602,7 @@ public class CryptoProviderCacheTheoryData : TheoryDataBase, IDisposable #if NETCOREAPP public CryptoProviderCache CryptoProviderCache { get; set; } -#elif NET461 || NET462 || NET472 +#elif NET462 || NET472 public CryptoProviderCache CryptoProviderCache { get; set; } #endif diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs index 7b44f1b844..ad787f01d4 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/EncryptingCredentialsTests.cs @@ -57,7 +57,7 @@ public static TheoryData ConstructorATheoryData new EncryptingCredentialsTheoryData { Key = null, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'key'"), TestId = "NullKey" @@ -73,7 +73,7 @@ public static TheoryData ConstructorATheoryData new EncryptingCredentialsTheoryData { Key = Default.AsymmetricEncryptionKeyPublic, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = String.Empty, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'enc'"), TestId = "EmptyEncString" @@ -89,7 +89,7 @@ public static TheoryData ConstructorATheoryData new EncryptingCredentialsTheoryData { Key = Default.AsymmetricEncryptionKeyPublic, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'enc'"), TestId = "NullEncString" @@ -97,7 +97,7 @@ public static TheoryData ConstructorATheoryData new EncryptingCredentialsTheoryData { Key = Default.AsymmetricEncryptionKeyPublic, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, TestId = "ValidTest" } diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs index e78a0391b6..f293582ed3 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/IdentityComparerTests.cs @@ -575,9 +575,9 @@ public void CompareStrings() var string2 = "goodbye"; IdentityComparer.AreEqual(string1, string2, context); - Assert.True(context.Diffs.Count(s => s == "str1 != str2, StringComparison: 'Ordinal'") == 1); - Assert.True(context.Diffs[1] == string1); - Assert.True(context.Diffs[3] == string2); + Assert.True(context.Diffs.Count(s => s == "'str1' != 'str2', StringComparison: 'Ordinal'") == 1); + Assert.True(context.Diffs[1] == $"'{string1}'"); + Assert.True(context.Diffs[3] == $"'{string2}'"); } [Fact] diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonUtilities.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonUtilities.cs index dfbc058216..8dd4f023f1 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonUtilities.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Json/JsonUtilities.cs @@ -7,6 +7,7 @@ using System.Text; using System.Text.Json; using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Microsoft.IdentityModel.JsonWebTokens; using Newtonsoft.Json.Linq; namespace Microsoft.IdentityModel.Tokens.Json.Tests @@ -169,6 +170,11 @@ public static void SetAdditionalDataValues(IDictionary dictionar dictionary["true"] = true; } + public static JsonWebToken CreateUnsignedJsonWebToken(string key, object value) + { + return new JsonWebToken(CreateUnsignedToken(key, value)); + } + public static string CreateUnsignedToken(string key, object value) { return EmptyHeader + "." + CreateEncodedJson(key, value) + "."; diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj b/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj index 571c3cdde6..1457cc8bd1 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Microsoft.IdentityModel.Tokens.Tests.csproj @@ -13,7 +13,6 @@ - diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/MultiThreadingTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/MultiThreadingTests.cs index c9e87b0abf..6352fc6fe4 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/MultiThreadingTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/MultiThreadingTests.cs @@ -121,7 +121,7 @@ public static TheoryData MultiThreadingCreateAndVerify { Claims = Default.PayloadDictionary, SigningCredentials = new SigningCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaSha256, SecurityAlgorithms.Sha256), - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaOaepKeyWrap, SecurityAlgorithms.Aes128CbcHmacSha256) + EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKey_2048, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes128CbcHmacSha256) }; var tokenValidationParametersEncryptedRsaKW = new TokenValidationParameters @@ -152,7 +152,7 @@ public static TheoryData MultiThreadingCreateAndVerify var jwtEncryptedDir = jwtSecurityTokenHandler.CreateEncodedJwt(securityTokenDescriptorEncryptedDir); -#if NET461 || NET462 || NET472 +#if NET462 || NET472 // RSACng var securityTokenDescriptorRsaCng = new SecurityTokenDescriptor { @@ -174,7 +174,7 @@ public static TheoryData MultiThreadingCreateAndVerify { Claims = Default.PayloadDictionary, SigningCredentials = new SigningCredentials(KeyingMaterial.RsaSecurityKeyCng_2048, SecurityAlgorithms.RsaSha256, SecurityAlgorithms.Sha256), - EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKeyCng_2048, SecurityAlgorithms.RsaOaepKeyWrap, SecurityAlgorithms.Aes128CbcHmacSha256) + EncryptingCredentials = new EncryptingCredentials(KeyingMaterial.RsaSecurityKeyCng_2048, SecurityAlgorithms.RsaOAEP, SecurityAlgorithms.Aes128CbcHmacSha256) }; var tokenValidationParametersEncryptedRsaKWCng = new TokenValidationParameters @@ -235,7 +235,7 @@ public static TheoryData MultiThreadingCreateAndVerify TokenDescriptor = securityTokenDescriptorEncryptedDir, ValidationParameters = tokenValidationParametersEncryptedDir }, -#if NET461 || NET462 || NET472 +#if NET462 || NET472 new MultiThreadingTheoryData { JwtSecurityTokenHandler = jwtSecurityTokenHandler, diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/RsaCryptoServiceProviderProxyTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/RsaCryptoServiceProviderProxyTests.cs index 5c7b217422..222938fe89 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/RsaCryptoServiceProviderProxyTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/RsaCryptoServiceProviderProxyTests.cs @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#if NET461 || NET462 || NET472 +#if NET462 || NET472 using System.Security.Cryptography.X509Certificates; #endif -#if NET461 || NET462 || NET472 +#if NET462 || NET472 using System; using System.Security.Cryptography; @@ -59,7 +59,7 @@ public static TheoryData RSADecryptTheo { get { -#if NET461 || NET462 || NET472 +#if NET462 || NET472 var rsaCsp = new RSACryptoServiceProvider(); rsaCsp.ImportParameters(KeyingMaterial.RsaParameters_2048); #else @@ -111,7 +111,7 @@ public static TheoryData RSAEncryptDecr { get { -#if NET461 || NET462 || NET472 +#if NET462 || NET472 var rsaFromX509Cert = new RSACryptoServiceProvider(); var rsaCng = KeyingMaterial.DefaultCert_2048.GetRSAPrivateKey() as RSACng; var parameters = rsaCng.ExportParameters(true); @@ -185,7 +185,7 @@ public static TheoryData RSAEncryptDecr { get { -#if NET461 || NET462 || NET472 +#if NET462 || NET472 var rsaCsp = new RSACryptoServiceProvider(); rsaCsp.ImportParameters(KeyingMaterial.RsaParameters_2048); #else @@ -254,7 +254,7 @@ public static TheoryData RSASignVerifyD { get { -#if NET461 || NET462 || NET472 +#if NET462 || NET472 var rsaCsp = new RSACryptoServiceProvider(); rsaCsp.ImportParameters(KeyingMaterial.RsaParameters_2048); #else @@ -328,7 +328,7 @@ public static TheoryData RSAVerifyDataT { get { -#if NET461 || NET462 || NET472 +#if NET462 || NET472 var rsaCsp = new RSACryptoServiceProvider(); rsaCsp.ImportParameters(KeyingMaterial.RsaParameters_2048); #else diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/RsaSecurityKeyTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/RsaSecurityKeyTests.cs index f39224deca..b41074c639 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/RsaSecurityKeyTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/RsaSecurityKeyTests.cs @@ -66,7 +66,7 @@ public void HasPrivateKey(string testId, AsymmetricSecurityKey key, bool expecte public static TheoryData HasPrivateKeyTheoryData() { var theoryData = new TheoryData(); -#if NET461 || NET462 +#if NET462 theoryData.Add( "KeyingMaterial.RsaSecurityKeyWithCspProvider_2048", KeyingMaterial.RsaSecurityKeyWithCspProvider_2048, @@ -80,7 +80,7 @@ public static TheoryData HasPrivateKeyTheoryData() ); #endif -#if NET461 || NET462 +#if NET462 theoryData.Add( "KeyingMaterial.RsaSecurityKeyWithCngProvider_2048", diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/SecurityKeyTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/SecurityKeyTests.cs index b2c606189f..2d4ef698b1 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/SecurityKeyTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/SecurityKeyTests.cs @@ -14,11 +14,8 @@ public class SecurityKeyTests [Fact] public void ComputeJwkThumbprint() { - var exception = Assert.Throws(() => new ManagedKeyVaultSecurityKey.ManagedKeyVaultSecurityKey("keyid").ComputeJwkThumbprint()); - Assert.Contains("IDX10710", exception.Message); - -#if NET461 || NET462 - exception = Assert.Throws(() => new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, false).ComputeJwkThumbprint()); +#if NET462 + var exception = Assert.Throws(() => new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, false).ComputeJwkThumbprint()); Assert.Contains("IDX10695", exception.Message); #else var ex = Record.Exception(() => new ECDsaSecurityKey(KeyingMaterial.JsonWebKeyP256, false).ComputeJwkThumbprint()); diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs index 1464e6d694..3668182bc0 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/SignatureProviderTests.cs @@ -1301,7 +1301,7 @@ internal static void AddSignUsingOffsets(byte[] bytes, SecurityKey securityKey, SignatureProvider = CreateProvider(securityKey, algorithm) }); -#if NET461 || NET462 +#if NET462 // RSA throws a different exception in the following three cases than HMAC or ECDSA 472+ theoryData.Add(new SignTheoryData($"{prefix}_CountNegative") { diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs index 041de6d40d..a44e4c7152 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/TokenValidationParametersTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.IdentityModel.Tokens.Tests { public class TokenValidationParametersTests { - int ExpectedPropertyCount = 59; + int ExpectedPropertyCount = 60; [Fact] public void Publics() @@ -71,6 +71,7 @@ public void Publics() IssuerSigningKey = issuerSigningKey, IssuerSigningKeyResolver = (token, securityToken, keyIdentifier, tvp) => { return new List { issuerSigningKey }; }, IssuerSigningKeys = issuerSigningKeys, + IssuerValidationDelegateAsync = Validators.ValidateIssuerAsync, IssuerValidator = ValidationDelegates.IssuerValidatorEcho, LifetimeValidator = ValidationDelegates.LifetimeValidatorReturnsTrue, LogTokenId = true, @@ -290,8 +291,9 @@ private TokenValidationParameters CreateTokenValidationParameters() validationParameters.IssuerSigningKeyResolverUsingConfiguration = ValidationDelegates.IssuerSigningKeyResolverUsingConfiguration; validationParameters.IssuerSigningKeyValidator = ValidationDelegates.IssuerSigningKeyValidator; validationParameters.IssuerSigningKeyValidatorUsingConfiguration = ValidationDelegates.IssuerSigningKeyValidatorUsingConfiguration; + validationParameters.IssuerValidationDelegateAsync = Validators.ValidateIssuerAsync; validationParameters.IssuerValidator = ValidationDelegates.IssuerValidatorEcho; - validationParameters.IssuerValidatorAsync = ValidationDelegates.IssuerValidatorAsync; + validationParameters.IssuerValidatorAsync = ValidationDelegates.IssuerValidatorInternalAsync; validationParameters.IssuerValidatorUsingConfiguration = ValidationDelegates.IssuerValidatorUsingConfigEcho; validationParameters.LifetimeValidator = ValidationDelegates.LifetimeValidatorReturnsTrue; validationParameters.NameClaimTypeRetriever = ValidationDelegates.NameClaimTypeRetriever; diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AsyncValidatorsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AsyncValidatorsTests.cs new file mode 100644 index 0000000000..f5bacfc0b7 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/AsyncValidatorsTests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.TestUtils; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Validation.Tests +{ + public class AsyncValidatorTests + { + [Theory, MemberData(nameof(AsyncIssuerValidatorTestCases))] + public async Task AsyncIssuerValidatorTests(IssuerValidatorTheoryData theoryData) + { + CompareContext context = TestUtilities.WriteHeader($"{this}.AsyncIssuerValidatorTests", theoryData); + try + { + IssuerValidationResult result = await Validators.ValidateIssuerAsync( + theoryData.Issuer, + theoryData.SecurityToken, + theoryData.ValidationParameters, + null, + CancellationToken.None).ConfigureAwait(false); + Exception exception = result.Exception; + context.Diffs.Add("Exception: " + exception.ToString()); + } + catch (Exception ex) + { + context.Diffs.Add("Exception: " + ex.ToString()); + } + } + + public static TheoryData AsyncIssuerValidatorTestCases + { + get + { + TheoryData theoryData = new TheoryData(); + + theoryData.Add(new IssuerValidatorTheoryData + { + Issuer = null, + ValidationParameters = new TokenValidationParameters(), + }); + + return theoryData; + } + } + } + + public class IssuerValidatorTheoryData : TheoryDataBase + { + public string Issuer { get; set; } + public TokenValidationParameters ValidationParameters { get; set; } + public SecurityToken SecurityToken { get; set; } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs new file mode 100644 index 0000000000..c8b5694cc6 --- /dev/null +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/IssuerValidationResultTests.cs @@ -0,0 +1,207 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Logging; +using Microsoft.IdentityModel.TestUtils; +using Microsoft.IdentityModel.Tokens.Json.Tests; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using Xunit; + +namespace Microsoft.IdentityModel.Tokens.Validation.Tests +{ + public class IssuerValidationResultTests + { + [Theory, MemberData(nameof(IssuerValdationResultsTestCases), DisableDiscoveryEnumeration = true)] + public async Task IssuerValidatorAsyncTests(IssuerValidationResultsTheoryData theoryData) + { + CompareContext context = TestUtilities.WriteHeader($"{this}.IssuerValidatorAsyncTests", theoryData); + + try + { + IssuerValidationResult issuerValidationResult = await Validators.ValidateIssuerAsync( + theoryData.Issuer, + theoryData.SecurityToken, + theoryData.ValidationParameters, + new CallContext(), + CancellationToken.None).ConfigureAwait(false); + + if (issuerValidationResult.Exception != null) + theoryData.ExpectedException.ProcessException(issuerValidationResult.Exception, context); + + IdentityComparer.AreIssuerValidationResultsEqual( + issuerValidationResult, + theoryData.IssuerValidationResult, + context); + } + catch (SecurityTokenInvalidIssuerException ex) + { + theoryData.ExpectedException.ProcessException(ex, context); + } + + TestUtilities.AssertFailIfErrors(context); + } + + public static TheoryData IssuerValdationResultsTestCases + { + get + { + TheoryData theoryData = new(); + + string validIssuer = Guid.NewGuid().ToString(); + string issClaim = Guid.NewGuid().ToString(); + theoryData.Add(new IssuerValidationResultsTheoryData("Invalid_Issuer") + { + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10205:"), + Issuer = issClaim, + IssuerValidationResult = new IssuerValidationResult( + issClaim, + ValidationFailureType.IssuerValidationFailed, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10205, + LogHelper.MarkAsNonPII(issClaim), + LogHelper.MarkAsNonPII(validIssuer), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(null)), + LogHelper.MarkAsNonPII(null)), + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true))), + IsValid = false, + SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), + ValidationParameters = new TokenValidationParameters { ValidIssuer = validIssuer } + }); + + theoryData.Add(new IssuerValidationResultsTheoryData("NULL_Issuer") + { + ExpectedException = ExpectedException.SecurityTokenInvalidIssuerException("IDX10211:"), + IssuerValidationResult = new IssuerValidationResult( + null, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10211, + LogHelper.MarkAsNonPII(null), + LogHelper.MarkAsNonPII(validIssuer), + LogHelper.MarkAsNonPII(Utility.SerializeAsSingleCommaDelimitedString(null)), + LogHelper.MarkAsNonPII(null)), + typeof(SecurityTokenInvalidIssuerException), + new StackFrame(true))), + IsValid = false, + SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), + ValidationParameters = new TokenValidationParameters(), + }); + + theoryData.Add(new IssuerValidationResultsTheoryData("NULL_ValidationParameters") + { + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + Issuer = issClaim, + IssuerValidationResult = new IssuerValidationResult( + issClaim, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("validationParameters")), + typeof(ArgumentNullException), + new StackFrame(true), + null)), + IsValid = false, + SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), + ValidationParameters = null + }); + + theoryData.Add(new IssuerValidationResultsTheoryData("NULL_SecurityToken") + { + ExpectedException = ExpectedException.ArgumentNullException("IDX10000:"), + Issuer = issClaim, + IssuerValidationResult = new IssuerValidationResult( + issClaim, + ValidationFailureType.NullArgument, + new ExceptionDetail( + new MessageDetail( + LogMessages.IDX10000, + LogHelper.MarkAsNonPII("securityToken")), + typeof(ArgumentNullException), + new StackFrame(true), + null)), + IsValid = false, + SecurityToken = null, + ValidationParameters = new TokenValidationParameters() + }); + + var validConfig = new OpenIdConnectConfiguration() { Issuer = issClaim }; + theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromConfig") + { + ExpectedException = ExpectedException.NoExceptionExpected, + Issuer = issClaim, + IssuerValidationResult = new IssuerValidationResult( + issClaim, + IssuerValidationResult.ValidationSource.IssuerIsConfigurationIssuer), + IsValid = true, + SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), + ValidationParameters = new TokenValidationParameters() + { + ConfigurationManager = new MockConfigurationManager(validConfig) + } + }); + + theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromValidationParametersValidIssuer") + { + ExpectedException = ExpectedException.NoExceptionExpected, + Issuer = issClaim, + IssuerValidationResult = new IssuerValidationResult( + issClaim, + IssuerValidationResult.ValidationSource.IssuerIsValidIssuer), + IsValid = true, + SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), + ValidationParameters = new TokenValidationParameters() + { + ValidIssuer = issClaim + } + }); + + theoryData.Add(new IssuerValidationResultsTheoryData("Valid_FromValidationParametersValidIssuers") + { + ExpectedException = ExpectedException.NoExceptionExpected, + Issuer = issClaim, + IssuerValidationResult = new IssuerValidationResult( + issClaim, + IssuerValidationResult.ValidationSource.IssuerIsAmongValidIssuers), + IsValid = true, + SecurityToken = JsonUtilities.CreateUnsignedJsonWebToken(JwtRegisteredClaimNames.Iss, issClaim), + ValidationParameters = new TokenValidationParameters() + { + ValidIssuers = [issClaim] + } + }); + + return theoryData; + } + } + } + + public class IssuerValidationResultsTheoryData : TheoryDataBase + { + public IssuerValidationResultsTheoryData(string testId) : base(testId) + { + } + + public BaseConfiguration Configuration { get; set; } + + public string Issuer { get; set; } + + internal IssuerValidationResult IssuerValidationResult { get; set; } + + public bool IsValid { get; set; } + + public SecurityToken SecurityToken { get; set; } + + public TokenValidationParameters ValidationParameters { get; set; } + + internal ValidationFailureType ValidationFailureType { get; set; } + } +} diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/ValidatorsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs similarity index 99% rename from test/Microsoft.IdentityModel.Tokens.Tests/ValidatorsTests.cs rename to test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs index d7af19095e..181c05e926 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/ValidatorsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/Validation/ValidatorsTests.cs @@ -8,8 +8,6 @@ using Microsoft.IdentityModel.TestUtils; using Xunit; -#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant - namespace Microsoft.IdentityModel.Tokens.Tests { public class ValidatorsTests @@ -510,5 +508,3 @@ public class AudienceValidationTheoryData : TheoryDataBase } } } - -#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant diff --git a/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs b/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs index 6590737fca..2dee4d2e62 100644 --- a/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs +++ b/test/Microsoft.IdentityModel.Tokens.Tests/X509EncryptingCredentialsTests.cs @@ -42,7 +42,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = null, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'certificate'"), TestId = "NullCertificate" @@ -58,7 +58,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = Default.Certificate, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = String.Empty, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'enc'"), TestId = "EmptyEncString" @@ -74,7 +74,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = Default.Certificate, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = null, ExpectedException = ExpectedException.ArgumentNullException("IDX10000: The parameter 'enc'"), TestId = "NullEncString" @@ -82,7 +82,7 @@ public static TheoryData ConstructorsTheory new X509EncryptingCredentialsTheoryData { Certificate = Default.Certificate, - Alg = SecurityAlgorithms.RsaOaepKeyWrap, + Alg = SecurityAlgorithms.RsaOAEP, Enc = SecurityAlgorithms.Aes128CbcHmacSha256, TestId = "ValidTest" } diff --git a/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs b/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs deleted file mode 100644 index 4e05268f1f..0000000000 --- a/test/Microsoft.IdentityModel.Validators.Tests/AadIssuerValidatorTests.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -using Xunit; - -namespace Microsoft.IdentityModel.Validators.Tests -{ - public class AadIssuerValidatorTests - { - [Theory] - [InlineData(ValidatorConstants.AadInstance + AadIssuerValidator.TenantIdTemplate, ValidatorConstants.AadInstance + ValidatorConstants.TenantIdAsGuid, true)] - [InlineData(ValidatorConstants.AadInstancePPE + AadIssuerValidator.TenantIdTemplate, ValidatorConstants.AadInstance + ValidatorConstants.TenantIdAsGuid, false)] - [InlineData(ValidatorConstants.AadInstance + AadIssuerValidator.TenantIdTemplate, ValidatorConstants.AadInstance + ValidatorConstants.B2CTenantAsGuid, false)] - [InlineData("", ValidatorConstants.AadInstance + ValidatorConstants.TenantIdAsGuid, false)] - [InlineData(ValidatorConstants.AadInstance + AadIssuerValidator.TenantIdTemplate, "", false)] - public static void IsValidIssuer_CanValidateTemplatedIssuers(string templatedIssuer, string issuer, bool expectedResult) - { - // act - var result = AadIssuerValidator.IsValidIssuer(templatedIssuer, ValidatorConstants.TenantIdAsGuid, issuer); - - // assert - Assert.Equal(expectedResult, result); - } - } -} diff --git a/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs b/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs index b979f7c49e..e768a2372f 100644 --- a/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs +++ b/test/Microsoft.IdentityModel.Validators.Tests/AadSigningKeyIssuerValidatorTests.cs @@ -325,21 +325,6 @@ public static TheoryData ValidateIssuerSigningKey } } - [Fact] - public static void CreateIssuer_ReturnsExpectedIssuer() - { - // arrange - var issuerTemplate = "{tenantId}"; - var issuer = ValidatorConstants.AadInstance + issuerTemplate; - int templateStartIndex = issuer.IndexOf(issuerTemplate); - - // act - var result = AadTokenValidationParametersExtension.CreateIssuer(issuer, issuerTemplate, ValidatorConstants.TenantIdAsGuid, templateStartIndex); - - // assert - Assert.Equal(ValidatorConstants.AadInstance + ValidatorConstants.TenantIdAsGuid, result); - } - private static OpenIdConnectConfiguration GetConfigurationMock() { var config = new OpenIdConnectConfiguration(); diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs index 220ed19b8e..243bc2da24 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/CreateAndValidateTokens.cs @@ -611,7 +611,7 @@ public static TheoryData RoundTripTokensTheoryData() ValidationParameters = Default.SymmetricEncryptSignTokenValidationParameters }); -#if NET461 || NET462 || NET_CORE +#if NET462 || NET_CORE // RsaPss is not supported on .NET < 4.6 var rsaPssSigningCredentials = new SigningCredentials(Default.AsymmetricSigningKey, SecurityAlgorithms.RsaSsaPssSha256); theoryData.Add(new JwtTheoryData diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.WithContextSwitches.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.WithContextSwitches.cs deleted file mode 100644 index 7e87575e11..0000000000 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.WithContextSwitches.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.IdentityModel.JsonWebTokens; -using Microsoft.IdentityModel.TestUtils; -using Microsoft.IdentityModel.Tokens; -using Xunit; - -namespace System.IdentityModel.Tokens.Jwt.Tests -{ - [CollectionDefinition("JwtSecurityTokenHandlerTestsWithContextSwitches", DisableParallelization = true)] - public class JwtSecurityTokenHandlerTestsWithContextSwitches - { - [Theory] - [InlineData(SecurityAlgorithms.RsaOAEP, true)] - [InlineData(SecurityAlgorithms.RsaOaepKeyWrap, false)] - public void JwtSecurityTokenHandler_CreateToken_AddShortFormMappingForRsaOAEP(string algorithm, bool useShortNameForRsaOaepKey) - { - AppContext.SetSwitch(X509EncryptingCredentials._useShortNameForRsaOaepKey, useShortNameForRsaOaepKey); - var encryptingCredentials = new X509EncryptingCredentials(Default.Certificate); - JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler(); - - JwtSecurityToken token = CreateJwtSecurityToken(tokenHandler, encryptingCredentials); - - Assert.Equal(token.Header.Alg, algorithm); - - AppContext.SetSwitch(X509EncryptingCredentials._useShortNameForRsaOaepKey, false); - } - - [Theory] - [InlineData(SecurityAlgorithms.RsaOAEP, true)] - [InlineData(SecurityAlgorithms.RsaOaepKeyWrap, false)] - public void JsonWebTokenHandler_CreateToken_AddShortFormMappingForRsaOAEP(string algorithm, bool useShortNameForRsaOaepKey) - { - AppContext.SetSwitch(X509EncryptingCredentials._useShortNameForRsaOaepKey, useShortNameForRsaOaepKey); - var encryptingCredentials = new X509EncryptingCredentials(Default.Certificate); - JsonWebTokenHandler tokenHandler = new JsonWebTokenHandler(); - - JsonWebToken jsonToken = new JsonWebToken(CreateJwtSecurityTokenAsString(tokenHandler, encryptingCredentials)); - - Assert.Equal(jsonToken.Alg, algorithm); - - AppContext.SetSwitch(X509EncryptingCredentials._useShortNameForRsaOaepKey, false); - } - - private JwtSecurityToken CreateJwtSecurityToken(JwtSecurityTokenHandler tokenHandler, X509EncryptingCredentials encryptingCredentials) - { - return tokenHandler.CreateJwtSecurityToken(CreateTokenDescriptor(encryptingCredentials)); - } - - private string CreateJwtSecurityTokenAsString(JsonWebTokenHandler tokenHandler, X509EncryptingCredentials encryptingCredentials) - { - return tokenHandler.CreateToken(CreateTokenDescriptor(encryptingCredentials)); - } - - private SecurityTokenDescriptor CreateTokenDescriptor(X509EncryptingCredentials encryptingCredentials) - { - return new SecurityTokenDescriptor - { - Issuer = Default.Issuer, - SigningCredentials = Default.AsymmetricSigningCredentials, - EncryptingCredentials = encryptingCredentials, - }; - } - } -} diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs index ea2c58a738..9400e6770a 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtSecurityTokenHandlerTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.IdentityModel.JsonWebTokens; diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs index 64344d495b..4de6f67bce 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/JwtTestDatasets.cs @@ -27,6 +27,7 @@ using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Http; using Microsoft.IdentityModel.JsonWebTokens; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; diff --git a/test/System.IdentityModel.Tokens.Jwt.Tests/System.IdentityModel.Tokens.Jwt.Tests.csproj b/test/System.IdentityModel.Tokens.Jwt.Tests/System.IdentityModel.Tokens.Jwt.Tests.csproj index 5ff3cf0408..b0ec53c7af 100644 --- a/test/System.IdentityModel.Tokens.Jwt.Tests/System.IdentityModel.Tokens.Jwt.Tests.csproj +++ b/test/System.IdentityModel.Tokens.Jwt.Tests/System.IdentityModel.Tokens.Jwt.Tests.csproj @@ -19,6 +19,10 @@ + + + +