diff --git a/Jellyfin.Api/Controllers/ApiKeyController.cs b/Jellyfin.Api/Controllers/ApiKeyController.cs
index 8c43d786a7..96efde5fbd 100644
--- a/Jellyfin.Api/Controllers/ApiKeyController.cs
+++ b/Jellyfin.Api/Controllers/ApiKeyController.cs
@@ -1,10 +1,8 @@
using System;
using System.ComponentModel.DataAnnotations;
-using System.Globalization;
+using System.Threading.Tasks;
using Jellyfin.Api.Constants;
-using MediaBrowser.Controller;
using MediaBrowser.Controller.Security;
-using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@@ -18,24 +16,15 @@ namespace Jellyfin.Api.Controllers
[Route("Auth")]
public class ApiKeyController : BaseJellyfinApiController
{
- private readonly ISessionManager _sessionManager;
- private readonly IServerApplicationHost _appHost;
- private readonly IAuthenticationRepository _authRepo;
+ private readonly IAuthenticationManager _authenticationManager;
///
/// Initializes a new instance of the class.
///
- /// Instance of interface.
- /// Instance of interface.
- /// Instance of interface.
- public ApiKeyController(
- ISessionManager sessionManager,
- IServerApplicationHost appHost,
- IAuthenticationRepository authRepo)
+ /// Instance of interface.
+ public ApiKeyController(IAuthenticationManager authenticationManager)
{
- _sessionManager = sessionManager;
- _appHost = appHost;
- _authRepo = authRepo;
+ _authenticationManager = authenticationManager;
}
///
@@ -46,14 +35,15 @@ namespace Jellyfin.Api.Controllers
[HttpGet("Keys")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
- public ActionResult> GetKeys()
+ public async Task>> GetKeys()
{
- var result = _authRepo.Get(new AuthenticationInfoQuery
- {
- HasUser = false
- });
+ var keys = await _authenticationManager.GetApiKeys();
- return result;
+ return new QueryResult
+ {
+ Items = keys,
+ TotalRecordCount = keys.Count
+ };
}
///
@@ -65,17 +55,10 @@ namespace Jellyfin.Api.Controllers
[HttpPost("Keys")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult CreateKey([FromQuery, Required] string app)
+ public async Task CreateKey([FromQuery, Required] string app)
{
- _authRepo.Create(new AuthenticationInfo
- {
- AppName = app,
- AccessToken = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture),
- DateCreated = DateTime.UtcNow,
- DeviceId = _appHost.SystemId,
- DeviceName = _appHost.FriendlyName,
- AppVersion = _appHost.ApplicationVersionString
- });
+ await _authenticationManager.CreateApiKey(app).ConfigureAwait(false);
+
return NoContent();
}
@@ -88,9 +71,10 @@ namespace Jellyfin.Api.Controllers
[HttpDelete("Keys/{key}")]
[Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
- public ActionResult RevokeKey([FromRoute, Required] string key)
+ public async Task RevokeKey([FromRoute, Required] Guid key)
{
- _sessionManager.RevokeToken(key);
+ await _authenticationManager.DeleteApiKey(key).ConfigureAwait(false);
+
return NoContent();
}
}
diff --git a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs
new file mode 100644
index 0000000000..37b8ee6e03
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Threading.Tasks;
+using Jellyfin.Data.Entities.Security;
+using MediaBrowser.Controller.Security;
+using Microsoft.EntityFrameworkCore;
+
+namespace Jellyfin.Server.Implementations.Security
+{
+ ///
+ public class AuthenticationManager : IAuthenticationManager
+ {
+ private readonly JellyfinDbProvider _dbProvider;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The database provider.
+ public AuthenticationManager(JellyfinDbProvider dbProvider)
+ {
+ _dbProvider = dbProvider;
+ }
+
+ ///
+ public async Task CreateApiKey(string name)
+ {
+ await using var dbContext = _dbProvider.CreateContext();
+
+ dbContext.ApiKeys.Add(new ApiKey(name));
+
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
+
+ ///
+ public async Task> GetApiKeys()
+ {
+ await using var dbContext = _dbProvider.CreateContext();
+
+ return await dbContext.ApiKeys
+ .AsAsyncEnumerable()
+ .Select(key => new AuthenticationInfo
+ {
+ AppName = key.Name,
+ AccessToken = key.AccessToken.ToString("N", CultureInfo.InvariantCulture),
+ DateCreated = key.DateCreated,
+ DeviceId = string.Empty,
+ DeviceName = string.Empty,
+ AppVersion = string.Empty
+ }).ToListAsync().ConfigureAwait(false);
+ }
+
+ ///
+ public async Task DeleteApiKey(Guid id)
+ {
+ await using var dbContext = _dbProvider.CreateContext();
+
+ var key = await dbContext.ApiKeys
+ .AsQueryable()
+ .Where(apiKey => apiKey.AccessToken == id)
+ .FirstOrDefaultAsync();
+
+ if (key == null)
+ {
+ return;
+ }
+
+ dbContext.Remove(key);
+
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Security/IAuthenticationManager.cs b/MediaBrowser.Controller/Security/IAuthenticationManager.cs
new file mode 100644
index 0000000000..46d0c66224
--- /dev/null
+++ b/MediaBrowser.Controller/Security/IAuthenticationManager.cs
@@ -0,0 +1,34 @@
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Security
+{
+ ///
+ /// Handles the retrieval and storage of API keys.
+ ///
+ public interface IAuthenticationManager
+ {
+ ///
+ /// Creates an API key.
+ ///
+ /// The name of the key.
+ /// A task representing the creation of the key.
+ Task CreateApiKey(string name);
+
+ ///
+ /// Gets the API keys.
+ ///
+ /// A task representing the retrieval of the API keys.
+ Task> GetApiKeys();
+
+ ///
+ /// Deletes an API key with the provided access token.
+ ///
+ /// The access token.
+ /// A task representing the deletion of the API key.
+ Task DeleteApiKey(Guid accessToken);
+ }
+}