From 5218ba0133f26213407c4c9f03b1e89fc094cc0a Mon Sep 17 00:00:00 2001 From: MithrandirTheWizard <88721351+MithrandirTheWizard@users.noreply.github.com> Date: Tue, 28 May 2024 17:35:24 +0200 Subject: [PATCH 1/4] Update IdentityProvidersHandler.cs --- .../Services/IdentityProvidersHandler.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IdentityProvidersHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IdentityProvidersHandler.cs index 9c33960..1cf44fc 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IdentityProvidersHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IdentityProvidersHandler.cs @@ -59,23 +59,23 @@ public async Task> GetIdentityProviders() return result; } - private static IdentityProvider CreateSpidIdentityProvider(IdPEntityConfiguration conf) - => new SpidIdentityProvider() - { - EntityConfiguration = conf, - Uri = conf.Subject ?? string.Empty, - OrganizationLogoUrl = conf.Metadata.OpenIdProvider.AdditionalData["logo_uri"] as string ?? string.Empty, - OrganizationName = conf.Metadata.OpenIdProvider.AdditionalData["organization_name"] as string ?? string.Empty, - SupportedAcrValues = conf.Metadata.OpenIdProvider.AcrValuesSupported.ToList(), - }; + private static IdentityProvider CreateSpidIdentityProvider(IdPEntityConfiguration conf) + => new SpidIdentityProvider() + { + EntityConfiguration = conf, + Uri = conf.Subject ?? string.Empty, + OrganizationLogoUrl = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("logo_uri", out object? logoUri) ? logoUri as string ?? string.Empty : string.Empty, + OrganizationName = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("organization_name", out object? organizationName) ? organizationName as string ?? string.Empty : string.Empty, + SupportedAcrValues = conf.Metadata.OpenIdProvider.AcrValuesSupported.ToList(), + }; - private static IdentityProvider CreateCieIdentityProvider(IdPEntityConfiguration conf) - => new CieIdentityProvider() - { - EntityConfiguration = conf, - Uri = conf.Subject ?? string.Empty, - OrganizationLogoUrl = conf.Metadata.OpenIdProvider.AdditionalData["logo_uri"] as string ?? string.Empty, - OrganizationName = conf.Metadata.OpenIdProvider.AdditionalData["organization_name"] as string ?? string.Empty, - SupportedAcrValues = conf.Metadata.OpenIdProvider.AcrValuesSupported.ToList(), - }; + private static IdentityProvider CreateCieIdentityProvider(IdPEntityConfiguration conf) + => new CieIdentityProvider() + { + EntityConfiguration = conf, + Uri = conf.Subject ?? string.Empty, + OrganizationLogoUrl = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("logo_uri", out object? logoUri) ? logoUri as string ?? string.Empty : string.Empty, + OrganizationName = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("organization_name", out object? organizationName) ? organizationName as string ?? string.Empty : string.Empty, + SupportedAcrValues = conf.Metadata.OpenIdProvider.AcrValuesSupported.ToList(), + }; } From 3d72bc2f44e9319dc266df4ba1aebe7e22af9ddf Mon Sep 17 00:00:00 2001 From: MithrandirTheWizard <88721351+MithrandirTheWizard@users.noreply.github.com> Date: Tue, 28 May 2024 17:37:11 +0200 Subject: [PATCH 2/4] Update SpidCieOIDCConfiguration.cs --- .../Models/SpidCieOIDCConfiguration.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieOIDCConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieOIDCConfiguration.cs index 1f30373..9d55b30 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieOIDCConfiguration.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieOIDCConfiguration.cs @@ -26,8 +26,8 @@ internal sealed class SAMetadata_SpidCieOIDCConfiguration [ExcludeFromCodeCoverage] internal sealed class SA_TrustMarkIssuer { - [JsonPropertyName("federation_status_endpoint")] - public string FederationStatusEndpoint { get; set; } + //[JsonPropertyName("federation_status_endpoint")] + //public string FederationStatusEndpoint { get; set; } } [ExcludeFromCodeCoverage] @@ -118,6 +118,9 @@ internal sealed class SA_SpidCieOIDCFederationEntity [JsonPropertyName("federation_list_endpoint")] public string FederationListEndpoint { get; set; } + + [JsonPropertyName("federation_trust_mark_status_endpoint")] + public string FederationTrustMarkStatusEndpoint { get; set; } } [ExcludeFromCodeCoverage] From 18f6baee2595adf1d780bfabc4279419761d7342 Mon Sep 17 00:00:00 2001 From: MithrandirTheWizard <88721351+MithrandirTheWizard@users.noreply.github.com> Date: Tue, 28 May 2024 17:38:29 +0200 Subject: [PATCH 3/4] Update RPOpenIdFederationMiddleware.cs --- .../RPOpenIdFederationMiddleware.cs | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/RPOpenIdFederationMiddleware.cs b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/RPOpenIdFederationMiddleware.cs index f71367f..54b3f00 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/RPOpenIdFederationMiddleware.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/RPOpenIdFederationMiddleware.cs @@ -132,34 +132,35 @@ private RPEntityConfiguration GetEntityConfiguration(RelyingParty rp, ICryptoSer } private SAEntityConfiguration GetEntityConfiguration(Aggregator agg, ICryptoService cryptoService) - { - return new SAEntityConfiguration() - { - ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(SpidCieConst.EntityConfigurationExpirationInMinutes), - IssuedAt = DateTimeOffset.UtcNow, - AuthorityHints = agg.AuthorityHints, - Issuer = agg.Id, - Subject = agg.Id, - TrustMarks = agg.TrustMarks, - JWKS = cryptoService.GetJWKS(agg.OpenIdFederationCertificates), - Metadata = new SAMetadata_SpidCieOIDCConfiguration() - { - FederationEntity = new SA_SpidCieOIDCFederationEntity() - { - Contacts = agg.Contacts, - HomepageUri = agg.HomepageUri, - LogoUri = agg.LogoUri, - OrganizationName = agg.OrganizationName, - PolicyUri = agg.PolicyUri, - FederationResolveEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.ResolveEndpointPath}", - FederationFetchEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.FetchEndpointPath}", - FederationListEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.ListEndpointPath}", - }, - TrustMarkIssuer = new SA_TrustMarkIssuer() - { - FederationStatusEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.TrustMarkStatusEndpointPath}" - } - } - }; - } + { + return new SAEntityConfiguration() + { + ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(SpidCieConst.EntityConfigurationExpirationInMinutes), + IssuedAt = DateTimeOffset.UtcNow, + AuthorityHints = agg.AuthorityHints, + Issuer = agg.Id, + Subject = agg.Id, + TrustMarks = agg.TrustMarks, + JWKS = cryptoService.GetJWKS(agg.OpenIdFederationCertificates), + Metadata = new SAMetadata_SpidCieOIDCConfiguration() + { + FederationEntity = new SA_SpidCieOIDCFederationEntity() + { + Contacts = agg.Contacts, + HomepageUri = agg.HomepageUri, + LogoUri = agg.LogoUri, + OrganizationName = agg.OrganizationName, + PolicyUri = agg.PolicyUri, + FederationResolveEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.ResolveEndpointPath}", + FederationFetchEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.FetchEndpointPath}", + FederationListEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.ListEndpointPath}", + FederationTrustMarkStatusEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.TrustMarkStatusEndpointPath}" + }//, + //TrustMarkIssuer = new SA_TrustMarkIssuer() + //{ + // FederationStatusEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.TrustMarkStatusEndpointPath}" + //} + } + }; + } } From bc9d6126a402a2c2e1fee5d6694e65b61f145973 Mon Sep 17 00:00:00 2001 From: Giulio Maffei Date: Mon, 9 Sep 2024 13:24:37 +0200 Subject: [PATCH 4/4] Managed SA and code refactoring --- README.md | 18 +- .../CieButtonTests.cs | 7 +- .../CieIdentityProvidersTests.cs | 4 +- .../CustomHttpClientHandlerTests.cs | 16 +- .../IntegrationTests/TestSettings.cs | 19 +- .../Mocks/MockBackchannel.cs | 11 +- .../Mocks/MockIdentityProvidersHandler.cs | 8 +- .../Mocks/MockOptionsMonitorSpidCieOptions.cs | 19 +- .../Mocks/MockTrustChainManager.cs | 49 +++-- .../SpidIdentityProvidersTests.cs | 15 +- .../TokenValidationRetrieverTests.cs | 7 +- .../{Models => }/CieConst.cs | 22 +- .../Configuration/ConfigurationManager.cs | 10 +- .../Configuration/CustomHttpClientHandler.cs | 47 ++++- .../OpenIdConnectOptionsProvider.cs | 16 +- .../Configuration/SpidCieOptions.cs | 2 +- .../Enums/ErrorCodes.cs | 11 + .../Enums/IdentityProviderTypes.cs | 7 + .../Enums/KeyUsageTypes.cs | 8 + .../Enums/SecurityLevels.cs | 8 + .../Events/PostStateCreatedContext.cs | 2 +- .../Events/SpidCieEvents.cs | 113 +++++++--- .../ApplicationBuilderExtensions.cs | 7 +- .../Extensions/ISpidCieOIDCBuilder.cs | 2 +- .../Extensions/SpidCieHandler.cs | 74 ++++--- .../Extensions/SpidCieOIDCBuilder.cs | 4 +- .../Helpers/ControlFlowHelpers.cs | 9 +- .../Helpers/JsonExtensions.cs | 8 +- .../Helpers/OptionsHelpers.cs | 63 +++++- .../Helpers/SerializationHelpers.cs | 14 +- .../Helpers/StringHelpers.cs | 12 +- .../Helpers/X509Helpers.cs | 63 +++--- .../Middlewares/CallbackRewriteMiddleware.cs | 9 +- .../FetchOpenIdFederationMiddleware.cs | 40 ++-- .../ListOpenIdFederationMiddleware.cs | 9 +- .../RPOpenIdFederationMiddleware.cs | 79 +++---- .../ResolveOpenIdFederationMiddleware.cs | 80 +++++-- ...ustMarkStatusOpenIdFederationMiddleware.cs | 33 ++- .../Models/Aggregator.cs | 37 ++-- .../Models/CieIdentityProvider.cs | 26 +-- .../Models/ClaimTypes.cs | 102 +++++---- .../Models/EntityConfiguration.cs | 17 -- .../Models/EntityStatement.cs | 17 -- .../Models/FederationEntityConfiguration.cs | 40 ---- .../Models/GenericError.cs | 23 +- .../Models/IdPEntityConfiguration.cs | 13 -- .../Models/IdentityProvider.cs | 41 ++-- .../Models/IdentityProviderType.cs | 7 - .../Models/JwtToken.cs | 43 ---- .../OIDCFederation/ConfigurationBaseInfo.cs | 35 ++++ .../EntityConfiguration.cs | 12 ++ .../ExtendedEntityConfiguration.cs | 12 ++ .../FederationEntityConfiguration.cs | 12 ++ .../OPEntityConfiguration.cs | 11 + .../RPEntityConfiguration.cs | 11 + .../SAEntityConfiguration.cs | 11 + .../TAEntityConfiguration.cs | 15 ++ .../EntityStatements/EntityStatement.cs | 16 ++ .../EntityStatements/SAJWKSValue.cs | 11 + .../SA_SpidCieOIDCConfiguration.cs | 11 + .../Models/OIDCFederation/JWKS.cs | 12 ++ .../Models/OIDCFederation/JsonWebKey.cs | 32 +++ .../Metadata/IntermediaryMetadataPolicy.cs | 12 ++ ...termediaryTokenSignedResaponseAlgorithm.cs | 16 ++ .../OPMetadata_SpidCieOIDCConfiguration.cs | 12 ++ .../RPMetadata_SpidCieOIDCConfiguration.cs | 14 ++ .../Metadata/RP_SpidCieOIDCConfiguration.cs | 58 ++++++ .../RP_SpidCieOIDCFederationEntity.cs | 27 +++ .../SAMetadata_SpidCieOIDCConfiguration.cs | 14 ++ .../SA_SpidCieOIDCFederationEntity.cs | 36 ++++ .../Metadata/SA_TrustMarkIssuer.cs | 10 + .../Metadata/TAFederationEntity.cs | 32 +++ .../TAMetadata_SpidCieOIDCConfiguration.cs | 11 + .../Resolves/OPResolveConfiguration.cs | 18 ++ .../Resolves/RPResolveConfiguration.cs | 18 ++ .../Models/OIDCFederation/TrustChain.cs | 26 +++ .../TrustMarks/TrustMarkDefinition.cs | 17 ++ .../TrustMarks/TrustMarkGovernmentIndex.cs | 11 + .../TrustMarks/TrustMarkPayload.cs | 33 +++ .../Models/RPEntityConfiguration.cs | 13 -- .../Models/RPOpenIdCoreCertificate.cs | 16 ++ .../Models/RelyingParty.cs | 46 ++-- .../Models/ResolveConfiguration.cs | 48 ----- .../Models/SAEntityConfiguration.cs | 13 -- .../Models/SecurityLevel.cs | 8 - .../Models/SpidCieConfiguration.cs | 5 +- .../Models/SpidCieOIDCConfiguration.cs | 152 -------------- .../Models/SpidIdentityProvider.cs | 9 +- .../Models/TAEntityConfiguration.cs | 44 ---- .../Models/TrustChain.cs | 14 -- .../Models/TrustMarkDefinition.cs | 20 -- .../Models/TrustMarkPayload.cs | 60 ------ .../Mvc/CieButtonTagHelper.cs | 83 ++++---- .../Mvc/Resources/cie-button.png | Bin 0 -> 9954 bytes .../Mvc/SpidButtonTagHelper.cs | 5 +- .../Mvc/SpidCSSTagHelper.cs | 2 +- .../Mvc/SpidJSTagHelper.cs | 2 +- .../Services/AggregatorsHandler.cs | 8 +- .../Services/CryptoService.cs | 74 ++++--- .../Defaults/DefaultAggregatorsRetriever.cs | 4 +- .../DefaultIdentityProviderSelector.cs | 6 +- .../DefaultIdentityProvidersRetriever.cs | 16 +- .../Services/Defaults/DefaultLogPersister.cs | 6 +- .../DefaultRelyingPartiesRetriever.cs | 4 +- .../Defaults/DefaultRelyingPartySelector.cs | 8 +- .../Services/IAggregatorsHandler.cs | 2 +- .../Services/IAggregatorsRetriever.cs | 2 +- .../Services/ICryptoService.cs | 19 +- .../Services/IIdentityProviderSelector.cs | 2 +- .../Services/IIdentityProvidersHandler.cs | 2 +- .../Services/IIdentityProvidersRetriever.cs | 2 +- .../Services/ILogPersister.cs | 2 +- .../Services/IMetadataPolicyHandler.cs | 2 +- .../Services/IRelyingPartiesHandler.cs | 4 +- .../Services/IRelyingPartiesRetriever.cs | 2 +- .../Services/IRelyingPartySelector.cs | 2 +- .../ITokenValidationParametersRetriever.cs | 4 +- .../Services/ITrustChainManager.cs | 11 +- .../Services/IdentityProvidersHandler.cs | 100 +++++---- .../Services/MetadataPolicyHandler.cs | 6 +- .../Services/RelyingPartiesHandler.cs | 4 +- .../TokenValidationParametersRetriever.cs | 39 +++- .../Services/TrustChainManager.cs | 197 ++++++++++++++---- .../Spid.Cie.OIDC.AspNetCore.csproj | 12 +- .../{Models => }/SpidCieConst.cs | 179 +++++----------- .../{Models => }/SpidConst.cs | 31 ++- 126 files changed, 1830 insertions(+), 1317 deletions(-) rename src/Spid.Cie.OIDC.AspNetCore/{Models => }/CieConst.cs (94%) create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Enums/ErrorCodes.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Enums/IdentityProviderTypes.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Enums/KeyUsageTypes.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Enums/SecurityLevels.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/EntityConfiguration.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/EntityStatement.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/FederationEntityConfiguration.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/IdPEntityConfiguration.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/IdentityProviderType.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/JwtToken.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/ConfigurationBaseInfo.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/EntityConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/ExtendedEntityConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/FederationEntityConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/OPEntityConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/RPEntityConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/SAEntityConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/TAEntityConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/EntityStatement.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/SAJWKSValue.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/SA_SpidCieOIDCConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/JWKS.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/JsonWebKey.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/IntermediaryMetadataPolicy.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/IntermediaryTokenSignedResaponseAlgorithm.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/OPMetadata_SpidCieOIDCConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RPMetadata_SpidCieOIDCConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RP_SpidCieOIDCConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RP_SpidCieOIDCFederationEntity.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SAMetadata_SpidCieOIDCConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SA_SpidCieOIDCFederationEntity.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SA_TrustMarkIssuer.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/TAFederationEntity.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/TAMetadata_SpidCieOIDCConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Resolves/OPResolveConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Resolves/RPResolveConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustChain.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkDefinition.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkGovernmentIndex.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkPayload.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/RPEntityConfiguration.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/RPOpenIdCoreCertificate.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/ResolveConfiguration.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/SAEntityConfiguration.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/SecurityLevel.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieOIDCConfiguration.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/TAEntityConfiguration.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/TrustChain.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/TrustMarkDefinition.cs delete mode 100644 src/Spid.Cie.OIDC.AspNetCore/Models/TrustMarkPayload.cs create mode 100644 src/Spid.Cie.OIDC.AspNetCore/Mvc/Resources/cie-button.png rename src/Spid.Cie.OIDC.AspNetCore/{Models => }/SpidCieConst.cs (80%) rename src/Spid.Cie.OIDC.AspNetCore/{Models => }/SpidConst.cs (97%) diff --git a/README.md b/README.md index 39f1942..dc2ca2c 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,8 @@ In particular, a 'SpidCie' section can be added to the configuration which has t "AuthorityHints": [ "http://trust-anchor.org:8000" ], "TrustMarks": [ { - "id": "https://www.spid.gov.it/openid-federation/agreement/sp-private", + "id": "https://preprod.oidc.registry.servizicie.interno.gov.it/intermediate/private", + "issuer": "https://preprod.oidc.registry.servizicie.interno.gov.it" "trust_mark": "eyJhbGc...." } ], @@ -200,8 +201,18 @@ In particular, a 'SpidCie' section can be added to the configuration which has t "MetadataPolicy": {}, "RelyingParties": [ { + "AuthorityHints": [ + "http://aspnetcore.aggregator.org:5000/" + ], "Id": "http://aspnetcore.aggregator.org:5000/TestRP/", "Name": "RP Test", + "OpenIdCoreCertificates": [ + { + "Algorithm": "RS256", //Or RSA-OAEP-256 + "Certificate": "base64", + "KeyUsage": "Signature" //Or Encryption + } + ], "OrganizationName": "RP Test", "OrganizationType": "Public", // or Private "HomepageUri": "http://aspnetcore.aggregator.org:5000/TestRP/", @@ -210,6 +221,9 @@ In particular, a 'SpidCie' section can be added to the configuration which has t "SecurityLevel": 2, "Contacts": [ "info@rptest.it" ], "LongSessionsEnabled": true, + "RedirectUris": [ + "http://aspnetcore.aggregator.org:5000/TestRP/signin-oidc-spidcie" + ] "RequestedClaims": [ "Name", "FamilyName", @@ -218,9 +232,11 @@ In particular, a 'SpidCie' section can be added to the configuration which has t "DateOfBirth", "PlaceOfBirth" ], + "SecurityLevel": "L1", //Or L2 or L3 "TrustMarks": [ { "Id": "https://registry.interno.gov.it/openid_relying_party/public/", + "Issuer": "http://aspnetcore.aggregator.org:5000", "TrustMark": "eyJhbGc...." } ] diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/CieButtonTests.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/CieButtonTests.cs index 6a4d706..0c352ad 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/CieButtonTests.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/CieButtonTests.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; +using Microsoft.AspNetCore.Mvc.Routing; +using Microsoft.AspNetCore.Razor.TagHelpers; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -25,7 +26,7 @@ public async Task CieButtonTagHelper() return Task.FromResult(tagHelperContent); }); - var tagHelper = new Mvc.CieButtonTagHelper(new Mocks.MockIdentityProvidersHandler(false)); + var tagHelper = new Mvc.CieButtonTagHelper(new UrlHelper(new Microsoft.AspNetCore.Mvc.ActionContext())/*new Mocks.MockIdentityProvidersHandler(false)*/); tagHelper.ChallengeUrl = "http://127.0.0.1:8002/"; await tagHelper.ProcessAsync(context, tagHelperOutput); } @@ -47,7 +48,7 @@ public async Task CieButtonTagHelperEmptyCollection() return Task.FromResult(tagHelperContent); }); - var tagHelper = new Mvc.CieButtonTagHelper(new Mocks.MockIdentityProvidersHandler(true)); + var tagHelper = new Mvc.CieButtonTagHelper(new UrlHelper(new Microsoft.AspNetCore.Mvc.ActionContext())/*new Mocks.MockIdentityProvidersHandler(true)*/); tagHelper.ChallengeUrl = "http://127.0.0.1:8002/"; await tagHelper.ProcessAsync(context, tagHelperOutput); } diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/CieIdentityProvidersTests.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/CieIdentityProvidersTests.cs index 2412705..469cc0a 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/CieIdentityProvidersTests.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/CieIdentityProvidersTests.cs @@ -15,9 +15,9 @@ public async Task TestFilterRequestedClaims() var idp = new CieIdentityProvider() { - EntityConfiguration = new IdPEntityConfiguration() + EntityConfiguration = new OPEntityConfiguration() { - Metadata = new IdPMetadata_SpidCieOIDCConfiguration() + Metadata = new OPMetadata_SpidCieOIDCConfiguration() { OpenIdProvider = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration() } diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/CustomHttpClientHandlerTests.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/CustomHttpClientHandlerTests.cs index 4322040..6ef8241 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/CustomHttpClientHandlerTests.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/CustomHttpClientHandlerTests.cs @@ -21,7 +21,7 @@ public async Task DecodeJoseResponseOK() { var handler = new CustomHttpClientHandler(new MockRelyingPartySelector(), new DefaultLogPersister(Mock.Of>()), - new MockCryptoService()); + new MockCryptoService(), new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var resourceName = "Spid.Cie.OIDC.AspNetCore.Tests.IntegrationTests.userInfoResponse.jose"; using var stream = typeof(MockBackchannel).Assembly.GetManifestResourceStream(resourceName); @@ -38,7 +38,7 @@ public async Task DecodeJoseResponseNoContent() { var handler = new CustomHttpClientHandler(new MockRelyingPartySelector(), new DefaultLogPersister(Mock.Of>()), - new MockCryptoService()); + new MockCryptoService(), new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var response = new HttpResponseMessage(); await Assert.ThrowsAnyAsync(() => handler.DecodeJoseResponse(response)); } @@ -48,7 +48,7 @@ public async Task DecodeJoseResponseNoRP() { var handler = new CustomHttpClientHandler(new MockRelyingPartySelector(true), new DefaultLogPersister(Mock.Of>()), - new MockCryptoService()); + new MockCryptoService(), new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var resourceName = "Spid.Cie.OIDC.AspNetCore.Tests.IntegrationTests.userInfoResponse.jose"; using var stream = typeof(MockBackchannel).Assembly.GetManifestResourceStream(resourceName); using var reader = new StreamReader(stream); @@ -64,7 +64,7 @@ public async Task DecodeJoseResponseNoEmptyContent() { var handler = new CustomHttpClientHandler(new MockRelyingPartySelector(), new DefaultLogPersister(Mock.Of>()), - new MockCryptoService()); + new MockCryptoService(), new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var response = new HttpResponseMessage(); response.Content = new StringContent(String.Empty, Encoding.UTF8, "application/jose"); await Assert.ThrowsAnyAsync(() => handler.DecodeJoseResponse(response)); @@ -75,7 +75,7 @@ public async Task DecodeJoseResponseNoKeys() { var handler = new CustomHttpClientHandler(new MockRelyingPartySelector(false, true), new DefaultLogPersister(Mock.Of>()), - new MockCryptoService()); + new MockCryptoService(), new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var resourceName = "Spid.Cie.OIDC.AspNetCore.Tests.IntegrationTests.userInfoResponse.jose"; using var stream = typeof(MockBackchannel).Assembly.GetManifestResourceStream(resourceName); using var reader = new StreamReader(stream); @@ -91,7 +91,7 @@ public async Task DecodeJoseResponseWrongContent() { var handler = new CustomHttpClientHandler(new MockRelyingPartySelector(), new DefaultLogPersister(Mock.Of>()), - new MockCryptoService()); + new MockCryptoService(), new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var resourceName = "Spid.Cie.OIDC.AspNetCore.Tests.IntegrationTests.jwtOP.json"; using var stream = typeof(MockBackchannel).Assembly.GetManifestResourceStream(resourceName); using var reader = new StreamReader(stream); @@ -107,7 +107,7 @@ public async Task DecodeJoseResponseWrongContentType() { var handler = new CustomHttpClientHandler(new MockRelyingPartySelector(), new DefaultLogPersister(Mock.Of>()), - new MockCryptoService()); + new MockCryptoService(), new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var resourceName = "Spid.Cie.OIDC.AspNetCore.Tests.IntegrationTests.jwtOP.json"; using var stream = typeof(MockBackchannel).Assembly.GetManifestResourceStream(resourceName); @@ -124,7 +124,7 @@ public async Task SendAsync() { var handler = new CustomHttpClientHandler(new MockRelyingPartySelector(true), new DefaultLogPersister(Mock.Of>()), - new MockCryptoService()); + new MockCryptoService(), new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var request = new HttpRequestMessage(); request.Content = new StringContent(string.Empty); request.RequestUri = new Uri("http://127.0.0.1"); diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/IntegrationTests/TestSettings.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/IntegrationTests/TestSettings.cs index b94a14e..412348a 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/IntegrationTests/TestSettings.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/IntegrationTests/TestSettings.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Spid.Cie.OIDC.AspNetCore.Configuration; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Helpers; using Spid.Cie.OIDC.AspNetCore.Models; using System; @@ -47,7 +48,7 @@ public TestSettings(Action configure) Contacts = new() { "info@rptest.it" }, AuthorityHints = new() { "http://127.0.0.1:8000/oidc/op/" }, RedirectUris = new() { "http://127.0.0.1:5000/signin-spidcie" }, - SecurityLevel = SecurityLevel.L2, + SecurityLevel = SecurityLevels.L2, LongSessionsEnabled = false, TrustMarks = new() { @@ -57,7 +58,21 @@ public TestSettings(Action configure) TrustMark = "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJ0cnVzdC1tYXJrK2p3dCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwLyIsImlhdCI6MTY0NzI3Njc2NiwiaWQiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9jZXJ0aWZpY2F0aW9uL3JwIiwibWFyayI6Imh0dHBzOi8vd3d3LmFnaWQuZ292Lml0L3RoZW1lcy9jdXN0b20vYWdpZC9sb2dvLnN2ZyIsInJlZiI6Imh0dHBzOi8vZG9jcy5pdGFsaWEuaXQvaXRhbGlhL3NwaWQvc3BpZC1yZWdvbGUtdGVjbmljaGUtb2lkYy9pdC9zdGFiaWxlL2luZGV4Lmh0bWwifQ.uTbO9gbx3cyNgs4LS-zij9kOC1alQuxFytsPNjwloGdnoGj_4PCJasMxmKVyUJXkXKQGeiG69oXBnf6sL9McYP6RYklhqFBR0hW4X5H5qc4vDYetDo8ajzocMZm050YzTrUObwy3OLOQRGLuWvg2uifRy8YCC0xD0OxoeBaEeURM_zkU3PFQ76RLP2W8b63J37behBevrO1lKJHhyfE4oJ6qFpR2Vk0367mMu7c0vhuTZYw8a5UkDbYR4L77vyzVlpE1duL5ibvREV4YMuMtWbI9fn1nlpgtmTp1Z089PN_PHVQHBrmHRG6jcwU6JCOdNXFBTsXtglU-xRng99Z6aQ" } }, - OpenIdCoreCertificates = new() { certificate }, + OpenIdCoreCertificates = + [ + new RPOpenIdCoreCertificate + { + Algorithm = "RS256", + Certificate = certificate, + KeyUsage = KeyUsageTypes.Signature + }, + new RPOpenIdCoreCertificate + { + Algorithm = "RSA-OAEP-256", + Certificate = certificate, + KeyUsage = KeyUsageTypes.Encryption + }, + ], OpenIdFederationCertificates = new() { certificate }, RequestedClaims = new() { diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockBackchannel.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockBackchannel.cs index c033c1b..f26312e 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockBackchannel.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockBackchannel.cs @@ -1,4 +1,5 @@ -using Spid.Cie.OIDC.AspNetCore.Configuration; +using Microsoft.AspNetCore.Http; +using Spid.Cie.OIDC.AspNetCore.Configuration; using Spid.Cie.OIDC.AspNetCore.Services; using System; using System.IO; @@ -13,10 +14,10 @@ internal partial class TestSettings { internal class MockBackchannel : CustomHttpClientHandler { - public MockBackchannel(IRelyingPartySelector rpSelector, - ILogPersister logPersister, - ICryptoService cryptoService) - : base(rpSelector, logPersister, cryptoService) + public MockBackchannel(IRelyingPartySelector rpSelector, ILogPersister logPersister, + ICryptoService cryptoService, IAggregatorsHandler aggregatorsHandler, + IHttpContextAccessor contextAccessor) + : base(rpSelector, logPersister, cryptoService, aggregatorsHandler, contextAccessor) { } diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockIdentityProvidersHandler.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockIdentityProvidersHandler.cs index 2e2c50e..15bc4e4 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockIdentityProvidersHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockIdentityProvidersHandler.cs @@ -37,9 +37,9 @@ public async Task> GetIdentityProviders() new SpidIdentityProvider(){ Uri = "http://127.0.0.1:8000/oidc/op/", SupportedAcrValues = new() { SpidCieConst.SpidL2, SpidCieConst.SpidL1, SpidCieConst.SpidL3 }, - EntityConfiguration = new IdPEntityConfiguration(){ + EntityConfiguration = new OPEntityConfiguration(){ Issuer = "http://127.0.0.1:8000/oidc/op/", - Metadata = new IdPMetadata_SpidCieOIDCConfiguration(){ + Metadata = new OPMetadata_SpidCieOIDCConfiguration(){ OpenIdProvider = conf } } @@ -47,9 +47,9 @@ public async Task> GetIdentityProviders() new CieIdentityProvider(){ Uri = "http://127.0.0.1:8002/oidc/op/", SupportedAcrValues = new() { SpidCieConst.SpidL2, SpidCieConst.SpidL1, SpidCieConst.SpidL3 }, - EntityConfiguration = new IdPEntityConfiguration(){ + EntityConfiguration = new OPEntityConfiguration(){ Issuer = "http://127.0.0.1:8002/oidc/op/", - Metadata = new IdPMetadata_SpidCieOIDCConfiguration(){ + Metadata = new OPMetadata_SpidCieOIDCConfiguration(){ OpenIdProvider = conf } } diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockOptionsMonitorSpidCieOptions.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockOptionsMonitorSpidCieOptions.cs index 8ad7fbe..a41f15e 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockOptionsMonitorSpidCieOptions.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockOptionsMonitorSpidCieOptions.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Options; using Spid.Cie.OIDC.AspNetCore.Configuration; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Models; using System; using System.Security.Cryptography.X509Certificates; @@ -34,7 +35,7 @@ public SpidCieOptions Get(string name) Contacts = new() { "info@rptest.it" }, AuthorityHints = new() { "http://127.0.0.1:8000/" }, RedirectUris = new() { "http://127.0.0.1:5000/signin-oidc" }, - SecurityLevel = SecurityLevel.L2, + SecurityLevel = SecurityLevels.L2, LongSessionsEnabled = false, TrustMarks = new() { @@ -44,7 +45,21 @@ public SpidCieOptions Get(string name) TrustMark = "eyJhbGciOiJSUzI1NiIsImtpZCI6IkZpZll4MDNibm9zRDhtNmdZUUlmTkhOUDljTV9TYW05VGM1bkxsb0lJcmMiLCJ0eXAiOiJ0cnVzdC1tYXJrK2p3dCJ9.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvIiwic3ViIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwLyIsImlhdCI6MTY0NzI3Njc2NiwiaWQiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9jZXJ0aWZpY2F0aW9uL3JwIiwibWFyayI6Imh0dHBzOi8vd3d3LmFnaWQuZ292Lml0L3RoZW1lcy9jdXN0b20vYWdpZC9sb2dvLnN2ZyIsInJlZiI6Imh0dHBzOi8vZG9jcy5pdGFsaWEuaXQvaXRhbGlhL3NwaWQvc3BpZC1yZWdvbGUtdGVjbmljaGUtb2lkYy9pdC9zdGFiaWxlL2luZGV4Lmh0bWwifQ.uTbO9gbx3cyNgs4LS-zij9kOC1alQuxFytsPNjwloGdnoGj_4PCJasMxmKVyUJXkXKQGeiG69oXBnf6sL9McYP6RYklhqFBR0hW4X5H5qc4vDYetDo8ajzocMZm050YzTrUObwy3OLOQRGLuWvg2uifRy8YCC0xD0OxoeBaEeURM_zkU3PFQ76RLP2W8b63J37behBevrO1lKJHhyfE4oJ6qFpR2Vk0367mMu7c0vhuTZYw8a5UkDbYR4L77vyzVlpE1duL5ibvREV4YMuMtWbI9fn1nlpgtmTp1Z089PN_PHVQHBrmHRG6jcwU6JCOdNXFBTsXtglU-xRng99Z6aQ" } }, - OpenIdCoreCertificates = _noKeys ? new() : new() { certificate }, + OpenIdCoreCertificates = _noKeys ? new() : new() + { + new RPOpenIdCoreCertificate + { + Algorithm = "RS256", + Certificate = certificate, + KeyUsage = KeyUsageTypes.Signature + }, + new RPOpenIdCoreCertificate + { + Algorithm = "RSA-OAEP-256", + Certificate = certificate, + KeyUsage = KeyUsageTypes.Encryption + } + }, OpenIdFederationCertificates = _noKeys ? new() : new() { certificate }, }); ; } diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockTrustChainManager.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockTrustChainManager.cs index 18f5a2e..47c7473 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockTrustChainManager.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/Mocks/MockTrustChainManager.cs @@ -4,42 +4,61 @@ namespace Spid.Cie.OIDC.AspNetCore.Tests.Mocks; -internal class MockTrustChainManager : ITrustChainManager +class MockTrustChainManager : ITrustChainManager { - public async Task BuildTrustChain(string url) + public async Task BuildTrustChain(string url) { await Task.CompletedTask; - var result = new IdPEntityConfiguration() + + var result = new OPEntityConfiguration() { Issuer = url, - Metadata = new IdPMetadata_SpidCieOIDCConfiguration() + Metadata = new OPMetadata_SpidCieOIDCConfiguration() { OpenIdProvider = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration() } }; + result.Metadata.OpenIdProvider.AdditionalData.Add("op_uri", "test"); result.Metadata.OpenIdProvider.AdditionalData.Add("op_name", "test"); result.Metadata.OpenIdProvider.AdditionalData.Add("logo_uri", "test"); result.Metadata.OpenIdProvider.AdditionalData.Add("organization_name", "test"); result.Metadata.OpenIdProvider.AcrValuesSupported.Add("test"); + return result; } - public TrustChain? GetResolvedTrustChain(string sub, string anchor) + public async Task BuildRPTrustChain(string url) { - return new TrustChain() + await Task.CompletedTask; + + var result = new RPEntityConfiguration() { - ExpiresOn = System.DateTimeOffset.MaxValue, - Chain = new System.Collections.Generic.List { "test1", "test2" }, - OpConf = new IdPEntityConfiguration() + Issuer = url, + Metadata = new RPMetadata_SpidCieOIDCConfiguration() { - Issuer = sub, - Metadata = new IdPMetadata_SpidCieOIDCConfiguration() + FederationEntity = new RP_SpidCieOIDCFederationEntity { - OpenIdProvider = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration() + + }, + OpenIdRelyingParty = new RP_SpidCieOIDCConfiguration + { + } - }, - TrustAnchorUsed = anchor + } }; + + return result; + } + + public TrustChain? GetResolvedTrustChain(string sub, string anchor) where T : EntityConfiguration + { + return typeof(T).Equals(typeof(OPEntityConfiguration)) ? new OPEntityConfiguration() + { + ExpiresOn = System.DateTimeOffset.MaxValue, + } as TrustChain : typeof(T).Equals(typeof(RPEntityConfiguration)) ? new RPEntityConfiguration + { + ExpiresOn = System.DateTimeOffset.MaxValue, + } as TrustChain : default; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/SpidIdentityProvidersTests.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/SpidIdentityProvidersTests.cs index cee9420..f98a61e 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/SpidIdentityProvidersTests.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/SpidIdentityProvidersTests.cs @@ -1,3 +1,4 @@ +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Models; using System.Linq; using System.Threading.Tasks; @@ -14,9 +15,9 @@ public async Task TestFilterRequestedClaims() var idp = new SpidIdentityProvider() { - EntityConfiguration = new IdPEntityConfiguration() + EntityConfiguration = new OPEntityConfiguration() { - Metadata = new IdPMetadata_SpidCieOIDCConfiguration() + Metadata = new OPMetadata_SpidCieOIDCConfiguration() { OpenIdProvider = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration() } @@ -66,9 +67,9 @@ public async Task TestGetAcrValue() var idp = new SpidIdentityProvider() { - EntityConfiguration = new IdPEntityConfiguration() + EntityConfiguration = new OPEntityConfiguration() { - Metadata = new IdPMetadata_SpidCieOIDCConfiguration() + Metadata = new OPMetadata_SpidCieOIDCConfiguration() { OpenIdProvider = new Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectConfiguration() } @@ -77,15 +78,15 @@ public async Task TestGetAcrValue() idp.SupportedAcrValues = new() { SpidCieConst.SpidL2, SpidCieConst.SpidL1, SpidCieConst.SpidL3 }; - var acr = idp.GetAcrValue(SecurityLevel.L2); + var acr = idp.GetAcrValue(SecurityLevels.L2); Assert.Contains(SpidCieConst.SpidL2, acr); - acr = idp.GetAcrValue(SecurityLevel.L1); + acr = idp.GetAcrValue(SecurityLevels.L1); Assert.Contains(SpidCieConst.SpidL1, acr); - acr = idp.GetAcrValue(SecurityLevel.L3); + acr = idp.GetAcrValue(SecurityLevels.L3); Assert.Contains(SpidCieConst.SpidL3, acr); } diff --git a/src/Spid.Cie.OIDC.AspNetCore.Tests/TokenValidationRetrieverTests.cs b/src/Spid.Cie.OIDC.AspNetCore.Tests/TokenValidationRetrieverTests.cs index 0b47c5e..c040eab 100644 --- a/src/Spid.Cie.OIDC.AspNetCore.Tests/TokenValidationRetrieverTests.cs +++ b/src/Spid.Cie.OIDC.AspNetCore.Tests/TokenValidationRetrieverTests.cs @@ -13,7 +13,8 @@ public async Task TestGetSelectedRelyingParty() { var _idpSelector = new MockIdentityProviderSelector(false); var _rpSelector = new MockRelyingPartySelector(); - var _retriever = new TokenValidationParametersRetriever(_idpSelector, _rpSelector); + var _retriever = new TokenValidationParametersRetriever(_idpSelector, + _rpSelector, new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); var rp = await _retriever.RetrieveTokenValidationParameter(); Assert.NotNull(rp); } @@ -23,7 +24,7 @@ public async Task TestGetSelectedRelyingPartyEmptyRP() { var _idpSelector = new MockIdentityProviderSelector(false); var _rpSelector = new MockRelyingPartySelector(true); - var _retriever = new TokenValidationParametersRetriever(_idpSelector, _rpSelector); + var _retriever = new TokenValidationParametersRetriever(_idpSelector, _rpSelector, new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); await Assert.ThrowsAnyAsync(async () => await _retriever.RetrieveTokenValidationParameter()); } @@ -32,7 +33,7 @@ public async Task TestGetSelectedRelyingPartyEmptyIdP() { var _idpSelector = new MockIdentityProviderSelector(true); var _rpSelector = new MockRelyingPartySelector(false); - var _retriever = new TokenValidationParametersRetriever(_idpSelector, _rpSelector); + var _retriever = new TokenValidationParametersRetriever(_idpSelector, _rpSelector, new MockAggregatorsHandler(), new MockHttpContextAccessor(false)); await Assert.ThrowsAnyAsync(async () => await _retriever.RetrieveTokenValidationParameter()); } } diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/CieConst.cs b/src/Spid.Cie.OIDC.AspNetCore/CieConst.cs similarity index 94% rename from src/Spid.Cie.OIDC.AspNetCore/Models/CieConst.cs rename to src/Spid.Cie.OIDC.AspNetCore/CieConst.cs index 06d161d..2b526c0 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/CieConst.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/CieConst.cs @@ -1,26 +1,24 @@ using System.Diagnostics.CodeAnalysis; -namespace Spid.Cie.OIDC.AspNetCore.Models; +namespace Spid.Cie.OIDC.AspNetCore; [ExcludeFromCodeCoverage] -internal class CieConst +class CieConst { - internal const string given_name = nameof(given_name); - internal const string family_name = nameof(family_name); internal const string email = nameof(email); - internal const string email_verified = nameof(email_verified); internal const string gender = nameof(gender); + internal const string address = nameof(address); internal const string birthdate = nameof(birthdate); + internal const string given_name = nameof(given_name); + internal const string family_name = nameof(family_name); internal const string phone_number = nameof(phone_number); - internal const string phone_number_verified = nameof(phone_number_verified); - internal const string address = nameof(address); internal const string place_of_birth = nameof(place_of_birth); + internal const string email_verified = nameof(email_verified); internal const string document_details = nameof(document_details); - + internal const string idANPR = $"{AttributesBaseURI}{nameof(idANPR)}"; internal const string AttributesBaseURI = "https://attributes.eid.gov.it/"; - - internal const string e_delivery_service = $"{AttributesBaseURI}{nameof(e_delivery_service)}"; + internal const string phone_number_verified = nameof(phone_number_verified); internal const string fiscal_number = $"{AttributesBaseURI}{nameof(fiscal_number)}"; - internal const string idANPR = $"{AttributesBaseURI}{nameof(idANPR)}"; + internal const string e_delivery_service = $"{AttributesBaseURI}{nameof(e_delivery_service)}"; internal const string physical_phone_number = $"{AttributesBaseURI}{nameof(physical_phone_number)}"; -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Configuration/ConfigurationManager.cs b/src/Spid.Cie.OIDC.AspNetCore/Configuration/ConfigurationManager.cs index 7afc068..d7939a0 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Configuration/ConfigurationManager.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Configuration/ConfigurationManager.cs @@ -9,9 +9,9 @@ namespace Spid.Cie.OIDC.AspNetCore.Configuration; -internal class ConfigurationManager : IConfigurationManager +class ConfigurationManager : IConfigurationManager { - private readonly IIdentityProviderSelector _idpSelector; + readonly IIdentityProviderSelector _idpSelector; public ConfigurationManager(IIdentityProviderSelector idpSelector) { @@ -21,11 +21,15 @@ public ConfigurationManager(IIdentityProviderSelector idpSelector) public async Task GetConfigurationAsync(CancellationToken cancel) { var idp = await _idpSelector.GetSelectedIdentityProvider(); + Throw.If(idp is null, ErrorLocalization.IdentityProviderNotFound); + var idpConf = idp!.EntityConfiguration.Metadata.OpenIdProvider; + Throw.If(idpConf is null, ErrorLocalization.IdentityProviderNotFound); + return idpConf!; } public void RequestRefresh() { } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Configuration/CustomHttpClientHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Configuration/CustomHttpClientHandler.cs index 9bab217..445b58d 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Configuration/CustomHttpClientHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Configuration/CustomHttpClientHandler.cs @@ -1,4 +1,7 @@ -using Spid.Cie.OIDC.AspNetCore.Helpers; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Http; +using Spid.Cie.OIDC.AspNetCore.Helpers; +using Spid.Cie.OIDC.AspNetCore.Models; using Spid.Cie.OIDC.AspNetCore.Services; using System; using System.Linq; @@ -9,19 +12,22 @@ namespace Spid.Cie.OIDC.AspNetCore.Configuration; -internal class CustomHttpClientHandler : HttpClientHandler +class CustomHttpClientHandler : HttpClientHandler { - private readonly IRelyingPartySelector _rpSelector; - private readonly ILogPersister _logPersister; - private readonly ICryptoService _cryptoService; + readonly ILogPersister _logPersister; + readonly ICryptoService _cryptoService; + readonly IAggregatorsHandler _aggHandler; + readonly IRelyingPartySelector _rpSelector; + readonly IHttpContextAccessor _httpContextAccessor; - public CustomHttpClientHandler(IRelyingPartySelector rpSelector, - ILogPersister logPersister, - ICryptoService cryptoService) + public CustomHttpClientHandler(IRelyingPartySelector rpSelector, ILogPersister logPersister, ICryptoService cryptoService, IAggregatorsHandler aggHandler, + IHttpContextAccessor httpContextAccessor) { + _aggHandler = aggHandler; _rpSelector = rpSelector; _logPersister = logPersister; _cryptoService = cryptoService; + _httpContextAccessor = httpContextAccessor; } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) @@ -41,22 +47,41 @@ public async Task DecodeJoseResponse(HttpResponseMessage re || ("application/jwt").Equals(response.Content.Headers.ContentType!.MediaType, StringComparison.OrdinalIgnoreCase)) { var token = await response.Content.ReadAsStringAsync(); - Throw.If(string.IsNullOrWhiteSpace(token), "No Body Content found in the Jose response"); + Throw.If(string.IsNullOrWhiteSpace(token), "No Body Content found in the Jose response"); Throw.If(token.Count(c => c == '.') != 2 && token.Count(c => c == '.') != 4, "Invalid Jose response according to https://www.rfc-editor.org/rfc/rfc7516#section-9"); var provider = await _rpSelector.GetSelectedRelyingParty(); + + if (provider == default) + { + var aggregators = await _aggHandler.GetAggregators(); + var uri = new Uri(UriHelper.GetEncodedUrl(_httpContextAccessor.HttpContext.Request)) + .GetLeftPart(UriPartial.Path) + .Replace(SpidCieConst.JsonEntityConfigurationPath, "") + .Replace(SpidCieConst.EntityConfigurationPath, "") + .Replace(SpidCieConst.CallbackPath, "") + .Replace(SpidCieConst.SignedOutCallbackPath, "") + .Replace(SpidCieConst.RemoteSignOutPath, "") + .EnsureTrailingSlash(); + + provider = aggregators.SelectMany(a => a.RelyingParties) + .OrderByDescending(r => r.Id.Length) + .FirstOrDefault(r => uri.StartsWith(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + } + Throw.If(provider is null, "No currently selected RelyingParty was found"); Throw.If(provider!.OpenIdCoreCertificates is null || provider!.OpenIdCoreCertificates.Count() == 0, "No OpenIdCore Certificates were found in the currently selected RelyingParty"); - var certificate = provider!.OpenIdCoreCertificates!.FirstOrDefault()!; - var decodedToken = _cryptoService.DecodeJose(token, certificate); + var openIdCoreCertificate = provider!.OpenIdCoreCertificates!.FirstOrDefault(occ => occ.KeyUsage == Enums.KeyUsageTypes.Encryption)!; + var decodedToken = _cryptoService.DecodeJose(token, openIdCoreCertificate.Certificate!); /* edit response to mantain detail of original request */ response.Content = new StringContent(decodedToken, Encoding.UTF8, "application/jwt"); } + return response; } } \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Configuration/OpenIdConnectOptionsProvider.cs b/src/Spid.Cie.OIDC.AspNetCore/Configuration/OpenIdConnectOptionsProvider.cs index 5e16258..c101bbf 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Configuration/OpenIdConnectOptionsProvider.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Configuration/OpenIdConnectOptionsProvider.cs @@ -12,14 +12,14 @@ namespace Spid.Cie.OIDC.AspNetCore.Configuration; -internal class OpenIdConnectOptionsProvider : IOptionsMonitor +class OpenIdConnectOptionsProvider : IOptionsMonitor { - private readonly ConcurrentDictionary> _cache; - private readonly IOptionsFactory _optionsFactory; - private readonly IIdentityProviderSelector _idpSelector; - private readonly IConfigurationManager _configurationManager; - private readonly CustomHttpClientHandler _httpClientHandler; - private readonly IHttpClientFactory _httpClientFactory; + readonly ConcurrentDictionary> _cache; + readonly IOptionsFactory _optionsFactory; + readonly IIdentityProviderSelector _idpSelector; + readonly IConfigurationManager _configurationManager; + readonly CustomHttpClientHandler _httpClientHandler; + readonly IHttpClientFactory _httpClientFactory; public OpenIdConnectOptionsProvider( IOptionsFactory optionsFactory, @@ -54,4 +54,4 @@ public OpenIdConnectOptions Get(string name) [ExcludeFromCodeCoverage] public IDisposable OnChange(Action listener) => throw new NotImplementedException(); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Configuration/SpidCieOptions.cs b/src/Spid.Cie.OIDC.AspNetCore/Configuration/SpidCieOptions.cs index e0d3315..5d20b40 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Configuration/SpidCieOptions.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Configuration/SpidCieOptions.cs @@ -32,4 +32,4 @@ public void LoadFromConfiguration(IConfiguration configuration) CieOPs.AddRange(conf.CieOPs); Aggregators.AddRange(conf.Aggregators); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Enums/ErrorCodes.cs b/src/Spid.Cie.OIDC.AspNetCore/Enums/ErrorCodes.cs new file mode 100644 index 0000000..6f13cc3 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Enums/ErrorCodes.cs @@ -0,0 +1,11 @@ +namespace Spid.Cie.OIDC.AspNetCore.Enums; + +enum ErrorCodes +{ + invalid_request, + invalid_client, + not_found, + server_error, + temporarily_unavailable, + unsupported_parameter +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Enums/IdentityProviderTypes.cs b/src/Spid.Cie.OIDC.AspNetCore/Enums/IdentityProviderTypes.cs new file mode 100644 index 0000000..1e55b5e --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Enums/IdentityProviderTypes.cs @@ -0,0 +1,7 @@ +namespace Spid.Cie.OIDC.AspNetCore.Enums; + +internal enum IdentityProviderTypes +{ + SPID, + CIE +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Enums/KeyUsageTypes.cs b/src/Spid.Cie.OIDC.AspNetCore/Enums/KeyUsageTypes.cs new file mode 100644 index 0000000..e5d1e16 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Enums/KeyUsageTypes.cs @@ -0,0 +1,8 @@ +namespace Spid.Cie.OIDC.AspNetCore.Enums +{ + public enum KeyUsageTypes + { + Signature, + Encryption + } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Enums/SecurityLevels.cs b/src/Spid.Cie.OIDC.AspNetCore/Enums/SecurityLevels.cs new file mode 100644 index 0000000..be7e529 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Enums/SecurityLevels.cs @@ -0,0 +1,8 @@ +namespace Spid.Cie.OIDC.AspNetCore.Enums; + +public enum SecurityLevels +{ + L1, + L2, + L3 +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Events/PostStateCreatedContext.cs b/src/Spid.Cie.OIDC.AspNetCore/Events/PostStateCreatedContext.cs index 94a09ba..3afc320 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Events/PostStateCreatedContext.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Events/PostStateCreatedContext.cs @@ -7,7 +7,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Events; [ExcludeFromCodeCoverage] -internal class PostStateCreatedContext : PropertiesContext +class PostStateCreatedContext : PropertiesContext { public PostStateCreatedContext( HttpContext context, diff --git a/src/Spid.Cie.OIDC.AspNetCore/Events/SpidCieEvents.cs b/src/Spid.Cie.OIDC.AspNetCore/Events/SpidCieEvents.cs index 08781e2..a0fe9a6 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Events/SpidCieEvents.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Events/SpidCieEvents.cs @@ -1,8 +1,10 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Spid.Cie.OIDC.AspNetCore.Configuration; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Helpers; using Spid.Cie.OIDC.AspNetCore.Models; using Spid.Cie.OIDC.AspNetCore.Resources; @@ -17,22 +19,20 @@ namespace Spid.Cie.OIDC.AspNetCore.Events; internal class SpidCieEvents : OpenIdConnectEvents { - private readonly IOptionsMonitor _options; - private readonly IRelyingPartySelector _rpSelector; - private readonly IIdentityProviderSelector _idpSelector; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ITokenValidationParametersRetriever _tokenValidationParametersRetriever; - private readonly ICryptoService _cryptoService; - - public SpidCieEvents(IOptionsMonitor options, - IIdentityProviderSelector idpSelector, - IRelyingPartySelector rpSelector, - ICryptoService cryptoService, - ITokenValidationParametersRetriever tokenValidationParametersRetriever, - IHttpContextAccessor httpContextAccessor) + readonly ICryptoService _cryptoService; + readonly IAggregatorsHandler _aggHandler; + readonly IRelyingPartySelector _rpSelector; + readonly IIdentityProviderSelector _idpSelector; + readonly IOptionsMonitor _options; + readonly IHttpContextAccessor _httpContextAccessor; + readonly ITokenValidationParametersRetriever _tokenValidationParametersRetriever; + + public SpidCieEvents(IOptionsMonitor options, IIdentityProviderSelector idpSelector, IRelyingPartySelector rpSelector, ICryptoService cryptoService, + IAggregatorsHandler aggHandler, ITokenValidationParametersRetriever tokenValidationParametersRetriever, IHttpContextAccessor httpContextAccessor) { _options = options; _rpSelector = rpSelector; + _aggHandler = aggHandler; _idpSelector = idpSelector; _httpContextAccessor = httpContextAccessor; _tokenValidationParametersRetriever = tokenValidationParametersRetriever; @@ -43,12 +43,33 @@ public SpidCieEvents(IOptionsMonitor options, public override async Task RedirectToIdentityProvider(RedirectContext context) { var identityProvider = await _idpSelector.GetSelectedIdentityProvider(); + Throw.If(identityProvider is null, ErrorLocalization.IdentityProviderNotFound); var relyingParty = await _rpSelector.GetSelectedRelyingParty(); + + if (relyingParty == default) + { + var aggregators = await _aggHandler.GetAggregators(); + var uri = new Uri(UriHelper.GetEncodedUrl(_httpContextAccessor.HttpContext.Request)) + .GetLeftPart(UriPartial.Path) + .Replace(SpidCieConst.JsonEntityConfigurationPath, "") + .Replace(SpidCieConst.EntityConfigurationPath, "") + .Replace(SpidCieConst.CallbackPath, "") + .Replace(SpidCieConst.SignedOutCallbackPath, "") + .Replace(SpidCieConst.RemoteSignOutPath, "") + .EnsureTrailingSlash(); + + relyingParty = aggregators.SelectMany(a => a.RelyingParties) + .OrderByDescending(r => r.Id.Length) + .FirstOrDefault(r => uri.StartsWith(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + } + Throw.If(relyingParty is null, ErrorLocalization.RelyingPartyNotFound); - context.ProtocolMessage.IssuerAddress = identityProvider!.EntityConfiguration.Metadata.OpenIdProvider!.AuthorizationEndpoint; + context.ProtocolMessage.AuthorizationEndpoint = identityProvider?.EntityConfiguration?.Metadata?.OpenIdProvider?.AuthorizationEndpoint!; + context.ProtocolMessage.IssuerAddress = identityProvider?.EntityConfiguration?.Metadata?.OpenIdProvider?.AuthorizationEndpoint!; + context.ProtocolMessage.TokenEndpoint = identityProvider?.EntityConfiguration?.Metadata?.OpenIdProvider?.TokenEndpoint!; context.ProtocolMessage.ClientId = relyingParty!.Id; context.ProtocolMessage.RedirectUri = $"{relyingParty!.Id.RemoveTrailingSlash()}{SpidCieConst.CallbackPath}"; context.ProtocolMessage.AcrValues = identityProvider.GetAcrValue(relyingParty.SecurityLevel); @@ -65,28 +86,44 @@ public override async Task RedirectToIdentityProvider(RedirectContext context) public virtual async Task PostStateCreated(PostStateCreatedContext context) { var identityProvider = await _idpSelector.GetSelectedIdentityProvider(); + Throw.If(identityProvider is null, ErrorLocalization.IdentityProviderNotFound); var relyingParty = await _rpSelector.GetSelectedRelyingParty(); - Throw.If(relyingParty is null, ErrorLocalization.RelyingPartyNotFound); + if (relyingParty == default) + { + var aggregators = await _aggHandler.GetAggregators(); + var uri = new Uri(UriHelper.GetEncodedUrl(_httpContextAccessor.HttpContext.Request)) + .GetLeftPart(UriPartial.Path) + .Replace(SpidCieConst.JsonEntityConfigurationPath, "") + .Replace(SpidCieConst.EntityConfigurationPath, "") + .Replace(SpidCieConst.CallbackPath, "") + .Replace(SpidCieConst.SignedOutCallbackPath, "") + .Replace(SpidCieConst.RemoteSignOutPath, "") + .EnsureTrailingSlash(); + + relyingParty = aggregators.SelectMany(a => a.RelyingParties) + .OrderByDescending(r => r.Id.Length) + .FirstOrDefault(r => uri.StartsWith(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + } + + Throw.If(relyingParty is null, ErrorLocalization.RelyingPartyNotFound); Throw.If(relyingParty!.OpenIdCoreCertificates is null || relyingParty!.OpenIdCoreCertificates.Count() == 0, "No OpenIdCore certificates were found in the currently selected RelyingParty"); - var certificate = relyingParty!.OpenIdCoreCertificates!.FirstOrDefault()!; + + var certificate = relyingParty!.OpenIdCoreCertificates!.FirstOrDefault(occ => occ.KeyUsage == KeyUsageTypes.Signature)!; context.ProtocolMessage.SetParameter(SpidCieConst.RequestParameter, - GenerateJWTRequest(identityProvider!, relyingParty!, context.ProtocolMessage, certificate)); + GenerateJWTRequest(identityProvider!, relyingParty!, context.ProtocolMessage, certificate.Certificate!)); } - private string GenerateJWTRequest(IdentityProvider idp, - RelyingParty relyingParty, - OpenIdConnectMessage protocolMessage, - X509Certificate2 certificate) + string GenerateJWTRequest(IdentityProvider idp, RelyingParty relyingParty, OpenIdConnectMessage protocolMessage, X509Certificate2 certificate) { return _cryptoService.CreateJWT(certificate!, new Dictionary() { { SpidCieConst.Iss, protocolMessage.ClientId }, - { SpidCieConst.Sub, protocolMessage.ClientId }, + //{ SpidCieConst.Sub, protocolMessage.ClientId }, { SpidCieConst.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, { SpidCieConst.Exp, DateTimeOffset.UtcNow.AddMinutes(SpidCieConst.EntityConfigurationExpirationInMinutes).ToUnixTimeSeconds() }, { SpidCieConst.Aud, new string[] { idp.EntityConfiguration.Issuer, idp.EntityConfiguration.Metadata.OpenIdProvider!.AuthorizationEndpoint } }, @@ -110,6 +147,7 @@ public override async Task MessageReceived(MessageReceivedContext context) if (!string.IsNullOrWhiteSpace(context.ProtocolMessage!.Error)) { var ex = new Exception(context.ProtocolMessage.ErrorDescription ?? context.ProtocolMessage.Error); + ex.Data.Add(nameof(context.ProtocolMessage.Error), context.ProtocolMessage.Error); ex.Data.Add(nameof(context.ProtocolMessage.ErrorDescription), context.ProtocolMessage.ErrorDescription); ex.Data.Add(nameof(context.ProtocolMessage.ErrorUri), context.ProtocolMessage.ErrorUri); @@ -118,12 +156,12 @@ public override async Task MessageReceived(MessageReceivedContext context) else { context.Properties!.Items.TryGetValue(SpidCieConst.IdPSelectorKey, out var provider); + if (!string.IsNullOrWhiteSpace(provider)) - { _httpContextAccessor.HttpContext!.Items.Add(SpidCieConst.IdPSelectorKey, provider); - } context.Properties!.Items.TryGetValue(SpidCieConst.RPSelectorKey, out var clientId); + if (!string.IsNullOrWhiteSpace(clientId)) { context.Options.ClientId = clientId; @@ -132,28 +170,47 @@ public override async Task MessageReceived(MessageReceivedContext context) context.Options.TokenValidationParameters = await _tokenValidationParametersRetriever.RetrieveTokenValidationParameter(); } + await base.MessageReceived(context); } public override async Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context) { var identityProvider = await _idpSelector.GetSelectedIdentityProvider(); + Throw.If(identityProvider is null, ErrorLocalization.IdentityProviderNotFound); var relyingParty = await _rpSelector.GetSelectedRelyingParty(); + + if (relyingParty == default) + { + var aggregators = await _aggHandler.GetAggregators(); + var uri = new Uri(UriHelper.GetEncodedUrl(_httpContextAccessor.HttpContext.Request)) + .GetLeftPart(UriPartial.Path) + .Replace(SpidCieConst.JsonEntityConfigurationPath, "") + .Replace(SpidCieConst.EntityConfigurationPath, "") + .Replace(SpidCieConst.CallbackPath, "") + .Replace(SpidCieConst.SignedOutCallbackPath, "") + .Replace(SpidCieConst.RemoteSignOutPath, "") + .EnsureTrailingSlash(); + + relyingParty = aggregators.SelectMany(a => a.RelyingParties) + .OrderByDescending(r => r.Id.Length) + .FirstOrDefault(r => uri.StartsWith(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + } + Throw.If(relyingParty is null, ErrorLocalization.RelyingPartyNotFound); Throw.If(relyingParty!.OpenIdCoreCertificates is null || relyingParty!.OpenIdCoreCertificates.Count() == 0, "No OpenIdCore Keys were found in the currently selected RelyingParty"); - var certificate = relyingParty!.OpenIdCoreCertificates!.FirstOrDefault()!; + var certificate = relyingParty!.OpenIdCoreCertificates!.FirstOrDefault(occ => occ.KeyUsage == KeyUsageTypes.Signature)!; Throw.If(context.TokenEndpointRequest is null, $"No Token Endpoint Request found in the current context"); context.TokenEndpointRequest!.ClientAssertionType = SpidCieConst.ClientAssertionTypeValue; context.TokenEndpointRequest!.ClientAssertion = _cryptoService.CreateClientAssertion(identityProvider!.EntityConfiguration.Metadata.OpenIdProvider!.TokenEndpoint!, - relyingParty.Id!, - certificate); + relyingParty.Id!, certificate.Certificate!); await base.AuthorizationCodeReceived(context); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Extensions/ApplicationBuilderExtensions.cs b/src/Spid.Cie.OIDC.AspNetCore/Extensions/ApplicationBuilderExtensions.cs index 00217fc..689ccdb 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Extensions/ApplicationBuilderExtensions.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; @@ -46,9 +47,13 @@ public static ISpidCieOIDCBuilder AddSpidCieOIDC(this AuthenticationBuilder buil options.GetClaimsFromUserInfoEndpoint = true; options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet; + options.RequireHttpsMetadata = true; options.Scope.Clear(); options.Scope.Add(SpidCieConst.OpenIdScope); + options.SignInScheme = IdentityConstants.ExternalScheme; + + options.Events.OnRedirectToIdentityProvider = context => context.HttpContext.RequestServices.GetRequiredService().RedirectToIdentityProvider(context); options.Events.OnMessageReceived = context => context.HttpContext.RequestServices.GetRequiredService().MessageReceived(context); options.Events.OnAuthorizationCodeReceived = context => context.HttpContext.RequestServices.GetRequiredService().AuthorizationCodeReceived(context); @@ -125,4 +130,4 @@ public static IApplicationBuilder UseSpidCieOIDC(this IApplicationBuilder builde .UseMiddleware() .UseMiddleware(); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Extensions/ISpidCieOIDCBuilder.cs b/src/Spid.Cie.OIDC.AspNetCore/Extensions/ISpidCieOIDCBuilder.cs index 0fe2ba7..945f0ab 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Extensions/ISpidCieOIDCBuilder.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Extensions/ISpidCieOIDCBuilder.cs @@ -11,4 +11,4 @@ public interface ISpidCieOIDCBuilder /// The services. /// IServiceCollection Services { get; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Extensions/SpidCieHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Extensions/SpidCieHandler.cs index 39ee231..d202135 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Extensions/SpidCieHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Extensions/SpidCieHandler.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -22,37 +23,32 @@ namespace Spid.Cie.OIDC.AspNetCore.Extensions; -internal class SpidCieHandler : OpenIdConnectHandler +class SpidCieHandler : OpenIdConnectHandler { - private OpenIdConnectConfiguration? _configuration; - private const string NonceProperty = "N"; - private readonly SpidCieEvents _events; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly IIdentityProvidersHandler _idpHandler; - private readonly IRelyingPartiesHandler _rpHandler; - private readonly IRelyingPartySelector _relyingPartySelector; - private readonly ICryptoService _cryptoService; - - public SpidCieHandler(IOptionsMonitor options, - IHttpContextAccessor httpContextAccessor, - ILoggerFactory logger, - HtmlEncoder htmlEncoder, - UrlEncoder encoder, - ISystemClock clock, - IIdentityProvidersHandler idpHandler, - IRelyingPartiesHandler rpHandler, - IRelyingPartySelector relyingPartySelector, - ICryptoService cryptoService, - SpidCieEvents events) + readonly SpidCieEvents _events; + const string NonceProperty = "N"; + readonly ICryptoService _cryptoService; + readonly IAggregatorsHandler _aggHandler; + readonly IRelyingPartiesHandler _rpHandler; + OpenIdConnectConfiguration? _configuration; + readonly IIdentityProvidersHandler _idpHandler; + readonly IHttpContextAccessor _httpContextAccessor; + readonly IRelyingPartySelector _relyingPartySelector; + + + public SpidCieHandler(IOptionsMonitor options, IHttpContextAccessor httpContextAccessor, ILoggerFactory logger, HtmlEncoder htmlEncoder, + UrlEncoder encoder, ISystemClock clock, IIdentityProvidersHandler idpHandler, IRelyingPartiesHandler rpHandler, + IRelyingPartySelector relyingPartySelector, ICryptoService cryptoService, IAggregatorsHandler aggHandler, SpidCieEvents events) : base(options, logger, htmlEncoder, encoder, clock) { + Events = events; _events = events; - _httpContextAccessor = httpContextAccessor; - _idpHandler = idpHandler; _rpHandler = rpHandler; - _relyingPartySelector = relyingPartySelector; + _aggHandler = aggHandler; + _idpHandler = idpHandler; _cryptoService = cryptoService; - Events = events; + _httpContextAccessor = httpContextAccessor; + _relyingPartySelector = relyingPartySelector; } protected new SpidCieEvents Events @@ -86,6 +82,7 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop }; #region Pkce + var bytes = new byte[32]; RandomNumberGenerator.Fill(bytes); var codeVerifier = Microsoft.AspNetCore.Authentication.Base64UrlTextEncoder.Encode(bytes); @@ -98,10 +95,28 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop message.Parameters.Add(OAuthConstants.CodeChallengeKey, codeChallenge); message.Parameters.Add(OAuthConstants.CodeChallengeMethodKey, OAuthConstants.CodeChallengeMethodS256); - #endregion + #endregion var relyingParty = await _relyingPartySelector.GetSelectedRelyingParty(); + + if (relyingParty == default) + { + var aggregators = await _aggHandler.GetAggregators(); + var uri = new Uri(UriHelper.GetEncodedUrl(_httpContextAccessor.HttpContext.Request)) + .GetLeftPart(UriPartial.Path) + .Replace(SpidCieConst.JsonEntityConfigurationPath, "") + .Replace(SpidCieConst.EntityConfigurationPath, "") + .Replace(SpidCieConst.CallbackPath, "") + .Replace(SpidCieConst.SignedOutCallbackPath, "") + .Replace(SpidCieConst.RemoteSignOutPath, "") + .EnsureTrailingSlash(); + + relyingParty = aggregators.SelectMany(a => a.RelyingParties) + .OrderByDescending(r => r.Id.Length) + .FirstOrDefault(r => uri.StartsWith(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + } + Throw.If(relyingParty is null, ErrorLocalization.RelyingPartyNotFound); Options.NonceCookie.Path = $"{new Uri(relyingParty!.Id).AbsolutePath.RemoveTrailingSlash()}/{SpidCieConst.CallbackPath.RemoveLeadingSlash()}"; @@ -112,7 +127,6 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop Options.CorrelationCookie.Path = $"{new Uri(relyingParty!.Id).AbsolutePath.RemoveTrailingSlash()}/{SpidCieConst.CallbackPath.RemoveLeadingSlash()}"; GenerateCorrelationId(properties); - var redirectContext = new RedirectContext(Context, Scheme, Options, properties) { ProtocolMessage = message @@ -229,7 +243,7 @@ private async Task RevokeToken(string accessToken) Throw.If(rp!.OpenIdCoreCertificates is null || rp!.OpenIdCoreCertificates.Count() == 0, "No OpenIdCore certificates were found in the currently selected RelyingParty"); - var certificate = rp!.OpenIdCoreCertificates!.FirstOrDefault()!; + var certificate = rp!.OpenIdCoreCertificates!.FirstOrDefault(occ => occ.KeyUsage == Enums.KeyUsageTypes.Signature)!; var revocationEndpoint = idp!.EntityConfiguration.Metadata.OpenIdProvider!.AdditionalData[SpidCieConst.RevocationEndpoint] as string; Throw.If(string.IsNullOrWhiteSpace(revocationEndpoint), @@ -243,7 +257,7 @@ private async Task RevokeToken(string accessToken) ClientAssertion = new ClientAssertion() { Type = SpidCieConst.ClientAssertionTypeValue, - Value = _cryptoService.CreateClientAssertion(idp!.EntityConfiguration.Metadata.OpenIdProvider!.AdditionalData["revocation_endpoint"] as string, clientId!, certificate) + Value = _cryptoService.CreateClientAssertion(idp!.EntityConfiguration.Metadata.OpenIdProvider!.AdditionalData["revocation_endpoint"] as string, clientId!, certificate.Certificate!) }, Token = accessToken }; @@ -255,4 +269,4 @@ private async Task RevokeToken(string accessToken) Logger.LogWarning($"AccessToken Revocation returned http status {responseMessage.HttpStatusCode} - Error: {responseMessage.Error}"); } } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Extensions/SpidCieOIDCBuilder.cs b/src/Spid.Cie.OIDC.AspNetCore/Extensions/SpidCieOIDCBuilder.cs index 746a4a9..04ae442 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Extensions/SpidCieOIDCBuilder.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Extensions/SpidCieOIDCBuilder.cs @@ -3,7 +3,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Extensions; -internal class SpidCieOIDCBuilder : ISpidCieOIDCBuilder +class SpidCieOIDCBuilder : ISpidCieOIDCBuilder { public SpidCieOIDCBuilder(IServiceCollection services) { @@ -11,4 +11,4 @@ public SpidCieOIDCBuilder(IServiceCollection services) } public IServiceCollection Services { get; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Helpers/ControlFlowHelpers.cs b/src/Spid.Cie.OIDC.AspNetCore/Helpers/ControlFlowHelpers.cs index fc99320..a88966d 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Helpers/ControlFlowHelpers.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Helpers/ControlFlowHelpers.cs @@ -2,16 +2,11 @@ namespace Spid.Cie.OIDC.AspNetCore.Helpers; -internal static class Throw - where T : Exception +static class Throw where T : Exception { public static void If(bool condition, string message) { if (condition) throw (Exception)Activator.CreateInstance(typeof(T), message)!; } -} - - - - +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Helpers/JsonExtensions.cs b/src/Spid.Cie.OIDC.AspNetCore/Helpers/JsonExtensions.cs index 7baa725..332fac9 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Helpers/JsonExtensions.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Helpers/JsonExtensions.cs @@ -1,14 +1,14 @@ using Newtonsoft.Json.Linq; -using System; namespace Spid.Cie.OIDC.AspNetCore.Helpers; -internal static class JsonExtensions + +static class JsonExtensions { public static bool IsNullOrWhiteSpace(this JToken token) { return (token == null) || (token.Type == JTokenType.Null) || (!token.HasValues) - || (token.ToString() == String.Empty); + || (token.ToString() == string.Empty); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Helpers/OptionsHelpers.cs b/src/Spid.Cie.OIDC.AspNetCore/Helpers/OptionsHelpers.cs index 5b5955f..b6020f6 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Helpers/OptionsHelpers.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Helpers/OptionsHelpers.cs @@ -1,5 +1,7 @@ using Microsoft.Extensions.Configuration; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Models; +using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography.X509Certificates; @@ -7,7 +9,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Helpers; -internal static class OptionsHelpers +static class OptionsHelpers { internal static SpidCieConfiguration CreateFromConfiguration(IConfiguration configuration) { @@ -37,7 +39,7 @@ internal static SpidCieConfiguration CreateFromConfiguration(IConfiguration conf HomepageUri = relyingPartySection.GetValue("HomepageUri") ?? string.Empty, LogoUri = relyingPartySection.GetValue("LogoUri") ?? string.Empty, PolicyUri = relyingPartySection.GetValue("PolicyUri") ?? string.Empty, - SecurityLevel = securityLevel == 1 ? SecurityLevel.L1 : securityLevel == 3 ? SecurityLevel.L3 : SecurityLevel.L2, + SecurityLevel = securityLevel == 1 ? SecurityLevels.L1 : securityLevel == 3 ? SecurityLevels.L3 : SecurityLevels.L2, Contacts = relyingPartySection.GetSection("Contacts").Get?>() ?? new List(), RedirectUris = new List() { $"{(relyingPartySection.GetValue("Id") ?? string.Empty).RemoveTrailingSlash()}{SpidCieConst.CallbackPath}" }, LongSessionsEnabled = relyingPartySection.GetValue("LongSessionsEnabled") ?? false, @@ -52,9 +54,7 @@ internal static SpidCieConfiguration CreateFromConfiguration(IConfiguration conf OpenIdFederationCertificates = relyingPartySection.GetSection("OpenIdFederationCertificates").GetChildren() .Select(GetCertificate) .ToList(), - OpenIdCoreCertificates = relyingPartySection.GetSection("OpenIdCoreCertificates").GetChildren() - .Select(GetCertificate) - .ToList(), + OpenIdCoreCertificates = relyingPartySection.GetSection("OpenIdCoreCertificates").GetChildren().Select(GetOpenIdCoreCertificate).ToList(), RequestedClaims = (relyingPartySection.GetSection("RequestedClaims").Get?>() ?? new List()) .Select(c => ClaimTypes.FromName(c)) .ToList() @@ -87,6 +87,9 @@ internal static SpidCieConfiguration CreateFromConfiguration(IConfiguration conf Issuer = trustMarksSection.GetValue("Issuer"), TrustMark = trustMarksSection.GetValue("TrustMark") }).ToList(), + //OpenIdCoreCertificates = aggregatorSection.GetSection("OpenIdCoreCertificates").GetChildren() + // .Select(GetCertificate) + // .ToList(), OpenIdFederationCertificates = aggregatorSection.GetSection("OpenIdFederationCertificates").GetChildren() .Select(GetCertificate) .ToList(), @@ -102,7 +105,7 @@ internal static SpidCieConfiguration CreateFromConfiguration(IConfiguration conf var relyingParty = new RelyingParty { Id = relyingPartySection.GetValue("Id") ?? string.Empty, - SecurityLevel = securityLevel == 1 ? SecurityLevel.L1 : securityLevel == 3 ? SecurityLevel.L3 : SecurityLevel.L2, + SecurityLevel = securityLevel == 1 ? SecurityLevels.L1 : securityLevel == 3 ? SecurityLevels.L3 : SecurityLevels.L2, Contacts = relyingPartySection.GetSection("Contacts").Get?>() ?? new List(), RedirectUris = new List() { $"{(relyingPartySection.GetValue("Id") ?? string.Empty).RemoveTrailingSlash()}{SpidCieConst.CallbackPath}" }, OrganizationType = relyingPartySection.GetValue("OrganizationType") ?? string.Empty, @@ -121,7 +124,8 @@ internal static SpidCieConfiguration CreateFromConfiguration(IConfiguration conf TrustMark = trustMarksSection.GetValue("TrustMark") }).ToList(), OpenIdFederationCertificates = aggregator.OpenIdFederationCertificates, - OpenIdCoreCertificates = aggregator.OpenIdFederationCertificates, + //OpenIdCoreCertificates = aggregator.OpenIdCoreCertificates, + OpenIdCoreCertificates = relyingPartySection.GetSection("OpenIdCoreCertificates").GetChildren().Select(GetOpenIdCoreCertificate).ToList(), RequestedClaims = (relyingPartySection.GetSection("RequestedClaims").Get?>() ?? new List()) .Select(c => ClaimTypes.FromName(c)) .ToList() @@ -136,7 +140,7 @@ internal static SpidCieConfiguration CreateFromConfiguration(IConfiguration conf return options; } - private static X509Certificate2 GetCertificate(IConfigurationSection openIdFederationCertificatesSection) + static X509Certificate2 GetCertificate(IConfigurationSection openIdFederationCertificatesSection) { var certificateSource = openIdFederationCertificatesSection.GetValue("Source"); if (certificateSource.Equals("File", System.StringComparison.OrdinalIgnoreCase)) @@ -156,4 +160,45 @@ private static X509Certificate2 GetCertificate(IConfigurationSection openIdFeder throw new System.Exception($"Invalid Certificate Source {certificateSource}"); } -} + + static RPOpenIdCoreCertificate GetOpenIdCoreCertificate(IConfigurationSection openIdFederationCertificatesSection) + { + var certificateSource = openIdFederationCertificatesSection.GetValue("Source"); + + if (string.IsNullOrWhiteSpace(certificateSource)) + throw new Exception("Certificate Source is required!"); + + if (certificateSource.Equals("File", StringComparison.OrdinalIgnoreCase)) + { + var storeConfiguration = openIdFederationCertificatesSection.GetSection("File"); + var path = storeConfiguration.GetValue("Path"); + var password = storeConfiguration.GetValue("Password"); + var algorithm = storeConfiguration.GetValue("Algorithm"); + var keyUsage = storeConfiguration.GetValue("KeyUsage"); + + return new RPOpenIdCoreCertificate + { + Algorithm = algorithm!, + Certificate = X509Helpers.GetCertificateFromFile(path!, password!), + KeyUsage = keyUsage + }; + } + else if (certificateSource.Equals("Raw", StringComparison.OrdinalIgnoreCase)) + { + var storeConfiguration = openIdFederationCertificatesSection.GetSection("Raw"); + var certificate = storeConfiguration.GetValue("Certificate"); + var key = storeConfiguration.GetValue("Password"); + var algorithm = storeConfiguration.GetValue("Algorithm"); + var keyUsage = storeConfiguration.GetValue("KeyUsage"); + + return new RPOpenIdCoreCertificate + { + Algorithm = algorithm!, + Certificate = X509Helpers.GetCertificateFromStrings(certificate!, key!), + KeyUsage = keyUsage + }; + } + + throw new Exception($"Invalid Certificate Source {certificateSource}"); + } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Helpers/SerializationHelpers.cs b/src/Spid.Cie.OIDC.AspNetCore/Helpers/SerializationHelpers.cs index 1d22554..0d8becd 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Helpers/SerializationHelpers.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Helpers/SerializationHelpers.cs @@ -9,20 +9,22 @@ namespace Spid.Cie.OIDC.AspNetCore.Helpers; -internal static class SerializationHelpers +static class SerializationHelpers { public static string ToJsonString(this JsonDocument jdoc) { using var stream = new MemoryStream(); - Utf8JsonWriter writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }); + var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }); + jdoc.WriteTo(writer); writer.Flush(); + return Encoding.UTF8.GetString(stream.ToArray()); } public static JsonNode? Serialize(IConfigurationSection config) { - JsonObject obj = new(); + var obj = new JsonObject(); if (config is not null) { @@ -45,7 +47,7 @@ public static string ToJsonString(this JsonDocument jdoc) } } - if (obj.Count() == 0 && config is IConfigurationSection section) + if (obj.Count == 0 && config is IConfigurationSection section) { if (bool.TryParse(section.Value, out bool boolean)) { @@ -67,9 +69,9 @@ public static string ToJsonString(this JsonDocument jdoc) } } -internal class STJSerializer : IJsonSerializer +class STJSerializer : IJsonSerializer { - public object Deserialize(Type type, string json) + public object? Deserialize(Type type, string json) { return JsonSerializer.Deserialize(json, type); } diff --git a/src/Spid.Cie.OIDC.AspNetCore/Helpers/StringHelpers.cs b/src/Spid.Cie.OIDC.AspNetCore/Helpers/StringHelpers.cs index da9cb83..a78bd5a 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Helpers/StringHelpers.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Helpers/StringHelpers.cs @@ -1,13 +1,11 @@ namespace Spid.Cie.OIDC.AspNetCore.Helpers; -internal static class StringHelpers +static class StringHelpers { public static string EnsureTrailingSlash(this string url) { if (!url.EndsWith("/")) - { return url + "/"; - } return url; } @@ -15,9 +13,7 @@ public static string EnsureTrailingSlash(this string url) public static string RemoveLeadingSlash(this string url) { if (url.StartsWith("/")) - { url = url.Substring(1); - } return url; } @@ -25,12 +21,8 @@ public static string RemoveLeadingSlash(this string url) public static string RemoveTrailingSlash(this string url) { if (url.EndsWith("/")) - { url = url.Substring(0, url.Length - 1); - } return url; } - - -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Helpers/X509Helpers.cs b/src/Spid.Cie.OIDC.AspNetCore/Helpers/X509Helpers.cs index bd71d60..ade92dc 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Helpers/X509Helpers.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Helpers/X509Helpers.cs @@ -2,39 +2,40 @@ using System; using System.Security.Cryptography.X509Certificates; -namespace Spid.Cie.OIDC.AspNetCore.Helpers +namespace Spid.Cie.OIDC.AspNetCore.Helpers; + +static class X509Helpers { - internal static class X509Helpers + /// + /// Get certificate from file path and password + /// + /// + /// + /// + public static X509Certificate2 GetCertificateFromFile(string certFilePath, string certPassword) { - /// - /// Get certificate from file path and password - /// - /// - /// - /// - public static X509Certificate2 GetCertificateFromFile(string certFilePath, string certPassword) - { - Throw.If(string.IsNullOrWhiteSpace(certFilePath), ErrorLocalization.CertificatePathNullOrEmpty); - Throw.If(string.IsNullOrWhiteSpace(certPassword), ErrorLocalization.CertificatePasswordNullOrEmpty); + Throw.If(string.IsNullOrWhiteSpace(certFilePath), ErrorLocalization.CertificatePathNullOrEmpty); + Throw.If(string.IsNullOrWhiteSpace(certPassword), ErrorLocalization.CertificatePasswordNullOrEmpty); - return new X509Certificate2(certFilePath, - certPassword, - X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); - } + return new X509Certificate2(certFilePath, + certPassword, + X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); + } - /// - /// Get certificate from file path and password - /// - /// - /// - /// - public static X509Certificate2 GetCertificateFromStrings(string certificateString64, string certPassword) - { - Throw.If(string.IsNullOrWhiteSpace(certificateString64), ErrorLocalization.CertificateRawStringNullOrEmpty); - Throw.If(string.IsNullOrWhiteSpace(certPassword), ErrorLocalization.CertificatePasswordNullOrEmpty); - var certificateBytes = Convert.FromBase64String(certificateString64); - return new X509Certificate2(certificateBytes, certPassword, - X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); - } + /// + /// Get certificate from file path and password + /// + /// + /// + /// + public static X509Certificate2 GetCertificateFromStrings(string certificateString64, string certPassword) + { + Throw.If(string.IsNullOrWhiteSpace(certificateString64), ErrorLocalization.CertificateRawStringNullOrEmpty); + Throw.If(string.IsNullOrWhiteSpace(certPassword), ErrorLocalization.CertificatePasswordNullOrEmpty); + + var certificateBytes = Convert.FromBase64String(certificateString64); + + return new X509Certificate2(certificateBytes, certPassword, + X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/CallbackRewriteMiddleware.cs b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/CallbackRewriteMiddleware.cs index d41ebd7..d4d92e1 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/CallbackRewriteMiddleware.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/CallbackRewriteMiddleware.cs @@ -6,9 +6,9 @@ namespace Spid.Cie.OIDC.AspNetCore.Middlewares; -internal class CallbackRewriteMiddleware +class CallbackRewriteMiddleware { - private readonly RequestDelegate _next; + readonly RequestDelegate _next; public CallbackRewriteMiddleware(RequestDelegate next) { @@ -18,12 +18,11 @@ public CallbackRewriteMiddleware(RequestDelegate next) public async Task Invoke(HttpContext context) { CheckAndReplaceCallbackPath(context, SpidCieConst.CallbackPath); - CheckAndReplaceCallbackPath(context, SpidCieConst.SignedOutCallbackPath); - CheckAndReplaceCallbackPath(context, SpidCieConst.RemoteSignOutPath); await _next(context); + return; } @@ -36,4 +35,4 @@ private static void CheckAndReplaceCallbackPath(HttpContext context, string tail context.Request.Path = tail; } } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/FetchOpenIdFederationMiddleware.cs b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/FetchOpenIdFederationMiddleware.cs index 7106790..d79c422 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/FetchOpenIdFederationMiddleware.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/FetchOpenIdFederationMiddleware.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Helpers; using Spid.Cie.OIDC.AspNetCore.Models; using Spid.Cie.OIDC.AspNetCore.Services; @@ -13,9 +14,9 @@ namespace Spid.Cie.OIDC.AspNetCore.Middlewares; -internal class FetchOpenIdFederationMiddleware +class FetchOpenIdFederationMiddleware { - private readonly RequestDelegate _next; + readonly RequestDelegate _next; public FetchOpenIdFederationMiddleware(RequestDelegate next) { @@ -34,10 +35,10 @@ public async Task Invoke(HttpContext context, } var uri = new Uri(UriHelper.GetEncodedUrl(context.Request)) - .GetLeftPart(UriPartial.Path) - .Replace(SpidCieConst.FetchEndpointPath, "") - .EnsureTrailingSlash() - .ToString(); + .GetLeftPart(UriPartial.Path) + .Replace(SpidCieConst.FetchEndpointPath, "") + .EnsureTrailingSlash() + .ToString(); string? sub = context.Request.Query.ContainsKey("sub") ? context.Request.Query["sub"] : default; string? iss = context.Request.Query.ContainsKey("iss") ? context.Request.Query["iss"] : uri; @@ -48,7 +49,7 @@ public async Task Invoke(HttpContext context, context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, + ErrorCode = ErrorCodes.invalid_request, ErrorDescription = "'sub' and 'iss' query parameters are mandatory" })); await context.Response.Body.FlushAsync(); @@ -64,7 +65,7 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, + ErrorCode = ErrorCodes.invalid_request, ErrorDescription = "'iss' not found" })); await context.Response.Body.FlushAsync(); @@ -79,21 +80,22 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, + ErrorCode = ErrorCodes.invalid_request, ErrorDescription = "'iss' doesn't have a signing certificate" })); await context.Response.Body.FlushAsync(); return; } - var relyingParty = aggregate.RelyingParties.FirstOrDefault(r => sub.Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + var relyingParty = aggregate.RelyingParties.FirstOrDefault(r => sub.EnsureTrailingSlash().Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + if (relyingParty is null) { context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, + ErrorCode = ErrorCodes.invalid_request, ErrorDescription = "'sub' not found" })); await context.Response.Body.FlushAsync(); @@ -106,7 +108,7 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, + ErrorCode = ErrorCodes.invalid_request, ErrorDescription = "Invalid TrustMarks for 'sub'" })); await context.Response.Body.FlushAsync(); @@ -121,14 +123,22 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() Subject = sub, JWKS = cryptoService.GetJWKS(new List() { certificate }), MetadataPolicy = aggregate.MetadataPolicy, - TrustMarks = relyingParty.TrustMarks.ToArray() + TrustMarks = relyingParty.TrustMarks, + AuthorityHints = relyingParty.AuthorityHints, + OpenIdRelyingParty = new SA_SpidCieOIDCConfiguration + { + Value = new SAJWKSValue + { + JWKS = cryptoService.GetJWKS(relyingParty.OpenIdCoreCertificates) + } + } }; string token = cryptoService.CreateJWT(certificate, response); - context.Response.ContentType = SpidCieConst.ResolveContentType; + context.Response.ContentType = SpidCieConst.EntityConfigurationContentType; await context.Response.WriteAsync(token); await context.Response.Body.FlushAsync(); return; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/ListOpenIdFederationMiddleware.cs b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/ListOpenIdFederationMiddleware.cs index 4bc9563..5597307 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/ListOpenIdFederationMiddleware.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/ListOpenIdFederationMiddleware.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Helpers; using Spid.Cie.OIDC.AspNetCore.Models; using Spid.Cie.OIDC.AspNetCore.Services; @@ -11,9 +12,9 @@ namespace Spid.Cie.OIDC.AspNetCore.Middlewares; -internal class ListOpenIdFederationMiddleware +class ListOpenIdFederationMiddleware { - private readonly RequestDelegate _next; + readonly RequestDelegate _next; public ListOpenIdFederationMiddleware(RequestDelegate next) { @@ -46,7 +47,7 @@ public async Task Invoke(HttpContext context, context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, + ErrorCode = ErrorCodes.invalid_request, ErrorDescription = "Aggregator not found" })); await context.Response.Body.FlushAsync(); @@ -60,4 +61,4 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() await context.Response.Body.FlushAsync(); return; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/RPOpenIdFederationMiddleware.cs b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/RPOpenIdFederationMiddleware.cs index 54b3f00..a6f8444 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/RPOpenIdFederationMiddleware.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/RPOpenIdFederationMiddleware.cs @@ -11,9 +11,9 @@ namespace Spid.Cie.OIDC.AspNetCore.Middlewares; -internal class RPOpenIdFederationMiddleware +class RPOpenIdFederationMiddleware { - private readonly RequestDelegate _next; + readonly RequestDelegate _next; public RPOpenIdFederationMiddleware(RequestDelegate next) { @@ -35,14 +35,16 @@ public async Task Invoke(HttpContext context, IRelyingPartiesHandler rpHandler, .Replace(SpidCieConst.JsonEntityConfigurationPath, "") .Replace(SpidCieConst.EntityConfigurationPath, "") .EnsureTrailingSlash(); + var rp = rps.FirstOrDefault(r => uri.EnsureTrailingSlash().Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); - var rp = rps.FirstOrDefault(r => uri.Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); if (rp != null) { var certificate = rp.OpenIdFederationCertificates?.FirstOrDefault(); + if (certificate is not null) { var entityConfiguration = GetEntityConfiguration(rp, cryptoService); + if (context.Request.Path.Value!.EndsWith(SpidCieConst.EntityConfigurationPath)) { string token = cryptoService.CreateJWT(certificate, entityConfiguration); @@ -50,6 +52,7 @@ public async Task Invoke(HttpContext context, IRelyingPartiesHandler rpHandler, context.Response.ContentType = SpidCieConst.EntityConfigurationContentType; await context.Response.WriteAsync(token); await context.Response.Body.FlushAsync(); + return; } else @@ -57,6 +60,7 @@ public async Task Invoke(HttpContext context, IRelyingPartiesHandler rpHandler, context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(entityConfiguration)); await context.Response.Body.FlushAsync(); + return; } } @@ -64,13 +68,16 @@ public async Task Invoke(HttpContext context, IRelyingPartiesHandler rpHandler, else { var aggs = await aggregatorsHandler.GetAggregators(); - var aggregate = aggs.FirstOrDefault(r => uri.Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + var aggregate = aggs.FirstOrDefault(r => uri.EnsureTrailingSlash().Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + if (aggregate is not null) { var certificate = aggregate.OpenIdFederationCertificates?.FirstOrDefault(); + if (certificate is not null) { var entityConfiguration = GetEntityConfiguration(aggregate, cryptoService); + if (context.Request.Path.Value!.EndsWith(SpidCieConst.EntityConfigurationPath)) { string token = cryptoService.CreateJWT(certificate, entityConfiguration); @@ -78,6 +85,7 @@ public async Task Invoke(HttpContext context, IRelyingPartiesHandler rpHandler, context.Response.ContentType = SpidCieConst.EntityConfigurationContentType; await context.Response.WriteAsync(token); await context.Response.Body.FlushAsync(); + return; } else @@ -85,13 +93,14 @@ public async Task Invoke(HttpContext context, IRelyingPartiesHandler rpHandler, context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(entityConfiguration)); await context.Response.Body.FlushAsync(); + return; } } } } - context.Response.StatusCode = (int)HttpStatusCode.NotFound; + context.Response.StatusCode = (int)HttpStatusCode.NotFound; } private RPEntityConfiguration GetEntityConfiguration(RelyingParty rp, ICryptoService cryptoService) @@ -132,35 +141,31 @@ private RPEntityConfiguration GetEntityConfiguration(RelyingParty rp, ICryptoSer } private SAEntityConfiguration GetEntityConfiguration(Aggregator agg, ICryptoService cryptoService) - { - return new SAEntityConfiguration() - { - ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(SpidCieConst.EntityConfigurationExpirationInMinutes), - IssuedAt = DateTimeOffset.UtcNow, - AuthorityHints = agg.AuthorityHints, - Issuer = agg.Id, - Subject = agg.Id, - TrustMarks = agg.TrustMarks, - JWKS = cryptoService.GetJWKS(agg.OpenIdFederationCertificates), - Metadata = new SAMetadata_SpidCieOIDCConfiguration() - { - FederationEntity = new SA_SpidCieOIDCFederationEntity() - { - Contacts = agg.Contacts, - HomepageUri = agg.HomepageUri, - LogoUri = agg.LogoUri, - OrganizationName = agg.OrganizationName, - PolicyUri = agg.PolicyUri, - FederationResolveEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.ResolveEndpointPath}", - FederationFetchEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.FetchEndpointPath}", - FederationListEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.ListEndpointPath}", - FederationTrustMarkStatusEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.TrustMarkStatusEndpointPath}" - }//, - //TrustMarkIssuer = new SA_TrustMarkIssuer() - //{ - // FederationStatusEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.TrustMarkStatusEndpointPath}" - //} - } - }; - } -} + { + return new SAEntityConfiguration() + { + ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(SpidCieConst.EntityConfigurationExpirationInMinutes), + IssuedAt = DateTimeOffset.UtcNow, + AuthorityHints = agg.AuthorityHints, + Issuer = agg.Id, + Subject = agg.Id, + TrustMarks = agg.TrustMarks, + JWKS = cryptoService.GetJWKS(agg.OpenIdFederationCertificates), + Metadata = new SAMetadata_SpidCieOIDCConfiguration() + { + FederationEntity = new SA_SpidCieOIDCFederationEntity() + { + Contacts = agg.Contacts, + HomepageUri = agg.HomepageUri, + LogoUri = agg.LogoUri, + OrganizationName = agg.OrganizationName, + PolicyUri = agg.PolicyUri, + FederationResolveEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.ResolveEndpointPath}", + FederationFetchEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.FetchEndpointPath}", + FederationListEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.ListEndpointPath}", + FederationTrustMarkStatusEndpoint = $"{agg.Id.EnsureTrailingSlash()}{SpidCieConst.TrustMarkStatusEndpointPath}" + } + } + }; + } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/ResolveOpenIdFederationMiddleware.cs b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/ResolveOpenIdFederationMiddleware.cs index cf89f77..4ac01cb 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/ResolveOpenIdFederationMiddleware.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/ResolveOpenIdFederationMiddleware.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Helpers; using Spid.Cie.OIDC.AspNetCore.Models; using Spid.Cie.OIDC.AspNetCore.Services; @@ -12,24 +13,21 @@ namespace Spid.Cie.OIDC.AspNetCore.Middlewares; -internal class ResolveOpenIdFederationMiddleware +class ResolveOpenIdFederationMiddleware { - private readonly RequestDelegate _next; + readonly RequestDelegate _next; public ResolveOpenIdFederationMiddleware(RequestDelegate next) { _next = next; } - public async Task Invoke(HttpContext context, - IRelyingPartiesHandler rpHandler, - IAggregatorsHandler aggHandler, - ICryptoService cryptoService, - ITrustChainManager trustChainManager) + public async Task Invoke(HttpContext context, IRelyingPartiesHandler rpHandler, IAggregatorsHandler aggHandler, ICryptoService cryptoService, ITrustChainManager trustChainManager) { if (!context.Request.Path.Value!.EndsWith(SpidCieConst.ResolveEndpointPath, StringComparison.InvariantCultureIgnoreCase)) { await _next(context); + return; } @@ -42,37 +40,43 @@ public async Task Invoke(HttpContext context, context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, + ErrorCode = ErrorCodes.invalid_request, ErrorDescription = "'sub' and 'anchor' query parameters are mandatory" })); await context.Response.Body.FlushAsync(); + return; } - var rps = await rpHandler.GetRelyingParties(); - var aggs = await aggHandler.GetAggregators(); var uri = new Uri(UriHelper.GetEncodedUrl(context.Request)) .GetLeftPart(UriPartial.Path) .Replace(SpidCieConst.ResolveEndpointPath, "") .EnsureTrailingSlash() .ToString(); - + var rps = await rpHandler.GetRelyingParties(); + var aggregator = (await aggHandler.GetAggregators()).FirstOrDefault(r => uri.Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); var certificate = rps.FirstOrDefault(r => uri.Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase))?.OpenIdFederationCertificates?.FirstOrDefault() - ?? aggs.FirstOrDefault(r => uri.Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase))?.OpenIdFederationCertificates?.FirstOrDefault(); + ?? aggregator?.OpenIdFederationCertificates?.FirstOrDefault(); + if (certificate is not null) { - var trustChain = trustChainManager.GetResolvedTrustChain(sub, anchor); + var trustChain = trustChainManager.GetResolvedTrustChain(sub, anchor); + if (trustChain != null) { - var response = new ResolveConfiguration() + var response = new OPResolveConfiguration() { ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(SpidCieConst.EntityConfigurationExpirationInMinutes), IssuedAt = DateTimeOffset.UtcNow, Issuer = uri, - Metadata = trustChain.OpConf.Metadata, + //Metadata = trustChain.OpConf.Metadata, + Metadata = trustChain.EntityConfiguration.Metadata, Subject = sub, - TrustMarks = trustChain.OpConf.TrustMarks? - .Where(t => JsonSerializer.Deserialize(cryptoService.DecodeJWT(t.TrustMark)).ExpiresOn >= DateTimeOffset.UtcNow) + //TrustMarks = trustChain.OpConf.TrustMarks? + // .Where(t => JsonSerializer.Deserialize(cryptoService.DecodeJWT(t.TrustMark)).ExpiresOn >= DateTimeOffset.UtcNow) + // .ToList() ?? new List(), + TrustMarks = trustChain.EntityConfiguration.TrustMarks? + .Where(t => JsonSerializer.Deserialize(cryptoService.DecodeJWT(t.TrustMark)).ExpiresOn >= DateTimeOffset.UtcNow) .ToList() ?? new List(), TrustChain = trustChain.Chain }; @@ -82,17 +86,52 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() context.Response.ContentType = SpidCieConst.ResolveContentType; await context.Response.WriteAsync(token); await context.Response.Body.FlushAsync(); + return; } + else if ((aggregator.RelyingParties.Union(rps)).Any(rp => rp.Id.EnsureTrailingSlash().Equals(sub.EnsureTrailingSlash(), StringComparison.InvariantCultureIgnoreCase))) + { + var rpTrustChain = trustChainManager.GetResolvedTrustChain(sub, anchor); + + if (rpTrustChain == default) + { + _ = await trustChainManager.BuildRPTrustChain(sub); + rpTrustChain = trustChainManager.GetResolvedTrustChain(sub, anchor); + } + + if (rpTrustChain != default) + { + var response = new RPResolveConfiguration + { + ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(SpidCieConst.EntityConfigurationExpirationInMinutes), + IssuedAt = DateTimeOffset.UtcNow, + Issuer = uri, + Metadata = rpTrustChain.EntityConfiguration?.Metadata ?? new(), + Subject = sub, + TrustChain = rpTrustChain.Chain, + TrustMarks = rpTrustChain.EntityConfiguration?.TrustMarks? + .Where(t => JsonSerializer.Deserialize(cryptoService.DecodeJWT(t.TrustMark))?.ExpiresOn >= DateTimeOffset.UtcNow) + .ToList() ?? new() + }; + string token = cryptoService.CreateJWT(certificate, response); + + context.Response.ContentType = SpidCieConst.ResolveContentType; + await context.Response.WriteAsync(token); + await context.Response.Body.FlushAsync(); + + return; + } + } context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.not_found, + ErrorCode = ErrorCodes.not_found, ErrorDescription = "TrustChain not found" })); await context.Response.Body.FlushAsync(); + return; } @@ -100,10 +139,11 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.not_found, + ErrorCode = ErrorCodes.not_found, ErrorDescription = "'sub' not found" })); await context.Response.Body.FlushAsync(); + return; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/TrustMarkStatusOpenIdFederationMiddleware.cs b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/TrustMarkStatusOpenIdFederationMiddleware.cs index 5ee06fe..2c14e06 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Middlewares/TrustMarkStatusOpenIdFederationMiddleware.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Middlewares/TrustMarkStatusOpenIdFederationMiddleware.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Helpers; using Spid.Cie.OIDC.AspNetCore.Models; using Spid.Cie.OIDC.AspNetCore.Services; @@ -11,9 +12,9 @@ namespace Spid.Cie.OIDC.AspNetCore.Middlewares; -internal class TrustMarkStatusOpenIdFederationMiddleware +class TrustMarkStatusOpenIdFederationMiddleware { - private readonly RequestDelegate _next; + readonly RequestDelegate _next; public TrustMarkStatusOpenIdFederationMiddleware(RequestDelegate next) { @@ -38,19 +39,32 @@ public async Task Invoke(HttpContext context, .EnsureTrailingSlash() .ToString(); - string? sub = context.Request.Form.ContainsKey("sub") ? context.Request.Form["sub"] : default; - string? id = context.Request.Form.ContainsKey("id") ? context.Request.Form["id"] : default; + string? sub, id; - if (string.IsNullOrWhiteSpace(sub) || string.IsNullOrWhiteSpace(id)) + if (context.Request.Form.ContainsKey("trust_mark")) + { + var trustmarkPayload = JsonSerializer.Deserialize(cryptoService.DecodeJWT(context.Request.Form["trust_mark"])); + + sub = trustmarkPayload?.Subject; + id = trustmarkPayload?.Id; + + } + else if (context.Request.Form.ContainsKey("sub") && (context.Request.Form.ContainsKey("id") || context.Request.Form.ContainsKey("trust_mark_id"))) + { + sub = context.Request.Form["sub"]; + id = context.Request.Form.ContainsKey("trust_mark_id") ? context.Request.Form["trust_mark_id"] : context.Request.Form["id"]; + } + else { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, - ErrorDescription = $"'{nameof(sub)}' and '{nameof(id)}' query parameters are mandatory" + ErrorCode = ErrorCodes.invalid_request, + ErrorDescription = "If 'trust_mark' is used, then 'sub' and 'trust_mark_id' are not needed. If 'trust_mark' is not used, then 'sub' and 'trust_mark_id' are REQUIRED." })); await context.Response.Body.FlushAsync(); + return; } @@ -59,13 +73,14 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() var relyingParty = aggs .SelectMany(s => s.RelyingParties) .FirstOrDefault(r => sub.EnsureTrailingSlash().Equals(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + if (relyingParty is null) { context.Response.StatusCode = (int)HttpStatusCode.NotFound; context.Response.ContentType = SpidCieConst.JsonContentType; await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() { - ErrorCode = ErrorCode.invalid_request, + ErrorCode = ErrorCodes.invalid_request, ErrorDescription = "'sub' not found" })); await context.Response.Body.FlushAsync(); @@ -83,4 +98,4 @@ await context.Response.WriteAsync(JsonSerializer.Serialize(new GenericError() await context.Response.Body.FlushAsync(); return; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/Aggregator.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/Aggregator.cs index 507ba3e..c974e53 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/Aggregator.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/Aggregator.cs @@ -8,20 +8,31 @@ namespace Spid.Cie.OIDC.AspNetCore.Models; [ExcludeFromCodeCoverage] public sealed class Aggregator { -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public string Id { get; set; } - public string Name { get; set; } - public string OrganizationName { get; set; } - public string HomepageUri { get; set; } - public string LogoUri { get; set; } - public string PolicyUri { get; set; } + public string? Id { get; set; } + + public string? Name { get; set; } + + public string? LogoUri { get; set; } + + public string? PolicyUri { get; set; } + + public string? Extension { get; set; } + + public string? HomepageUri { get; set; } + + public string? OrganizationName { get; set; } + + public string? OrganizationType { get; set; } + + public JsonDocument? MetadataPolicy { get; set; } + public List Contacts { get; set; } = new(); + public List AuthorityHints { get; set; } = new(); + + public List RelyingParties { get; set; } = new(); + public List TrustMarks { get; set; } = new(); + public List OpenIdFederationCertificates { get; set; } = new(); - public JsonDocument MetadataPolicy { get; set; } - public List RelyingParties { get; set; } = new(); - public string OrganizationType { get; set; } - public string Extension { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/CieIdentityProvider.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/CieIdentityProvider.cs index a38ff05..6b92836 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/CieIdentityProvider.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/CieIdentityProvider.cs @@ -1,29 +1,31 @@ -using System.Collections.Generic; +using Spid.Cie.OIDC.AspNetCore.Enums; +using System.Collections.Generic; using System.Linq; namespace Spid.Cie.OIDC.AspNetCore.Models; -internal sealed class CieIdentityProvider : IdentityProvider +sealed class CieIdentityProvider : IdentityProvider { - private static readonly Dictionary _claimsMapping = new Dictionary { - {nameof(ClaimTypes.Name) , CieConst.given_name}, - {nameof(ClaimTypes.FamilyName) , CieConst.family_name}, - {nameof(ClaimTypes.FiscalNumber) , CieConst.fiscal_number}, + static readonly Dictionary _claimsMapping = new() + { {nameof(ClaimTypes.Email) , CieConst.email}, + {nameof(ClaimTypes.Gender) , CieConst.gender}, + {nameof(ClaimTypes.IdANPR) , CieConst.idANPR}, + {nameof(ClaimTypes.Name) , CieConst.given_name}, {nameof(ClaimTypes.Address) , CieConst.address}, {nameof(ClaimTypes.DateOfBirth) , CieConst.birthdate}, - {nameof(ClaimTypes.Gender) , CieConst.gender}, - {nameof(ClaimTypes.PlaceOfBirth) , CieConst.place_of_birth}, + {nameof(ClaimTypes.FamilyName) , CieConst.family_name}, {nameof(ClaimTypes.PhoneNumber) , CieConst.phone_number}, - {nameof(ClaimTypes.DocumentDetails) , CieConst.document_details}, + {nameof(ClaimTypes.FiscalNumber) , CieConst.fiscal_number}, + {nameof(ClaimTypes.PlaceOfBirth) , CieConst.place_of_birth}, {nameof(ClaimTypes.EmailVerified) , CieConst.email_verified}, - {nameof(ClaimTypes.IdANPR) , CieConst.idANPR}, + {nameof(ClaimTypes.DocumentDetails) , CieConst.document_details}, {nameof(ClaimTypes.EDeliveryService) , CieConst.e_delivery_service}, {nameof(ClaimTypes.PhoneNumberVerified) , CieConst.phone_number_verified}, - {nameof(ClaimTypes.PhysicalPhoneNumber) , CieConst.physical_phone_number }, + {nameof(ClaimTypes.PhysicalPhoneNumber) , CieConst.physical_phone_number } }; - internal override IdentityProviderType Type { get => IdentityProviderType.CIE; } + internal override IdentityProviderTypes Type { get => IdentityProviderTypes.CIE; } public override IEnumerable FilterRequestedClaims(List requestedClaims) => requestedClaims diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/ClaimTypes.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/ClaimTypes.cs index c435be0..de869e0 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/ClaimTypes.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/ClaimTypes.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace Spid.Cie.OIDC.AspNetCore.Models; @@ -6,69 +7,88 @@ namespace Spid.Cie.OIDC.AspNetCore.Models; [ExcludeFromCodeCoverage] public sealed class ClaimTypes { - private static readonly Dictionary _types = new Dictionary() { + static readonly Dictionary _types = new() + { { nameof(Name), new ClaimTypes(nameof(Name)) }, - { nameof(FamilyName), new ClaimTypes(nameof(FamilyName)) }, - { nameof(FiscalNumber), new ClaimTypes(nameof(FiscalNumber)) }, - { nameof(Email), new ClaimTypes(nameof(Email)) }, - { nameof(EmailVerified), new ClaimTypes(nameof(EmailVerified)) }, - { nameof(DigitalAddress), new ClaimTypes(nameof(DigitalAddress)) }, { nameof(Mail), new ClaimTypes(nameof(Mail)) }, - { nameof(Address), new ClaimTypes(nameof(Address)) }, - { nameof(CompanyName), new ClaimTypes(nameof(CompanyName)) }, - { nameof(CountyOfBirth), new ClaimTypes(nameof(CountyOfBirth)) }, - { nameof(DateOfBirth), new ClaimTypes(nameof(DateOfBirth)) }, - { nameof(ExpirationDate), new ClaimTypes(nameof(ExpirationDate)) }, + { nameof(Email), new ClaimTypes(nameof(Email)) }, { nameof(Gender), new ClaimTypes(nameof(Gender)) }, { nameof(IdCard), new ClaimTypes(nameof(IdCard)) }, + { nameof(IdANPR), new ClaimTypes(nameof(IdANPR)) }, { nameof(IvaCode), new ClaimTypes(nameof(IvaCode)) }, - { nameof(PlaceOfBirth), new ClaimTypes(nameof(PlaceOfBirth)) }, - { nameof(RegisteredOffice), new ClaimTypes(nameof(RegisteredOffice)) }, + { nameof(Address), new ClaimTypes(nameof(Address)) }, { nameof(SpidCode), new ClaimTypes(nameof(SpidCode)) }, - { nameof(CompanyFiscalNumber), new ClaimTypes(nameof(CompanyFiscalNumber)) }, + { nameof(FamilyName), new ClaimTypes(nameof(FamilyName)) }, + { nameof(DateOfBirth), new ClaimTypes(nameof(DateOfBirth)) }, + { nameof(CompanyName), new ClaimTypes(nameof(CompanyName)) }, + { nameof(PhoneNumber), new ClaimTypes(nameof(PhoneNumber)) }, + { nameof(PlaceOfBirth), new ClaimTypes(nameof(PlaceOfBirth)) }, + { nameof(FiscalNumber), new ClaimTypes(nameof(FiscalNumber)) }, + { nameof(EmailVerified), new ClaimTypes(nameof(EmailVerified)) }, + { nameof(CountyOfBirth), new ClaimTypes(nameof(CountyOfBirth)) }, + { nameof(DigitalAddress), new ClaimTypes(nameof(DigitalAddress)) }, + { nameof(ExpirationDate), new ClaimTypes(nameof(ExpirationDate)) }, { nameof(DocumentDetails), new ClaimTypes(nameof(DocumentDetails)) }, - { nameof(IdANPR), new ClaimTypes(nameof(IdANPR)) }, + { nameof(RegisteredOffice), new ClaimTypes(nameof(RegisteredOffice)) }, { nameof(EDeliveryService), new ClaimTypes(nameof(EDeliveryService)) }, - { nameof(PhoneNumber), new ClaimTypes(nameof(PhoneNumber)) }, + { nameof(CompanyFiscalNumber), new ClaimTypes(nameof(CompanyFiscalNumber)) }, { nameof(PhoneNumberVerified), new ClaimTypes(nameof(PhoneNumberVerified)) }, { nameof(PhysicalPhoneNumber), new ClaimTypes(nameof(PhysicalPhoneNumber)) } }; - private ClaimTypes(string value) - { - Value = value; - } + private ClaimTypes(string value) => Value = value; - public static ClaimTypes FromName(string name) - { - return _types.ContainsKey(name) ? _types[name] : throw new System.Exception($"Invalid ClaimTypes name '{name}'"); - } + public static ClaimTypes FromName(string name) => _types.TryGetValue(name, out ClaimTypes? claim) ? claim : throw new Exception($"Invalid ClaimTypes name '{name}'"); public string Value { get; private set; } public static ClaimTypes Name { get { return _types[nameof(Name)]; } } - public static ClaimTypes FamilyName { get { return _types[nameof(FamilyName)]; } } - public static ClaimTypes FiscalNumber { get { return _types[nameof(FiscalNumber)]; } } - public static ClaimTypes Email { get { return _types[nameof(Email)]; } } - public static ClaimTypes DigitalAddress { get { return _types[nameof(DigitalAddress)]; } } + public static ClaimTypes Mail { get { return _types[nameof(Mail)]; } } - public static ClaimTypes Address { get { return _types[nameof(Address)]; } } - public static ClaimTypes CompanyName { get { return _types[nameof(CompanyName)]; } } - public static ClaimTypes CountyOfBirth { get { return _types[nameof(CountyOfBirth)]; } } - public static ClaimTypes DateOfBirth { get { return _types[nameof(DateOfBirth)]; } } - public static ClaimTypes ExpirationDate { get { return _types[nameof(ExpirationDate)]; } } + + public static ClaimTypes Email { get { return _types[nameof(Email)]; } } + + public static ClaimTypes IdANPR { get { return _types[nameof(IdANPR)]; } } + public static ClaimTypes Gender { get { return _types[nameof(Gender)]; } } + public static ClaimTypes IdCard { get { return _types[nameof(IdCard)]; } } + public static ClaimTypes IvaCode { get { return _types[nameof(IvaCode)]; } } - public static ClaimTypes PlaceOfBirth { get { return _types[nameof(PlaceOfBirth)]; } } - public static ClaimTypes RegisteredOffice { get { return _types[nameof(RegisteredOffice)]; } } + + public static ClaimTypes Address { get { return _types[nameof(Address)]; } } + public static ClaimTypes SpidCode { get { return _types[nameof(SpidCode)]; } } - public static ClaimTypes CompanyFiscalNumber { get { return _types[nameof(CompanyFiscalNumber)]; } } - public static ClaimTypes DocumentDetails { get { return _types[nameof(DocumentDetails)]; } } + + public static ClaimTypes FamilyName { get { return _types[nameof(FamilyName)]; } } + + public static ClaimTypes CompanyName { get { return _types[nameof(CompanyName)]; } } + + public static ClaimTypes DateOfBirth { get { return _types[nameof(DateOfBirth)]; } } + + public static ClaimTypes PhoneNumber { get { return _types[nameof(PhoneNumber)]; } } + + public static ClaimTypes PlaceOfBirth { get { return _types[nameof(PlaceOfBirth)]; } } + + public static ClaimTypes FiscalNumber { get { return _types[nameof(FiscalNumber)]; } } + + public static ClaimTypes CountyOfBirth { get { return _types[nameof(CountyOfBirth)]; } } + public static ClaimTypes EmailVerified { get { return _types[nameof(EmailVerified)]; } } - public static ClaimTypes IdANPR { get { return _types[nameof(IdANPR)]; } } + + public static ClaimTypes DigitalAddress { get { return _types[nameof(DigitalAddress)]; } } + + public static ClaimTypes ExpirationDate { get { return _types[nameof(ExpirationDate)]; } } + + public static ClaimTypes DocumentDetails { get { return _types[nameof(DocumentDetails)]; } } + + public static ClaimTypes RegisteredOffice { get { return _types[nameof(RegisteredOffice)]; } } + public static ClaimTypes EDeliveryService { get { return _types[nameof(EDeliveryService)]; } } - public static ClaimTypes PhoneNumber { get { return _types[nameof(PhoneNumber)]; } } + + public static ClaimTypes CompanyFiscalNumber { get { return _types[nameof(CompanyFiscalNumber)]; } } + public static ClaimTypes PhoneNumberVerified { get { return _types[nameof(PhoneNumberVerified)]; } } + public static ClaimTypes PhysicalPhoneNumber { get { return _types[nameof(PhysicalPhoneNumber)]; } } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/EntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/EntityConfiguration.cs deleted file mode 100644 index 87ec047..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/EntityConfiguration.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class EntityConfiguration : FederationEntityConfiguration -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("trust_marks")] - public List TrustMarks { get; set; } - - [JsonPropertyName("authority_hints")] - public List AuthorityHints { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/EntityStatement.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/EntityStatement.cs deleted file mode 100644 index dd575f0..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/EntityStatement.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class EntityStatement : FederationEntityConfiguration -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("metadata_policy")] - public JsonDocument MetadataPolicy { get; set; } - - [JsonPropertyName("trust_marks")] - public TrustMarkDefinition[] TrustMarks { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/FederationEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/FederationEntityConfiguration.cs deleted file mode 100644 index b23cd54..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/FederationEntityConfiguration.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class FederationEntityConfiguration -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("iss")] - public string Issuer { get; set; } - - [JsonPropertyName("sub")] - public string Subject { get; set; } - - [JsonIgnore()] - public DateTimeOffset IssuedAt { get; set; } - - [JsonPropertyName("iat")] - public long Iat - { - get => IssuedAt.ToUnixTimeSeconds(); - set => IssuedAt = DateTimeOffset.FromUnixTimeSeconds(value); - } - - [JsonIgnore()] - public DateTimeOffset ExpiresOn { get; set; } - - [JsonPropertyName("exp")] - public long Exp - { - get => ExpiresOn.ToUnixTimeSeconds(); - set => ExpiresOn = DateTimeOffset.FromUnixTimeSeconds(value); - } - - [JsonPropertyName("jwks")] - public JWKS JWKS { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/GenericError.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/GenericError.cs index be88c49..75e2792 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/GenericError.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/GenericError.cs @@ -1,4 +1,5 @@ -using System; +using Spid.Cie.OIDC.AspNetCore.Enums; +using System; using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; @@ -7,30 +8,16 @@ namespace Spid.Cie.OIDC.AspNetCore.Models; [ExcludeFromCodeCoverage] internal class GenericError { -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. [JsonIgnore()] - public ErrorCode ErrorCode { get; set; } + public ErrorCodes ErrorCode { get; set; } [JsonPropertyName("error")] public string Error { get => ErrorCode.ToString(); - set => ErrorCode = Enum.Parse(value); + set => ErrorCode = Enum.Parse(value); } - [JsonPropertyName("error_description")] - public string ErrorDescription { get; set; } - -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} - -internal enum ErrorCode -{ - invalid_request, - invalid_client, - not_found, - server_error, - temporarily_unavailable, - unsupported_parameter + public string? ErrorDescription { get; set; } } \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/IdPEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/IdPEntityConfiguration.cs deleted file mode 100644 index d382aeb..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/IdPEntityConfiguration.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class IdPEntityConfiguration : EntityConfiguration -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("metadata")] - public IdPMetadata_SpidCieOIDCConfiguration Metadata { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/IdentityProvider.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/IdentityProvider.cs index c110abc..eef4c8b 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/IdentityProvider.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/IdentityProvider.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using Spid.Cie.OIDC.AspNetCore.Enums; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace Spid.Cie.OIDC.AspNetCore.Models; @@ -6,25 +7,23 @@ namespace Spid.Cie.OIDC.AspNetCore.Models; [ExcludeFromCodeCoverage] public abstract class IdentityProvider { -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public string Uri { get; set; } - internal abstract IdentityProviderType Type { get; } - public string OrganizationName { get; set; } - public string OrganizationLogoUrl { get; set; } - public List SupportedAcrValues { get; set; } - internal IdPEntityConfiguration EntityConfiguration { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - - public string GetAcrValue(SecurityLevel securityLevel) - { - return securityLevel == SecurityLevel.L1 && SupportedAcrValues.Contains(SpidCieConst.SpidL1) - ? SpidCieConst.SpidL1 - : securityLevel == SecurityLevel.L2 && SupportedAcrValues.Contains(SpidCieConst.SpidL2) - ? SpidCieConst.SpidL2 - : securityLevel == SecurityLevel.L3 && SupportedAcrValues.Contains(SpidCieConst.SpidL3) - ? SpidCieConst.SpidL3 - : SpidCieConst.DefaultAcr; - } + public string? Uri { get; set; } + + public string? OrganizationName { get; set; } + + public string? OrganizationLogoUrl { get; set; } + + internal abstract IdentityProviderTypes Type { get; } + + public List SupportedAcrValues { get; set; } = new(); + + internal OPEntityConfiguration? EntityConfiguration { get; set; } + + public string GetAcrValue(SecurityLevels securityLevel) + => securityLevel == SecurityLevels.L1 && SupportedAcrValues.Contains(SpidCieConst.SpidL1) ? SpidCieConst.SpidL1 : + securityLevel == SecurityLevels.L2 && SupportedAcrValues.Contains(SpidCieConst.SpidL2) ? SpidCieConst.SpidL2 : + securityLevel == SecurityLevels.L3 && SupportedAcrValues.Contains(SpidCieConst.SpidL3) ? SpidCieConst.SpidL3 : + SpidCieConst.DefaultAcr; public abstract IEnumerable FilterRequestedClaims(List requestedClaims); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/IdentityProviderType.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/IdentityProviderType.cs deleted file mode 100644 index 44896e7..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/IdentityProviderType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Spid.Cie.OIDC.AspNetCore.Models; - -internal enum IdentityProviderType -{ - SPID, - CIE -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/JwtToken.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/JwtToken.cs deleted file mode 100644 index a10d7a2..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/JwtToken.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class JwtToken -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("iss")] - public string Issuer { get; set; } - - [JsonPropertyName("sub")] - public string Subject { get; set; } - - [JsonPropertyName("jti")] - public string Jti { get; set; } - - [JsonPropertyName("aud")] - public string[] Audiences { get; set; } - - [JsonIgnore()] - public DateTimeOffset IssuedAt { get; set; } - - [JsonPropertyName("iat")] - public long Iat - { - get => IssuedAt.ToUnixTimeSeconds(); - set => IssuedAt = DateTimeOffset.FromUnixTimeSeconds(value); - } - - [JsonIgnore()] - public DateTimeOffset ExpiresOn { get; set; } - - [JsonPropertyName("exp")] - public long Exp - { - get => ExpiresOn.ToUnixTimeSeconds(); - set => ExpiresOn = DateTimeOffset.FromUnixTimeSeconds(value); - } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/ConfigurationBaseInfo.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/ConfigurationBaseInfo.cs new file mode 100644 index 0000000..0d3bb0f --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/ConfigurationBaseInfo.cs @@ -0,0 +1,35 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class ConfigurationBaseInfo +{ + [JsonPropertyName("iss")] + public string? Issuer { get; set; } + + [JsonPropertyName("sub")] + public string? Subject { get; set; } + + [JsonIgnore()] + public DateTimeOffset IssuedAt { get; set; } + + [JsonPropertyName("iat")] + public long Iat + { + get => IssuedAt.ToUnixTimeSeconds(); + set => IssuedAt = DateTimeOffset.FromUnixTimeSeconds(value); + } + + [JsonIgnore()] + public DateTimeOffset ExpiresOn { get; set; } + + [JsonPropertyName("exp")] + public long Exp + { + get => ExpiresOn.ToUnixTimeSeconds(); + set => ExpiresOn = DateTimeOffset.FromUnixTimeSeconds(value); + } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/EntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/EntityConfiguration.cs new file mode 100644 index 0000000..c8a51cb --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/EntityConfiguration.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class EntityConfiguration : FederationEntityConfiguration +{ + [JsonPropertyName("authority_hints")] + public List AuthorityHints { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/ExtendedEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/ExtendedEntityConfiguration.cs new file mode 100644 index 0000000..2d17a5e --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/ExtendedEntityConfiguration.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class ExtendedEntityConfiguration : EntityConfiguration +{ + [JsonPropertyName("trust_marks")] + public List TrustMarks { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/FederationEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/FederationEntityConfiguration.cs new file mode 100644 index 0000000..7ead1e4 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/FederationEntityConfiguration.cs @@ -0,0 +1,12 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class FederationEntityConfiguration : ConfigurationBaseInfo +{ + [JsonPropertyName("jwks")] + public JWKS? JWKS { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/OPEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/OPEntityConfiguration.cs new file mode 100644 index 0000000..e5259ab --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/OPEntityConfiguration.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class OPEntityConfiguration : ExtendedEntityConfiguration +{ + [JsonPropertyName("metadata")] + public OPMetadata_SpidCieOIDCConfiguration? Metadata { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/RPEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/RPEntityConfiguration.cs new file mode 100644 index 0000000..2fc6f6b --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/RPEntityConfiguration.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class RPEntityConfiguration : ExtendedEntityConfiguration +{ + [JsonPropertyName("metadata")] + public RPMetadata_SpidCieOIDCConfiguration? Metadata { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/SAEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/SAEntityConfiguration.cs new file mode 100644 index 0000000..c3aabd0 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/SAEntityConfiguration.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class SAEntityConfiguration : ExtendedEntityConfiguration +{ + [JsonPropertyName("metadata")] + public SAMetadata_SpidCieOIDCConfiguration? Metadata { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/TAEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/TAEntityConfiguration.cs new file mode 100644 index 0000000..3583999 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityConfigurations/TAEntityConfiguration.cs @@ -0,0 +1,15 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class TAEntityConfiguration : FederationEntityConfiguration +{ + [JsonPropertyName("metadata")] + public TAMetadata_SpidCieOIDCConfiguration? Metadata { get; set; } + + //trust_mark_issuers + //constraints +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/EntityStatement.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/EntityStatement.cs new file mode 100644 index 0000000..13c55c1 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/EntityStatement.cs @@ -0,0 +1,16 @@ +using Spid.Cie.OIDC.AspNetCore.Models; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class EntityStatement : ExtendedEntityConfiguration +{ + [JsonPropertyName("metadata_policy")] + public JsonDocument? MetadataPolicy { get; set; } + + [JsonPropertyName("openid_relying_party")] + public SA_SpidCieOIDCConfiguration? OpenIdRelyingParty { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/SAJWKSValue.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/SAJWKSValue.cs new file mode 100644 index 0000000..d77733c --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/SAJWKSValue.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class SAJWKSValue +{ + [JsonPropertyName("value")] + public JWKS? JWKS { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/SA_SpidCieOIDCConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/SA_SpidCieOIDCConfiguration.cs new file mode 100644 index 0000000..3cbcf1a --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/EntityStatements/SA_SpidCieOIDCConfiguration.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class SA_SpidCieOIDCConfiguration +{ + [JsonPropertyName("jwks")] + public SAJWKSValue? Value { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/JWKS.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/JWKS.cs new file mode 100644 index 0000000..1e21c82 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/JWKS.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class JWKS +{ + [JsonPropertyName("keys")] + public List Keys { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/JsonWebKey.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/JsonWebKey.cs new file mode 100644 index 0000000..da80ef6 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/JsonWebKey.cs @@ -0,0 +1,32 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class JsonWebKey +{ + [JsonPropertyName("kty")] + public string? Kty { get; set; } + + [JsonPropertyName("use")] + public string? Use { get; set; } + + [JsonPropertyName("kid")] + public string? Kid { get; set; } + + [JsonPropertyName("e")] + public string? E { get; set; } + + [JsonPropertyName("n")] + public string? N { get; set; } + + [JsonPropertyName("alg")] + public string? Alg { get; set; } + + //[JsonPropertyName("x5t")] + //public string X5t { get; set; } + + //[JsonPropertyName("x5c")] + //public List X5c { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/IntermediaryMetadataPolicy.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/IntermediaryMetadataPolicy.cs new file mode 100644 index 0000000..9a78692 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/IntermediaryMetadataPolicy.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models +{ + [ExcludeFromCodeCoverage] + public sealed class IntermediaryMetadataPolicy + { + [JsonPropertyName("id_token_signed_response_alg")] + public IntermediaryTokenSignedResaponseAlgorithm? TokenSignedResaponseAlgorithmId { get; set; } + } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/IntermediaryTokenSignedResaponseAlgorithm.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/IntermediaryTokenSignedResaponseAlgorithm.cs new file mode 100644 index 0000000..d663279 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/IntermediaryTokenSignedResaponseAlgorithm.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models +{ + [ExcludeFromCodeCoverage] + public sealed class IntermediaryTokenSignedResaponseAlgorithm + { + [JsonPropertyName("essential")] + public bool Essential { get; set; } = true; + + [JsonPropertyName("one_of")] + public List ValidAlgorithms { get; set; } = new() { "RS256", "RS512", "ES256", "ES512", "PS256", "PS512" }; + } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/OPMetadata_SpidCieOIDCConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/OPMetadata_SpidCieOIDCConfiguration.cs new file mode 100644 index 0000000..fb82e9a --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/OPMetadata_SpidCieOIDCConfiguration.cs @@ -0,0 +1,12 @@ +using Microsoft.IdentityModel.Protocols.OpenIdConnect; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class OPMetadata_SpidCieOIDCConfiguration +{ + [JsonPropertyName("openid_provider")] + public OpenIdConnectConfiguration? OpenIdProvider { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RPMetadata_SpidCieOIDCConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RPMetadata_SpidCieOIDCConfiguration.cs new file mode 100644 index 0000000..b42caf4 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RPMetadata_SpidCieOIDCConfiguration.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class RPMetadata_SpidCieOIDCConfiguration +{ + [JsonPropertyName("openid_relying_party")] + public RP_SpidCieOIDCConfiguration? OpenIdRelyingParty { get; set; } + + [JsonPropertyName("federation_entity")] + public RP_SpidCieOIDCFederationEntity? FederationEntity { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RP_SpidCieOIDCConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RP_SpidCieOIDCConfiguration.cs new file mode 100644 index 0000000..cca3efc --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RP_SpidCieOIDCConfiguration.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class RP_SpidCieOIDCConfiguration +{ + [JsonPropertyName("application_type")] + public string ApplicationType { get; } = SpidCieConst.RPApplicationType; + + [JsonPropertyName("client_id")] + public string? ClientId { get; set; } + + [JsonPropertyName("client_registration_types")] + public List ClientRegistrationTypes { get; } = new() { SpidCieConst.RPClientRegistrationType }; + + [JsonPropertyName("jwks")] + public JWKS? JWKS { get; set; } + + [JsonPropertyName("client_name")] + public string? ClientName { get; set; } + + //contacts + /* + "contacts": [ + "ops@rp.example.it" + ] + */ + + [JsonPropertyName("grant_types")] + public List GrantTypes { get; set; } = new(); + + [JsonPropertyName("redirect_uris")] + public List RedirectUris { get; set; } = new(); + + [JsonPropertyName("response_types")] + public List ResponseTypes { get; set; } = new(); + + [JsonPropertyName("subject_type")] + public string SubjectType { get; } = SpidCieConst.RPSubjectType; + + [JsonPropertyName("userinfo_encrypted_response_enc")] + public string? UserinfoEncryptedResponseEnc { get; set; } = "A128CBC-HS256"; + + [JsonPropertyName("userinfo_encrypted_response_alg")] + public string? UserinfoEncryptedResponseAlg { get; set; } = "RSA-OAEP-256"; + + [JsonPropertyName("userinfo_signed_response_alg")] + public string? UserinfoSignedResponseAlg { get; set; } = "RS256"; + + [JsonPropertyName("token_endpoint_auth_method")] + public string? TokenEndpointAuthMethod { get; set; } = "private_key_jwt"; + + [JsonPropertyName("id_token_signed_response_alg")] + public string? IdTokenSignedResponseAlg { get; set; } = "RS256"; +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RP_SpidCieOIDCFederationEntity.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RP_SpidCieOIDCFederationEntity.cs new file mode 100644 index 0000000..e2d6078 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/RP_SpidCieOIDCFederationEntity.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class RP_SpidCieOIDCFederationEntity +{ + [JsonPropertyName("federation_resolve_endpoint")] + public string? FederationResolveEndpoint { get; set; } + + [JsonPropertyName("organization_name")] + public string? OrganizationName { get; set; } + + [JsonPropertyName("homepage_uri")] + public string? HomepageUri { get; set; } + + [JsonPropertyName("policy_uri")] + public string? PolicyUri { get; set; } + + [JsonPropertyName("logo_uri")] + public string? LogoUri { get; set; } + + [JsonPropertyName("contacts")] + public List Contacts { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SAMetadata_SpidCieOIDCConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SAMetadata_SpidCieOIDCConfiguration.cs new file mode 100644 index 0000000..8074b55 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SAMetadata_SpidCieOIDCConfiguration.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class SAMetadata_SpidCieOIDCConfiguration +{ + [JsonPropertyName("federation_entity")] + public SA_SpidCieOIDCFederationEntity? FederationEntity { get; set; } + + //[JsonPropertyName("trust_mark_issuer")] + //public SA_TrustMarkIssuer? TrustMarkIssuer { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SA_SpidCieOIDCFederationEntity.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SA_SpidCieOIDCFederationEntity.cs new file mode 100644 index 0000000..5ad546a --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SA_SpidCieOIDCFederationEntity.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class SA_SpidCieOIDCFederationEntity +{ + [JsonPropertyName("contacts")] + public List Contacts { get; set; } = new(); + + [JsonPropertyName("federation_fetch_endpoint")] + public string? FederationFetchEndpoint { get; set; } + + [JsonPropertyName("federation_resolve_endpoint")] + public string? FederationResolveEndpoint { get; set; } + + [JsonPropertyName("federation_list_endpoint")] + public string? FederationListEndpoint { get; set; } + + [JsonPropertyName("federation_trust_mark_status_endpoint")] + public string? FederationTrustMarkStatusEndpoint { get; set; } + + [JsonPropertyName("homepage_uri")] + public string? HomepageUri { get; set; } + + [JsonPropertyName("organization_name")] + public string? OrganizationName { get; set; } + + [JsonPropertyName("policy_uri")] + public string? PolicyUri { get; set; } + + [JsonPropertyName("logo_uri")] + public string? LogoUri { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SA_TrustMarkIssuer.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SA_TrustMarkIssuer.cs new file mode 100644 index 0000000..189fdb7 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/SA_TrustMarkIssuer.cs @@ -0,0 +1,10 @@ +//using System.Diagnostics.CodeAnalysis; + +//namespace Spid.Cie.OIDC.AspNetCore.Models; + +//[ExcludeFromCodeCoverage] +//sealed class SA_TrustMarkIssuer +//{ +// //[JsonPropertyName("federation_status_endpoint")] +// //public string FederationStatusEndpoint { get; set; } +//} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/TAFederationEntity.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/TAFederationEntity.cs new file mode 100644 index 0000000..8d2e869 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/TAFederationEntity.cs @@ -0,0 +1,32 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class TAFederationEntity +{ + [JsonPropertyName("name")] + public string? Name { get; set; } //"organization_name": "example TA" ? + + [JsonPropertyName("contacts")] + public string[] Contacts { get; set; } = Array.Empty(); + + //"policy_uri": "https://registry.agid.gov.it/policy", + + [JsonPropertyName("homepage_uri")] + public string? HomepageUri { get; set; } + + //"logo_uri":"https://registry.agid.gov.it/static/svg/logo.svg", + + [JsonPropertyName("federation_fetch_endpoint")] + public string? FederationFetchEndpoint { get; set; } + + //"federation_resolve_endpoint": "https://registry.agid.gov.it/resolve/", + + [JsonPropertyName("federation_list_endpoint")] + public string? FederationListEndpoint { get; set; } + + //"federation_trust_mark_status_endpoint": "https://registry.agid.gov.it/trust_mark_status/" +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/TAMetadata_SpidCieOIDCConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/TAMetadata_SpidCieOIDCConfiguration.cs new file mode 100644 index 0000000..4c6e5a3 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Metadata/TAMetadata_SpidCieOIDCConfiguration.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +sealed class TAMetadata_SpidCieOIDCConfiguration +{ + [JsonPropertyName("federation_entity")] + public TAFederationEntity? FederationEntity { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Resolves/OPResolveConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Resolves/OPResolveConfiguration.cs new file mode 100644 index 0000000..62f2a1b --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Resolves/OPResolveConfiguration.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class OPResolveConfiguration : ConfigurationBaseInfo +{ + [JsonPropertyName("metadata")] + public OPMetadata_SpidCieOIDCConfiguration? Metadata { get; set; } + + [JsonPropertyName("trust_marks")] + public List TrustMarks { get; set; } = new(); + + [JsonPropertyName("trust_chain")] + public List TrustChain { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Resolves/RPResolveConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Resolves/RPResolveConfiguration.cs new file mode 100644 index 0000000..118e5db --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/Resolves/RPResolveConfiguration.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class RPResolveConfiguration : ConfigurationBaseInfo +{ + [JsonPropertyName("metadata")] + public RPMetadata_SpidCieOIDCConfiguration? Metadata { get; set; } + + [JsonPropertyName("trust_marks")] + public List TrustMarks { get; set; } = new(); + + [JsonPropertyName("trust_chain")] + public List TrustChain { get; set; } = new(); +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustChain.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustChain.cs new file mode 100644 index 0000000..c0f2874 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustChain.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +//[ExcludeFromCodeCoverage] +//internal class TrustChain +//{ +// public DateTimeOffset ExpiresOn { get; set; } +// public IdPEntityConfiguration OpConf { get; set; } +// public List Chain { get; set; } +// public string TrustAnchorUsed { get; set; } +//} + +[ExcludeFromCodeCoverage] +class TrustChain where T : FederationEntityConfiguration +{ + public DateTimeOffset ExpiresOn { get; set; } + + public T? EntityConfiguration { get; set; } + + public List Chain { get; set; } = []; + + public string? TrustAnchorUsed { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkDefinition.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkDefinition.cs new file mode 100644 index 0000000..b47e78a --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkDefinition.cs @@ -0,0 +1,17 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +public sealed class TrustMarkDefinition +{ + [JsonPropertyName("id")] + public string? Id { get; set; } + + [JsonPropertyName("iss")] + public string? Issuer { get; set; } + + [JsonPropertyName("trust_mark")] + public string? TrustMark { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkGovernmentIndex.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkGovernmentIndex.cs new file mode 100644 index 0000000..2fa39d4 --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkGovernmentIndex.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; +using System.Diagnostics.CodeAnalysis; + +namespace Spid.Cie.OIDC.AspNetCore.Models.OIDCFederation.TrustMarks; + +[ExcludeFromCodeCoverage] +class TrustMarkGovernmentIndex +{ + [JsonProperty("ipa_code")] + public string? Code { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkPayload.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkPayload.cs new file mode 100644 index 0000000..8e26c1c --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/OIDCFederation/TrustMarks/TrustMarkPayload.cs @@ -0,0 +1,33 @@ +using Spid.Cie.OIDC.AspNetCore.Models.OIDCFederation.TrustMarks; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace Spid.Cie.OIDC.AspNetCore.Models; + +[ExcludeFromCodeCoverage] +class TrustMarkPayload : ConfigurationBaseInfo +{ + [JsonPropertyName("id")] + public string? Id { get; set; } + + [JsonPropertyName("logo_uri")] + public string? LogoUri { get; set; } + + [JsonPropertyName("ref")] + public string? Ref { get; set; } + + [JsonPropertyName("organization_type")] + public string? OrganizationType { get; set; } + + [JsonPropertyName("organization_name")] + public string? OrganizationName { get; set; } + + [JsonPropertyName("id_code")] + public TrustMarkGovernmentIndex? IdCode { get; set; } + + [JsonPropertyName("email")] + public string? Email { get; set; } + + [JsonPropertyName("sa_profile")] + public string? SAProfile { get; set; } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/RPEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/RPEntityConfiguration.cs deleted file mode 100644 index 62ddfab..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/RPEntityConfiguration.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class RPEntityConfiguration : EntityConfiguration -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("metadata")] - public RPMetadata_SpidCieOIDCConfiguration Metadata { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/RPOpenIdCoreCertificate.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/RPOpenIdCoreCertificate.cs new file mode 100644 index 0000000..5f49eea --- /dev/null +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/RPOpenIdCoreCertificate.cs @@ -0,0 +1,16 @@ +using Spid.Cie.OIDC.AspNetCore.Enums; +using System.Diagnostics.CodeAnalysis; +using System.Security.Cryptography.X509Certificates; + +namespace Spid.Cie.OIDC.AspNetCore.Models +{ + [ExcludeFromCodeCoverage] + public class RPOpenIdCoreCertificate + { + public string? Algorithm { get; set; } + + public KeyUsageTypes KeyUsage { get; set; } + + public X509Certificate2? Certificate { get; set; } + } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/RelyingParty.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/RelyingParty.cs index 470ff55..20b655f 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/RelyingParty.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/RelyingParty.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using Spid.Cie.OIDC.AspNetCore.Enums; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Security.Cryptography.X509Certificates; @@ -7,22 +8,35 @@ namespace Spid.Cie.OIDC.AspNetCore.Models; [ExcludeFromCodeCoverage] public sealed class RelyingParty { -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public string Id { get; set; } - public string Name { get; set; } - public SecurityLevel SecurityLevel { get; set; } - public List AuthorityHints { get; set; } = new(); - public List TrustMarks { get; set; } = new(); - public List OpenIdFederationCertificates { get; set; } = new(); - public List OpenIdCoreCertificates { get; set; } = new(); - public List Contacts { get; set; } = new(); + public string? Id { get; set; } + + public string? Name { get; set; } + + public string? LogoUri { get; set; } + + public string? PolicyUri { get; set; } + + public string? HomepageUri { get; set; } + + public string? OrganizationType { get; set; } + + public string? OrganizationName { get; set; } + public bool LongSessionsEnabled { get; set; } + + public SecurityLevels SecurityLevel { get; set; } + + public List Contacts { get; set; } = new(); + public List RedirectUris { get; set; } = new(); + + public List AuthorityHints { get; set; } = new(); + public List RequestedClaims { get; set; } = new(); - public string HomepageUri { get; set; } - public string LogoUri { get; set; } - public string OrganizationName { get; set; } - public string PolicyUri { get; set; } - public string OrganizationType { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + + public List TrustMarks { get; set; } = new(); + + public List OpenIdFederationCertificates { get; set; } = new(); + + public List OpenIdCoreCertificates { get; set; } = new(); } \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/ResolveConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/ResolveConfiguration.cs deleted file mode 100644 index 0434f34..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/ResolveConfiguration.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class ResolveConfiguration -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("iss")] - public string Issuer { get; set; } - - [JsonPropertyName("sub")] - public string Subject { get; set; } - - [JsonIgnore()] - public DateTimeOffset IssuedAt { get; set; } - - [JsonPropertyName("iat")] - public long Iat - { - get => IssuedAt.ToUnixTimeSeconds(); - set => IssuedAt = DateTimeOffset.FromUnixTimeSeconds(value); - } - - [JsonIgnore()] - public DateTimeOffset ExpiresOn { get; set; } - - [JsonPropertyName("exp")] - public long Exp - { - get => ExpiresOn.ToUnixTimeSeconds(); - set => ExpiresOn = DateTimeOffset.FromUnixTimeSeconds(value); - } - - [JsonPropertyName("metadata")] - public IdPMetadata_SpidCieOIDCConfiguration Metadata { get; set; } - - [JsonPropertyName("trust_marks")] - public List TrustMarks { get; set; } = new(); - - [JsonPropertyName("trust_chain")] - public List TrustChain { get; set; } = new(); - -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/SAEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/SAEntityConfiguration.cs deleted file mode 100644 index a6b2101..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/SAEntityConfiguration.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class SAEntityConfiguration : EntityConfiguration -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("metadata")] - public SAMetadata_SpidCieOIDCConfiguration Metadata { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/SecurityLevel.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/SecurityLevel.cs deleted file mode 100644 index af058dc..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/SecurityLevel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Spid.Cie.OIDC.AspNetCore.Models; - -public enum SecurityLevel -{ - L1, - L2, - L3 -} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieConfiguration.cs index 71b80c1..0d57564 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieConfiguration.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieConfiguration.cs @@ -4,7 +4,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Models; [ExcludeFromCodeCoverage] -internal sealed class SpidCieConfiguration +sealed class SpidCieConfiguration { public bool RequestRefreshToken { get; set; } = false; @@ -15,5 +15,4 @@ internal sealed class SpidCieConfiguration public List SpidOPs { get; set; } = new(); public List CieOPs { get; set; } = new(); - -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieOIDCConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieOIDCConfiguration.cs deleted file mode 100644 index 9d55b30..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieOIDCConfiguration.cs +++ /dev/null @@ -1,152 +0,0 @@ -using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -[ExcludeFromCodeCoverage] -internal class RequestAuthenticationMethodsSupported -{ - [JsonPropertyName("ar")] - public List Ar { get; set; } - -} - -[ExcludeFromCodeCoverage] -internal sealed class SAMetadata_SpidCieOIDCConfiguration -{ - [JsonPropertyName("federation_entity")] - public SA_SpidCieOIDCFederationEntity FederationEntity { get; set; } - [JsonPropertyName("trust_mark_issuer")] - public SA_TrustMarkIssuer TrustMarkIssuer { get; set; } -} - -[ExcludeFromCodeCoverage] -internal sealed class SA_TrustMarkIssuer -{ - //[JsonPropertyName("federation_status_endpoint")] - //public string FederationStatusEndpoint { get; set; } -} - -[ExcludeFromCodeCoverage] -internal sealed class RPMetadata_SpidCieOIDCConfiguration -{ - [JsonPropertyName("openid_relying_party")] - public RP_SpidCieOIDCConfiguration OpenIdRelyingParty { get; set; } - [JsonPropertyName("federation_entity")] - public RP_SpidCieOIDCFederationEntity FederationEntity { get; set; } -} - -[ExcludeFromCodeCoverage] -internal sealed class RP_SpidCieOIDCConfiguration -{ - [JsonPropertyName("client_registration_types")] - public List ClientRegistrationTypes { get; } = new() { SpidCieConst.RPClientRegistrationType }; - - [JsonPropertyName("application_type")] - public string ApplicationType { get; } = SpidCieConst.RPApplicationType; - - [JsonPropertyName("client_name")] - public string ClientName { get; set; } - - [JsonPropertyName("grant_types")] - public List GrantTypes { get; set; } - - [JsonPropertyName("jwks")] - public JWKS JWKS { get; set; } - - [JsonPropertyName("redirect_uris")] - public List RedirectUris { get; set; } - - [JsonPropertyName("response_types")] - public List ResponseTypes { get; set; } - - [JsonPropertyName("subject_type")] - public string SubjectType { get; } = SpidCieConst.RPSubjectType; - [JsonPropertyName("client_id")] - public string ClientId { get; set; } -} - - -[ExcludeFromCodeCoverage] -internal sealed class RP_SpidCieOIDCFederationEntity -{ - [JsonPropertyName("organization_name")] - public string OrganizationName { get; set; } - - [JsonPropertyName("homepage_uri")] - public string HomepageUri { get; set; } - - [JsonPropertyName("policy_uri")] - public string PolicyUri { get; set; } - - [JsonPropertyName("logo_uri")] - public string LogoUri { get; set; } - - [JsonPropertyName("contacts")] - public List Contacts { get; set; } - - [JsonPropertyName("federation_resolve_endpoint")] - public string FederationResolveEndpoint { get; set; } -} - -[ExcludeFromCodeCoverage] -internal sealed class SA_SpidCieOIDCFederationEntity -{ - [JsonPropertyName("organization_name")] - public string OrganizationName { get; set; } - - [JsonPropertyName("homepage_uri")] - public string HomepageUri { get; set; } - - [JsonPropertyName("policy_uri")] - public string PolicyUri { get; set; } - - [JsonPropertyName("logo_uri")] - public string LogoUri { get; set; } - - [JsonPropertyName("contacts")] - public List Contacts { get; set; } - - [JsonPropertyName("federation_resolve_endpoint")] - public string FederationResolveEndpoint { get; set; } - - [JsonPropertyName("federation_fetch_endpoint")] - public string FederationFetchEndpoint { get; set; } - - [JsonPropertyName("federation_list_endpoint")] - public string FederationListEndpoint { get; set; } - - [JsonPropertyName("federation_trust_mark_status_endpoint")] - public string FederationTrustMarkStatusEndpoint { get; set; } -} - -[ExcludeFromCodeCoverage] -internal sealed class IdPMetadata_SpidCieOIDCConfiguration -{ - [JsonPropertyName("openid_provider")] - public OpenIdConnectConfiguration? OpenIdProvider { get; set; } -} - -[ExcludeFromCodeCoverage] -internal sealed class JWKS -{ - [JsonPropertyName("keys")] - public List Keys { get; set; } = new(); -} - -[ExcludeFromCodeCoverage] -internal class JsonWebKey -{ - public string kty { get; set; } - public string use { get; set; } - public string kid { get; set; } - //public string x5t { get; set; } - public string e { get; set; } - public string n { get; set; } - //public List x5c { get; set; } - //public string alg { get; set; } -} -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidIdentityProvider.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/SpidIdentityProvider.cs index 564ab3a..a3cbdda 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidIdentityProvider.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Models/SpidIdentityProvider.cs @@ -1,9 +1,10 @@ -using System.Collections.Generic; +using Spid.Cie.OIDC.AspNetCore.Enums; +using System.Collections.Generic; using System.Linq; namespace Spid.Cie.OIDC.AspNetCore.Models; -internal sealed class SpidIdentityProvider : IdentityProvider +sealed class SpidIdentityProvider : IdentityProvider { private static readonly Dictionary _claimsMapping = new Dictionary { {nameof(ClaimTypes.Name) , SpidConst.given_name}, @@ -28,11 +29,11 @@ internal sealed class SpidIdentityProvider : IdentityProvider {nameof(ClaimTypes.PhoneNumber) , SpidConst.phone_number}, }; - internal override IdentityProviderType Type { get => IdentityProviderType.SPID; } + internal override IdentityProviderTypes Type { get => IdentityProviderTypes.SPID; } public override IEnumerable FilterRequestedClaims(List requestedClaims) => requestedClaims .Where(c => _claimsMapping.ContainsKey(c.Value)) .Select(c => _claimsMapping[c.Value]) .ToList()!; -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/TAEntityConfiguration.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/TAEntityConfiguration.cs deleted file mode 100644 index d69c66d..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/TAEntityConfiguration.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class TAEntityConfiguration : FederationEntityConfiguration -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - [JsonPropertyName("metadata")] - public TAMetadata_SpidCieOIDCConfiguration Metadata { get; set; } -} - - - -[ExcludeFromCodeCoverage] -internal sealed class TAMetadata_SpidCieOIDCConfiguration -{ - [JsonPropertyName("federation_entity")] - public TAFederationEntity FederationEntity { get; set; } -} - -[ExcludeFromCodeCoverage] -internal sealed class TAFederationEntity -{ - - [JsonPropertyName("contacts")] - public string[] Contacts { get; set; } - - [JsonPropertyName("federation_fetch_endpoint")] - public string FederationFetchEndpoint { get; set; } - - [JsonPropertyName("homepage_uri")] - public string HomepageUri { get; set; } - - [JsonPropertyName("name")] - public string Name { get; set; } - - [JsonPropertyName("federation_list_endpoint")] - public string FederationListEndpoint { get; set; } - -} - -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/TrustChain.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/TrustChain.cs deleted file mode 100644 index 3c4aca1..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/TrustChain.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class TrustChain -{ - public DateTimeOffset ExpiresOn { get; set; } - public IdPEntityConfiguration OpConf { get; set; } - public List Chain { get; set; } - public string TrustAnchorUsed { get; set; } -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/TrustMarkDefinition.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/TrustMarkDefinition.cs deleted file mode 100644 index b454bf4..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/TrustMarkDefinition.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -public sealed class TrustMarkDefinition -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - - [JsonPropertyName("id")] - public string Id { get; set; } - [JsonPropertyName("iss")] - public string Issuer { get; set; } - - [JsonPropertyName("trust_mark")] - public string TrustMark { get; set; } - -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/TrustMarkPayload.cs b/src/Spid.Cie.OIDC.AspNetCore/Models/TrustMarkPayload.cs deleted file mode 100644 index 8420fa0..0000000 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/TrustMarkPayload.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; - -namespace Spid.Cie.OIDC.AspNetCore.Models; - -[ExcludeFromCodeCoverage] -internal class TrustMarkPayload -{ - [JsonPropertyName("iss")] - public string Issuer { get; set; } - - [JsonPropertyName("sub")] - public string Subject { get; set; } - - [JsonIgnore()] - public DateTimeOffset IssuedAt { get; set; } - - [JsonPropertyName("iat")] - public long Iat - { - get => IssuedAt.ToUnixTimeSeconds(); - set => IssuedAt = DateTimeOffset.FromUnixTimeSeconds(value); - } - - [JsonIgnore()] - public DateTimeOffset ExpiresOn { get; set; } - - [JsonPropertyName("exp")] - public long Exp - { - get => ExpiresOn.ToUnixTimeSeconds(); - set => ExpiresOn = DateTimeOffset.FromUnixTimeSeconds(value); - } - - [JsonPropertyName("id")] - public string Id { get; set; } - - [JsonPropertyName("logo_uri")] - public string LogoUri { get; set; } - - [JsonPropertyName("ref")] - public string Ref { get; set; } - - [JsonPropertyName("organization_type")] - public string OrganizationType { get; set; } - - [JsonPropertyName("organization_name")] - public string OrganizationName { get; set; } - - [JsonPropertyName("id_code")] - public string IdCode { get; set; } - - [JsonPropertyName("email")] - public string Email { get; set; } - - [JsonPropertyName("sa_profile")] - public string SAProfile { get; set; } - -} diff --git a/src/Spid.Cie.OIDC.AspNetCore/Mvc/CieButtonTagHelper.cs b/src/Spid.Cie.OIDC.AspNetCore/Mvc/CieButtonTagHelper.cs index 7b98f92..35fe3a4 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Mvc/CieButtonTagHelper.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Mvc/CieButtonTagHelper.cs @@ -1,68 +1,56 @@ -using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Razor.TagHelpers; -using Spid.Cie.OIDC.AspNetCore.Services; +using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Spid.Cie.OIDC.AspNetCore.Mvc; public class CieButtonTagHelper : TagHelper { -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - private static string _buttonImage; - private static readonly object _lockobj = new object(); - private static readonly Dictionary _classNames = new() - { - { CieButtonSize.Small, ("s", "small") }, - { CieButtonSize.Medium, ("m", "medium") }, - { CieButtonSize.Large, ("l", "large") }, - { CieButtonSize.ExtraLarge, ("xl", "xlarge") } - }; - - private readonly IIdentityProvidersHandler _idpHandler; - - public CieButtonTagHelper(IIdentityProvidersHandler idpHandler) + static string? _buttonImage; + readonly static object _lockobj = new(); + readonly static Dictionary _classNames = new() + { + { CieButtonSize.Small, "150" }, + { CieButtonSize.Medium, "220" }, + { CieButtonSize.Large, "280" }, + { CieButtonSize.ExtraLarge, "340" } + }; + IUrlHelper _urlHelper; + + public CieButtonTagHelper(IUrlHelper urlHelper) { - _idpHandler = idpHandler; + _urlHelper = urlHelper; } public CieButtonSize Size { get; set; } + public string CircleImagePath { get; set; } + public string ChallengeUrl { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "div"; - output.Content.AppendHtml(await CreateHeaderAsync()); + output.Content.AppendHtml(CreateHeader()); } - private async Task CreateHeaderAsync() + private TagBuilder CreateHeader() { var spanIcon = new TagBuilder("span"); - spanIcon.AddCssClass("italia-it-button-icon"); - spanIcon.InnerHtml.AppendHtml(GetSerializedButtonImage()); - - var spanText = new TagBuilder("span"); - spanText.AddCssClass("italia-it-button-text"); - spanText.InnerHtml.AppendHtml("Entra con CIE"); - - var identityProviders = await _idpHandler.GetIdentityProviders(); - var idp = identityProviders.FirstOrDefault(p => p.Type == Models.IdentityProviderType.CIE); + var imgIcon = new TagBuilder("img"); + imgIcon.Attributes.Add("src", String.IsNullOrWhiteSpace(CircleImagePath) ? GetSerializedButtonImage() : _urlHelper.Content(CircleImagePath)); + imgIcon.Attributes.Add("alt", "Entra con CIE"); + imgIcon.Attributes.Add("style", $"width: {_classNames[Size]}px;"); + spanIcon.InnerHtml.AppendHtml(imgIcon); var a = new TagBuilder("a"); - if (idp != null) - a.Attributes.Add("href", $"{ChallengeUrl}{(ChallengeUrl.Contains("?") ? "&" : "?")}provider={idp.Uri}"); - else - a.Attributes.Add("style", "pointer-events: none;"); - a.AddCssClass($"italia-it-button italia-it-button-size-{_classNames[Size].ShortClassName} button-cie"); + a.Attributes.Add("href", ChallengeUrl); a.InnerHtml.AppendHtml(spanIcon); - a.InnerHtml.AppendHtml(spanText); return a; } @@ -74,11 +62,16 @@ private string GetSerializedButtonImage() { if (_buttonImage == null) { - using var resourceStream = GetType().Assembly.GetManifestResourceStream("Spid.Cie.OIDC.AspNetCore.Mvc.Resources.cie-ico-circle-bb.svg"); - using var writer = new MemoryStream(); - resourceStream!.CopyTo(writer); - writer.Seek(0, SeekOrigin.Begin); - _buttonImage = Encoding.UTF8.GetString(writer.ToArray()); + + using (var resourceStream = GetType().Assembly.GetManifestResourceStream("Spid.Cie.OIDC.AspNetCore.Mvc.Resources.cie-button.png")) + { + using (var writer = new MemoryStream()) + { + resourceStream.CopyTo(writer); + writer.Seek(0, SeekOrigin.Begin); + _buttonImage = $"data:image/png;base64,{Convert.ToBase64String(writer.ToArray())}"; + } + } } } } @@ -93,4 +86,4 @@ public enum CieButtonSize Medium, Large, ExtraLarge -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Mvc/Resources/cie-button.png b/src/Spid.Cie.OIDC.AspNetCore/Mvc/Resources/cie-button.png new file mode 100644 index 0000000000000000000000000000000000000000..ea633b418db9fb61cec8c6076434bda38ad86576 GIT binary patch literal 9954 zcmcI|2T+q;_ID60U_%g5r1xTg009!ZfD~!ck(!WDqy&;c=v@(z-b5*iAkqb-#84DO zdPk(ARB1s0iBi9SzGZjb-P!rie3{9er=0UU{oLo=2t6IOvouUJ007{uhPsMA06?)x zzDArnMqUqcjRXOJGpdft%6ck}7&m8_HvpjCQfz`VHE6q(Wv;K`5UiIIHmR~`q~TsI2lH@iC^3M zR=?Io>KeE1PT!E>!7OwGo)~l_YHr-J1kiBbEr0hybSP}|%%w$&CjeU;s?8voSdr9n zdOA3TvvN-<)3G;?oeC}lb(9DSe{BHE1?zQ(OHk9=Q_t=myHBDFN?p$_zYe$`pyw+7 zOs}9ZkzF$GEr27Xz%7O;wZJWf$>i)3Hy@gP@;D&v{Y)Dg(CZqEHol;6a@}DnXjq$* z_9md0Lz%gg@f#JbnzC&96Prgg%5t9`erM3_Zg96di&^)AG?`{}*l>kM&Gq|DYJEL$ zJ^nM2gKg{OfJ*rn6{GH#u2SpIn_q~Yoo3ZAwvc_dP1`_!`owpR8~L27*vQ*#;F%7{ zOS>TIvd2M;tdwO}-o+>E)|f`i3Yjyv-Mw5w`^wa!ReNYy_)RrA&t&?(R(gIKO3lGsTx(j@+ z>T1u`4VzJ?-GlV#7@p6!JV$hJ(u23lkNwoEABM+2yqfp+!`&+xzBW zbe`?Bv3l|xp8Sl#qO#XBWIQ$aHTq_w1aj}fBACWf`**3L<#ib5)$(VDXdkylB}i0x zxp7#;-3gG@x&1J%D2BeLeJ)yCuO*Z*)btI-=b+;kU)l#dvNPTv^L^y|N#wg0_a}-g ztrXWk6~v!0%Q@Sv{^=o=<{N8T>R`WNVP`kvQ;|=02VNS=y$)G z_0Op0C&`Fmb3rdEp6NiZg(lMtm`T^3!TWm&rqL>(cOCs#`nhk=HbwNkQ_rS%jXXIM z>~)P<8Af`u=XUVaq2UWXD?Rko`BNs;nvGrg(|5~J>#$&VMQS;fJLv9hsQox=xz7yD zi*`1AT$*0$cu5W8=DYXGPf#fSIR{P?WY6~bv~vqzjXr>VE=tj8g0jQ(nHwqxJ%_ef z?QVV8T&Bsl?P4ksviF)dKS(bX1P1jyM1jkihvio!Jeg&pSQ;NuoR*2Kv|)VNBq&=O z$$GXVn*$XQ`fXN;^_#-(Hwk&8n>Drj2jY(+b+s5lw~gMPI0jiq?&xX@Pj*=zm5<0?e1 ziPoF5l#x#*1_5BT)xN$LZ0$E}dl6W%N1-}uH+%K5S8m@OdP?Z!-IVrWt&0@wvv*)S zAqspDVFoLC$oBs2blI6}R}*2FGISALVG~oC@ec*-_erMbw^;e6&!pcaG%_$p=Y8_? zgUT~nN1DvYeC6Hc4!F_~OPbE2#)e#ZdzV)66CE!t`jq%7WSC~7WFtkRzz<1}3zM`F zD!hHcUrtZG$HIl(FS!f2Q@TriyIdCfBW_rYGhRzwIEP(dHbPC1J2`$R(K9|K0jgn{ zgF^tzc}7B+HM-evww`WFp3Ru$n$=mSM=PkN!qupsyfdImN=uqcDo!$PSJh<9%_>my zcyi)aVm^NG_@Lk&uJ0GWpZlJil9E!H(lF!&EF1DG7A%&#ZU2hsRF}6nknYGl%Ka#$ z>Oob8=L65gtw^1`u>s}9E1TE1#eC%UsPdpUCMFZV)sG20Qq3sx3O< z#p0@YDtY?$S>37`td@}_b;4??c`_!|H%L#29ux9%_c94qwN}BC`gIvT_TMv?5;s@B zb8Q~q&fPZO)cvA>usL(}MEG0#Yj9^|Ivjh2_AK+6niKdF3?~$>zPh@}v~<2Ai)RL+pXX`$-njhW6!TlQTk`O=`{s5p(kjfdN3K->?HD_^ zJAs|gI&AgU0b>=4_Io>@tiw*4#&J0!x1yUvdmm6eIa8veLY=L+7f*uqb@_W8{Exk zsmI91Sk`eOH6}hRexHknYx#y1E*D>J8D(+RVl5UP6UkEPX1MWU;hBq?{R1n=_U4x! z*Gbo(uI3Z2gk?gkhxM1qrTzY-*jxu-e{R84`VuRH73(DHQ?_`zTP*G({ZKK!a*lGTc9}cIgJllav09t$ zUzRN}!dd1$t1Qbhp54QVpjQub9xgpRS=L=vK0M_9rKW7gIF>+f&&U_y6m#<1_>T|Y zaP-V{&-r$tnbPtwzpfuG5yXfJdR;zH!gJnLEuTc@lv>%Jo{_eZoVh?FJ`epn{!3%8 zzP>VYY^Q$4*O}%fKOte6KAE7@uHH4A(kBIiPme}eCf4#LYrmEpfuX<6Rv2jI&FVsA zjqC+cOZ8gU?QYL^cgEIwS+SIgL_Ico{Gnsv>&I_d)Yp$YQB5?me-`|lxbkGhXhati zf;L{jMqO7)&SnLvSpWm#uVxSgNG?`>lUudHyRUX9j?ad4hJ@1jYh=#ON<=v}4?uH- z`^ECnK5sTF2YivBau>9UyxF9_1K|7$O2ruu%c=Np|o z;#o;5DZ3JKLur>WWizP?t*M>XDQq?PA>AP@flxw4lV-EIa!rvyxwxo zHJN+vG3XKL(OxiIkn+81*`(;e6j)Gyo8GvBFjVBJu{w4(F589Lv;za|ig`zDoBE*Lh;3oJ}meC#wOmk9I+IPFDE`>@bnWR!imHNb608Npv|c zxm*GZnfIGR&&9|M4>1kjYJhLbp0NGtfnbeqcTSpT{-GOtX(aVV?La0-&vUO>J8_mVp z=6lr(Qyk+04TZkd>zN~UuNBhwhl@9FvDUK%95jaJoUCSBXOs8k4%{p&+yw2p&(U}A zeb<%CWL=6`soz|9r$ETm$ofu7+RG)GtYTUgb`H+%?Ob0An=D~0XQUKKtrzpI+^hV! zTvZ=d?~9_D+-v{_+ONB|_%@L86mtB%`W0rl7HvQ9<>?k@*~(8{;lF%2ZXHqk_8H)W z%%hC6Z#q^1y#o|B79k7a5lgGE6T}*6P~wK$jJ>_NO;X~)Ly;pg**qyX9&_Q4-VcxOG%kk)o6n{=H~o*J0Sm# zikH-nzMFTUY0VoAbqjl^N2~*fztLhn$a4j?o4OeW01)On{8M=4DtH0_r_>z{O|hoh zT2Nb*i!cm(b2sus@u(qhSn=;bD zQQZfPH1N?ewDobag&;T;Ks$fqKbt9^pdC*N2ZqIN6U#uugKE zN{0g3O||vdl~HIUJ6Ko}XbX}6vr9pQL6YJU5Xd!lF;S4Th^Vv(NE`@~gi44)#l+Zu z|2WCg&0ud1v6A=>wk|}@~Z&xhL z3+RgB`U61)iLpgHx?vqruIz`1FgVHsE5}LJ^p^=PZhykMVt%`c>@X28n41VlSoF}O zKM1w8|2wIR%b(O3tg0tj%5Q!D5g23W?S>T5M`BPOXj`PJC(;$m^@lJw2NV{CaX|eC z-Tr+2FB&3j{}kxvfp$J}55iUi>5OzCV`Ip)pdx(a!sSA=TD~YPe#sFjrfohKd{~St(&hM+6iiDUFa6 z1=#^%;s^v#LRta_gxiYR0i|upD@Tmg0l5EbjKn8uiQnTY{?jZx)cfr zNl8m0L}5Tl2pj|iBPAq&FeI34tt0{l6Gea}BqYFppy{F=$>jrc{yXZSRtPd85-uqY zhk+nKQ6v%$lt3V)fG}yY%OnwSNhDZG)D{AkO)DoNT1JBZeGa@830KfOP+T0Z9h^FS+rH41==6;$Uc`l07*P|62lz z{3i=AFwg(4Kg>>CLK=Yp10m9OQa}kiuq{v;AprqOfyJb4VPawu5HRu&`~Uyx|J%W~ z4lq}HB)QFsaQ@#d`rp$1pCa-P1^9oC$Uj>oa@b;jC7j5==i*APMESSv z@DKlx_t?YBKR0*s&7YqKNLO;QqRE^8I$K`@0B~VVLq*BZYjick+w0;SOUqtfrC``I z{uf?`VMC##TYYNgW!qk|RkE3Sm6MuTOgi+U76O+-E<(7ugKPj0FX)rx1y2b81dWs5|Ltz#%`k&m**Ea}koQtUh?&>N0PMJ_{bM~M zUAs$eX0ED*p+=cz>A^QolR5ok`5M%%(O=M-?up7Tm^Jg8a*qQPiSkX)HVhV4fs(UQ zP>W<=d+zC_LljGKs04DpBpWkwDSYL6CIu7gnY36MjaxdkBz8%e#wO26XY zc>s=+A=^Sa7s}flYwa@WSzplKPwahV>as-$p+ABn-m;>RDpoMMWic@CW}a3_ICX@T z`ROh)?Xz0@df{yrq6qa}?IR!@m##>+>6We8ZO4kH(A>JF0MaJap$qX&os^ga#uX8P zd>4#YzU)s`DA}n&yyp7WTW#lN8&023(iu%S(63peeZQWJ`dWk?f_|+-{)$^s$Wp+0LRO0Xj1&5 zwd}kvfG}T&6F*efce7qdkVcdTi(((Sd$}FUA62O4wrw9e_MGtfg_%b1;|+z8q!z0e zF}-rlo9L{ny2I>MB+BJftW%|f%xlqZ1HKA2hDrIq5-1>}@2P;Zs=S2Mhs(?Vo35tdJ{8qHwrk*cyZ)}S#SIoS9d=xN`q+Bt*E8dFtjRl#l(`SsBJR(v5sOQ6 znGD&^-xR+j5$Ta{fqiM{T+R4lUhsYoY7>Y*5bKpzoP6)(;!@b)^|PWtP!{mW$+Ml;F7xXe@?Pi%zd)cX7Hz!YvKKWW{#j~IY zH4`=g=^=LnYqWxGm8kvFga>33J#_(B>@(Tt*r4WchugqA3UmDC&6c>fM!6fT4-<=s zaB>HDke)WX0K%17$D5ElQ^{y|Y()!lFajJ7rH&B`S6y)A#92<%`KuAv95_g|?Z%55 z+Rt>1MX$=UrtnrBe5n!i2#`vx{t-5g3L6jJzdvmK4v%PgVB}f{;t{J-NNiuyIIqon{BT()%4dZm}A5 zEPq@BYwlS%p5dRJdp=fC=l%vekMVPA3(>4bzwF(vHgAk8-KC7YwhIL{{B&+`v7K(K z;BkK=;+V*$ibBY<(*kq*gV|VBngG{`!MY#SrR5IqivnKEn$5xRQ~f-+Q2Dv;kD#&> z-ck$U@HV>j8NTwFwJ_$;&W?9gkB1ZAj4X_f__geNcK%?BAP&#!&VB-4S-nmr*cTmU zh}Nhy(Hep;#8KZ#%pmM!w|aYOTClWEw9MA;x?RECPGx?rB|rkv%DU`sJzn%v_zu~w*4j)huzdwEXK9bz@{k->(V}~Mg+*@@E&OP- zuZNVhC*|&K=|=mc>?lrjSn6;v=;B%}-NJI9cp0amrKO6Enmf%KYi zUNmFjWP!ZlVsAKa(atLX*>?zAOXWt)dF#F@ zf9Qa{j&pb$@A$|p4T)c7=X9a$k9LJHCIbn=86Mh+Xk%3RFgunOgNQ#1DmZf*9 zypD?q%`8z3&F5ENnp4;0f!!GsPwxIc@^d`)`A5vwmSy(xH#M=m54;S1;Wy@bP7h!? zNkK$+t$Jac&P!8K=V?p)1|^p!>CMj=a-LyAzW2j;bP)IG-}G%|D-!p4aE(n*+sGSs zrlgssQ!#Kzr?6{sbaJG(Ki>L-k3gyW$K0jkJ2&57d!6DLe;*xq{p0o8ZzGEAy z^>vcryZJ9(N3XoY7Zwz*&abCbjHCb;3g+Kk>gfE`8ld{*S_`sPXp;h;m{r{&;LHv6 z?;xGZvlHZ(?`fTYSZzKmYfmeV4?w#OSx*2NJDFEwa<aSQmPU(jTD{!4F!B(;R;0Y-6R+dV% zZmXrq*D+rsMo8j2A3frY)(ktOw%zHFaSmK-U6i%1WZWpJ*YUkfD4px$(ZopqRz%Zk zmM4%6`k^<9$dkmjo!4VfnLxk_UOnHjm!ddz`CX1V{&dVPf;Y)#yHA-Yu_w=g3yj~H zfjJtPj8Ru=R~Z#SRwrZI^BrFq-5x#OR_q{`_JP*BX!pB1SmS zI&a4zfRs@40J?q2QXv+Wt1>(?C>I$q~a zgPc*mznW!cRv!SzcT>D6u0CDn^MXf$>pY<$FFkBkh_LHzebK zKE0|_sG;z3IidZPBx|DYfnt-QLL7nfW9xxKx0ip!0~<12HK_d~kG(ndEp#{C0q7G5 zztKco(r@zBCsIr(5?xbwHZS)rrVm%-WyWRD4x2uacE8-kJi;1IIX)|KoJ~}sTg_^w&5GATk6c1A zbW{!U1J(vFv+6&Y-{+g_4s-61ZVB_dsL^n$KN4V z%v?Du&rXU6;K1#_N>6VzF6P8{q!VHS_!DjBEQ`LHYtQwVPy4Cd^8AvMO6Xs>LjJPG zmBl2lTIx9Ey$+HQAWu$%4dW+&1dm%j3h@UJFFzIhg3Pu5LH+_v3s}g~MW>7H>D<|b zn;FPn!+A(0GtR|4B#`F1yMfHFTRf&VG;b1K7rN?66If@7#U9x9+4bnv4F?|Ff_Z93X=l2AF`O7jC~fz5bp}<; zhwKU8dr;4$1om5ei*MUbAp}+)gN#i{7sHb1-9>$Qx9vP{N(h=B@D!j+p1blId8$8} zD&iB@<$#>%o}Z1|%$kW~=GZwSuBKBT6}eb8eoHrGyiSCaM)2_w31e{sv4*icF}XeJ zy!E+Y-ONXe7{JOyrPvf>A9rXJYy7KEGWm~o$SqLf!&a=+?2_y?NO4W_42-}(Q!+Y_*5!AG4_$m*0(HaGhn8TmqvWw{ zm+B0By?$J+TMKUNjFnam5xmA_(7e(`J>^2HDTxdK(btZ>H~1dGB=~t ztw)yEfILU@+lP+5I#k#M)WckQj4UQfpXE1gtJnz^|d`!d&whoi6}QTV21qRU_rcX#H(%YM2S z!U_i5p<(1XeP0N-MKuCq^nDI}RHN#8kP?MAQ44>5<|5mOia|`ykn$0~c2`|xT?M{) z;7)~FMv+ZQU@b>=mmOcn<5B3abn2kwNdL{p)tRN+Eq8*cJP-=!qBYGjJic-t4YoqF zLjw19lK-FvlV zj1!Z~x#EuYXLG+saEWSd@B?e4{9_mQn!FXzpyHus69ckhB6E4Ke8R_Q{2vP*F&l|F zKl(-+$wWZ&F}MmsLQ`%EE(t>pnu!mY#KVgr$(hLbC?B)us>u}^yck=NCc{#N0 o=nUmw@BZyXh0F(dv+zUVLrL;QLuq$CG7msQRY#@dx=qOc18xi;xc~qF literal 0 HcmV?d00001 diff --git a/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidButtonTagHelper.cs b/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidButtonTagHelper.cs index 99d1cf4..e23c059 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidButtonTagHelper.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidButtonTagHelper.cs @@ -76,7 +76,8 @@ private async Task CreateButtons() listContainer.AddCssClass("spid-idp-button-menu"); var identityProviders = await _idpHandler.GetIdentityProviders(); - foreach (var idp in identityProviders.Where(i => i.Type == Models.IdentityProviderType.SPID)) + + foreach (var idp in identityProviders.Where(i => i.Type == Enums.IdentityProviderTypes.SPID)) { var itemContainer = new TagBuilder("li"); itemContainer.AddCssClass("spid-idp-button-link"); @@ -129,4 +130,4 @@ public enum SpidButtonSize Medium, Large, ExtraLarge -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidCSSTagHelper.cs b/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidCSSTagHelper.cs index 87da497..d3c2e79 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidCSSTagHelper.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidCSSTagHelper.cs @@ -32,4 +32,4 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu await Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidJSTagHelper.cs b/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidJSTagHelper.cs index 69cbe28..04d4dc1 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidJSTagHelper.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Mvc/SpidJSTagHelper.cs @@ -32,4 +32,4 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu await Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/AggregatorsHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/AggregatorsHandler.cs index fa04a6d..e6c0229 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/AggregatorsHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/AggregatorsHandler.cs @@ -7,10 +7,10 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal class AggregatorsHandler : IAggregatorsHandler +class AggregatorsHandler : IAggregatorsHandler { - private readonly IOptionsMonitor _options; - private readonly IAggregatorsRetriever _aggRetriever; + readonly IOptionsMonitor _options; + readonly IAggregatorsRetriever _aggRetriever; public AggregatorsHandler(IOptionsMonitor options, IAggregatorsRetriever aggRetriever) @@ -23,4 +23,4 @@ public async Task> GetAggregators() { return _options.CurrentValue.Aggregators.Union(await _aggRetriever.GetAggregators()).ToList(); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/CryptoService.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/CryptoService.cs index 8407512..a5edc71 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/CryptoService.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/CryptoService.cs @@ -13,25 +13,25 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal class CryptoService : ICryptoService +class CryptoService : ICryptoService { - private static readonly STJSerializer _jsonSerializer = new STJSerializer(); - private static readonly UtcDateTimeProvider _utcDateTimeProvider = new UtcDateTimeProvider(); - private static readonly JwtBase64UrlEncoder _jwtBase64UrlEncoder = new JwtBase64UrlEncoder(); - private static readonly JwtValidator _jwtValidator = new JwtValidator(_jsonSerializer, _utcDateTimeProvider, new ValidationParameters() + static readonly STJSerializer _jsonSerializer = new STJSerializer(); + static readonly UtcDateTimeProvider _utcDateTimeProvider = new UtcDateTimeProvider(); + static readonly JwtBase64UrlEncoder _jwtBase64UrlEncoder = new JwtBase64UrlEncoder(); + static readonly JwtValidator _jwtValidator = new JwtValidator(_jsonSerializer, _utcDateTimeProvider, new ValidationParameters() { ValidateSignature = true, ValidateExpirationTime = false, ValidateIssuedTime = false, TimeMargin = 300 }, _jwtBase64UrlEncoder); - private static readonly IJwtDecoder _decoder = new JwtDecoder(_jsonSerializer, _jwtBase64UrlEncoder); + static readonly IJwtDecoder _decoder = new JwtDecoder(_jsonSerializer, _jwtBase64UrlEncoder); public RSA GetRSAPublicKey(Models.JsonWebKey key) => RSA.Create(new RSAParameters() { - Modulus = WebEncoders.Base64UrlDecode(key.n), - Exponent = WebEncoders.Base64UrlDecode(key.e), + Modulus = WebEncoders.Base64UrlDecode(key.N), + Exponent = WebEncoders.Base64UrlDecode(key.E), }); public string DecodeJWTHeader(string jwt) @@ -61,42 +61,61 @@ public JWKS GetJWKS(List certificates) var jsonWebKey = GetJsonWebKey(c); var exponent = WebEncoders.Base64UrlEncode(parameters.Exponent!); var modulus = WebEncoders.Base64UrlEncode(parameters.Modulus!); + return new Models.JsonWebKey() { - kty = jsonWebKey.Kty, - use = jsonWebKey.Use ?? "sig", - kid = jsonWebKey.Kid, - //x5t = jsonWebKey.X5t, - e = exponent, - n = modulus, - //x5c = jsonWebKey.X5c.ToList(), - //alg = jsonWebKey.Alg ?? "RS256", + Kty = jsonWebKey.Kty, + Use = jsonWebKey.Use ?? "sig", + Kid = jsonWebKey.Kid, + //X5t = jsonWebKey.X5t, + E = exponent, + N = modulus, + //X5c = jsonWebKey.X5c.ToList(), + Alg = jsonWebKey.Alg ?? "RS256", + }; + }).ToList() + }; + + public JWKS GetJWKS(List certificates) + => new JWKS() + { + Keys = certificates.Select(c => + { + var parameters = c.Certificate.GetRSAPublicKey()!.ExportParameters(false); + var jsonWebKey = GetJsonWebKey(c.Certificate); + var exponent = WebEncoders.Base64UrlEncode(parameters.Exponent!); + var modulus = WebEncoders.Base64UrlEncode(parameters.Modulus!); + + return new Models.JsonWebKey() + { + Kty = jsonWebKey.Kty, + Use = c.KeyUsage == Enums.KeyUsageTypes.Signature ? "sig" : c.KeyUsage == Enums.KeyUsageTypes.Encryption ? "enc" : default, + Kid = jsonWebKey.Kid, + //X5t = jsonWebKey.X5t, + E = exponent, + N = modulus, + //X5c = jsonWebKey.X5c.ToList(), + Alg = c.Algorithm }; }).ToList() }; public string CreateJWT(X509Certificate2 certificate, object payload) { + var key = GetJsonWebKey(certificate); RSA publicKey = certificate.GetRSAPublicKey()!; RSA privateKey = certificate.GetRSAPrivateKey()!; - - IJwtEncoder encoder = new JwtEncoder(new RS256Algorithm(publicKey, privateKey), - _jsonSerializer, - _jwtBase64UrlEncoder); - - var key = GetJsonWebKey(certificate); - + IJwtEncoder encoder = new JwtEncoder(new RS256Algorithm(publicKey, privateKey), _jsonSerializer, _jwtBase64UrlEncoder); var token = encoder.Encode(new Dictionary() { { SpidCieConst.Kid , key.Kid }, { SpidCieConst.Typ, SpidCieConst.TypValue } }, payload, null); + return token; } - public string CreateClientAssertion(string aud, - string clientId, - X509Certificate2 certificate) + public string CreateClientAssertion(string aud, string clientId, X509Certificate2 certificate) => CreateJWT(certificate, new Dictionary() { { SpidCieConst.Iss, clientId! }, @@ -109,5 +128,4 @@ public string CreateClientAssertion(string aud, private static Microsoft.IdentityModel.Tokens.JsonWebKey GetJsonWebKey(X509Certificate2 certificate) => JsonWebKeyConverter.ConvertFromX509SecurityKey(new X509SecurityKey(certificate)); - -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultAggregatorsRetriever.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultAggregatorsRetriever.cs index 4a178d0..46adbf0 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultAggregatorsRetriever.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultAggregatorsRetriever.cs @@ -5,11 +5,11 @@ namespace Spid.Cie.OIDC.AspNetCore.Services.Defaults; -internal class DefaultAggregatorsRetriever : IAggregatorsRetriever +class DefaultAggregatorsRetriever : IAggregatorsRetriever { public async Task> GetAggregators() { await Task.CompletedTask; return Enumerable.Empty(); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultIdentityProviderSelector.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultIdentityProviderSelector.cs index 4b16894..616e436 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultIdentityProviderSelector.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultIdentityProviderSelector.cs @@ -5,7 +5,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services.Defaults; -internal class DefaultIdentityProviderSelector : IIdentityProviderSelector +class DefaultIdentityProviderSelector : IIdentityProviderSelector { private readonly IIdentityProvidersHandler _idpHandler; private readonly IHttpContextAccessor _httpContextAccessor; @@ -21,10 +21,10 @@ public DefaultIdentityProviderSelector(IHttpContextAccessor httpContextAccessor, var identityProviders = await _idpHandler.GetIdentityProviders(); var provider = (string?)_httpContextAccessor.HttpContext!.Request.Query[SpidCieConst.IdPSelectorKey] ?? (string?)_httpContextAccessor.HttpContext!.Items[SpidCieConst.IdPSelectorKey]; + if (!string.IsNullOrWhiteSpace(provider)) - { return identityProviders.FirstOrDefault(idp => idp.Uri.Equals(provider, System.StringComparison.InvariantCultureIgnoreCase)); - } + return default; } } diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultIdentityProvidersRetriever.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultIdentityProvidersRetriever.cs index cdb0b70..d7f4ad9 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultIdentityProvidersRetriever.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultIdentityProvidersRetriever.cs @@ -4,17 +4,9 @@ namespace Spid.Cie.OIDC.AspNetCore.Services.Defaults; -internal class DefaultIdentityProvidersRetriever : IIdentityProvidersRetriever +class DefaultIdentityProvidersRetriever : IIdentityProvidersRetriever { - public async Task> GetSpidIdentityProviders() - { - await Task.CompletedTask; - return Enumerable.Empty(); - } + public async Task> GetSpidIdentityProviders() => await Task.FromResult(Enumerable.Empty()); - public async Task> GetCieIdentityProviders() - { - await Task.CompletedTask; - return Enumerable.Empty(); - } -} + public async Task> GetCieIdentityProviders() => await Task.FromResult(Enumerable.Empty()); +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultLogPersister.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultLogPersister.cs index 94f33f2..bfbab50 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultLogPersister.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultLogPersister.cs @@ -5,9 +5,9 @@ namespace Spid.Cie.OIDC.AspNetCore.Services.Defaults; -internal class DefaultLogPersister : ILogPersister +class DefaultLogPersister : ILogPersister { - private readonly ILogger _logger; + readonly ILogger _logger; public DefaultLogPersister(ILogger logger) { @@ -37,4 +37,4 @@ public Task LogResponse(Uri requestUri, HttpStatusCode statusCode, string? conte _logger.LogInformation($"LogResponse from {requestUri.OriginalString}: {content}"); return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultRelyingPartiesRetriever.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultRelyingPartiesRetriever.cs index 7136354..7a7ab6b 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultRelyingPartiesRetriever.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultRelyingPartiesRetriever.cs @@ -5,11 +5,11 @@ namespace Spid.Cie.OIDC.AspNetCore.Services.Defaults; -internal class DefaultRelyingPartiesRetriever : IRelyingPartiesRetriever +class DefaultRelyingPartiesRetriever : IRelyingPartiesRetriever { public async Task> GetRelyingParties() { await Task.CompletedTask; return Enumerable.Empty(); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultRelyingPartySelector.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultRelyingPartySelector.cs index ec7fede..1a8de59 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultRelyingPartySelector.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/Defaults/DefaultRelyingPartySelector.cs @@ -8,10 +8,10 @@ namespace Spid.Cie.OIDC.AspNetCore.Services.Defaults; -internal class DefaultRelyingPartySelector : IRelyingPartySelector +class DefaultRelyingPartySelector : IRelyingPartySelector { - private readonly IRelyingPartiesHandler _retriever; - private readonly IHttpContextAccessor _httpContextAccessor; + readonly IRelyingPartiesHandler _retriever; + readonly IHttpContextAccessor _httpContextAccessor; public DefaultRelyingPartySelector(IRelyingPartiesHandler retriever, IHttpContextAccessor httpContextAccessor) @@ -37,4 +37,4 @@ public DefaultRelyingPartySelector(IRelyingPartiesHandler retriever, return rp; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IAggregatorsHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IAggregatorsHandler.cs index 2cf1f16..65ab8ef 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IAggregatorsHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IAggregatorsHandler.cs @@ -4,7 +4,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal interface IAggregatorsHandler +interface IAggregatorsHandler { Task> GetAggregators(); } diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IAggregatorsRetriever.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IAggregatorsRetriever.cs index cba1c0c..3332585 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IAggregatorsRetriever.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IAggregatorsRetriever.cs @@ -7,4 +7,4 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; public interface IAggregatorsRetriever { Task> GetAggregators(); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/ICryptoService.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/ICryptoService.cs index 2fe0d52..fd25ba0 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/ICryptoService.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/ICryptoService.cs @@ -5,14 +5,23 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal interface ICryptoService +interface ICryptoService { - string CreateClientAssertion(string aud, string clientId, X509Certificate2 certificate); - string DecodeJose(string jose, X509Certificate2 certificate); string DecodeJWT(string jwt); + string DecodeJWTHeader(string jwt); + + RSA GetRSAPublicKey(JsonWebKey key); + JWKS GetJWKS(List certificates); - RSA GetRSAPublicKey(Models.JsonWebKey key); - string CreateJWT(X509Certificate2 certificate, object payload); + string ValidateJWTSignature(string jwt, RSA publicKey); + + JWKS GetJWKS(List certificates); + + string DecodeJose(string jose, X509Certificate2 certificate); + + string CreateJWT(X509Certificate2 certificate, object payload); + + string CreateClientAssertion(string aud, string clientId, X509Certificate2 certificate); } diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProviderSelector.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProviderSelector.cs index f118be1..b957710 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProviderSelector.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProviderSelector.cs @@ -3,7 +3,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal interface IIdentityProviderSelector +interface IIdentityProviderSelector { Task GetSelectedIdentityProvider(); } diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProvidersHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProvidersHandler.cs index 12e5bfe..da8f4d9 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProvidersHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProvidersHandler.cs @@ -7,4 +7,4 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; public interface IIdentityProvidersHandler { Task> GetIdentityProviders(); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProvidersRetriever.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProvidersRetriever.cs index addea25..d26c74f 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProvidersRetriever.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IIdentityProvidersRetriever.cs @@ -7,4 +7,4 @@ public interface IIdentityProvidersRetriever { Task> GetSpidIdentityProviders(); Task> GetCieIdentityProviders(); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/ILogPersister.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/ILogPersister.cs index 1471925..d2ea15e 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/ILogPersister.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/ILogPersister.cs @@ -10,4 +10,4 @@ public interface ILogPersister Task LogGetEntityStatement(string url, string esJwt); Task LogRequest(Uri requestUri, string? content); Task LogResponse(Uri requestUri, HttpStatusCode statusCode, string? content); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IMetadataPolicyHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IMetadataPolicyHandler.cs index b9fb292..2d20ed1 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IMetadataPolicyHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IMetadataPolicyHandler.cs @@ -2,7 +2,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal interface IMetadataPolicyHandler +interface IMetadataPolicyHandler { OpenIdConnectConfiguration? ApplyMetadataPolicy(string opDecodedJwt, string metadataPolicy); } diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartiesHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartiesHandler.cs index 1c8c1b5..6b4ab64 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartiesHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartiesHandler.cs @@ -4,7 +4,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal interface IRelyingPartiesHandler +interface IRelyingPartiesHandler { Task> GetRelyingParties(); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartiesRetriever.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartiesRetriever.cs index df4869a..4b2b5cd 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartiesRetriever.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartiesRetriever.cs @@ -7,4 +7,4 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; public interface IRelyingPartiesRetriever { Task> GetRelyingParties(); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartySelector.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartySelector.cs index 659811e..67480aa 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartySelector.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IRelyingPartySelector.cs @@ -3,7 +3,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal interface IRelyingPartySelector +interface IRelyingPartySelector { Task GetSelectedRelyingParty(); } diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/ITokenValidationParametersRetriever.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/ITokenValidationParametersRetriever.cs index 2bcd119..eea4c24 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/ITokenValidationParametersRetriever.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/ITokenValidationParametersRetriever.cs @@ -3,7 +3,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal interface ITokenValidationParametersRetriever +interface ITokenValidationParametersRetriever { Task RetrieveTokenValidationParameter(); -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/ITrustChainManager.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/ITrustChainManager.cs index 6f87337..02db3a1 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/ITrustChainManager.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/ITrustChainManager.cs @@ -3,8 +3,11 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal interface ITrustChainManager +interface ITrustChainManager { - Task BuildTrustChain(string url); - TrustChain? GetResolvedTrustChain(string sub, string anchor); -} + Task BuildTrustChain(string url); + + Task BuildRPTrustChain(string url); + + TrustChain? GetResolvedTrustChain(string sub, string anchor) where T : EntityConfiguration; +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/IdentityProvidersHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/IdentityProvidersHandler.cs index 1cf44fc..a17234a 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/IdentityProvidersHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/IdentityProvidersHandler.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Spid.Cie.OIDC.AspNetCore.Configuration; +using Spid.Cie.OIDC.AspNetCore.Enums; using Spid.Cie.OIDC.AspNetCore.Models; using System.Collections.Generic; using System.Linq; @@ -8,74 +9,69 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal class IdentityProvidersHandler : IIdentityProvidersHandler +class IdentityProvidersHandler : IIdentityProvidersHandler { - private readonly IOptionsMonitor _options; - private readonly IIdentityProvidersRetriever _idpRetriever; - private readonly ITrustChainManager _trustChainManager; - private readonly ILogger _logger; + readonly ITrustChainManager _trustChainManager; + readonly IOptionsMonitor _options; + readonly IIdentityProvidersRetriever _idpRetriever; + readonly ILogger _logger; - public IdentityProvidersHandler(IOptionsMonitor options, - IIdentityProvidersRetriever idpRetriever, - ITrustChainManager trustChainManager, - ILogger logger) + public IdentityProvidersHandler(IOptionsMonitor options, IIdentityProvidersRetriever idpRetriever, + ITrustChainManager trustChainManager, ILogger logger) { + _logger = logger; _options = options; _idpRetriever = idpRetriever; _trustChainManager = trustChainManager; - _logger = logger; } public async Task> GetIdentityProviders() { - List result = new(); + List result = new(); - var spidIdP = _options.CurrentValue.SpidOPs - .Union(await _idpRetriever.GetSpidIdentityProviders()) - .ToList(); - foreach (var url in spidIdP) + var idpUrls = _options.CurrentValue.CieOPs.Union(await _idpRetriever.GetCieIdentityProviders()).Select(ip => new { - var idpConf = await _trustChainManager.BuildTrustChain(url); - - if (idpConf is not null) - { - result.Add(CreateSpidIdentityProvider(idpConf)); - } - } + Type = IdentityProviderTypes.CIE, + Url = ip + }).Union(_options.CurrentValue.SpidOPs.Union(await _idpRetriever.GetSpidIdentityProviders()).Select(ip => new + { + Type = IdentityProviderTypes.SPID, + Url = ip + })).ToList(); - var cieIdP = _options.CurrentValue.CieOPs - .Union(await _idpRetriever.GetCieIdentityProviders()) - .ToList(); - foreach (var url in cieIdP) + foreach (var idp in idpUrls) { - var idpConf = await _trustChainManager.BuildTrustChain(url); + var idpConf = await _trustChainManager.BuildTrustChain(idp.Url); - if (idpConf is not null) - { - result.Add(CreateCieIdentityProvider(idpConf)); - } + if (idpConf != null) + result.Add(idp.Type == IdentityProviderTypes.CIE ? CreateIdentityProvider(idpConf) : + CreateIdentityProvider(idpConf)); } - return result; + return result.Where(r => r != default).ToList()!; } - private static IdentityProvider CreateSpidIdentityProvider(IdPEntityConfiguration conf) - => new SpidIdentityProvider() - { - EntityConfiguration = conf, - Uri = conf.Subject ?? string.Empty, - OrganizationLogoUrl = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("logo_uri", out object? logoUri) ? logoUri as string ?? string.Empty : string.Empty, - OrganizationName = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("organization_name", out object? organizationName) ? organizationName as string ?? string.Empty : string.Empty, - SupportedAcrValues = conf.Metadata.OpenIdProvider.AcrValuesSupported.ToList(), - }; - - private static IdentityProvider CreateCieIdentityProvider(IdPEntityConfiguration conf) - => new CieIdentityProvider() - { - EntityConfiguration = conf, - Uri = conf.Subject ?? string.Empty, - OrganizationLogoUrl = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("logo_uri", out object? logoUri) ? logoUri as string ?? string.Empty : string.Empty, - OrganizationName = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("organization_name", out object? organizationName) ? organizationName as string ?? string.Empty : string.Empty, - SupportedAcrValues = conf.Metadata.OpenIdProvider.AcrValuesSupported.ToList(), - }; -} + static T? CreateIdentityProvider(OPEntityConfiguration conf) + where T : IdentityProvider + { + return conf == default ? default : + typeof(T).Equals(typeof(SpidIdentityProvider)) ? + new SpidIdentityProvider() + { + EntityConfiguration = conf, + Uri = conf.Subject ?? string.Empty, + OrganizationLogoUrl = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("logo_uri", out object? spidLogoUri) ? spidLogoUri as string ?? string.Empty : string.Empty, + OrganizationName = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("organization_name", out object? spidOrganizationName) ? spidOrganizationName as string ?? string.Empty : string.Empty, + SupportedAcrValues = conf.Metadata.OpenIdProvider.AcrValuesSupported.ToList(), + } as T : + typeof(T).Equals(typeof(CieIdentityProvider)) ? + new CieIdentityProvider() + { + EntityConfiguration = conf, + Uri = conf.Subject ?? string.Empty, + OrganizationLogoUrl = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("logo_uri", out object? cieLogoUri) ? cieLogoUri as string ?? string.Empty : string.Empty, + OrganizationName = conf.Metadata.OpenIdProvider.AdditionalData.TryGetValue("organization_name", out object? cieOrganizationName) ? cieOrganizationName as string ?? string.Empty : string.Empty, + SupportedAcrValues = conf.Metadata.OpenIdProvider.AcrValuesSupported.ToList(), + } as T : default; + } +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/MetadataPolicyHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/MetadataPolicyHandler.cs index 5654fed..af10858 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/MetadataPolicyHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/MetadataPolicyHandler.cs @@ -7,7 +7,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal class MetadataPolicyHandler : IMetadataPolicyHandler +class MetadataPolicyHandler : IMetadataPolicyHandler { private readonly ILogger _logger; @@ -196,6 +196,4 @@ public MetadataPolicyHandler(ILogger logger) { return token.FirstOrDefault(p => ((JProperty)p).Name.Equals(name, System.StringComparison.OrdinalIgnoreCase)) as JProperty; } -} - - +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/RelyingPartiesHandler.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/RelyingPartiesHandler.cs index e2272ef..e1b5d5f 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/RelyingPartiesHandler.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/RelyingPartiesHandler.cs @@ -7,7 +7,7 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal class RelyingPartiesHandler : IRelyingPartiesHandler +class RelyingPartiesHandler : IRelyingPartiesHandler { private readonly IOptionsMonitor _options; private readonly IRelyingPartiesRetriever _rpRetriever; @@ -25,4 +25,4 @@ public async Task> GetRelyingParties() .Union(_options.CurrentValue.Aggregators?.SelectMany(a => a.RelyingParties)) .Union(await _rpRetriever.GetRelyingParties()).ToList(); } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/TokenValidationParametersRetriever.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/TokenValidationParametersRetriever.cs index cf6ce02..66de5bd 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/TokenValidationParametersRetriever.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/TokenValidationParametersRetriever.cs @@ -1,6 +1,7 @@ -using Microsoft.IdentityModel.Tokens; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Tokens; using Spid.Cie.OIDC.AspNetCore.Helpers; -using Spid.Cie.OIDC.AspNetCore.Models; using Spid.Cie.OIDC.AspNetCore.Resources; using System; using System.Linq; @@ -8,16 +9,20 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal class TokenValidationParametersRetriever : ITokenValidationParametersRetriever +class TokenValidationParametersRetriever : ITokenValidationParametersRetriever { - private readonly IRelyingPartySelector _rpSelector; - private readonly IIdentityProviderSelector _idpSelector; + readonly IAggregatorsHandler _aggHandler; + readonly IRelyingPartySelector _rpSelector; + readonly IIdentityProviderSelector _idpSelector; + readonly IHttpContextAccessor _httpContextAccessor; - public TokenValidationParametersRetriever(IIdentityProviderSelector idpSelector, - IRelyingPartySelector rpSelector) + public TokenValidationParametersRetriever(IIdentityProviderSelector idpSelector, IRelyingPartySelector rpSelector, IAggregatorsHandler aggHandler, + IHttpContextAccessor httpContextAccessor) { + _aggHandler = aggHandler; _rpSelector = rpSelector; _idpSelector = idpSelector; + _httpContextAccessor = httpContextAccessor; } public async Task RetrieveTokenValidationParameter() @@ -26,6 +31,24 @@ public async Task RetrieveTokenValidationParameter() Throw.If(identityProvider is null, ErrorLocalization.IdentityProviderNotFound); var relyingParty = await _rpSelector.GetSelectedRelyingParty(); + + if (relyingParty == default) + { + var aggregators = await _aggHandler.GetAggregators(); + var uri = new Uri(UriHelper.GetEncodedUrl(_httpContextAccessor.HttpContext.Request)) + .GetLeftPart(UriPartial.Path) + .Replace(SpidCieConst.JsonEntityConfigurationPath, "") + .Replace(SpidCieConst.EntityConfigurationPath, "") + .Replace(SpidCieConst.CallbackPath, "") + .Replace(SpidCieConst.SignedOutCallbackPath, "") + .Replace(SpidCieConst.RemoteSignOutPath, "") + .EnsureTrailingSlash(); + + relyingParty = aggregators.SelectMany(a => a.RelyingParties) + .OrderByDescending(r => r.Id.Length) + .FirstOrDefault(r => uri.StartsWith(r.Id.EnsureTrailingSlash(), StringComparison.OrdinalIgnoreCase)); + } + Throw.If(relyingParty is null, ErrorLocalization.RelyingPartyNotFound); return new TokenValidationParameters @@ -42,4 +65,4 @@ public async Task RetrieveTokenValidationParameter() ValidIssuer = identityProvider.EntityConfiguration.Issuer }; } -} +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Services/TrustChainManager.cs b/src/Spid.Cie.OIDC.AspNetCore/Services/TrustChainManager.cs index cee97f3..7254f66 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Services/TrustChainManager.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/Services/TrustChainManager.cs @@ -15,57 +15,182 @@ namespace Spid.Cie.OIDC.AspNetCore.Services; -internal class TrustChainManager : ITrustChainManager +class TrustChainManager : ITrustChainManager { - private readonly HttpClient _httpClient; - private readonly ICryptoService _cryptoService; - private readonly ILogPersister _logPersister; - private readonly ILogger _logger; - private readonly IMetadataPolicyHandler _metadataPolicyHandler; - private static readonly ConcurrentDictionary _trustChainCache = new(); - private static readonly SemaphoreSlim _syncLock = new SemaphoreSlim(1, 1); - - public TrustChainManager(IHttpClientFactory httpClientFactory, - ICryptoService cryptoService, - IMetadataPolicyHandler metadataPolicyHandler, - ILogPersister logPersister, - ILogger logger) + readonly HttpClient _httpClient; + readonly ILogPersister _logPersister; + readonly ICryptoService _cryptoService; + readonly ILogger _logger; + static readonly SemaphoreSlim _syncLock = new(1, 1); + readonly IMetadataPolicyHandler _metadataPolicyHandler; + static readonly ConcurrentDictionary> _rpTrustChainCache = new(); + static readonly ConcurrentDictionary> _idpTrustChainCache = new(); + + public TrustChainManager(IHttpClientFactory httpClientFactory, ICryptoService cryptoService, IMetadataPolicyHandler metadataPolicyHandler, + ILogPersister logPersister, ILogger logger) { - _httpClient = httpClientFactory.CreateClient(SpidCieConst.BackchannelClientName); - _cryptoService = cryptoService; - _logPersister = logPersister; _logger = logger; + _logPersister = logPersister; + _cryptoService = cryptoService; _metadataPolicyHandler = metadataPolicyHandler; + _httpClient = httpClientFactory.CreateClient(SpidCieConst.BackchannelClientName); } - public TrustChain? GetResolvedTrustChain(string sub, string anchor) + public TrustChain? GetResolvedTrustChain(string sub, string anchor) where T : EntityConfiguration { - if (_trustChainCache.ContainsKey(sub) - && _trustChainCache[sub].TrustAnchorUsed.Equals(anchor, StringComparison.OrdinalIgnoreCase) - && _trustChainCache[sub].ExpiresOn >= DateTimeOffset.UtcNow) + return typeof(T).Equals(typeof(OPEntityConfiguration)) && _idpTrustChainCache.ContainsKey(sub) && + _idpTrustChainCache[sub].TrustAnchorUsed.Equals(anchor, StringComparison.OrdinalIgnoreCase) && _idpTrustChainCache[sub].ExpiresOn >= DateTimeOffset.UtcNow ? + _idpTrustChainCache[sub] as TrustChain : + typeof(T).Equals(typeof(RPEntityConfiguration)) && _rpTrustChainCache.ContainsKey(sub) && + _rpTrustChainCache[sub].TrustAnchorUsed.Equals(anchor, StringComparison.OrdinalIgnoreCase) && _rpTrustChainCache[sub].ExpiresOn >= DateTimeOffset.UtcNow ? + _rpTrustChainCache[sub] as TrustChain : default; + } + + public async Task BuildRPTrustChain(string url) + { + if (!_idpTrustChainCache.ContainsKey(url) || _idpTrustChainCache[url].ExpiresOn < DateTimeOffset.UtcNow) { - return _trustChainCache[sub]; + if (!await _syncLock.WaitAsync(TimeSpan.FromSeconds(10))) + { + _logger.LogWarning("TrustChain cache Sync Lock expired."); + return default; + } + + if (!_idpTrustChainCache.ContainsKey(url) || _idpTrustChainCache[url].ExpiresOn < DateTimeOffset.UtcNow) + { + List trustChain = new(); + string? trustAnchorUsed = default; + + try + { + (RPEntityConfiguration rpConf, string? decodedRPJwt, string? rpJwt) = await ValidateAndDecodeEntityConfiguration(url); + + if (rpConf is null || rpJwt is null || rpConf.ExpiresOn < DateTime.UtcNow) + { + _logger.LogWarning($"EntityConfiguration not retrieved for RP {url}"); + + return default; + } + + bool rpValidated = false; + DateTimeOffset expiresOn = rpConf.ExpiresOn; + + foreach (var saHint in rpConf.AuthorityHints ?? new()) + { + trustChain.Clear(); + + (SAEntityConfiguration? saConf, string? decodedSAJwt, string? saJwt) = await ValidateAndDecodeEntityConfiguration(saHint); + + if (saConf is null || saJwt is null || saConf.ExpiresOn < DateTime.UtcNow) + { + _logger.LogWarning($"EntityConfiguration not retrieved for SA {saHint}"); + + continue; + } + + trustChain.Add(saJwt); + + if (saConf.ExpiresOn < expiresOn) + expiresOn = saConf.ExpiresOn; + + var saFetchUrl = $"{saConf.Metadata.FederationEntity.FederationFetchEndpoint}?sub={url}"; + (EntityStatement? rpEntityStatement, string? decodedRPEsJwt, string? esRPJwt) = await GetAndValidateEntityStatement(saFetchUrl, rpJwt); + + if (rpEntityStatement is null || esRPJwt is null || rpEntityStatement.ExpiresOn < DateTime.UtcNow) + { + _logger.LogWarning($"EntityStatement not retrieved for RP {url}"); + continue; + } + + trustChain.Add(esRPJwt); + + if (rpEntityStatement.ExpiresOn < expiresOn) + expiresOn = rpEntityStatement.ExpiresOn; + + foreach (var taHint in saConf.AuthorityHints ?? new()) + { + (TAEntityConfiguration? taConf, string? decodedTAJwt, string? taJwt) = await ValidateAndDecodeEntityConfiguration(taHint); + + if (taConf is null || taJwt is null || taConf.ExpiresOn < DateTime.UtcNow) + { + _logger.LogWarning($"EntityConfiguration not retrieved for TA {taHint}"); + + continue; + } + + trustChain.Add(taJwt); + + if (taConf.ExpiresOn < expiresOn) + expiresOn = taConf.ExpiresOn; + + var taFetchUrl = $"{taConf.Metadata.FederationEntity.FederationFetchEndpoint}?sub={saConf.Subject}"; + (EntityStatement? saEntityStatement, string? decodedSAEsJwt, string? esSAJwt) = await GetAndValidateEntityStatement(taFetchUrl, saJwt); + + if (saEntityStatement is null || esSAJwt is null || saEntityStatement.ExpiresOn < DateTime.UtcNow) + { + _logger.LogWarning($"EntityStatement not retrieved for SA {saConf.Subject}"); + continue; + } + + trustChain.Add(esSAJwt); + trustChain.Add(rpJwt); + + if (saEntityStatement.ExpiresOn < expiresOn) + expiresOn = saEntityStatement.ExpiresOn; + + rpValidated = true; + trustAnchorUsed = taHint; + break; + } + } + + if (rpValidated && rpConf is not null && trustAnchorUsed is not null) + { + _rpTrustChainCache.AddOrUpdate(url, new TrustChain() + { + ExpiresOn = expiresOn, + EntityConfiguration = rpConf, + Chain = trustChain, + TrustAnchorUsed = trustAnchorUsed + }, (oldValue, newValue) => newValue); + } + } + catch (Exception ex) + { + _logger.LogError(ex, ex.Message); + throw; + } + finally + { + _syncLock.Release(); + } + } } - return null; + + return _rpTrustChainCache.ContainsKey(url) && _rpTrustChainCache[url].ExpiresOn.Add(SpidCieConst.TrustChainExpirationGracePeriod) > DateTimeOffset.UtcNow + ? _rpTrustChainCache[url].EntityConfiguration + : null; } - public async Task BuildTrustChain(string url) + + public async Task BuildTrustChain(string url) { - if (!_trustChainCache.ContainsKey(url) || _trustChainCache[url].ExpiresOn < DateTimeOffset.UtcNow) + if (!_idpTrustChainCache.ContainsKey(url) || _idpTrustChainCache[url].ExpiresOn < DateTimeOffset.UtcNow) { if (!await _syncLock.WaitAsync(TimeSpan.FromSeconds(10))) { _logger.LogWarning("TrustChain cache Sync Lock expired."); return default; } - if (!_trustChainCache.ContainsKey(url) || _trustChainCache[url].ExpiresOn < DateTimeOffset.UtcNow) + + if (!_idpTrustChainCache.ContainsKey(url) || _idpTrustChainCache[url].ExpiresOn < DateTimeOffset.UtcNow) { try { List trustChain = new(); string? trustAnchorUsed = default; - (IdPEntityConfiguration? opConf, string? decodedOPJwt, string? opJwt) = await ValidateAndDecodeEntityConfiguration(url); + (OPEntityConfiguration? opConf, string? decodedOPJwt, string? opJwt) = await ValidateAndDecodeEntityConfiguration(url); if (opConf is null || opJwt is null || opConf.ExpiresOn < DateTime.UtcNow) { _logger.LogWarning($"EntityConfiguration not retrieved for OP {url}"); @@ -105,7 +230,7 @@ public TrustChainManager(IHttpClientFactory httpClientFactory, var esExpiresOn = entityStatement.ExpiresOn; // Apply policy - opConf!.Metadata!.OpenIdProvider = _metadataPolicyHandler.ApplyMetadataPolicy(decodedOPJwt!, entityStatement.MetadataPolicy.ToJsonString()); + //opConf!.Metadata!.OpenIdProvider = _metadataPolicyHandler.ApplyMetadataPolicy(decodedOPJwt!, entityStatement.MetadataPolicy.ToJsonString()); if (opConf!.Metadata!.OpenIdProvider is not null) { @@ -146,10 +271,11 @@ public TrustChainManager(IHttpClientFactory httpClientFactory, } if (opValidated && opConf is not null && trustAnchorUsed is not null) { - _trustChainCache.AddOrUpdate(url, new TrustChain() + _idpTrustChainCache.AddOrUpdate(url, new TrustChain() { ExpiresOn = expiresOn, - OpConf = opConf, + EntityConfiguration = opConf, + //OpConf = opConf, Chain = trustChain, TrustAnchorUsed = trustAnchorUsed }, (oldValue, newValue) => newValue); @@ -166,8 +292,8 @@ public TrustChainManager(IHttpClientFactory httpClientFactory, } } } - return _trustChainCache.ContainsKey(url) && _trustChainCache[url].ExpiresOn.Add(SpidCieConst.TrustChainExpirationGracePeriod) > DateTimeOffset.UtcNow - ? _trustChainCache[url].OpConf + return _idpTrustChainCache.ContainsKey(url) && _idpTrustChainCache[url].ExpiresOn.Add(SpidCieConst.TrustChainExpirationGracePeriod) > DateTimeOffset.UtcNow + ? _idpTrustChainCache[url].EntityConfiguration//_trustChainCache[url].OpConf : null; } @@ -197,7 +323,7 @@ public TrustChainManager(IHttpClientFactory httpClientFactory, var kid = (string)header[SpidCieConst.Kid]; Throw.If(string.IsNullOrWhiteSpace(kid), $"No Kid specified in the EntityConfiguration JWT Header for url {metadataAddress}: {decodedJwtHeader}"); - var key = conf!.JWKS.Keys.FirstOrDefault(k => k.kid.Equals(kid, StringComparison.InvariantCultureIgnoreCase)); + var key = conf!.JWKS.Keys.FirstOrDefault(k => k.Kid.Equals(kid, StringComparison.InvariantCultureIgnoreCase)); Throw.If(key is null, $"No key found with kid {kid} for url {metadataAddress}: {decodedJwtHeader}"); RSA publicKey = _cryptoService.GetRSAPublicKey(key!); @@ -237,7 +363,7 @@ public TrustChainManager(IHttpClientFactory httpClientFactory, var kid = (string)opHeader[SpidCieConst.Kid]; Throw.If(string.IsNullOrWhiteSpace(kid), $"No Kid specified in the EntityConfiguration JWT Header: {decodedOpJwtHeader}"); - var key = entityStatement!.JWKS.Keys.FirstOrDefault(k => k.kid.Equals(kid, StringComparison.InvariantCultureIgnoreCase)); + var key = entityStatement!.JWKS.Keys.FirstOrDefault(k => k.Kid.Equals(kid, StringComparison.InvariantCultureIgnoreCase)); Throw.If(key is null, $"No key found with kid {kid} in the EntityStatement at url {url}: {decodedEsJwt}"); RSA publicKey = _cryptoService.GetRSAPublicKey(key!); @@ -253,5 +379,4 @@ public TrustChainManager(IHttpClientFactory httpClientFactory, return default; } } -} - +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Spid.Cie.OIDC.AspNetCore.csproj b/src/Spid.Cie.OIDC.AspNetCore/Spid.Cie.OIDC.AspNetCore.csproj index 1f6cec7..b076f12 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Spid.Cie.OIDC.AspNetCore.csproj +++ b/src/Spid.Cie.OIDC.AspNetCore/Spid.Cie.OIDC.AspNetCore.csproj @@ -27,6 +27,7 @@ + @@ -34,6 +35,7 @@ + @@ -48,7 +50,7 @@ - + @@ -80,4 +82,12 @@ ErrorLocalization.Designer.cs + + + + + + + + diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieConst.cs b/src/Spid.Cie.OIDC.AspNetCore/SpidCieConst.cs similarity index 80% rename from src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieConst.cs rename to src/Spid.Cie.OIDC.AspNetCore/SpidCieConst.cs index b47e3cc..3f7dc85 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidCieConst.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/SpidCieConst.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; -namespace Spid.Cie.OIDC.AspNetCore.Models; +namespace Spid.Cie.OIDC.AspNetCore; /// /// Default values related to Spid authentication handler @@ -9,140 +9,69 @@ namespace Spid.Cie.OIDC.AspNetCore.Models; [ExcludeFromCodeCoverage] public sealed class SpidCieConst { - /// - /// The default authentication type used when registering the SpidHandler. - /// - public const string AuthenticationScheme = "SpidCieOIDC"; - - /// - /// The default display name used when registering the SpidHandler. - /// - public const string DisplayName = "SpidCieOIDC"; - - /// - /// Constant used to identify userstate inside AuthenticationProperties that have been serialized in the 'wctx' parameter. - /// - public const string UserstatePropertiesKey = "SpidCieOIDC.Userstate"; - - /// - /// The cookie name - /// - public const string CookieName = "SpidCieOIDC.Properties"; - - public const string SpidLevelBaseURI = "https://www.spid.gov.it/"; - public const string SpidL1 = $"{SpidLevelBaseURI}{nameof(SpidL1)}"; - public const string SpidL2 = $"{SpidLevelBaseURI}{nameof(SpidL2)}"; - public const string SpidL3 = $"{SpidLevelBaseURI}{nameof(SpidL3)}"; - public const string DefaultAcr = SpidL2; - - public const string ResponseType = "code"; - - public const string AuthorizationCode = "authorization_code"; - - public const string RefreshToken = "refresh_token"; - - public const string OpenIdScope = "openid"; - - public const string OfflineScope = "offline_access"; - - public const string Prompt = "consent login"; - - public const string JWKGeneratorPath = "generatejwk"; - - public const string JWKGeneratorContentType = "application/json"; - - public const string OPListPath = "list/?type=openid_provider"; - - public const string EntityConfigurationPath = ".well-known/openid-federation"; - public const string JsonEntityConfigurationPath = ".well-known/openid-federation/json"; - - public const string ResolveEndpointPath = "resolve"; - - public const string FetchEndpointPath = "fetch"; - - public const string ListEndpointPath = "list"; - - public const string TrustMarkStatusEndpointPath = "trust_mark_status"; - - - public const string EntityConfigurationContentType = "application/entity-statement+jwt"; - public const string JsonContentType = "application/json"; - - public const string ResolveContentType = "application/resolve-response+jwt"; - - public const int EntityConfigurationExpirationInMinutes = 2880; - - public static TimeSpan TrustChainExpirationGracePeriod = TimeSpan.FromHours(24); - - public const string RPApplicationType = "web"; - - public const string RPClientRegistrationType = "automatic"; - - public const string RPSubjectType = "pairwise"; - - public const string RequestParameter = "request"; - - public const string ClientId = "client_id"; - - public const string ResponseTypeParameter = "response_type"; - - public const string Scope = "scope"; - - public const string CodeChallenge = "code_challenge"; - - public const string CodeChallengeMethod = "code_challenge_method"; - - public const string Nonce = "nonce"; - - public const string PromptParameter = "prompt"; - - public const string RedirectUri = "redirect_uri"; - - public const string AcrValues = "acr_values"; - - public const string State = "state"; - - public const string Claims = "claims"; - public const string Kid = "kid"; - public const string Typ = "typ"; - - public const string TypValue = "entity-statement+jwt"; - public const string Iss = "iss"; - public const string Sub = "sub"; - public const string Iat = "iat"; - public const string Exp = "exp"; - public const string Aud = "aud"; - public const string Jti = "jti"; - - public const string ClientAssertion = "client_assertion"; - - public const string ClientAssertionType = "client_assertion_type"; - - public const string ClientAssertionTypeValue = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; - + public const string Nonce = "nonce"; + public const string Scope = "scope"; + public const string State = "state"; public const string Token = "token"; - - public const string IdPSelectorKey = "provider"; - + public const string Claims = "claims"; + public const string DefaultAcr = SpidL2; + public const string ResponseType = "code"; + public const string OpenIdScope = "openid"; + public const string ClientId = "client_id"; + public const string AcrValues = "acr_values"; + public const string Prompt = "consent login"; + public const string ListEndpointPath = "list"; + public const string RPApplicationType = "web"; + public const string RPSubjectType = "pairwise"; + public const string PromptParameter = "prompt"; public const string RPSelectorKey = "clientId"; - + public const string IdPSelectorKey = "provider"; + public const string DisplayName = "SpidCieOIDC";// The default display name used when registering the SpidHandler. + public const string FetchEndpointPath = "fetch"; + public const string RedirectUri = "redirect_uri"; + public const string RequestParameter = "request"; public const string DummyUrl = "https://dummy.org"; - - public const string RevocationEndpoint = "revocation_endpoint"; - - public const string CallbackPath = "/signin-spidcie"; - - public const string SignedOutCallbackPath = "/signout-callback-spidcie"; - + public const string RefreshToken = "refresh_token"; + public const string OfflineScope = "offline_access"; + public const string ResolveEndpointPath = "resolve"; + public const string JWKGeneratorPath = "generatejwk"; + public const string CodeChallenge = "code_challenge"; + public const string CallbackPath = "/signin-oidc-spidcie"; + public const string TypValue = "entity-statement+jwt"; + public const string ClientAssertion = "client_assertion"; + public const string JsonContentType = "application/json"; + public const string AuthenticationScheme = "SpidCieOIDC";// The default authentication type used when registering the SpidHandler. + public const string CookieName = "SpidCieOIDC.Properties"; // The cookie name + public const string RPClientRegistrationType = "automatic"; public const string RemoteSignOutPath = "/signout-spidcie"; - + public const string ResponseTypeParameter = "response_type"; + public const string AuthorizationCode = "authorization_code"; + public const string OPListPath = "list/?type=openid_provider"; + public const string RevocationEndpoint = "revocation_endpoint"; + public const int EntityConfigurationExpirationInMinutes = 2880; public const string BackchannelClientName = "SpidCieBackchannel"; -} + public const string JWKGeneratorContentType = "application/json"; + public const string CodeChallengeMethod = "code_challenge_method"; + public const string ClientAssertionType = "client_assertion_type"; + public const string SpidLevelBaseURI = "https://www.spid.gov.it/"; + public const string SpidL1 = $"{SpidLevelBaseURI}{nameof(SpidL1)}"; + public const string SpidL2 = $"{SpidLevelBaseURI}{nameof(SpidL2)}"; + public const string SpidL3 = $"{SpidLevelBaseURI}{nameof(SpidL3)}"; + public const string UserstatePropertiesKey = "SpidCieOIDC.Userstate";// Constant used to identify userstate inside AuthenticationProperties that have been serialized in the 'wctx' parameter. + public const string TrustMarkStatusEndpointPath = "trust_mark_status"; + public const string SignedOutCallbackPath = "/signout-callback-spidcie"; + public const string ResolveContentType = "application/resolve-response+jwt"; + public const string EntityConfigurationPath = ".well-known/openid-federation"; + public static TimeSpan TrustChainExpirationGracePeriod = TimeSpan.FromHours(24); + public const string JsonEntityConfigurationPath = ".well-known/openid-federation/json"; + public const string EntityConfigurationContentType = "application/entity-statement+jwt"; + public const string ClientAssertionTypeValue = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; +} \ No newline at end of file diff --git a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidConst.cs b/src/Spid.Cie.OIDC.AspNetCore/SpidConst.cs similarity index 97% rename from src/Spid.Cie.OIDC.AspNetCore/Models/SpidConst.cs rename to src/Spid.Cie.OIDC.AspNetCore/SpidConst.cs index f9ccd2d..e9fa341 100644 --- a/src/Spid.Cie.OIDC.AspNetCore/Models/SpidConst.cs +++ b/src/Spid.Cie.OIDC.AspNetCore/SpidConst.cs @@ -1,30 +1,29 @@ using System.Diagnostics.CodeAnalysis; -namespace Spid.Cie.OIDC.AspNetCore.Models; +namespace Spid.Cie.OIDC.AspNetCore; [ExcludeFromCodeCoverage] internal class SpidConst { - internal const string AttributesBaseURI = "https://attributes.eid.gov.it"; - + internal const string email = nameof(email); + internal const string gender = nameof(gender); + internal const string birthdate = nameof(birthdate); internal const string given_name = nameof(given_name); internal const string family_name = nameof(family_name); - internal const string fiscal_number = $"{AttributesBaseURI}{nameof(fiscal_number)}"; - internal const string email = nameof(email); - internal const string e_delivery_service = $"{AttributesBaseURI}{nameof(e_delivery_service)}"; + internal const string phone_number = nameof(phone_number); + internal const string place_of_birth = nameof(place_of_birth); internal const string mail = $"{AttributesBaseURI}{nameof(mail)}"; + internal const string document_details = nameof(document_details); + internal const string idCard = $"{AttributesBaseURI}{nameof(idCard)}"; internal const string address = $"{AttributesBaseURI}{nameof(address)}"; + internal const string AttributesBaseURI = "https://attributes.eid.gov.it"; + internal const string spid_code = $"{AttributesBaseURI}{nameof(spid_code)}"; + internal const string vat_number = $"{AttributesBaseURI}{nameof(vat_number)}"; internal const string company_name = $"{AttributesBaseURI}{nameof(company_name)}"; - internal const string countyOfBirth = $"{AttributesBaseURI}{nameof(countyOfBirth)}"; - internal const string birthdate = nameof(birthdate); internal const string eid_exp_date = $"{AttributesBaseURI}{nameof(eid_exp_date)}"; - internal const string gender = nameof(gender); - internal const string idCard = $"{AttributesBaseURI}{nameof(idCard)}"; - internal const string vat_number = $"{AttributesBaseURI}{nameof(vat_number)}"; - internal const string phone_number = nameof(phone_number); - internal const string place_of_birth = nameof(place_of_birth); - internal const string document_details = nameof(document_details); + internal const string fiscal_number = $"{AttributesBaseURI}{nameof(fiscal_number)}"; + internal const string countyOfBirth = $"{AttributesBaseURI}{nameof(countyOfBirth)}"; internal const string registered_office = $"{AttributesBaseURI}{nameof(registered_office)}"; - internal const string spid_code = $"{AttributesBaseURI}{nameof(spid_code)}"; + internal const string e_delivery_service = $"{AttributesBaseURI}{nameof(e_delivery_service)}"; internal const string company_fiscal_number = $"{AttributesBaseURI}{nameof(company_fiscal_number)}"; -} +} \ No newline at end of file