/* * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) * See https://github.com/openiddict/openiddict-core for more information concerning * the license and the contributors participating to this project. */ using System.Collections.Concurrent; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using OpenIddict.Extensions; namespace OpenIddict.Client.SystemIntegration; /// /// Contains the APIs needed to coordinate authentication operations that happen in a different context. /// [EditorBrowsable(EditorBrowsableState.Never)] public sealed class OpenIddictClientSystemIntegrationMarshal { private readonly ConcurrentDictionary TaskCompletionSource)>> _operations = new(); /// /// Determines whether the authentication demand corresponding to the specified nonce is tracked. /// /// The nonce, used as a unique identifier. /// if the operation is tracked, otherwise. internal bool IsTracked(string nonce) => _operations.ContainsKey(nonce); /// /// Tries to add the specified authentication demand to the list of tracked operations. /// /// The nonce, used as a unique identifier. /// The request forgery protection associated with the specified authentication demand. /// if the operation could be added, otherwise. internal bool TryAdd(string nonce, string protection) => _operations.TryAdd(nonce, new(() => ( RequestForgeryProtection: protection, Semaphore: new SemaphoreSlim(initialCount: 1, maxCount: 1), TaskCompletionSource: new(TaskCreationOptions.RunContinuationsAsynchronously)))); /// /// Tries to acquire a lock on the authentication demand corresponding to the specified nonce. /// /// The nonce, used as a unique identifier. /// if the lock could be taken, otherwise. internal bool TryAcquireLock(string nonce) => _operations.TryGetValue(nonce, out var operation) && operation.Value.Semaphore.Wait(TimeSpan.Zero); /// /// Tries to resolve the authentication context associated with the specified nonce. /// /// The nonce, used as a unique identifier. /// The authentication context associated with the tracked operation. /// if the context could be resolved, otherwise. internal bool TryGetResult(string nonce, [NotNullWhen(true)] out ProcessAuthenticationContext? context) { if (!_operations.TryGetValue(nonce, out var operation)) { context = null; return false; } if (!operation.IsValueCreated || !operation.Value.TaskCompletionSource.Task.IsCompleted) { context = null; return false; } context = operation.Value.TaskCompletionSource.Task.Result; return true; } /// /// Tries to wait for the authentication demand associated with the specified nonce to complete. /// /// The nonce, used as a unique identifier. /// The that can be used to abort the operation. /// if the authentication demand is tracked, otherwise. /// The operation was canceled by the user. internal async Task TryWaitForCompletionAsync(string nonce, CancellationToken cancellationToken) { if (!_operations.TryGetValue(nonce, out var operation)) { return false; } await operation.Value.TaskCompletionSource.Task.WaitAsync(cancellationToken); return true; } /// /// Tries to resolve the request forgery protection associated with the specified authentication demand. /// /// The nonce, used as a unique identifier. /// The request forgery protection associated with the specified authentication demand. /// if the operation could be validated, otherwise. internal bool TryGetRequestForgeryProtection(string nonce, [NotNullWhen(true)] out string? protection) { if (_operations.TryGetValue(nonce, out var operation)) { protection = operation.Value.RequestForgeryProtection; return true; } protection = null; return false; } /// /// Tries to complete the specified authentication demand. /// /// The nonce, used as a unique identifier. /// The authentication context that will be returned to the caller. /// if the operation could be completed, otherwise. internal bool TryComplete(string nonce, ProcessAuthenticationContext context) => _operations.TryGetValue(nonce, out var operation) && operation.Value.TaskCompletionSource.TrySetResult(context); /// /// Tries to remove the specified authentication operation from the list of tracked operations. /// /// The nonce, used as a unique identifier. /// if the operation could be removed, otherwise. internal bool TryRemove(string nonce) => _operations.TryRemove(nonce, out _); }