diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 1f647b78bc..d0fcf06e79 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -183,6 +183,12 @@ namespace Emby.Server.Implementations.HttpServer.Security originalAuthenticationInfo.UserName = authInfo.User.Username; updateToken = true; } + + authInfo.IsApiKey = true; + } + else + { + authInfo.IsApiKey = false; } if (updateToken) diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs index c4567d058c..7d68aecf99 100644 --- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs @@ -1,5 +1,4 @@ -using System; -using System.Security.Claims; +using System.Security.Claims; using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; @@ -51,6 +50,13 @@ namespace Jellyfin.Api.Auth bool localAccessOnly = false, bool requiredDownloadPermission = false) { + // ApiKey is currently global admin, always allow. + var isApiKey = ClaimHelpers.GetIsApiKey(claimsPrincipal); + if (isApiKey) + { + return true; + } + // Ensure claim has userId. var userId = ClaimHelpers.GetUserId(claimsPrincipal); if (!userId.HasValue) @@ -58,12 +64,6 @@ namespace Jellyfin.Api.Auth return false; } - // UserId of Guid.Empty means token is an apikey. - if (userId.Equals(Guid.Empty)) - { - return true; - } - // Ensure userId links to a valid user. var user = _userManager.GetUserById(userId.Value); if (user == null) diff --git a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs index ec5d172a21..e8cc389072 100644 --- a/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs +++ b/Jellyfin.Api/Auth/CustomAuthenticationHandler.cs @@ -1,4 +1,3 @@ -using System; using System.Globalization; using System.Security.Authentication; using System.Security.Claims; @@ -45,8 +44,7 @@ namespace Jellyfin.Api.Auth { var authorizationInfo = _authService.Authenticate(Request); var role = UserRoles.User; - // UserId of Guid.Empty means token is an apikey. - if (authorizationInfo.UserId.Equals(Guid.Empty) || authorizationInfo.User.HasPermission(PermissionKind.IsAdministrator)) + if (authorizationInfo.IsApiKey || authorizationInfo.User.HasPermission(PermissionKind.IsAdministrator)) { role = UserRoles.Administrator; } @@ -61,6 +59,7 @@ namespace Jellyfin.Api.Auth new Claim(InternalClaimTypes.Client, authorizationInfo.Client), new Claim(InternalClaimTypes.Version, authorizationInfo.Version), new Claim(InternalClaimTypes.Token, authorizationInfo.Token), + new Claim(InternalClaimTypes.IsApiKey, authorizationInfo.IsApiKey.ToString(CultureInfo.InvariantCulture)) }; var identity = new ClaimsIdentity(claims, Scheme.Name); diff --git a/Jellyfin.Api/Constants/InternalClaimTypes.cs b/Jellyfin.Api/Constants/InternalClaimTypes.cs index 4d7c7135d5..8323312e51 100644 --- a/Jellyfin.Api/Constants/InternalClaimTypes.cs +++ b/Jellyfin.Api/Constants/InternalClaimTypes.cs @@ -34,5 +34,10 @@ /// Token. /// </summary> public const string Token = "Jellyfin-Token"; + + /// <summary> + /// Is Api Key. + /// </summary> + public const string IsApiKey = "Jellyfin-IsApiKey"; } } diff --git a/Jellyfin.Api/Helpers/ClaimHelpers.cs b/Jellyfin.Api/Helpers/ClaimHelpers.cs index df235ced25..29e6b4193e 100644 --- a/Jellyfin.Api/Helpers/ClaimHelpers.cs +++ b/Jellyfin.Api/Helpers/ClaimHelpers.cs @@ -63,6 +63,19 @@ namespace Jellyfin.Api.Helpers public static string? GetToken(in ClaimsPrincipal user) => GetClaimValue(user, InternalClaimTypes.Token); + /// <summary> + /// Gets a flag specifying whether the request is using an api key. + /// </summary> + /// <param name="user">Current claims principal.</param> + /// <returns>The flag specifying whether the request is using an api key.</returns> + public static bool GetIsApiKey(in ClaimsPrincipal user) + { + var claimValue = GetClaimValue(user, InternalClaimTypes.IsApiKey); + return !string.IsNullOrEmpty(claimValue) + && bool.TryParse(claimValue, out var parsedClaimValue) + && parsedClaimValue; + } + private static string? GetClaimValue(in ClaimsPrincipal user, string name) { return user?.Identities diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs index 735c46ef86..5c642edff2 100644 --- a/MediaBrowser.Controller/Net/AuthorizationInfo.cs +++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs @@ -1,10 +1,11 @@ -#pragma warning disable CS1591 - using System; using Jellyfin.Data.Entities; namespace MediaBrowser.Controller.Net { + /// <summary> + /// The request authorization info. + /// </summary> public class AuthorizationInfo { /// <summary> @@ -43,6 +44,14 @@ namespace MediaBrowser.Controller.Net /// <value>The token.</value> public string Token { get; set; } + /// <summary> + /// Gets or sets a value indicating whether the authorization is from an api key. + /// </summary> + public bool IsApiKey { get; set; } + + /// <summary> + /// Gets or sets the user making the request. + /// </summary> public User User { get; set; } } }