Skip to main content
Jellyfin provides a comprehensive user management system with flexible authentication providers, granular permissions, and access control policies. The system supports multiple authentication methods and allows administrators to control what users can access and do.

User System Overview

The user system is built around the UserManager service and the User entity:
Jellyfin.Server.Implementations/Users/UserManager.cs
public partial class UserManager : IUserManager
{
    private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
    private readonly IReadOnlyCollection<IAuthenticationProvider> _authenticationProviders;
    private readonly IReadOnlyCollection<IPasswordResetProvider> _passwordResetProviders;
    private readonly IDictionary<Guid, User> _users;
    
    public IEnumerable<User> Users => _users.Values;
    public IEnumerable<Guid> UsersIds => _users.Keys;
    
    public event EventHandler<GenericEventArgs<User>>? OnUserUpdated;
}
Users are cached in memory for fast access and synchronized with the database for persistence.

User Entity

The User entity contains all user-related data:
Jellyfin.Database.Implementations/Entities/User.cs
public class User : IHasPermissions, IHasConcurrencyToken
{
    // Identity
    public Guid Id { get; set; }
    public string Username { get; set; }
    public string? Password { get; set; }
    
    // Authentication
    public string AuthenticationProviderId { get; set; }
    public string PasswordResetProviderId { get; set; }
    public bool MustUpdatePassword { get; set; }
    
    // Security
    public int InvalidLoginAttemptCount { get; set; }
    public int? LoginAttemptsBeforeLockout { get; set; }
    public int MaxActiveSessions { get; set; }
    
    // Activity tracking
    public DateTime? LastActivityDate { get; set; }
    public DateTime? LastLoginDate { get; set; }
    
    // Preferences
    public bool EnableLocalPassword { get; set; }
    public bool EnableUserPreferenceAccess { get; set; }
    public bool EnableAutoLogin { get; set; }
    
    // Playback preferences
    public SubtitlePlaybackMode SubtitleMode { get; set; }
    public string? AudioLanguagePreference { get; set; }
    public string? SubtitleLanguagePreference { get; set; }
    public bool RememberAudioSelections { get; set; }
    public bool RememberSubtitleSelections { get; set; }
    public bool EnableNextEpisodeAutoPlay { get; set; }
    
    // Parental controls
    public int? MaxParentalRatingScore { get; set; }
    
    // Relationships
    public virtual ICollection<Permission> Permissions { get; private set; }
    public virtual ICollection<Preference> Preferences { get; private set; }
    public virtual ICollection<AccessSchedule> AccessSchedules { get; private set; }
    public virtual ImageInfo? ProfileImage { get; set; }
}

User Management Operations

Creating Users

Interface
public interface IUserManager
{
    Task<User> CreateUserAsync(string name);
}
internal async Task<User> CreateUserInternalAsync(
    string name, 
    JellyfinDbContext dbContext)
{
    // Validate username
    ThrowIfInvalidUsername(name);
    
    // Create user with defaults
    var newUser = new User(
        name,
        _defaultAuthenticationProvider.GetType().Name,
        _defaultPasswordResetProvider.GetType().Name)
    {
        EnableUserPreferenceAccess = true,
        RememberAudioSelections = true,
        RememberSubtitleSelections = true,
        EnableNextEpisodeAutoPlay = true,
        HidePlayedInLatest = true
    };
    
    // Apply default permissions
    foreach (var defaultPermission in GetDefaultPermissions())
    {
        newUser.Permissions.Add(defaultPermission);
    }
    
    dbContext.Users.Add(newUser);
    await dbContext.SaveChangesAsync().ConfigureAwait(false);
    
    return newUser;
}

Updating Users

public async Task UpdateUserAsync(User user)
{
    var dbContext = await _dbProvider.CreateDbContextAsync()
        .ConfigureAwait(false);
    
    await using (dbContext.ConfigureAwait(false))
    {
        await UpdateUserInternalAsync(dbContext, user)
            .ConfigureAwait(false);
    }
    
    var eventArgs = new UserUpdatedEventArgs(user);
    await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
    OnUserUpdated?.Invoke(this, eventArgs);
}

Deleting Users

