sync public allowlist from private myoffice
This commit is contained in:
@@ -1,192 +0,0 @@
|
||||
namespace MyOffice.Web.Auth;
|
||||
|
||||
using System.Security.Cryptography;
|
||||
using DbContext;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using OpenIddict.Server;
|
||||
using OpenIddict.Validation.AspNetCore;
|
||||
using static OpenIddict.Abstractions.OpenIddictConstants;
|
||||
using static OpenIddict.Server.OpenIddictServerEvents;
|
||||
|
||||
public static class OpenIddictServiceCollectionExtensions
|
||||
{
|
||||
public static IServiceCollection AddMyOfficeOpenIddict(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration,
|
||||
IHostEnvironment environment
|
||||
)
|
||||
{
|
||||
services.AddOpenIddict()
|
||||
.AddCore(options =>
|
||||
{
|
||||
options.UseEntityFrameworkCore()
|
||||
.UseDbContext<AppDbContext>();
|
||||
})
|
||||
.AddServer(options =>
|
||||
{
|
||||
options.SetAuthorizationEndpointUris("/connect/authorize")
|
||||
.SetTokenEndpointUris("/connect/token")
|
||||
.SetUserInfoEndpointUris("/connect/userinfo")
|
||||
.SetEndSessionEndpointUris("/connect/logout");
|
||||
|
||||
options.AllowAuthorizationCodeFlow()
|
||||
.AllowPasswordFlow()
|
||||
.AllowRefreshTokenFlow()
|
||||
.AllowCustomFlow(OpenIddictAuthConstants.ExternalGrantType);
|
||||
|
||||
options.RegisterScopes(
|
||||
Scopes.OpenId,
|
||||
Scopes.Email,
|
||||
Scopes.Profile,
|
||||
Scopes.Roles,
|
||||
Scopes.OfflineAccess,
|
||||
OpenIddictAuthConstants.ApiScope,
|
||||
OpenIddictAuthConstants.RolesScope);
|
||||
|
||||
ConfigureCryptography(options, configuration, environment);
|
||||
|
||||
var aspNetCoreBuilder = options.UseAspNetCore()
|
||||
.EnableAuthorizationEndpointPassthrough()
|
||||
.EnableUserInfoEndpointPassthrough()
|
||||
.EnableEndSessionEndpointPassthrough();
|
||||
|
||||
// Local HTTP only. In production nginx terminates TLS and
|
||||
// ForwardedHeaders restores https:// for OpenIddict.
|
||||
// Docker demo also serves plain HTTP (OpenIddict:AllowHttp / Environment=Docker).
|
||||
if (environment.IsDevelopment()
|
||||
|| IsDockerEnvironment(environment)
|
||||
|| configuration.GetValue("OpenIddict:AllowHttp", false))
|
||||
{
|
||||
aspNetCoreBuilder.DisableTransportSecurityRequirement();
|
||||
}
|
||||
|
||||
// Password and external grants need custom handlers (separate registrations —
|
||||
// a second UseScopedHandler() on the same builder replaces the first).
|
||||
options.AddEventHandler<HandleTokenRequestContext>(builder =>
|
||||
builder.UseScopedHandler<PasswordGrantHandler>());
|
||||
|
||||
options.AddEventHandler<HandleTokenRequestContext>(builder =>
|
||||
builder.UseScopedHandler<ExternalGrantHandler>());
|
||||
})
|
||||
.AddValidation(options =>
|
||||
{
|
||||
options.UseLocalServer();
|
||||
options.UseAspNetCore();
|
||||
});
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
|
||||
options.DefaultAuthenticateScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
|
||||
});
|
||||
|
||||
services.AddHostedService<OpenIddictSeeder>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static void ConfigureCryptography(
|
||||
OpenIddictServerBuilder options,
|
||||
IConfiguration configuration,
|
||||
IHostEnvironment environment
|
||||
)
|
||||
{
|
||||
// Dev: certs in the user profile. Docker/demo: ephemeral keys (no cert store in containers).
|
||||
if (string.IsNullOrWhiteSpace(configuration["OpenIddict:SigningKeyPath"])
|
||||
&& (environment.IsDevelopment()
|
||||
|| IsDockerEnvironment(environment)
|
||||
|| configuration.GetValue("OpenIddict:UseEphemeralKeys", false)))
|
||||
{
|
||||
if (IsDockerEnvironment(environment)
|
||||
|| configuration.GetValue("OpenIddict:UseEphemeralKeys", false))
|
||||
{
|
||||
options.AddEphemeralEncryptionKey()
|
||||
.AddEphemeralSigningKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
options.AddDevelopmentEncryptionCertificate()
|
||||
.AddDevelopmentSigningCertificate();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var contentRoot = configuration.GetValue<string>(WebHostDefaults.ContentRootKey)
|
||||
?? throw new InvalidOperationException("Content root path is not configured.");
|
||||
|
||||
var signingPath = ResolveKeyPath(
|
||||
configuration["OpenIddict:SigningKeyPath"],
|
||||
contentRoot,
|
||||
"keys/signing.pem");
|
||||
var encryptionPath = ResolveKeyPath(
|
||||
configuration["OpenIddict:EncryptionKeyPath"],
|
||||
contentRoot,
|
||||
"keys/encryption.pem");
|
||||
|
||||
if (!File.Exists(signingPath))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"OpenIddict signing key not found at '{signingPath}'. " +
|
||||
"Set OpenIddict:SigningKeyPath or place keys/signing.pem under the content root.");
|
||||
}
|
||||
|
||||
options.AddSigningKey(LoadRsaSecurityKey(signingPath));
|
||||
|
||||
if (File.Exists(encryptionPath) &&
|
||||
!string.Equals(encryptionPath, signingPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options.AddEncryptionKey(LoadRsaSecurityKey(encryptionPath));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prefer a dedicated encryption key. Fallback keeps older single-key setups working.
|
||||
options.AddEncryptionKey(LoadRsaSecurityKey(signingPath));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDockerEnvironment(IHostEnvironment environment) =>
|
||||
string.Equals(environment.EnvironmentName, "Docker", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private static string ResolveKeyPath(string? configuredPath, string contentRoot, string defaultRelative)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(configuredPath))
|
||||
{
|
||||
return Path.IsPathRooted(configuredPath)
|
||||
? configuredPath
|
||||
: Path.GetFullPath(Path.Combine(contentRoot, configuredPath));
|
||||
}
|
||||
|
||||
return Path.GetFullPath(Path.Combine(contentRoot, defaultRelative));
|
||||
}
|
||||
|
||||
private static RsaSecurityKey LoadRsaSecurityKey(string path)
|
||||
{
|
||||
var privateKey = File.ReadAllText(path)
|
||||
.Replace("-----BEGIN RSA PRIVATE KEY-----", string.Empty, StringComparison.Ordinal)
|
||||
.Replace("-----END RSA PRIVATE KEY-----", string.Empty, StringComparison.Ordinal)
|
||||
.Replace("-----BEGIN PRIVATE KEY-----", string.Empty, StringComparison.Ordinal)
|
||||
.Replace("-----END PRIVATE KEY-----", string.Empty, StringComparison.Ordinal)
|
||||
.Replace("\r", string.Empty, StringComparison.Ordinal)
|
||||
.Replace("\n", string.Empty, StringComparison.Ordinal);
|
||||
|
||||
var rsa = RSA.Create();
|
||||
var keyBytes = Convert.FromBase64String(privateKey);
|
||||
|
||||
try
|
||||
{
|
||||
rsa.ImportRSAPrivateKey(keyBytes, out _);
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{
|
||||
rsa.ImportPkcs8PrivateKey(keyBytes, out _);
|
||||
}
|
||||
|
||||
return new RsaSecurityKey(rsa);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user