Skip to content

authd-oidc: offline login fails with UNIQUE constraint on users_to_local_groups after first online sign-in when owner_extra_groups is set #1495

@yetanotheralex

Description

@yetanotheralex

Summary

After a successful online sign-in via the OIDC broker, every subsequent offline sign-in by the same user fails immediately with:

authd[…]: failed to update user "<user>": failed to add user to local group: \
UNIQUE constraint failed: users_to_local_groups.uid, users_to_local_groups.group_name

The user is denied authentication at GDM and the local groups configured under owner_extra_groups (and/or extra_groups) are silently dropped from the daemon's group table. Reproducible deterministically with nmcli networking off after the first online sign-in.

Versions

  • authd deb 0.5.5 from ppa:ubuntu-enterprise-desktop/authd on Ubuntu 24.04.3
  • authd-oidc snap 0.4.0 from stable (also reproduces against latest/edge as of 2026-04-28)
  • Identity provider: Okta (Org Authorization Server)

Configuration that triggers it

Single-user laptop, /var/snap/authd-oidc/current/broker.conf:

[oidc]
issuer = https://<tenant>.oktapreview.com
client_id = <client_id>
extra_scopes = offline_access

[users]
allowed_users = OWNER
owner_extra_groups = sudo, docker, dialout

Reproducer

  1. Configure broker.conf as above. Sign in successfully online as <user> via GDM. JIT provisioning works; id <user> shows the user with sudo, docker, dialout. The daemon's users_to_local_groups table contains exactly:

    <uid>|dialout
    <uid>|docker
    <uid>|sudo
    
  2. Sign out. nmcli networking off. Sign in again at GDM with the same user.

  3. Expected: offline cached sign-in succeeds, id still shows the same group set.

    Actual: GDM rejects with "Authentication failure". journalctl -u authd shows the UNIQUE constraint failed error above. The daemon then falls back to writing the user without the local groups, so on the next online sign-in id is missing sudo / docker / dialout until the broker cache is wiped and the user signs in online again.

Root cause

Broker.finishAuth in authd-oidc-brokers/internal/broker/broker.go unconditionally appends b.cfg.extraGroups and (when applicable) b.cfg.ownerExtraGroups to authInfo.UserInfo.Groups and then writes the result back to disk via CacheAuthInfo. The append is correct on the online path because the calling code resets authInfo.UserInfo.Groups = groups to a fresh provider response before reaching finishAuth, so the configured extras only ever appear once. On the offline path that reset is skipped (the getGroups call failed because there is no network), so authInfo is loaded from cache with the previous extras already present, and finishAuth appends them a second time.

The daemon's handleUsersToLocalGroupsUpdate does DELETE FROM users_to_local_groups WHERE uid = ?; INSERT … VALUES (?, ?), (?, ?), … inside one transaction. The duplicates introduced by the broker collide on the (uid, group_name) UNIQUE constraint intra-transaction, the whole INSERT rolls back, and the user ends up with no local groups (the DELETE half is also rolled back, but the broker keeps duplicating on every retry so the cache stays polluted).

Proposed fix

Two complementary fixes:

  1. authd-oidc-brokers: deduplicate the configured extras against authInfo.UserInfo.Groups before appending. Trivial 15-line patch in Broker.finishAuth. PR follows.

  2. authd (defence in depth): switch handleUsersToLocalGroupsUpdate to either an INSERT … ON CONFLICT DO NOTHING or to deduplicating the slice in the daemon before the bulk INSERT. Even if the broker is misbehaving, the daemon should not refuse to update the user's group list.

I have a working patch for (1) deployed in production for ~24h with no regressions. Happy to file (2) as a separate issue if useful.

Workaround for affected operators

Stop the broker, wipe the polluted cache, delete the stale rows for the affected user, restart:

sudo systemctl stop snap.authd-oidc.authd-oidc
sudo rm -rf /var/snap/authd-oidc/current/cache/*
sudo sqlite3 /var/lib/authd/authd.sqlite3 \
  "DELETE FROM users_to_local_groups WHERE uid = (SELECT uid FROM users WHERE name = '<user>');"
sudo systemctl start snap.authd-oidc.authd-oidc

Then sign in once online (re-seeds the cache cleanly), then offline works. Until the broker patch lands, this needs to be repeated after every offline failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions