Fix issues with QuickConnect and AuthenticationDb
This commit is contained in:
parent
ae878fa051
commit
397868be95
|
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Authentication;
|
using MediaBrowser.Controller.Authentication;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Controller.QuickConnect;
|
using MediaBrowser.Controller.QuickConnect;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.QuickConnect;
|
using MediaBrowser.Model.QuickConnect;
|
||||||
|
@ -29,8 +30,9 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int Timeout = 10;
|
private const int Timeout = 10;
|
||||||
|
|
||||||
private readonly RNGCryptoServiceProvider _rng = new();
|
private readonly RNGCryptoServiceProvider _rng = new ();
|
||||||
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new();
|
private readonly ConcurrentDictionary<string, QuickConnectResult> _currentRequests = new ();
|
||||||
|
private readonly ConcurrentDictionary<string, (DateTime Timestamp, AuthenticationResult AuthenticationResult)> _authorizedSecrets = new ();
|
||||||
|
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly ILogger<QuickConnectManager> _logger;
|
private readonly ILogger<QuickConnectManager> _logger;
|
||||||
|
@ -68,14 +70,41 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <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();
|
AssertActive();
|
||||||
ExpireRequests();
|
ExpireRequests();
|
||||||
|
|
||||||
var secret = GenerateSecureRandom();
|
var secret = GenerateSecureRandom();
|
||||||
var code = GenerateCode();
|
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;
|
_currentRequests[code] = result;
|
||||||
return result;
|
return result;
|
||||||
|
@ -135,19 +164,41 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||||
throw new InvalidOperationException("Request is already authorized");
|
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.
|
// 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;
|
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>
|
/// <summary>
|
||||||
/// Dispose.
|
/// Dispose.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -189,7 +240,7 @@ namespace Emby.Server.Implementations.QuickConnect
|
||||||
// Expire stale connection requests
|
// Expire stale connection requests
|
||||||
foreach (var (_, currentRequest) in _currentRequests)
|
foreach (var (_, currentRequest) in _currentRequests)
|
||||||
{
|
{
|
||||||
if (expireAll || currentRequest.DateAdded > minTime)
|
if (expireAll || currentRequest.DateAdded < minTime)
|
||||||
{
|
{
|
||||||
var code = currentRequest.Code;
|
var code = currentRequest.Code;
|
||||||
_logger.LogDebug("Removing expired request {Code}", 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1432,16 +1432,21 @@ namespace Emby.Server.Implementations.Session
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Authenticates the new session.
|
/// Authenticates the new session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">The request.</param>
|
/// <param name="request">The authenticationrequest.</param>
|
||||||
/// <returns>Task{SessionInfo}.</returns>
|
/// <returns>The authentication result.</returns>
|
||||||
public Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request)
|
public Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request)
|
||||||
{
|
{
|
||||||
return AuthenticateNewSessionInternal(request, true);
|
return AuthenticateNewSessionInternal(request, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<AuthenticationResult> AuthenticateQuickConnect(Guid userId)
|
/// <summary>
|
||||||
|
/// Directly authenticates the session without enforcing password.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The authentication request.</param>
|
||||||
|
/// <returns>The authentication result.</returns>
|
||||||
|
public Task<AuthenticationResult> AuthenticateDirect(AuthenticationRequest request)
|
||||||
{
|
{
|
||||||
return AuthenticateNewSessionInternal(new AuthenticationRequest { UserId = userId }, false);
|
return AuthenticateNewSessionInternal(request, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<AuthenticationResult> AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword)
|
private async Task<AuthenticationResult> AuthenticateNewSessionInternal(AuthenticationRequest request, bool enforcePassword)
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Authentication;
|
using MediaBrowser.Controller.Authentication;
|
||||||
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Controller.QuickConnect;
|
using MediaBrowser.Controller.QuickConnect;
|
||||||
using MediaBrowser.Model.QuickConnect;
|
using MediaBrowser.Model.QuickConnect;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
@ -18,14 +19,17 @@ namespace Jellyfin.Api.Controllers
|
||||||
public class QuickConnectController : BaseJellyfinApiController
|
public class QuickConnectController : BaseJellyfinApiController
|
||||||
{
|
{
|
||||||
private readonly IQuickConnect _quickConnect;
|
private readonly IQuickConnect _quickConnect;
|
||||||
|
private readonly IAuthorizationContext _authContext;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="QuickConnectController"/> class.
|
/// Initializes a new instance of the <see cref="QuickConnectController"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="quickConnect">Instance of the <see cref="IQuickConnect"/> interface.</param>
|
/// <param name="quickConnect">Instance of the <see cref="IQuickConnect"/> interface.</param>
|
||||||
public QuickConnectController(IQuickConnect quickConnect)
|
/// <param name="authContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
|
||||||
|
public QuickConnectController(IQuickConnect quickConnect, IAuthorizationContext authContext)
|
||||||
{
|
{
|
||||||
_quickConnect = quickConnect;
|
_quickConnect = quickConnect;
|
||||||
|
_authContext = authContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -48,11 +52,12 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="QuickConnectResult"/> with a secret and code for future use or an error message.</returns>
|
/// <returns>A <see cref="QuickConnectResult"/> with a secret and code for future use or an error message.</returns>
|
||||||
[HttpGet("Initiate")]
|
[HttpGet("Initiate")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public ActionResult<QuickConnectResult> Initiate()
|
public async Task<ActionResult<QuickConnectResult>> Initiate()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return _quickConnect.TryConnect();
|
var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false);
|
||||||
|
return _quickConnect.TryConnect(auth);
|
||||||
}
|
}
|
||||||
catch (AuthenticationException)
|
catch (AuthenticationException)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,7 @@ using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
|
using MediaBrowser.Controller.QuickConnect;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
|
@ -38,6 +39,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
private readonly IAuthorizationContext _authContext;
|
private readonly IAuthorizationContext _authContext;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly IQuickConnect _quickConnectManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="UserController"/> class.
|
/// Initializes a new instance of the <see cref="UserController"/> class.
|
||||||
|
@ -49,6 +51,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <param name="authContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
|
/// <param name="authContext">Instance of the <see cref="IAuthorizationContext"/> interface.</param>
|
||||||
/// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
/// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
|
||||||
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
|
||||||
|
/// <param name="quickConnectManager">Instance of the <see cref="IQuickConnect"/> interface.</param>
|
||||||
public UserController(
|
public UserController(
|
||||||
IUserManager userManager,
|
IUserManager userManager,
|
||||||
ISessionManager sessionManager,
|
ISessionManager sessionManager,
|
||||||
|
@ -56,7 +59,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
IDeviceManager deviceManager,
|
IDeviceManager deviceManager,
|
||||||
IAuthorizationContext authContext,
|
IAuthorizationContext authContext,
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
ILogger<UserController> logger)
|
ILogger<UserController> logger,
|
||||||
|
IQuickConnect quickConnectManager)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
|
@ -65,6 +69,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
_authContext = authContext;
|
_authContext = authContext;
|
||||||
_config = config;
|
_config = config;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_quickConnectManager = quickConnectManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -228,23 +233,11 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="Task"/> containing an <see cref="AuthenticationRequest"/> with information about the new session.</returns>
|
/// <returns>A <see cref="Task"/> containing an <see cref="AuthenticationRequest"/> with information about the new session.</returns>
|
||||||
[HttpPost("AuthenticateWithQuickConnect")]
|
[HttpPost("AuthenticateWithQuickConnect")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult<AuthenticationResult>> AuthenticateWithQuickConnect([FromBody, Required] QuickConnectDto request)
|
public ActionResult<AuthenticationResult> AuthenticateWithQuickConnect([FromBody, Required] QuickConnectDto request)
|
||||||
{
|
{
|
||||||
var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var authRequest = new AuthenticationRequest
|
return _quickConnectManager.GetAuthorizedRequest(request.Secret);
|
||||||
{
|
|
||||||
App = auth.Client,
|
|
||||||
AppVersion = auth.Version,
|
|
||||||
DeviceId = auth.DeviceId,
|
|
||||||
DeviceName = auth.Device,
|
|
||||||
};
|
|
||||||
|
|
||||||
return await _sessionManager.AuthenticateQuickConnect(
|
|
||||||
authRequest,
|
|
||||||
request.Token).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
catch (SecurityException e)
|
catch (SecurityException e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,9 +8,9 @@ namespace Jellyfin.Api.Models.UserDtos
|
||||||
public class QuickConnectDto
|
public class QuickConnectDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the quick connect token.
|
/// Gets or sets the quick connect secret.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public string? Token { get; set; }
|
public string Secret { get; set; } = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Authentication;
|
||||||
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.QuickConnect;
|
using MediaBrowser.Model.QuickConnect;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.QuickConnect
|
namespace MediaBrowser.Controller.QuickConnect
|
||||||
|
@ -18,8 +19,9 @@ namespace MediaBrowser.Controller.QuickConnect
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initiates a new quick connect request.
|
/// Initiates a new quick connect request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="authorizationInfo">The initiator authorization info.</param>
|
||||||
/// <returns>A quick connect result with tokens to proceed or throws an exception if not active.</returns>
|
/// <returns>A quick connect result with tokens to proceed or throws an exception if not active.</returns>
|
||||||
QuickConnectResult TryConnect();
|
QuickConnectResult TryConnect(AuthorizationInfo authorizationInfo);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks the status of an individual request.
|
/// Checks the status of an individual request.
|
||||||
|
@ -35,5 +37,12 @@ namespace MediaBrowser.Controller.QuickConnect
|
||||||
/// <param name="code">Identifying code for the request.</param>
|
/// <param name="code">Identifying code for the request.</param>
|
||||||
/// <returns>A boolean indicating if the authorization completed successfully.</returns>
|
/// <returns>A boolean indicating if the authorization completed successfully.</returns>
|
||||||
Task<bool> AuthorizeRequest(Guid userId, string code);
|
Task<bool> AuthorizeRequest(Guid userId, string code);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the authorized request for the secret.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="secret">The secret.</param>
|
||||||
|
/// <returns>The authentication result.</returns>
|
||||||
|
AuthenticationResult GetAuthorizedRequest(string secret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,12 +273,7 @@ namespace MediaBrowser.Controller.Session
|
||||||
/// <returns>Task{SessionInfo}.</returns>
|
/// <returns>Task{SessionInfo}.</returns>
|
||||||
Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request);
|
Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request);
|
||||||
|
|
||||||
/// <summary>
|
Task<AuthenticationResult> AuthenticateDirect(AuthenticationRequest request);
|
||||||
/// Authenticates a new session with quick connect.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId">The user id.</param>
|
|
||||||
/// <returns>Task{SessionInfo}.</returns>
|
|
||||||
Task<AuthenticationResult> AuthenticateQuickConnect(Guid userId);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reports the capabilities.
|
/// Reports the capabilities.
|
||||||
|
|
|
@ -13,17 +13,32 @@ namespace MediaBrowser.Model.QuickConnect
|
||||||
/// <param name="secret">The secret used to query the request state.</param>
|
/// <param name="secret">The secret used to query the request state.</param>
|
||||||
/// <param name="code">The code used to allow the request.</param>
|
/// <param name="code">The code used to allow the request.</param>
|
||||||
/// <param name="dateAdded">The time when the request was created.</param>
|
/// <param name="dateAdded">The time when the request was created.</param>
|
||||||
public QuickConnectResult(string secret, string code, DateTime dateAdded)
|
/// <param name="deviceId">The requesting device id.</param>
|
||||||
|
/// <param name="deviceName">The requesting device name.</param>
|
||||||
|
/// <param name="appName">The requesting app name.</param>
|
||||||
|
/// <param name="appVersion">The requesting app version.</param>
|
||||||
|
public QuickConnectResult(
|
||||||
|
string secret,
|
||||||
|
string code,
|
||||||
|
DateTime dateAdded,
|
||||||
|
string deviceId,
|
||||||
|
string deviceName,
|
||||||
|
string appName,
|
||||||
|
string appVersion)
|
||||||
{
|
{
|
||||||
Secret = secret;
|
Secret = secret;
|
||||||
Code = code;
|
Code = code;
|
||||||
DateAdded = dateAdded;
|
DateAdded = dateAdded;
|
||||||
|
DeviceId = deviceId;
|
||||||
|
DeviceName = deviceName;
|
||||||
|
AppName = appName;
|
||||||
|
AppVersion = appVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this request is authorized.
|
/// Gets or sets a value indicating whether this request is authorized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Authenticated => Authentication != null;
|
public bool Authenticated { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the secret value used to uniquely identify this request. Can be used to retrieve authentication information.
|
/// Gets the secret value used to uniquely identify this request. Can be used to retrieve authentication information.
|
||||||
|
@ -36,9 +51,24 @@ namespace MediaBrowser.Model.QuickConnect
|
||||||
public string Code { get; }
|
public string Code { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the private access token.
|
/// Gets the requesting device id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid? Authentication { get; set; }
|
public string DeviceId { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the requesting device name.
|
||||||
|
/// </summary>
|
||||||
|
public string DeviceName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the requesting app name.
|
||||||
|
/// </summary>
|
||||||
|
public string AppName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the requesting app version.
|
||||||
|
/// </summary>
|
||||||
|
public string AppVersion { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DateTime that this request was created.
|
/// Gets or sets the DateTime that this request was created.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user