public async Task DeleteUserAsync(Guid userId)
{
    var user = GetUserById(userId) 
        ?? throw new ResourceNotFoundException();
    
    var dbContext = await _dbProvider.CreateDbContextAsync()
        .ConfigureAwait(false);
    
    await using (dbContext.ConfigureAwait(false))
    {
        dbContext.Users.Remove(user);
        await dbContext.SaveChangesAsync().ConfigureAwait(false);
    }
    
    _users.Remove(userId);
}

Authentication Providers

Jellyfin supports pluggable authentication providers:
MediaBrowser.Controller/Authentication/IAuthenticationProvider.cs
public interface IAuthenticationProvider
{
    string Name { get; }
    bool IsEnabled { get; }
    
    Task<ProviderAuthenticationResult> Authenticate(
        string username, 
        string password);
    
    Task ChangePassword(User user, string newPassword);
}

Built-in Providers

The DefaultAuthenticationProvider handles local password authentication:
public class DefaultAuthenticationProvider : IAuthenticationProvider
{
    public async Task<ProviderAuthenticationResult> Authenticate(
        string username, 
        string password)
    {
        var user = _userManager.GetUserByName(username);
        
        if (user == null)
            throw new AuthenticationException("Invalid username or password");
        
        // Verify password hash
        var calculatedHash = _cryptoProvider.ComputeHash(
            user.Password.Id,
            Encoding.UTF8.GetBytes(password),
            user.Password.Salt);
        
        if (!calculatedHash.SequenceEqual(user.Password.Hash))
        {
            throw new AuthenticationException("Invalid username or password");
        }
        
        return new ProviderAuthenticationResult
        {
            Username = user.Username
        };
    }
}
Custom authentication providers can be created by implementing IAuthenticationProvider in a plugin.

Authentication Flow

The authentication process follows these steps:
1

User Credentials

Client submits username and password to /Users/AuthenticateByName endpoint.
2

User Lookup

UserManager retrieves the user by username:
public User? GetUserByName(string name)
{
    return _users.Values.FirstOrDefault(u => 
        string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase));
}
3

Provider Selection

The appropriate authentication provider is selected based on the user’s AuthenticationProviderId.
4

Authentication

The provider validates the credentials:
public async Task<User?> AuthenticateUser(
    string username, 
    string password, 
    string remoteEndPoint, 
    bool isUserSession)
{
    var provider = GetAuthenticationProvider(user);
    var result = await provider.Authenticate(username, password)
        .ConfigureAwait(false);
    
    // Update last login date
    user.LastLoginDate = DateTime.UtcNow;
    user.InvalidLoginAttemptCount = 0;
    
    await UpdateUserAsync(user).ConfigureAwait(false);
    
    return user;
}
5

Token Generation

A session token is generated and returned to the client for subsequent requests.
6

Session Tracking

The SessionManager tracks the active session with device information and capabilities.

User Policies

User policies control what users can access and do:
MediaBrowser.Model/Users/UserPolicy.cs
public class UserPolicy
{
    // Administrative
    public bool IsAdministrator { get; set; }
    public bool IsHidden { get; set; }
    public bool IsDisabled { get; set; }
    
    // Content management
    public bool EnableCollectionManagement { get; set; }
    public bool EnableSubtitleManagement { get; set; }
    public bool EnableLyricManagement { get; set; }
    
    // Content deletion
    public bool EnableContentDeletion { get; set; }
    public string[] EnableContentDeletionFromFolders { get; set; }
    
    // Playback
    public bool EnableMediaPlayback { get; set; }
    public bool EnableAudioPlaybackTranscoding { get; set; }
    public bool EnableVideoPlaybackTranscoding { get; set; }
    public bool EnablePlaybackRemuxing { get; set; }
    public bool ForceRemoteSourceTranscoding { get; set; }
    
    // Transcoding
    public bool EnableSyncTranscoding { get; set; }
    public bool EnableMediaConversion { get; set; }
    
    // Live TV
    public bool EnableLiveTvManagement { get; set; }
    public bool EnableLiveTvAccess { get; set; }
    
    // Sharing and downloads
    public bool EnablePublicSharing { get; set; }
    public bool EnableContentDownloading { get; set; }
    public bool EnableRemoteAccess { get; set; }
    
