mirror of
https://github.com/bitwarden/server.git
synced 2024-11-22 12:15:36 +01:00
internal identity authorization
This commit is contained in:
parent
25899fd326
commit
ff01ce5ca7
@ -11,6 +11,7 @@ namespace Bit.Core
|
|||||||
public virtual string LogDirectory { get; set; }
|
public virtual string LogDirectory { get; set; }
|
||||||
public virtual string LicenseDirectory { get; set; }
|
public virtual string LicenseDirectory { get; set; }
|
||||||
public virtual string PushRelayBaseUri { get; set; }
|
public virtual string PushRelayBaseUri { get; set; }
|
||||||
|
public virtual string InternalIdentityKey { get; set; }
|
||||||
public virtual bool DisableUserRegistration { get; set; }
|
public virtual bool DisableUserRegistration { get; set; }
|
||||||
public virtual InstallationSettings Installation { get; set; } = new InstallationSettings();
|
public virtual InstallationSettings Installation { get; set; } = new InstallationSettings();
|
||||||
public virtual BaseServiceUriSettings BaseServiceUri { get; set; } = new BaseServiceUriSettings();
|
public virtual BaseServiceUriSettings BaseServiceUri { get; set; } = new BaseServiceUriSettings();
|
||||||
|
@ -6,6 +6,7 @@ using Bit.Core.Repositories;
|
|||||||
using System;
|
using System;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using IdentityModel;
|
using IdentityModel;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.IdentityServer
|
namespace Bit.Core.IdentityServer
|
||||||
{
|
{
|
||||||
@ -14,15 +15,19 @@ namespace Bit.Core.IdentityServer
|
|||||||
private static IDictionary<string, Client> _apiClients = StaticClients.GetApiClients();
|
private static IDictionary<string, Client> _apiClients = StaticClients.GetApiClients();
|
||||||
|
|
||||||
private readonly IInstallationRepository _installationRepository;
|
private readonly IInstallationRepository _installationRepository;
|
||||||
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
public ClientStore(
|
public ClientStore(
|
||||||
IInstallationRepository installationRepository)
|
IInstallationRepository installationRepository,
|
||||||
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_installationRepository = installationRepository;
|
_installationRepository = installationRepository;
|
||||||
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Client> FindClientByIdAsync(string clientId)
|
public async Task<Client> FindClientByIdAsync(string clientId)
|
||||||
{
|
{
|
||||||
if(clientId.StartsWith("installation."))
|
if(!_globalSettings.SelfHosted && clientId.StartsWith("installation."))
|
||||||
{
|
{
|
||||||
var idParts = clientId.Split('.');
|
var idParts = clientId.Split('.');
|
||||||
if(idParts.Length > 1 && Guid.TryParse(idParts[1], out Guid id))
|
if(idParts.Length > 1 && Guid.TryParse(idParts[1], out Guid id))
|
||||||
@ -44,6 +49,29 @@ namespace Bit.Core.IdentityServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(_globalSettings.SelfHosted && clientId.StartsWith("internal.") &&
|
||||||
|
CoreHelpers.SettingHasValue(_globalSettings.InternalIdentityKey))
|
||||||
|
{
|
||||||
|
var idParts = clientId.Split('.');
|
||||||
|
if(idParts.Length > 1)
|
||||||
|
{
|
||||||
|
var id = idParts[1];
|
||||||
|
if(!string.IsNullOrWhiteSpace(id))
|
||||||
|
{
|
||||||
|
return new Client
|
||||||
|
{
|
||||||
|
ClientId = $"internal.{id}",
|
||||||
|
RequireClientSecret = true,
|
||||||
|
ClientSecrets = { new Secret(_globalSettings.InternalIdentityKey.Sha256()) },
|
||||||
|
AllowedScopes = new string[] { "internal" },
|
||||||
|
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
||||||
|
AccessTokenLifetime = 3600 * 24,
|
||||||
|
Enabled = true,
|
||||||
|
Claims = new List<Claim> { new Claim(JwtClaimTypes.Subject, id) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return _apiClients.ContainsKey(clientId) ? _apiClients[clientId] : null;
|
return _apiClients.ContainsKey(clientId) ? _apiClients[clientId] : null;
|
||||||
}
|
}
|
||||||
|
@ -12,35 +12,44 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
public abstract class BaseRelayPushNotificationService
|
public abstract class BaseIdentityClientService
|
||||||
{
|
{
|
||||||
|
private readonly string _identityScope;
|
||||||
|
private readonly string _identityClientId;
|
||||||
|
private readonly string _identityClientSecret;
|
||||||
|
private readonly ILogger<BaseIdentityClientService> _logger;
|
||||||
|
|
||||||
private dynamic _decodedToken;
|
private dynamic _decodedToken;
|
||||||
private DateTime? _nextAuthAttempt = null;
|
private DateTime? _nextAuthAttempt = null;
|
||||||
private readonly ILogger<BaseRelayPushNotificationService> _logger;
|
|
||||||
|
|
||||||
public BaseRelayPushNotificationService(
|
public BaseIdentityClientService(
|
||||||
GlobalSettings globalSettings,
|
string baseClientServerUri,
|
||||||
ILogger<BaseRelayPushNotificationService> logger)
|
string baseIdentityServerUri,
|
||||||
|
string identityScope,
|
||||||
|
string identityClientId,
|
||||||
|
string identityClientSecret,
|
||||||
|
ILogger<BaseIdentityClientService> logger)
|
||||||
{
|
{
|
||||||
|
_identityScope = identityScope;
|
||||||
|
_identityClientId = identityClientId;
|
||||||
|
_identityClientSecret = identityClientSecret;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
GlobalSettings = globalSettings;
|
|
||||||
|
|
||||||
PushClient = new HttpClient
|
Client = new HttpClient
|
||||||
{
|
{
|
||||||
BaseAddress = new Uri(globalSettings.PushRelayBaseUri)
|
BaseAddress = new Uri(baseClientServerUri)
|
||||||
};
|
};
|
||||||
PushClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
|
||||||
IdentityClient = new HttpClient
|
IdentityClient = new HttpClient
|
||||||
{
|
{
|
||||||
BaseAddress = new Uri(globalSettings.Installation.IdentityUri)
|
BaseAddress = new Uri(baseIdentityServerUri)
|
||||||
};
|
};
|
||||||
IdentityClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
IdentityClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpClient PushClient { get; private set; }
|
protected HttpClient Client { get; private set; }
|
||||||
protected HttpClient IdentityClient { get; private set; }
|
protected HttpClient IdentityClient { get; private set; }
|
||||||
protected GlobalSettings GlobalSettings { get; private set; }
|
|
||||||
protected string AccessToken { get; private set; }
|
protected string AccessToken { get; private set; }
|
||||||
|
|
||||||
protected async Task<bool> HandleTokenStateAsync()
|
protected async Task<bool> HandleTokenStateAsync()
|
||||||
@ -63,9 +72,9 @@ namespace Bit.Core.Services
|
|||||||
Content = new FormUrlEncodedContent(new Dictionary<string, string>
|
Content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{ "grant_type", "client_credentials" },
|
{ "grant_type", "client_credentials" },
|
||||||
{ "scope", "api.push" },
|
{ "scope", _identityScope },
|
||||||
{ "client_id", $"installation.{GlobalSettings.Installation.Id}" },
|
{ "client_id", _identityClientId },
|
||||||
{ "client_secret", $"{GlobalSettings.Installation.Key}" }
|
{ "client_secret", _identityClientSecret }
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,7 +85,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(12339, e, "Unable to auth for push.");
|
_logger.LogError(12339, e, "Unable to authenticate with identity server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(response == null)
|
if(response == null)
|
@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
public class RelayPushNotificationService : BaseRelayPushNotificationService, IPushNotificationService
|
public class RelayPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly ILogger<RelayPushNotificationService> _logger;
|
private readonly ILogger<RelayPushNotificationService> _logger;
|
||||||
@ -19,7 +19,13 @@ namespace Bit.Core.Services
|
|||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
ILogger<RelayPushNotificationService> logger)
|
ILogger<RelayPushNotificationService> logger)
|
||||||
: base(globalSettings, logger)
|
: base(
|
||||||
|
globalSettings.PushRelayBaseUri,
|
||||||
|
globalSettings.Installation.IdentityUri,
|
||||||
|
"api.push",
|
||||||
|
$"installation.{globalSettings.Installation.Id}",
|
||||||
|
globalSettings.Installation.Key,
|
||||||
|
logger)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@ -168,12 +174,12 @@ namespace Bit.Core.Services
|
|||||||
var message = new TokenHttpRequestMessage(requestModel, AccessToken)
|
var message = new TokenHttpRequestMessage(requestModel, AccessToken)
|
||||||
{
|
{
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/send"))
|
RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/send"))
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PushClient.SendAsync(message);
|
await Client.SendAsync(message);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
|
@ -9,14 +9,20 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
public class RelayPushRegistrationService : BaseRelayPushNotificationService, IPushRegistrationService
|
public class RelayPushRegistrationService : BaseIdentityClientService, IPushRegistrationService
|
||||||
{
|
{
|
||||||
private readonly ILogger<RelayPushRegistrationService> _logger;
|
private readonly ILogger<RelayPushRegistrationService> _logger;
|
||||||
|
|
||||||
public RelayPushRegistrationService(
|
public RelayPushRegistrationService(
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
ILogger<RelayPushRegistrationService> logger)
|
ILogger<RelayPushRegistrationService> logger)
|
||||||
: base(globalSettings, logger)
|
: base(
|
||||||
|
globalSettings.PushRelayBaseUri,
|
||||||
|
globalSettings.Installation.IdentityUri,
|
||||||
|
"api.push",
|
||||||
|
$"installation.{globalSettings.Installation.Id}",
|
||||||
|
globalSettings.Installation.Key,
|
||||||
|
logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@ -42,12 +48,12 @@ namespace Bit.Core.Services
|
|||||||
var message = new TokenHttpRequestMessage(requestModel, AccessToken)
|
var message = new TokenHttpRequestMessage(requestModel, AccessToken)
|
||||||
{
|
{
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/register"))
|
RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/register"))
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PushClient.SendAsync(message);
|
await Client.SendAsync(message);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
@ -66,12 +72,12 @@ namespace Bit.Core.Services
|
|||||||
var message = new TokenHttpRequestMessage(AccessToken)
|
var message = new TokenHttpRequestMessage(AccessToken)
|
||||||
{
|
{
|
||||||
Method = HttpMethod.Delete,
|
Method = HttpMethod.Delete,
|
||||||
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/", deviceId))
|
RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/", deviceId))
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PushClient.SendAsync(message);
|
await Client.SendAsync(message);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
@ -96,12 +102,12 @@ namespace Bit.Core.Services
|
|||||||
var message = new TokenHttpRequestMessage(requestModel, AccessToken)
|
var message = new TokenHttpRequestMessage(requestModel, AccessToken)
|
||||||
{
|
{
|
||||||
Method = HttpMethod.Put,
|
Method = HttpMethod.Put,
|
||||||
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/add-organization"))
|
RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/add-organization"))
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PushClient.SendAsync(message);
|
await Client.SendAsync(message);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
@ -126,12 +132,12 @@ namespace Bit.Core.Services
|
|||||||
var message = new TokenHttpRequestMessage(requestModel, AccessToken)
|
var message = new TokenHttpRequestMessage(requestModel, AccessToken)
|
||||||
{
|
{
|
||||||
Method = HttpMethod.Put,
|
Method = HttpMethod.Put,
|
||||||
RequestUri = new Uri(string.Concat(PushClient.BaseAddress, "/push/delete-organization"))
|
RequestUri = new Uri(string.Concat(Client.BaseAddress, "/push/delete-organization"))
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await PushClient.SendAsync(message);
|
await Client.SendAsync(message);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
|
@ -311,12 +311,9 @@ namespace Bit.Core.Utilities
|
|||||||
|
|
||||||
public static bool SettingHasValue(string setting)
|
public static bool SettingHasValue(string setting)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(setting) || setting.Equals("SECRET") || setting.Equals("REPLACE"))
|
var normalizedSetting = setting?.ToLowerInvariant();
|
||||||
{
|
return !string.IsNullOrWhiteSpace(normalizedSetting) && !normalizedSetting.Equals("secret") &&
|
||||||
return false;
|
!normalizedSetting.Equals("replace");
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Base64UrlEncode(byte[] input)
|
public static string Base64UrlEncode(byte[] input)
|
||||||
|
@ -234,7 +234,7 @@ namespace Bit.Core.Utilities
|
|||||||
|
|
||||||
public static void AddIdentityAuthenticationServices(
|
public static void AddIdentityAuthenticationServices(
|
||||||
this IServiceCollection services, GlobalSettings globalSettings, IHostingEnvironment environment,
|
this IServiceCollection services, GlobalSettings globalSettings, IHostingEnvironment environment,
|
||||||
Action<AuthorizationOptions> addAuthorization = null)
|
Action<AuthorizationOptions> addAuthorization)
|
||||||
{
|
{
|
||||||
services
|
services
|
||||||
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
|
.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
|
||||||
@ -248,21 +248,13 @@ namespace Bit.Core.Utilities
|
|||||||
options.SupportedTokens = SupportedTokens.Jwt;
|
options.SupportedTokens = SupportedTokens.Jwt;
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddAuthorization(config =>
|
if(addAuthorization != null)
|
||||||
{
|
{
|
||||||
if(addAuthorization != null)
|
services.AddAuthorization(config =>
|
||||||
{
|
{
|
||||||
addAuthorization?.Invoke(config);
|
addAuthorization.Invoke(config);
|
||||||
}
|
});
|
||||||
else
|
}
|
||||||
{
|
|
||||||
config.AddPolicy("Application", policy =>
|
|
||||||
{
|
|
||||||
policy.RequireAuthenticatedUser();
|
|
||||||
policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IIdentityServerBuilder AddCustomIdentityServerServices(
|
public static IIdentityServerBuilder AddCustomIdentityServerServices(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using IdentityModel;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
@ -36,7 +37,15 @@ namespace Bit.Events
|
|||||||
services.AddScoped<CurrentContext>();
|
services.AddScoped<CurrentContext>();
|
||||||
|
|
||||||
// Identity
|
// Identity
|
||||||
services.AddIdentityAuthenticationServices(globalSettings, Environment);
|
services.AddIdentityAuthenticationServices(globalSettings, Environment, config =>
|
||||||
|
{
|
||||||
|
config.AddPolicy("Application", policy =>
|
||||||
|
{
|
||||||
|
policy.RequireAuthenticatedUser();
|
||||||
|
policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application");
|
||||||
|
policy.RequireClaim(JwtClaimTypes.Scope, "api");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
services.AddScoped<IEventService, EventService>();
|
services.AddScoped<IEventService, EventService>();
|
||||||
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.SignalR;
|
|||||||
|
|
||||||
namespace Bit.Hub
|
namespace Bit.Hub
|
||||||
{
|
{
|
||||||
[Authorize("Application")]
|
[Authorize("Internal")]
|
||||||
public class EventsController : Controller
|
public class EventsController : Controller
|
||||||
{
|
{
|
||||||
private readonly IHubContext<SyncHub> _syncHubContext;
|
private readonly IHubContext<SyncHub> _syncHubContext;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using IdentityModel;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
@ -37,7 +38,20 @@ namespace Bit.Hub
|
|||||||
services.AddScoped<CurrentContext>();
|
services.AddScoped<CurrentContext>();
|
||||||
|
|
||||||
// Identity
|
// Identity
|
||||||
services.AddIdentityAuthenticationServices(globalSettings, Environment);
|
services.AddIdentityAuthenticationServices(globalSettings, Environment, config =>
|
||||||
|
{
|
||||||
|
config.AddPolicy("Application", policy =>
|
||||||
|
{
|
||||||
|
policy.RequireAuthenticatedUser();
|
||||||
|
policy.RequireClaim(JwtClaimTypes.AuthenticationMethod, "Application");
|
||||||
|
policy.RequireClaim(JwtClaimTypes.Scope, "api");
|
||||||
|
});
|
||||||
|
config.AddPolicy("Internal", policy =>
|
||||||
|
{
|
||||||
|
policy.RequireAuthenticatedUser();
|
||||||
|
policy.RequireClaim(JwtClaimTypes.Scope, "internal");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// SignalR
|
// SignalR
|
||||||
services.AddSignalR();
|
services.AddSignalR();
|
||||||
|
@ -49,7 +49,8 @@ namespace Bit.Setup
|
|||||||
["globalSettings__dataProtection__directory"] = $"{OutputDirectory}/core/aspnet-dataprotection",
|
["globalSettings__dataProtection__directory"] = $"{OutputDirectory}/core/aspnet-dataprotection",
|
||||||
["globalSettings__logDirectory"] = $"{OutputDirectory}/logs",
|
["globalSettings__logDirectory"] = $"{OutputDirectory}/logs",
|
||||||
["globalSettings__licenseDirectory"] = $"{OutputDirectory}/core/licenses",
|
["globalSettings__licenseDirectory"] = $"{OutputDirectory}/core/licenses",
|
||||||
["globalSettings__duo__aKey"] = $"{Helpers.SecureRandomString(64, alpha: true, numeric: true)}",
|
["globalSettings__internalIdentityKey"] = Helpers.SecureRandomString(64, alpha: true, numeric: true),
|
||||||
|
["globalSettings__duo__aKey"] = Helpers.SecureRandomString(64, alpha: true, numeric: true),
|
||||||
["globalSettings__installation__id"] = InstallationId?.ToString(),
|
["globalSettings__installation__id"] = InstallationId?.ToString(),
|
||||||
["globalSettings__installation__key"] = InstallationKey,
|
["globalSettings__installation__key"] = InstallationKey,
|
||||||
["globalSettings__yubico__clientId"] = "REPLACE",
|
["globalSettings__yubico__clientId"] = "REPLACE",
|
||||||
|
Loading…
Reference in New Issue
Block a user