Skip to content

Commit

Permalink
Merge pull request #897 from ITfoxtec/test
Browse files Browse the repository at this point in the history
Test
  • Loading branch information
Revsgaard authored Aug 6, 2024
2 parents 847e1bd + 4fd35a0 commit 96bd2f8
Show file tree
Hide file tree
Showing 52 changed files with 612 additions and 684 deletions.
12 changes: 4 additions & 8 deletions azuredeploy.json
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,7 @@
"dependsOn": [
"[concat('Microsoft.Web/sites/', variables('foxidsDefaultName'))]",
"[resourceId('microsoft.insights/components', variables('foxidsDefaultName'))]",
"[concat('Microsoft.DocumentDB/databaseAccounts/', variables('foxidsDefaultName'))]",
"[concat('Microsoft.KeyVault/vaults/', variables('foxidsDefaultName'))]"
"[concat('Microsoft.DocumentDB/databaseAccounts/', variables('foxidsDefaultName'))]"
],
"properties": {
"WEBSITES_ENABLE_APP_SERVICE_STORAGE": false,
Expand All @@ -353,12 +352,11 @@
"Settings__TrustProxySchemeHeader": true,
"Settings__Options__Log": "ApplicationInsights",
"Settings__Options__DataStorage": "CosmosDb",
"Settings__Options__KeyStorage": "KeyVault",
"Settings__Options__KeyStorage": "None",
"Settings__Options__Cache": "Redis",
"Settings__Options__DataCache": "Default",
"ApplicationInsights__ConnectionString": "[reference(concat('microsoft.insights/components/', variables('foxidsDefaultName'))).ConnectionString]",
"Settings__CosmosDb__EndpointUri": "[reference(concat('Microsoft.DocumentDb/databaseAccounts/', variables('foxidsDefaultName'))).documentEndpoint]",
"Settings__KeyVault__EndpointUri": "[reference(concat('Microsoft.KeyVault/vaults/', variables('foxidsDefaultName'))).vaultUri]",
"Settings__Sendgrid__FromEmail": "[parameters('sendgridFromEmail')]"
}
},
Expand All @@ -369,8 +367,7 @@
"dependsOn": [
"[concat('Microsoft.Web/sites/', variables('foxidsControlSiteName'))]",
"[resourceId('microsoft.insights/components', variables('foxidsDefaultName'))]",
"[concat('Microsoft.DocumentDB/databaseAccounts/', variables('foxidsDefaultName'))]",
"[concat('Microsoft.KeyVault/vaults/', variables('foxidsDefaultName'))]"
"[concat('Microsoft.DocumentDB/databaseAccounts/', variables('foxidsDefaultName'))]"
],
"properties": {
"WEBSITES_ENABLE_APP_SERVICE_STORAGE": false,
Expand All @@ -384,12 +381,11 @@
"Settings__FoxIDsControlEndpoint": "[variables('foxidsControlSiteEndpoint')]",
"Settings__Options__Log": "ApplicationInsights",
"Settings__Options__DataStorage": "CosmosDb",
"Settings__Options__KeyStorage": "KeyVault",
"Settings__Options__KeyStorage": "None",
"Settings__Options__Cache": "Redis",
"Settings__Options__DataCache": "Default",
"Settings__MasterSeedEnabled": true,
"Settings__CosmosDb__EndpointUri": "[reference(concat('Microsoft.DocumentDb/databaseAccounts/', variables('foxidsDefaultName'))).documentEndpoint]",
"Settings__KeyVault__EndpointUri": "[reference(concat('Microsoft.KeyVault/vaults/', variables('foxidsDefaultName'))).vaultUri]",
"Settings__ApplicationInsights__WorkspaceId": "[reference(concat('microsoft.operationalinsights/workspaces/', variables('foxidsDefaultName'))).customerId]"
}
},
Expand Down
26 changes: 8 additions & 18 deletions docs/certificates.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
# Certificates

When a environment is created it is default equipped with a self-signed certificate stored in Cosmos DB, called a contained certificate. The certificate can afterword's be updated / changed and likewise the certificate container type can be changed.
When a environment is created it is default equipped with a automatically renewed self-signed certificate. You can optionally change the certificate container type.

