mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-26 18:54:48 +03:00
Fix issues with QuickConnect and AuthenticationDb
This commit is contained in:
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Authentication;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.QuickConnect;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.QuickConnect;
|
||||
@@ -29,8 +30,9 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||
/// </summary>
|
||||
private const int Timeout = 10;
|
||||
|
||||
private readonly RNGCryptoServiceProvider _rng = new();
|
||||
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new();
|
||||
private readonly RNGCryptoServiceProvider _rng = new ();
|
||||
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new ();
|
||||
private readonly ConcurrentDictionary<string, (DateTime Timestamp, AuthenticationResult AuthenticationResult)> _authorizedSecrets = new ();
|
||||
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly ILogger<QuickConnectManager> _logger;
|
||||
@@ -68,14 +70,41 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public QuickConnectResult TryConnect()
|
||||
public QuickConnectResult TryConnect(AuthorizationInfo authorizationInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(authorizationInfo.DeviceId))
|
||||
{
|
||||
throw new ArgumentException(nameof(authorizationInfo.DeviceId) + " is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(authorizationInfo.Device))
|
||||
{
|
||||
throw new ArgumentException(nameof(authorizationInfo.Device) + " is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(authorizationInfo.Client))
|
||||
{
|
||||
throw new ArgumentException(nameof(authorizationInfo.Client) + " is required");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(authorizationInfo.Version))
|
||||
{
|
||||
throw new ArgumentException(nameof(authorizationInfo.Version) + "is required");
|
||||
}
|
||||
|
||||
AssertActive();
|
||||
ExpireRequests();
|
||||
|
||||
var secret = GenerateSecureRandom();
|
||||
var code = GenerateCode();
|
||||
var result = new QuickConnectResult(secret, code, DateTime.UtcNow);
|
||||
var result = new QuickConnectResult(
|
||||
secret,
|
||||
code,
|
||||
DateTime.UtcNow,
|
||||
authorizationInfo.DeviceId,
|
||||
authorizationInfo.Device,
|
||||
authorizationInfo.Client,
|
||||
authorizationInfo.Version);
|
||||
|
||||
_currentRequests[code] = result;
|
||||
return result;
|
||||
@@ -135,19 +164,41 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||
throw new InvalidOperationException("Request is already authorized");
|
||||
}
|
||||
|
||||
var token = Guid.NewGuid();
|
||||
result.Authentication = token;
|
||||
|
||||
// Change the time on the request so it expires one minute into the future. It can't expire immediately as otherwise some clients wouldn't ever see that they have been authenticated.
|
||||
result.DateAdded = DateTime.Now.Add(TimeSpan.FromMinutes(1));
|
||||
result.DateAdded = DateTime.UtcNow.Add(TimeSpan.FromMinutes(1));
|
||||
|
||||
await _sessionManager.AuthenticateQuickConnect(userId).ConfigureAwait(false);
|
||||
var authenticationResult = await _sessionManager.AuthenticateDirect(new AuthenticationRequest
|
||||
{
|
||||
UserId = userId,
|
||||
DeviceId = result.DeviceId,
|
||||
DeviceName = result.DeviceName,
|
||||
App = result.AppName,
|
||||
AppVersion = result.AppVersion
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
_logger.LogDebug("Authorizing device with code {Code} to login as user {userId}", code, userId);
|
||||
_authorizedSecrets[result.Secret] = (DateTime.UtcNow, authenticationResult);
|
||||
result.Authenticated = true;
|
||||
_currentRequests[code] = result;
|
||||
|
||||
_logger.LogDebug("Authorizing device with code {Code} to login as user {UserId}", code, userId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public AuthenticationResult GetAuthorizedRequest(string secret)
|
||||
{
|
||||
AssertActive();
|
||||
ExpireRequests();
|
||||
|
||||
if (!_authorizedSecrets.TryGetValue(secret, out var result))
|
||||
{
|
||||
throw new ResourceNotFoundException("Unable to find request");
|
||||
}
|
||||
|
||||
return result.AuthenticationResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose.
|
||||
/// </summary>
|
||||
@@ -189,7 +240,7 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||
// Expire stale connection requests
|
||||
foreach (var (_, currentRequest) in _currentRequests)
|
||||
{
|
||||
if (expireAll || currentRequest.DateAdded > minTime)
|
||||
if (expireAll || currentRequest.DateAdded < minTime)
|
||||
{
|
||||
var code = currentRequest.Code;
|
||||
_logger.LogDebug("Removing expired request {Code}", code);
|
||||
@@ -200,6 +251,18 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (secret, (timestamp, _)) in _authorizedSecrets)
|
||||
{
|
||||
if (expireAll || timestamp < minTime)
|
||||
{
|
||||
_logger.LogDebug("Removing expired secret {Secret}", secret);
|
||||
if (!_authorizedSecrets.TryRemove(secret, out _))
|
||||
{
|
||||
_logger.LogWarning("Secret {Secret} already expired", secret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user