    // Device control
    public bool EnableSharedDeviceControl { get; set; }
    public bool EnableAllDevices { get; set; }
    public string[] EnabledDevices { get; set; }
    
    // Folder access
    public bool EnableAllFolders { get; set; }
    public Guid[] EnabledFolders { get; set; }
    
    // Parental controls
    public int? MaxParentalRating { get; set; }
    public UnratedItem[] BlockUnratedItems { get; set; }
    public string[] BlockedTags { get; set; }
    public string[] AllowedTags { get; set; }
    
    // Session limits
    public int LoginAttemptsBeforeLockout { get; set; }
    public int MaxActiveSessions { get; set; }
    
    // Access scheduling
    public AccessSchedule[] AccessSchedules { get; set; }
    
    // Sync Play
    public SyncPlayUserAccessType SyncPlayAccess { get; set; }
}

Policy Examples

var adminPolicy = new UserPolicy
{
    IsAdministrator = true,
    EnableAllFolders = true,
    EnableAllDevices = true,
    EnableMediaPlayback = true,
    EnableContentDeletion = true,
    EnableLiveTvManagement = true,
    EnableRemoteAccess = true
};

Password Management

Changing Passwords

public async Task ChangePassword(User user, string newPassword)
{
    var authProvider = GetAuthenticationProvider(user);
    await authProvider.ChangePassword(user, newPassword)
        .ConfigureAwait(false);
    
    user.MustUpdatePassword = false;
    await UpdateUserAsync(user).ConfigureAwait(false);
}

Password Reset

public async Task<ForgotPasswordResult> StartForgotPasswordProcess(
    string enteredUsername, 
    bool isInNetwork)
{
    var user = GetUserByName(enteredUsername);
    var provider = GetPasswordResetProvider(user);
    
    return await provider.StartForgotPasswordProcess(user, isInNetwork)
        .ConfigureAwait(false);
}

public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
{
    // Validate PIN and reset password
    var result = await _passwordResetProvider.RedeemPasswordResetPin(pin)
        .ConfigureAwait(false);
    
    return result;
}

Access Schedules

Access schedules restrict when users can access the server:
public class AccessSchedule
{
    public DayOfWeek DayOfWeek { get; set; }
    public double StartHour { get; set; }  // 0-24
    public double EndHour { get; set; }    // 0-24
}
Access schedules are enforced at the session level. Users with active sessions when a schedule ends will be logged out automatically.

Permissions

Fine-grained permissions control specific actions:
public class Permission
{
    public PermissionKind Kind { get; set; }
    public bool Value { get; set; }
}

public enum PermissionKind
{
    IsAdministrator,
    IsHidden,
    IsDisabled,
    EnableAllDevices,
    EnableAllFolders,
    EnableContentDeletion,
    EnableContentDownloading,
    EnableMediaConversion,
    EnableMediaPlayback,
    EnablePlaybackRemuxing,
    EnablePublicSharing,
    EnableRemoteAccess,
    EnableSyncTranscoding
}

Username Validation

Usernames must meet certain criteria:
[GeneratedRegex(@"^(?!\s)[\w\ \-'._@+]+(?<!\s)$")]
private static partial Regex ValidUsernameRegex();

private void ThrowIfInvalidUsername(string name)
{
    if (string.IsNullOrWhiteSpace(name))
    {
        throw new ArgumentException("Username cannot be empty");
    }
    
    if (!ValidUsernameRegex().IsMatch(name))
    {
        throw new ArgumentException(
            "Username can only contain letters, numbers, dashes, " +
            "underscores, apostrophes, periods, spaces, and at-signs.");
    }
}

Security Features

Failed login attempts are tracked and can trigger account lockout:
if (user.LoginAttemptsBeforeLockout.HasValue && 
    user.InvalidLoginAttemptCount >= user.LoginAttemptsBeforeLockout)
{
    user.AuthenticationProviderId = _invalidAuthProvider.Name;
    await UpdateUserAsync(user).ConfigureAwait(false);
    throw new SecurityException("User account locked due to too many failed attempts");
}

Next Steps

Architecture

Learn about Jellyfin’s architecture

Media Libraries

Understand media organization

Plugins

Extend authentication with plugins

API Reference

User management API endpoints