authd is an authentication daemon for cloud-based identity providers (MS Entra ID, Google IAM). It's a hybrid Go/Rust/C project that provides:
- authd daemon (Go): Main authentication service with gRPC API
- PAM modules (Go): Two implementations - native shared library for GDM, and C-wrapper+executable for other PAM apps
- NSS module (Rust): Name Service Switch integration for user/group lookups
- Brokers (Go): Pluggable D-Bus-based providers that interface with identity providers
- Internal: gRPC for PAM/NSS ↔ authd (defined in
internal/proto/authd/authd.proto) - External: D-Bus for authd ↔ brokers (interface in
examplebroker/com.ubuntu.auth.ExampleBroker.xml) - Daemon: Systemd socket activation via
internal/daemon/daemon.go - Data flow: PAM/NSS → gRPC → authd → D-Bus → broker → identity provider
cmd/authd/,cmd/authctl/: Main binariesinternal/brokers/: Broker manager and D-Bus integrationinternal/services/: gRPC service implementations (PAM, NSS, user management)internal/users/: User/group database management (SQLite + BoltDB legacy)pam/: PAM module with two build modes (seepam/README.md)nss/: Rust NSS module usinglibnsscrateexamplebroker/: Reference broker implementation
# Full Debian package (includes all components + tests)
debuild --prepend-path=${HOME}/.cargo/bin
# Individual components (development)
go build ./cmd/authd # authd daemon only
go generate ./pam/ && go build -o ./pam/authd-pam ./pam # PAM helper client
cargo build # NSS (debug mode)- Run tests:
go test ./...(add-racefor race detection) - Golden files: Use
internal/testutils/goldenpackage- Update with
TESTS_UPDATE_GOLDEN=1 go test ./... - Compare/update:
golden.CheckOrUpdate(t, got)orgolden.CheckOrUpdateYAML(t, got)
- Update with
- Test helpers with underscores: Functions prefixed
Z_ForTests_are test-only exports (e.g.,Z_ForTests_CreateDBFromYAML) - Environment variables:
AUTHD_SKIP_ROOT_TESTS=1: Skip tests that fail when run as root
- Broker provider build tags: An untagged broker test run does not compile
the provider-specific wiring. When changing broker code, run tests for every
provider configuration:
The
go -C authd-oidc-brokers test ./... go -C authd-oidc-brokers test -tags withgoogle ./... go -C authd-oidc-brokers generate --tags withmsentraid ./internal/providers/msentraid/... go -C authd-oidc-brokers test -tags withmsentraid ./...
withmsentraidgeneration step requires the recursivelibhimmelblausubmodule and generates thehimmelblau.hand library artifacts. - Broker linting: Pass provider tags explicitly, for example:
scripts/golangci-lint -C authd-oidc-brokers run --build-tags withmsentraid. Use--build-tags withgooglefor Google-specific changes.
Critical: Run go generate before building PAM or when modifying protobuf files:
go generate ./pam/ # PAM module (creates .so files)
go generate ./internal/proto/authd/ # Regenerate protobuf
go generate ./shell-completion/ # Shell completions- Brokers are discovered from
/usr/share/authd/brokers/*.conf(D-Bus service files) - First broker is always the local broker (no config file)
- Manager in
internal/brokers/manager.gohandles session→broker and user→broker mappings - Brokers must implement the D-Bus interface defined in
internal/brokers/dbusbroker.go
The PAM module has two implementations (see pam/README.md):
- GDM mode (
pam_authd.so): Native Go shared library with GDM JSON protocol support - Generic mode (
pam_authd_exec.so+authd-pamexecutable): C wrapper launching Go program via private D-Bus- Required for reliability with non-GDM PAM apps (avoids Go threading issues)
- Migrating from BoltDB to SQLite:
internal/users/db/handles both - User/group data cached locally in
/var/lib/authd/authd.db - ID allocation:
internal/users/idlimitsgenerator/generates UID/GID ranges - Group file updates:
internal/users/localentries/handles local system files
- Use
testify/requirefor assertions (notassert) - Golden files in
testdata/golden/subdirectories matching test structure - Test-only exports via
export_test.gofiles (no build tag, package-level visibility) - PAM integration tests use
ptytestinpam/integration-tests/
- Update
internal/proto/authd/authd.proto - Run
go generate ./internal/proto/authd/ - Implement in service (e.g.,
internal/services/pam/pam.go) - Add tests with golden files
- Implement D-Bus interface from
examplebroker/com.ubuntu.auth.ExampleBroker.xml - Create
.conffile in/usr/share/authd/brokers/ - Register D-Bus service with systemd
- Logs via
github.com/canonical/authd/logpackage (supports systemd journal) - Enable debug:
authd daemon -vvv(3 levels of verbosity) - Socket path:
/run/authd.sock(override withAUTHD_NSS_SOCKETfor NSS tests)
Always pass --no-pager to git commands that may invoke a pager, to prevent them from hanging:
git --no-pager diff
git --no-pager show
git --no-pager logAgent-authored changes should generally be committed once complete. Do not leave finished agent-created changes uncommitted unless the user explicitly asks for no commit.
When creating commits for agent-authored changes, use atomic commits: each commit must contain one coherent, self-contained logical change.
Explain why, not what — the diff shows what changed.
- For bug fixes, describe the observable symptom before the root cause
- Document non-obvious decisions and rejected alternatives
- One-liners are fine for mechanical changes; anything behavioral needs a body
- Try to keep the subject line at 72 characters or less; wrap body lines at 72 characters (URLs that cannot be split are the only accepted exception)
Don't narrate your activity ("Fixed X as requested") or describe the diff ("Add null check before calling Process()").
Use git commit -F - with a heredoc and insert explicit newlines at
word boundaries before reaching 72 characters. Do not use
git commit -m "..." for multi-line messages — it will not wrap.
- Go: See
go.modfor version requirements, uses go modules with vendoring - Rust: Cargo with vendor filtering (see
Cargo.tomlworkspace) - Required:
libpam-dev,libglib2.0-dev,protoc,cargo-vendor-filterer - Optional:
delta(colored diffs in tests)
- Follow Effective Go for Go style conventions
- Use
go fmtandgofmt -s - Rust: Standard cargo fmt conventions
After making changes to Go files, run scripts/golangci-lint to check for lint errors:
scripts/golangci-lint runIf the changed files are below authd-oidc-brokers/, use the -C flag to run the linter in that directory:
scripts/golangci-lint -C authd-oidc-brokers run