| | | 1 | | using System.Text.Json; |
| | | 2 | | |
| | | 3 | | namespace Elsa.ExternalAuthentication.Models; |
| | | 4 | | |
| | | 5 | | public enum ConnectionSourceOwnership |
| | | 6 | | { |
| | | 7 | | Configuration, |
| | | 8 | | Database |
| | | 9 | | } |
| | | 10 | | |
| | | 11 | | public enum ConnectionScopeKind |
| | | 12 | | { |
| | | 13 | | Host, |
| | | 14 | | DefaultTenant, |
| | | 15 | | Tenant |
| | | 16 | | } |
| | | 17 | | |
| | | 18 | | public enum ConnectionLifecycle |
| | | 19 | | { |
| | | 20 | | Draft, |
| | | 21 | | Disabled, |
| | | 22 | | Enabled, |
| | | 23 | | Archived |
| | | 24 | | } |
| | | 25 | | |
| | | 26 | | public enum ConnectionValidity |
| | | 27 | | { |
| | | 28 | | Unknown, |
| | | 29 | | Valid, |
| | | 30 | | Invalid |
| | | 31 | | } |
| | | 32 | | |
| | | 33 | | public enum AuthenticationClientType |
| | | 34 | | { |
| | | 35 | | Confidential, |
| | | 36 | | Public |
| | | 37 | | } |
| | | 38 | | |
| | | 39 | | public enum BrokerTransactionPurpose |
| | | 40 | | { |
| | | 41 | | ExternalSignIn, |
| | | 42 | | LocalSignIn, |
| | | 43 | | Preview, |
| | | 44 | | UpstreamLogout |
| | | 45 | | } |
| | | 46 | | |
| | | 47 | | public enum UpstreamLogoutMode |
| | | 48 | | { |
| | | 49 | | Disabled, |
| | | 50 | | UserChoice, |
| | | 51 | | Always |
| | | 52 | | } |
| | | 53 | | |
| | | 54 | | public enum ConnectionObservationStatus |
| | | 55 | | { |
| | | 56 | | Succeeded, |
| | | 57 | | Failed, |
| | | 58 | | Warning |
| | | 59 | | } |
| | | 60 | | |
| | | 61 | | public enum LoginMethodKind |
| | | 62 | | { |
| | | 63 | | Local, |
| | | 64 | | External |
| | | 65 | | } |
| | | 66 | | |
| | | 67 | | public enum BrowserCredentialPersistence |
| | | 68 | | { |
| | | 69 | | Memory, |
| | | 70 | | SessionStorage, |
| | | 71 | | DurableStorage |
| | | 72 | | } |
| | | 73 | | |
| | | 74 | | public sealed record ConnectionScope(ConnectionScopeKind Kind, string TenantId) |
| | | 75 | | { |
| | | 76 | | public const string HostTenantId = "*"; |
| | | 77 | | public const string DefaultTenantId = ""; |
| | | 78 | | |
| | | 79 | | public static ConnectionScope Host { get; } = new(ConnectionScopeKind.Host, HostTenantId); |
| | | 80 | | public static ConnectionScope DefaultTenant { get; } = new(ConnectionScopeKind.DefaultTenant, DefaultTenantId); |
| | | 81 | | } |
| | | 82 | | |
| | | 83 | | public enum SecretBindingOwnership |
| | | 84 | | { |
| | | 85 | | External, |
| | | 86 | | Managed |
| | | 87 | | } |
| | | 88 | | |
| | | 89 | | public sealed record SecretBinding(string ResolverType, string Reference, string? ExpectedType = null, string? ExpectedS |
| | | 90 | | |
| | | 91 | | public sealed record SecretBindingState(bool IsConfigured, bool IsResolvable); |
| | | 92 | | public sealed record SecretBindingPresentation(string Ownership, bool IsConfigured, bool IsResolvable); |
| | | 93 | | |
| | | 94 | | public sealed record PolicySelection(string Type, int SettingsVersion, JsonElement Settings); |
| | | 95 | | |
| | | 96 | | public sealed record GrantSourceSelection(string Type, int SettingsVersion, JsonElement Settings, int Order); |
| | | 97 | | |
| | | 98 | | public sealed record ClaimProjection( |
| | | 99 | | IReadOnlySet<string> AllowedClaimTypes, |
| | | 100 | | IReadOnlySet<string> RedactedClaimTypes, |
| | | 101 | | int MaximumClaimCount, |
| | | 102 | | int MaximumValueLength, |
| | | 103 | | int MaximumTotalBytes) |
| | | 104 | | { |
| | | 105 | | public static ClaimProjection Empty { get; } = new(new HashSet<string>(StringComparer.Ordinal), new HashSet<string>( |
| | | 106 | | } |
| | | 107 | | |
| | | 108 | | public sealed class IdentityProviderConnection |
| | | 109 | | { |
| | | 110 | | public string Id { get; set; } = null!; |
| | | 111 | | public string TenantId { get; set; } = null!; |
| | | 112 | | public string Key { get; set; } = null!; |
| | | 113 | | public string AdapterType { get; set; } = null!; |
| | | 114 | | public int AdapterSettingsVersion { get; set; } |
| | | 115 | | public JsonElement AdapterSettings { get; set; } |
| | | 116 | | public IDictionary<string, SecretBinding> SecretBindings { get; set; } = new Dictionary<string, SecretBinding>(Strin |
| | | 117 | | public string DisplayName { get; set; } = null!; |
| | | 118 | | public string? IconId { get; set; } |
| | | 119 | | public int DisplayOrder { get; set; } |
| | | 120 | | public bool IsPreferred { get; set; } |
| | | 121 | | public bool IsEnabled { get; set; } |
| | | 122 | | /// <summary>When configuration-owned, explicitly shadows a database-owned connection with the same scoped key.</sum |
| | | 123 | | public bool OverridesConfigurationConnection { get; set; } |
| | | 124 | | public DateTimeOffset? ArchivedAt { get; set; } |
| | | 125 | | public PolicySelection? UnlinkedPolicy { get; set; } |
| | | 126 | | public ICollection<GrantSourceSelection> PermissionGrantSources { get; set; } = new List<GrantSourceSelection>(); |
| | | 127 | | public ClaimProjection ClaimProjection { get; set; } = ClaimProjection.Empty; |
| | | 128 | | public UpstreamLogoutMode UpstreamLogoutMode { get; set; } |
| | | 129 | | public long Revision { get; set; } |
| | | 130 | | public string MaterialRevision { get; set; } = null!; |
| | | 131 | | public DateTimeOffset CreatedAt { get; set; } |
| | | 132 | | public DateTimeOffset UpdatedAt { get; set; } |
| | | 133 | | } |
| | | 134 | | |
| | | 135 | | public sealed record ExternalIdentity(string Issuer, string Subject, IReadOnlyDictionary<string, IReadOnlyCollection<str |
| | | 136 | | |
| | | 137 | | public sealed record AuthenticationClient |
| | | 138 | | { |
| | | 139 | | public AuthenticationClient() |
| | | 140 | | { |
| | | 141 | | } |
| | | 142 | | |
| | | 143 | | public AuthenticationClient( |
| | | 144 | | string ClientId, |
| | | 145 | | string DisplayName, |
| | | 146 | | AuthenticationClientType ClientType, |
| | | 147 | | IReadOnlySet<Uri> CallbackUris, |
| | | 148 | | IReadOnlySet<Uri> LogoutCallbackUris, |
| | | 149 | | IReadOnlySet<string> AllowedOrigins, |
| | | 150 | | IReadOnlySet<string> AllowedReturnPathPrefixes, |
| | | 151 | | SecretBinding? SecretBinding, |
| | | 152 | | bool IsEnabled) |
| | | 153 | | { |
| | | 154 | | this.ClientId = ClientId; |
| | | 155 | | this.DisplayName = DisplayName; |
| | | 156 | | this.ClientType = ClientType; |
| | | 157 | | this.CallbackUris = CallbackUris.ToHashSet(); |
| | | 158 | | this.LogoutCallbackUris = LogoutCallbackUris.ToHashSet(); |
| | | 159 | | this.AllowedOrigins = AllowedOrigins.ToHashSet(StringComparer.Ordinal); |
| | | 160 | | this.AllowedReturnPathPrefixes = AllowedReturnPathPrefixes.ToHashSet(StringComparer.Ordinal); |
| | | 161 | | this.SecretBinding = SecretBinding; |
| | | 162 | | this.IsEnabled = IsEnabled; |
| | | 163 | | } |
| | | 164 | | |
| | | 165 | | public string ClientId { get; init; } = ""; |
| | | 166 | | public string DisplayName { get; init; } = ""; |
| | | 167 | | public AuthenticationClientType ClientType { get; init; } |
| | | 168 | | public HashSet<Uri> CallbackUris { get; init; } = new(); |
| | | 169 | | public HashSet<Uri> LogoutCallbackUris { get; init; } = new(); |
| | | 170 | | public HashSet<string> AllowedOrigins { get; init; } = new(StringComparer.Ordinal); |
| | | 171 | | public HashSet<string> AllowedReturnPathPrefixes { get; init; } = new(StringComparer.Ordinal); |
| | | 172 | | public SecretBinding? SecretBinding { get; init; } |
| | | 173 | | public bool IsEnabled { get; init; } |
| | | 174 | | |
| | | 175 | | public void Deconstruct( |
| | | 176 | | out string ClientId, |
| | | 177 | | out string DisplayName, |
| | | 178 | | out AuthenticationClientType ClientType, |
| | | 179 | | out IReadOnlySet<Uri> CallbackUris, |
| | | 180 | | out IReadOnlySet<Uri> LogoutCallbackUris, |
| | | 181 | | out IReadOnlySet<string> AllowedOrigins, |
| | | 182 | | out IReadOnlySet<string> AllowedReturnPathPrefixes, |
| | | 183 | | out SecretBinding? SecretBinding, |
| | | 184 | | out bool IsEnabled) |
| | | 185 | | { |
| | | 186 | | ClientId = this.ClientId; |
| | | 187 | | DisplayName = this.DisplayName; |
| | | 188 | | ClientType = this.ClientType; |
| | | 189 | | CallbackUris = this.CallbackUris; |
| | | 190 | | LogoutCallbackUris = this.LogoutCallbackUris; |
| | | 191 | | AllowedOrigins = this.AllowedOrigins; |
| | | 192 | | AllowedReturnPathPrefixes = this.AllowedReturnPathPrefixes; |
| | | 193 | | SecretBinding = this.SecretBinding; |
| | | 194 | | IsEnabled = this.IsEnabled; |
| | | 195 | | } |
| | | 196 | | } |
| | | 197 | | |
| | | 198 | | public sealed class BrokerTransaction |
| | | 199 | | { |
| | | 200 | | public string HandleHash { get; set; } = null!; |
| | | 201 | | public BrokerTransactionPurpose Purpose { get; set; } |
| | | 202 | | public string ClientId { get; set; } = null!; |
| | | 203 | | public Uri CallbackUri { get; set; } = null!; |
| | | 204 | | public string ReturnPath { get; set; } = null!; |
| | | 205 | | /// <summary> |
| | | 206 | | /// Caller-owned opaque state returned only to the registered callback URI. |
| | | 207 | | /// </summary> |
| | | 208 | | public string? ClientState { get; set; } |
| | | 209 | | public string TenantId { get; set; } = null!; |
| | | 210 | | public string? ConnectionId { get; set; } |
| | | 211 | | /// <summary>Logical callback key, retained alongside the immutable in-flight record ID.</summary> |
| | | 212 | | public string? ConnectionKey { get; set; } |
| | | 213 | | public string? ConnectionMaterialRevision { get; set; } |
| | | 214 | | public string? SecretGenerationFingerprint { get; set; } |
| | | 215 | | public string PkceChallenge { get; set; } = null!; |
| | | 216 | | public string? ProviderNonce { get; set; } |
| | | 217 | | public byte[] ProtectedPayload { get; set; } = []; |
| | | 218 | | public DateTimeOffset ExpiresAt { get; set; } |
| | | 219 | | public DateTimeOffset? ConsumedAt { get; set; } |
| | | 220 | | } |
| | | 221 | | |
| | | 222 | | public sealed class AuthorizationGrant |
| | | 223 | | { |
| | | 224 | | public string CodeHash { get; set; } = null!; |
| | | 225 | | public string ClientId { get; set; } = null!; |
| | | 226 | | public Uri CallbackUri { get; set; } = null!; |
| | | 227 | | public string TenantId { get; set; } = null!; |
| | | 228 | | public string UserId { get; set; } = null!; |
| | | 229 | | public string? ExternalSessionId { get; set; } |
| | | 230 | | public string PkceChallenge { get; set; } = null!; |
| | | 231 | | public DateTimeOffset ExpiresAt { get; set; } |
| | | 232 | | public DateTimeOffset? ConsumedAt { get; set; } |
| | | 233 | | } |
| | | 234 | | |
| | | 235 | | public sealed class ExternalAuthenticationSession |
| | | 236 | | { |
| | | 237 | | public string Id { get; set; } = null!; |
| | | 238 | | public string AuthenticationClientId { get; set; } = null!; |
| | | 239 | | public string TenantId { get; set; } = null!; |
| | | 240 | | public string UserId { get; set; } = null!; |
| | | 241 | | /// <summary>Normalized logical connection key resolved in the session target tenant.</summary> |
| | | 242 | | public string ConnectionKey { get; set; } = null!; |
| | | 243 | | public string ConnectionMaterialRevision { get; set; } = null!; |
| | | 244 | | public string? SecretGenerationFingerprint { get; set; } |
| | | 245 | | public string Issuer { get; set; } = null!; |
| | | 246 | | public string SubjectHash { get; set; } = null!; |
| | | 247 | | public IReadOnlyCollection<PermissionGrant> ExternalGrants { get; set; } = []; |
| | | 248 | | public DateTimeOffset StartedAt { get; set; } |
| | | 249 | | public DateTimeOffset LastRefreshedAt { get; set; } |
| | | 250 | | public DateTimeOffset ExpiresAt { get; set; } |
| | | 251 | | public DateTimeOffset RefreshExpiresAt { get; set; } |
| | | 252 | | /// <summary>The hash of the currently issued refresh token, or <see langword="null"/> until one is issued.</summary |
| | | 253 | | public string? CurrentRefreshTokenHash { get; set; } |
| | | 254 | | public long RefreshGeneration { get; set; } |
| | | 255 | | public DateTimeOffset? RevokedAt { get; set; } |
| | | 256 | | public string? RevocationReason { get; set; } |
| | | 257 | | /// <summary>Data-protected upstream ID-token/logout hint retained only for enabled upstream logout.</summary> |
| | | 258 | | public byte[]? ProtectedUpstreamLogoutHint { get; set; } |
| | | 259 | | } |
| | | 260 | | |
| | | 261 | | /// <summary>Restricts administrative session queries to a single tenant and safe metadata fields.</summary> |
| | | 262 | | public sealed class ExternalAuthenticationSessionFilter |
| | | 263 | | { |
| | | 264 | | public string TenantId { get; set; } = null!; |
| | | 265 | | public string? UserId { get; set; } |
| | | 266 | | public string? ConnectionKey { get; set; } |
| | | 267 | | /// <summary><c>active</c>, <c>revoked</c>, or null for both.</summary> |
| | | 268 | | public string? Status { get; set; } |
| | | 269 | | } |
| | | 270 | | |
| | 7 | 271 | | public sealed record ConnectionObservation( |
| | 11 | 272 | | string ConnectionId, |
| | 8 | 273 | | string TestedMaterialRevision, |
| | 5 | 274 | | DateTimeOffset ObservedAt, |
| | 5 | 275 | | ConnectionObservationStatus Status, |
| | 3 | 276 | | string Category, |
| | 1 | 277 | | TimeSpan Duration, |
| | 4 | 278 | | string Summary, |
| | 1 | 279 | | IReadOnlyCollection<string> Warnings, |
| | 8 | 280 | | string CorrelationId); |
| | | 281 | | |
| | | 282 | | public sealed record LoginMethod(string Id, string Key, LoginMethodKind Kind, string DisplayName, string? IconId, int Or |
| | | 283 | | |
| | | 284 | | public sealed record ExternalIdentityLink(string Id, string TenantId, string ConnectionKey, string Issuer, string Subjec |
| | | 285 | | |
| | | 286 | | public sealed class ExternalIdentityLinkFilter |
| | | 287 | | { |
| | | 288 | | public string TenantId { get; set; } = null!; |
| | | 289 | | public string? UserId { get; set; } |
| | | 290 | | public string? ConnectionKey { get; set; } |
| | | 291 | | } |
| | | 292 | | |
| | | 293 | | public sealed record PreviewResult( |
| | | 294 | | string HandleHash, |
| | | 295 | | string AdministratorId, |
| | | 296 | | string TenantId, |
| | | 297 | | string ConnectionId, |
| | | 298 | | string MaterialRevision, |
| | | 299 | | string Issuer, |
| | | 300 | | string MaskedSubject, |
| | | 301 | | IReadOnlyDictionary<string, IReadOnlyCollection<string>> ProjectedClaims, |
| | | 302 | | string PolicyDecision, |
| | | 303 | | IReadOnlyCollection<PermissionGrant> PermissionProjection, |
| | | 304 | | IReadOnlyCollection<string> Warnings, |
| | | 305 | | DateTimeOffset ExpiresAt, |
| | | 306 | | DateTimeOffset? ConsumedAt); |
| | | 307 | | |
| | | 308 | | public sealed class ConnectionFilter |
| | | 309 | | { |
| | | 310 | | public string? Search { get; set; } |
| | | 311 | | public ConnectionSourceOwnership? Ownership { get; set; } |
| | | 312 | | public ConnectionScope? Scope { get; set; } |
| | | 313 | | public string? AdapterType { get; set; } |
| | | 314 | | public bool? IsEnabled { get; set; } |
| | | 315 | | public bool? IsArchived { get; set; } |
| | | 316 | | } |
| | | 317 | | |
| | | 318 | | public abstract record ConnectionMutationResult |
| | | 319 | | { |
| | | 320 | | private ConnectionMutationResult() { } |
| | | 321 | | public sealed record Created(IdentityProviderConnection Connection) : ConnectionMutationResult; |
| | | 322 | | public sealed record Updated(IdentityProviderConnection Connection) : ConnectionMutationResult; |
| | | 323 | | public sealed record NotFound : ConnectionMutationResult; |
| | | 324 | | public sealed record DuplicateKey : ConnectionMutationResult; |
| | | 325 | | public sealed record RevisionConflict(long CurrentRevision) : ConnectionMutationResult; |
| | | 326 | | } |