There are tree different certificate container types:
There are two different certificate container types:

**Contained certificates (default)**
- Certificates is stored in Cosmos DB including private key.
- Self-signed certificates is created by FoxIDs or you can upload your one certificates.
- Support primary and secondary certificates, and certificate swap.
- Not automatically renewed.
- No cost per signing.
**Renewed self-signed certificates (default)**
- Automatically created self-signed certificates.
- Automatically renewed with 3 month validity period. Renewed 10 days before expiration and promoted to primary certificate 5 days before expiration.

**Key Vault, renewed self-signed certificates**
- Certificates is stored in Key Vault and the private key is not exportable.
- Self-signed certificates is created by Key Vault.
- Automatically renewed with 3 month validity period. Renewed 10 days before expiration and exposed as the secondary certificate. Promoted to be the primary certificate 5 days before expiration.
- Key Vault cost per signing.

**Key Vault, upload your one certificate *(future support)***
- Certificates is stored in Key Vault and the private key is not exportable.
- Not automatically renewed.
- Key Vault cost per signing.
**Self-signed or your certificates**
- Automatically created self-signed certificates or upload your one certificates.
- NOT automatically renewed.

3 changes: 0 additions & 3 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ Navigating to the application registration then click Show advanced and add a `*
Yes FoxIDs support to forward the login hint from an authentication method to an external IdP or another FoxIDs application registration. In OpenID Connect the login hint is forwarded in the `login_hint` parameter.
In SAML 2.0 the login hint is forwarded as a `NameID` with the Email Format `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress` in the `Subject` element.

##### Way am I unable to login for a moment when I change the certificate container types to 'Key Vault renewed self-signed certificates'?
The first certificate have to be generated by Key Vault before the environment can perform logins again. Thereafter the certificate is renewed seamlessly.

##### I am unable to logout of a client using OIDC if I login and theafter changed the certificate container type.
The problem occurs if the OIDC logout require an ID Token before accepting logout. In this case the ID Token is invalid because the container type and there by the signing certificate have changed.
Solution: You need to close the browser and start over.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
using System.Security.Cryptography.X509Certificates;
using System;
using FoxIDs.Infrastructure.Security;
using Microsoft.Extensions.DependencyInjection;
using FoxIDs.Models.Config;
using System.ComponentModel.DataAnnotations;

namespace FoxIDs.Controllers
Expand All @@ -25,18 +23,14 @@ namespace FoxIDs.Controllers
[TenantScopeAuthorize(Constants.ControlApi.Segment.Party)]
public abstract class GenericOAuthClientKeyUpPartyController<TParty, TClient> : ApiController where TParty : OAuthUpParty<TClient> where TClient : OAuthUpClient
{
private readonly FoxIDsControlSettings settings;
private readonly TelemetryScopedLogger logger;
private readonly IServiceProvider serviceProvider;
private readonly IMapper mapper;
private readonly ITenantDataRepository tenantDataRepository;
private readonly PlanCacheLogic planCacheLogic;

public GenericOAuthClientKeyUpPartyController(FoxIDsControlSettings settings, TelemetryScopedLogger logger, IServiceProvider serviceProvider, IMapper mapper, ITenantDataRepository tenantDataRepository, PlanCacheLogic planCacheLogic) : base(logger)
public GenericOAuthClientKeyUpPartyController(TelemetryScopedLogger logger, IMapper mapper, ITenantDataRepository tenantDataRepository, PlanCacheLogic planCacheLogic) : base(logger)
{
this.settings = settings;
this.logger = logger;
this.serviceProvider = serviceProvider;
this.mapper = mapper;
this.tenantDataRepository = tenantDataRepository;
this.planCacheLogic = planCacheLogic;
Expand Down Expand Up @@ -93,36 +87,24 @@ public GenericOAuthClientKeyUpPartyController(FoxIDsControlSettings settings, Te

var oauthUpParty = await tenantDataRepository.GetAsync<TParty>(await UpParty.IdFormatAsync(RouteBinding, keyRequest.PartyName));

var clientKey = new ClientKey();
if(settings.Options.KeyStorage == KeyStorageOptions.None)
var certificate = keyRequest.Password.IsNullOrWhiteSpace() switch
{
var certificate = keyRequest.Password.IsNullOrWhiteSpace() switch
{
true => new X509Certificate2(WebEncoders.Base64UrlDecode(keyRequest.Certificate), string.Empty, keyStorageFlags: X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable),
false => new X509Certificate2(WebEncoders.Base64UrlDecode(keyRequest.Certificate), keyRequest.Password, keyStorageFlags: X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable),
};
if (!keyRequest.Password.IsNullOrWhiteSpace() && !certificate.HasPrivateKey)
{
throw new ValidationException("Unable to read the certificates private key. E.g, try to convert the certificate and save the certificate with 'TripleDES-SHA1'.");
}
var jwt = await certificate.ToFTJsonWebKeyAsync(includePrivateKey: true);
clientKey.Type = ClientKeyTypes.Contained;
clientKey.ExternalName = Guid.NewGuid().ToString();
clientKey.Key = jwt;
clientKey.PublicKey = jwt.GetPublicKey();
}
else if (settings.Options.KeyStorage == KeyStorageOptions.None)
true => new X509Certificate2(WebEncoders.Base64UrlDecode(keyRequest.Certificate), string.Empty, keyStorageFlags: X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable),
false => new X509Certificate2(WebEncoders.Base64UrlDecode(keyRequest.Certificate), keyRequest.Password, keyStorageFlags: X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable),
};
if (!keyRequest.Password.IsNullOrWhiteSpace() && !certificate.HasPrivateKey)
{
(var externalName, var publicCertificate, var externalId) = await GetExternalKeyLogic().ImportExternalKeyAsync(WebEncoders.Base64UrlDecode(keyRequest.Certificate), keyRequest.Password, upPartyName: keyRequest.PartyName);
clientKey.Type = ClientKeyTypes.KeyVaultImport;
clientKey.ExternalName = externalName;
clientKey.ExternalId = externalId;
clientKey.PublicKey = new X509Certificate2(publicCertificate).ToFTJsonWebKey();
throw new ValidationException("Unable to read the certificates private key. E.g, try to convert the certificate and save the certificate with 'TripleDES-SHA1'.");
}
else

var jwt = await certificate.ToFTJsonWebKeyAsync(includePrivateKey: true);
var clientKey = new ClientKey
{
throw new NotSupportedException();
}
Type = ClientKeyTypes.Contained,
ExternalName = Guid.NewGuid().ToString(),
Key = jwt,
PublicKey = jwt.GetPublicKey()
};

var secondaryKey = oauthUpParty.Client.ClientKeys?.Count() > 1 ? oauthUpParty.Client.ClientKeys[2] : null;
oauthUpParty.Client.ClientKeys = new List<ClientKey>
Expand Down Expand Up @@ -172,10 +154,6 @@ protected async Task<IActionResult> Delete(string name)
{
oauthUpParty.Client.ClientKeys.Remove(key);
await tenantDataRepository.UpdateAsync(oauthUpParty);
if (key.Type == ClientKeyTypes.KeyVaultImport)
{
await GetExternalKeyLogic().DeleteExternalKeyAsync(externalName);
}
}

return NoContent();
Expand All @@ -190,7 +168,5 @@ protected async Task<IActionResult> Delete(string name)
throw;
}
}

private ExternalKeyLogic GetExternalKeyLogic() => serviceProvider.GetService<ExternalKeyLogic>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
using System.Collections.Generic;
using FoxIDs.Logic;
using Microsoft.AspNetCore.Http;
using System;
using FoxIDs.Models.Config;

namespace FoxIDs.Controllers
{
Expand All @@ -18,7 +16,7 @@ namespace FoxIDs.Controllers
/// </summary>
public class TOidcClientKeyUpPartyController : GenericOAuthClientKeyUpPartyController<OidcUpParty, OidcUpClient>
{
public TOidcClientKeyUpPartyController(FoxIDsControlSettings settings, TelemetryScopedLogger logger, IServiceProvider serviceProvider, IMapper mapper, ITenantDataRepository tenantDataRepository, PlanCacheLogic planCacheLogic) : base(settings, logger, serviceProvider, mapper, tenantDataRepository, planCacheLogic)
public TOidcClientKeyUpPartyController(TelemetryScopedLogger logger, IMapper mapper, ITenantDataRepository tenantDataRepository, PlanCacheLogic planCacheLogic) : base(logger, mapper, tenantDataRepository, planCacheLogic)
{ }

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/FoxIDs.Control/Controllers/Tenants/TTenantController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ public TTenantController(FoxIDsControlSettings settings, TelemetryScopedLogger l
await tenantCacheLogic.InvalidateCustomDomainCacheAsync(tenant.CustomDomain);
}

await masterTenantLogic.CreateMasterTrackDocumentAsync(tenant.Name, plan.GetKeyType(settings.Options.KeyStorage == KeyStorageOptions.KeyVault));
await masterTenantLogic.CreateMasterTrackDocumentAsync(tenant.Name);
var mLoginUpParty = await masterTenantLogic.CreateMasterLoginDocumentAsync(tenant.Name);
await masterTenantLogic.CreateFirstAdminUserDocumentAsync(tenant.Name, tenant.AdministratorEmail, tenant.AdministratorPassword, tenant.ChangeAdministratorPassword, true, tenant.ConfirmAdministratorAccount);
await masterTenantLogic.CreateMasterFoxIDsControlApiResourceDocumentAsync(tenant.Name);
await masterTenantLogic.CreateMasterControlClientDocmentAsync(tenant.Name, tenant.ControlClientBaseUri, mLoginUpParty);

await masterTenantLogic.CreateDefaultTracksDocmentsAsync(tenant.Name, plan.GetKeyType(settings.Options.KeyStorage == KeyStorageOptions.KeyVault));
await masterTenantLogic.CreateDefaultTracksDocmentsAsync(tenant.Name);

return Created(mapper.Map<Api.Tenant>(mTenant));
}
Expand Down
17 changes: 1 addition & 16 deletions src/FoxIDs.Control/Controllers/Tracks/TTrackController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public TTrackController(FoxIDsControlSettings settings, TelemetryScopedLogger lo
}

var mTrack = mapper.Map<Track>(track);
await trackLogic.CreateTrackDocumentAsync(mTrack, await GetKeyTypeAsync());
await trackLogic.CreateTrackDocumentAsync(mTrack);
await trackLogic.CreateLoginDocumentAsync(mTrack);

return Created(mapper.Map<Api.Track>(mTrack));
Expand All @@ -118,21 +118,6 @@ public TTrackController(FoxIDsControlSettings settings, TelemetryScopedLogger lo
}
}

private async Task<TrackKeyTypes> GetKeyTypeAsync()
{
if (settings.Options.KeyStorage != KeyStorageOptions.KeyVault)
{
return TrackKeyTypes.Contained;
}

Plan plan = null;
if (!RouteBinding.PlanName.IsNullOrEmpty())
{
plan = await planCacheLogic.GetPlanAsync(RouteBinding.PlanName);
}
return plan.GetKeyType(settings.Options.KeyStorage == KeyStorageOptions.KeyVault);
}

/// <summary>
/// Update environment.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System.Net;
using System.ComponentModel.DataAnnotations;
using ITfoxtec.Identity;
using FoxIDs.Logic;
Expand Down Expand Up @@ -117,8 +116,8 @@ public TTrackKeyContainedController(TelemetryScopedLogger logger, IMapper mapper

if (trackKeyRequest.CreateSelfSigned)
{
var certificate = await RouteBinding.CreateSelfSignedCertificateBySubjectAsync();
mTrackKey.Key = await certificate.ToFTJsonWebKeyAsync(true);
var certificateItem = await RouteBinding.CreateSelfSignedCertificateBySubjectAsync();
mTrackKey.Key = await certificateItem.Certificate.ToFTJsonWebKeyAsync(true);
}

if (trackKeyRequest.IsPrimary)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System.Net;
using System.ComponentModel.DataAnnotations;
using FoxIDs.Logic;
using FoxIDs.Infrastructure.Security;
Expand Down
Loading

0 comments on commit 96bd2f8

Please sign in to comment.