"jane+john" and "john+jane" name the same group, but they did not behave
that way. Jellyfin only routes a login here when no account matches the
typed name, so logging in with the reversed spelling of an existing group
found nothing and quietly created a second shared account for the same
two people - each with its own watched state.
Group identity is now order-independent:
- Member names are sorted alphabetically when building an account name,
so a given set of members always produces the same name.
- Before creating anything, the login path looks for an existing group
whose members are exactly the named set, compared as a set rather than
a sequence, and logs into that account if it finds one.
- Stored member lists are kept in the same canonical order on create and
update, so a group's stored order does not depend on the order an
admin happened to select members in.
Passing no name through to provisioning lets it generate the canonical
name, rather than preserving whatever order was typed.
Members are also now checked in the order they were typed, stopping at
the first match, so whoever puts their own name first is verified first.
Verification is a deliberately slow hash comparison, so the ordering is
worth having; it is only a preference, and any member's password still
unlocks the group.
Previously the shared account's libraries were chosen independently of
its members, so a group could see a library that one of its members was
blocked from - joining a group became a way to gain access. That was
especially sharp with auto-created groups, where no admin is in the loop.
A shared account is now granted exactly the libraries every member can
already reach. If one member is blocked from a library, no group
containing them can see it. The account is therefore always a subset of
what each member could reach alone, which is what makes creating groups
at the login screen safe to leave on by default.
Details:
- "Enable all folders" is expanded to concrete library ids before
intersecting, since it cannot otherwise be compared with an explicit
list. Shared accounts are always given an explicit list, never the
all-folders permission, so newly added libraries do not silently widen
an existing group.
- Explicitly blocked folders are subtracted even for members who
otherwise have access to everything.
- Fails closed: an unresolvable member contributes no access rather than
being treated as unrestricted.
- Recomputed when membership changes, and re-applied to every group at
startup so narrowing a member's own access narrows their groups.
Drops the now-meaningless EnableAllFolders/EnabledFolders provisioning
inputs and the DynamicGroupsEnableAllFolders setting. Adds 8 tests
covering the intersection rules.
Replaces the plugin template with a working plugin that lets several
users share one viewing account while keeping their individual watched
lists accurate.
Three pieces:
- Auto-creating groups. Logging in as "alice+bob" with any named
member's own password provisions the shared account and signs you in.
Verified against 10.11.5: AuthenticateUser offers unmatched usernames
to every enabled provider and re-queries afterwards, which is the hook
this relies on. Gated on a real member password so knowing two
usernames is not enough to create an account.
- Multi-password authentication. IRequiresResolvedUser hands us the
resolved shared account; each member's live stored hash is checked via
ICryptoProvider.Verify. Deliberately avoids re-entering
UserManager.AuthenticateUser, which would trip every member's
failed-attempt counter whenever a different member's password matched.
- One-way played-state sync. Shared account to members only, filtered to
PlaybackFinished/TogglePlayed/Import so playback progress ticks are
ignored. No loop guard needed: member writes carry a non-shared id.
Membership is stored as user IDs rather than re-parsed from the username,
so shared accounts can be renamed freely. The +/name collision resolves
itself because Jellyfin only consults the plugin when no local user
matches the typed name.
Targets Jellyfin 10.11.x / net9.0. Adds Gitea CI (test, build, release),
a builder image, and 34 tests covering the auth and sync rules.
The markdown codeblocks in the README were using `json`, which has no support for comments, making them highlighted in red. `jsonc` does have support, correctly rendering the codeblocks.
This commit describes how to setup an automation for Visual Studio Code.
The automation aims to build the plugin and then start the server and
optionally the web-client. This way, only one IDE project has to be
opened which starts all necessary dependencies.