/*
* 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 _);
}