Users, Permissions, and Authentication
Audience: User, Org Admin, System Admin, Support, Developer Related: Organizations · Authentication Providers · Security And Scoping
Blackcap supports internal/password authentication, invitations, password reset, TOTP MFA, recovery codes, trusted-browser tokens, API Tokens, and configured external login providers. System Admin, Org Admin, and ordinary organization-user permissions are evaluated together with the Active Organization.
Internal IDs, raw tables, credentials, and diagnostic payloads belong in developer or support tooling, not user-facing instructions. Feature documents describe the permissions that apply to their workflow; this document describes the shared model.
Internal user management
This build adds a modular internal auth foundation for Blackcap Pi.
Architecture Summary
Core modules:
inky_admin/services/auth_service.py— SQLite persistence, users, invites, password resets, user-owned API tokens, password hashing, and session helpers.inky_admin/auth/permissions.py— centralized roles, permission constants, permission checks, and decorators.inky_admin/services/audit_service.py— foundational activity/audit logging.inky_admin/blueprints/user_management.py— User Management, API tokens, and Activity Log UI routes.inky_admin/blueprints/auth.py— setup, login, invite acceptance, password reset, password change, provider-login entry/callback routes, and MFA setup/challenge routes.inky_admin/blueprints/auth_providers.py— provider configuration, organization provider enablement, organization MFA policy, admin MFA reset routes, and trusted-browser clearing routes.inky_admin/services/auth_provider_registry.py— provider metadata/adapter registry for Google, Apple, LinkedIn, Facebook, X, GitHub, and Microsoft.inky_admin/services/auth_provider_service.py— provider configuration, org enablement, identity-link, callback validation, and account-matching service logic.inky_admin/services/mfa_service.py— TOTP setup/challenge, recovery codes, trusted-browser tokens, and MFA reset/disable behavior.inky_admin/templates/auth/*— setup, login, invite acceptance, password reset, password change, MFA setup/challenge, and recovery-code screens.inky_admin/templates/users/*— User Management, My API Tokens, My Account provider links, and MFA controls.inky_admin/templates/audit/activity_log.html— lightweight activity feed.
Existing app wiring remains blueprint-based. inky_admin_app.py should remain thin; authentication/provider/MFA behavior belongs in blueprints and services.
Role / Permission Model
Roles:
| Role | Intended Scope |
|---|---|
system_admin |
Full device/platform control |
org_admin |
Organization-level admin; no deep device config |
data_admin |
Destructive recipe/shopping-list data admin; no user management |
operator |
Operational/editor role; no destructive deletes |
contributor |
Household/shopping/extension user |
Permissions are centralized in inky_admin/auth/permissions.py and checked through:
has_permission(user, permission)current_user_has(permission)@require_permission(...)- central request permission mapping in
security_service.py
Storage
Default SQLite database:
[auth]
database_path = data/blackcap.db
invite_expiration_hours = 168
password_reset_expiration_hours = 2
audit_retention_days = 180
Core auth tables:
usersinvite_tokenspassword_reset_tokensapi_tokensaudit_logauth_meta
Provider/MFA tables now extend the same user model:
organization_auth_provider_settingsorganization_auth_policiesuser_identity_linksuser_mfa_methodsuser_mfa_recovery_codesuser_mfa_trusted_browsers
Provider credentials live in provider_configs using auth_* provider types. Organization provider enablement and MFA policy remain organization-scoped. Organization login-provider policy distinguishes verified-email auto-linking, invite/existing-user expectations, and signed-in self-link permission. The current implementation does not include open self-registration from external providers; invite-only off does not create a new user by itself.
Bootstrap
When no users exist, authenticated browser requests redirect to:
/setup
The first account is created as system_admin.
Invite and Password Flows
Invite and reset emails reuse the same SMTP configuration used by Share Recipe via send_plain_email(...) in recipe_email_service.py.
Flows included:
- invite user by email + role
- default Users page invite view limited to pending invites, with an AJAX status filter for accepted, expired, revoked, or all invite history
- accept invite
- revoke invite
- resend invite
- forgot password
- reset password by email link
- admin-triggered password reset
- self password change
Tokens are one-time-use where appropriate and expire.
User-Owned Chrome Extension Tokens
New API tokens:
- belong to a user
- are stored hashed only
- display once at creation
- have a user-provided label; the default label is “Chrome Extension” because these tokens are commonly used by the Blackcap browser extension
- track created timestamp, last used timestamp, IP, and user agent
- can be revoked
- are named in a popup from the Users page instead of an inline token-label field
- are eligible for scheduled purge after revocation; the default retention is 7 days
The Chrome extension API now accepts user-owned tokens and attributes pushed recipes to the token owner.
Legacy global extension token remains temporarily supported for compatibility but is marked as legacy in the UI.
Audit Logging
Event coverage includes:
- bootstrap admin created
- login
- failed login
- logout
- invite sent/resent/revoked/accepted
- password reset requested/completed
- password changed
- API token created/revoked
- Chrome extension recipe push
- provider login start/success/failure where practical
- provider identity link/unlink
- platform provider configured/enabled/disabled/secret changed
- organization provider enablement/policy changes
- MFA setup/challenge success/failure
- MFA recovery-code use
- MFA disable/reset
- trusted-browser creation/revocation
Do not log OAuth tokens, TOTP secrets, TOTP codes, recovery codes, or raw trusted-browser tokens.
Restart / Test Commands
sudo systemctl restart inky_admin.service
journalctl -u inky_admin.service -f
If running directly:
cd /home/pi/Blackcap-Pi
/home/pi/inky_env/bin/python3 -m compileall inky_admin
sudo systemctl restart inky_admin.service
Practical Testing Checklist
- Visit
/setupon a clean DB and create first system admin. - Log out and log back in with email/password.
- Enable MFA for a user, log out, and confirm password login requires TOTP.
- Use the trusted-browser checkbox and confirm a later password login skips the MFA prompt for that browser.
- Enable org-required internal-login MFA and confirm non-enrolled users are forced through setup.
- Use forgot-password flow with SMTP configured.
- Invite a contributor and accept the invite.
- Revoke an invite and confirm the link no longer works.
- Resend an invite and confirm the new link works.
- Change a user's role and verify sidebar/route access changes.
- Deactivate a user and confirm login fails.
- Create a user-owned API token from My API Tokens.
- Paste token into Chrome extension and push a recipe.
- Confirm recipe record has extension attribution fields.
- Revoke token and confirm extension API returns unauthorized.
- Confirm revoked tokens remain visible briefly, then are removed by the scheduled purge policy after the configured retention period.
- Confirm the Users page only shows pending invites by default and can filter by accepted, expired, revoked, or all invites.
- Configure a login provider, enable it for the organization, and confirm only enabled providers show on the login page.
- Confirm Activity Log records auth/token/invite/extension/provider/MFA events.
- Confirm existing Share Recipe email still works.
- Confirm legacy extension token still works if already configured.
Organization Support
Users, invites, API tokens, provider identity links, organization provider enablement, MFA policy, and audit context are organization-aware. Org admins manage users and organization login/MFA policy inside their active organization context, while system admins manage platform-wide provider credentials and can switch organization context intentionally for troubleshooting.
External Provider Login and Internal MFA
Local password auth remains the recovery-safe baseline for accounts that are not linked to an external provider. Once an account is linked to Google, Microsoft, or another configured provider, Blackcap treats that provider as the password authority for that account: the My Account page disables password changes, the header account dropdown hides the Password shortcut, and internal/password login is blocked until the provider is unlinked. External provider login still links back to the same users table and does not replace the Blackcap permission model.
Provider configuration is split by scope:
- platform credentials in Configuration → Platform → Authentication Providers;
- organization enablement in Configuration → Organization → Login Providers;
- user links in My Account.
The login page accepts ?org=org_... for organization context, with ?organization=... as a compatibility alias. The org ID is not shown on the login screen.
MFA uses TOTP for internal/password login and can also protect externally linked accounts when the user has enabled Blackcap MFA. External-provider logins skip Blackcap MFA only when the provider explicitly reports MFA was already satisfied for that login. A user with MFA enabled is challenged on future internal/password logins even if org-wide MFA is off; an organization can also require MFA enrollment for internal logins. Users may trust a browser for 90 days after successful MFA. Admins can clear trusted browsers without resetting MFA, or reset MFA when the user needs to enroll again. Kiosk/display-client token routes remain outside the user-login/MFA flow.
See users-permissions-and-authentication.md for provider setup, route details, data model, and testing checklist.
Recipe Ownership Update
Recipes now track creator attribution when added from the Admin UI, mobile UI, captured-photo workflow, or Chrome extension API token. Contributors and operators can edit metadata, edit content, upload Made It photos, and delete only recipes they created. Older recipes with no creator attribution are treated as not owned by contributors/operators. System admins, organization admins, and data admins can manage all recipes. Legacy/global Chrome extension tokens are no longer supported; each extension must use a user-owned API token.
My Account page notes
The account page formats user-facing timestamps as mm/dd/yy h:mm am/pm, presents profile/security fields as Blackcap-styled cards, and shows recent user activity in a compact table. Account actions are rendered as button controls. Linked external-provider accounts show a single active provider and an unlink action; unlinked accounts show a provider dropdown for linking one enabled provider.
Self-managed API-token organization selection
The token page uses friendly organization display names. System Admin identities can choose any non-deleted organization available to the platform role; other users remain membership-scoped. A single valid organization is selected automatically and shown read-only rather than requiring an empty or redundant selector.
Authentication providers and MFA
Blackcap Pi now has a provider-based authentication foundation while preserving the local/self-hosted login model that works well on a Raspberry Pi.
The final model separates authentication into four clear concerns:
- Platform Authentication Provider Configuration stores application/client-level OAuth or OIDC values such as client ID, client secret, scopes, callback URL/base URL, provider metadata, logo, and platform enabled/disabled status.
- Organization Login Provider Enablement controls which configured platform providers an organization allows, including allowed email domains, invite-only behavior, verified-email auto-linking, and self-link permission.
- User Identity Links connect a Blackcap user to a provider subject/account ID. Blackcap still owns roles, permissions, organization membership, API/display/client tokens, audit logging, and session policy.
- Blackcap MFA protects the user identity across internal and external login. If any active organization membership requires MFA, the user must enroll Blackcap MFA before either login path can grant a full session. A provider MFA signal may avoid a duplicate challenge only after enrollment.
This keeps the self-hosted Raspberry Pi flow simple while leaving room for future SaaS/GCP deployment where org context may be selected by subdomain, invite link, or explicit query string.
Configuration UI locations
Authentication provider setup intentionally lives in Configuration, not on standalone sidebar pages.
| Scope | UI location | Purpose |
|---|---|---|
| Platform | Configuration → Platform → Authentication Providers | System-admin-managed OAuth/OIDC application credentials and platform availability. |
| Organization | Configuration → Organization → Login Providers | Org-admin-managed provider enablement, domain restrictions, invite/auto-link policy, organization MFA requirement, and user MFA reset/status. |
| User | My Account | User-owned provider links and MFA enrollment/disable actions. |
The former standalone routes still exist for compatibility/bookmarks, but redirect into Configuration:
/admin/platform/auth-providers/admin/organization/login-providers
The provider configuration tables are intentionally compact row-based forms with provider logos, checkboxes, text boxes, and right-pinned action buttons. Wide tables use a floating header and viewport-level horizontal scrollbar so the column labels and actions remain usable while scrolling. Checkbox cells intentionally rely on the table header plus informative tooltips rather than repeating labels in every row.
Login organization context
The login page accepts ?org=org_... to choose the organization context for provider buttons. ?organization=org_... is accepted as a compatibility alias. If no org is supplied, the default organization is used. The selected org ID is not displayed on the login screen; it is carried as hidden context for the login/provider flow. Internal email/password login remains the primary visual flow, while enabled external providers are shown as compact logo-only buttons with accessible labels/tooltips such as Continue with Microsoft.
The My Account page shows one external login provider at a time. If a provider is linked, the account page shows that provider and an unlink action; if no provider is linked, it shows a provider dropdown for linking exactly one enabled/self-linkable provider. Linked-provider accounts treat the external provider as the password authority: the account page disables password changes, the header account dropdown hides the Password item, and internal/password login is blocked until the external provider is unlinked.
Examples:
/login?org=org_01c17a1ea07f4253
/auth/google/start?org=org_01c17a1ea07f4253
Invite links derive organization context from the invite itself.
Platform provider configuration
System admins manage platform-level provider credentials from Configuration → Platform → Authentication Providers.
Each provider row includes:
- provider logo and provider name
- configured/not configured status
- platform enabled checkbox
- protocol and callback URL/help link
- click/copy affordance for the provider callback URL
- client ID text box
- masked client secret text box that preserves the current secret when left blank
- optional redirect base URL override text box
- scopes text box
Callback URL precedence is provider-specific Redirect Base override, then Configuration → Platform → Security → Login Provider External Base URL, then the general External Base URL. This keeps Alexa/Google Home account linking on standard HTTPS port 443 while allowing browser OAuth/OIDC login providers to use an Admin UI port such as :9443.
- explicit clear-secret checkbox
- emoji-only save action pinned on the right
Providers added in the registry:
- Apple
- X
- GitHub
- Microsoft
New providers are disabled until configured and platform-enabled. Existing legacy Google INI configuration is still read as a fallback so upgraded installs keep working until Google is saved into the provider-config page.
Platform provider credentials are application-level settings. They are not organization connections and should not be stored on organization records.
Organization provider enablement
Org admins manage org-level login provider settings from Configuration → Organization → Login Providers.
A provider can be enabled for an organization only when it is configured and enabled at the platform level. Org settings include:
- enabled/disabled checkbox
- allowed email domains
- auto-link verified email to an existing active user
- invite-only/existing-user requirement
- whether users can self-link from My Account
- emoji-only save action pinned on the right
The organization settings mean:
| Setting | Meaning | Recommended first use |
|---|---|---|
| Enabled | Allows the organization to use the provider after the provider is configured and enabled at the platform level. | Turn on only for providers you have tested. |
| Allowed domains | Optional comma-separated email domains. If populated, a provider email must match one of these domains. | Leave blank for household/self-hosted use; restrict for organizations later. |
| Auto-link | Allows a verified/trusted provider email to link to an existing active Blackcap user in the organization. It does not create a new user by itself. | Enable for trusted providers such as Google/Microsoft after testing. |
| Invite-only | Policy flag for requiring an existing user or invite before provider login creates/activates access. Since open self-registration is not implemented yet, turning this off does not by itself let random provider users create accounts. | Keep enabled for the current self-hosted/admin-managed model. |
| Self-link | Allows an already signed-in Blackcap user to connect that provider from My Account. It is for account linking, not anonymous first-time login. | Optional; enable once you want users to manage their own linked providers. |
Recommended current behavior is conservative:
- A system admin configures and platform-enables a provider.
- An org admin enables the provider for that organization.
- Login succeeds only if both platform and organization enablement allow the provider.
- Existing-user/invite matching remains required unless a future self-registration flow explicitly changes that policy.
Default organization provider template and sync
The Default organization is the login-provider template for new organizations. When a System Admin creates a new organization, Blackcap copies the providers that are both platform-available and enabled for the Default organization. The copy includes allowed domains, auto-link, invite-only, and self-link settings.
For organizations that already existed before a new provider was configured, System Admins can use Configuration → Platform → Authentication Providers → Default org login provider sync. The sync is versioned. Saving a Default-org login-provider setting, or making a platform provider newly available while the Default org already allows it, publishes a new Default login-provider template version. Target organizations track the latest version they have seen, and each provider row tracks its source version.
The normal sync only applies missing or older non-overridden provider rows. If a provider was introduced in template version 2 and an organization is already at version 2 or later with that provider disabled, another push will not re-enable it. Direct organization-level saves are treated as local overrides and are preserved by later syncs. The optional override checkbox intentionally replaces existing organization provider settings with the Default organization template and should be used only after deliberate review. Sync actions are audited.
External login flow
External login uses the provider registry/adapters rather than copying route logic for each provider:
login route -> auth_provider_service -> provider registry/adapter metadata -> auth/session service
The flow validates OAuth state, validates OIDC nonce where applicable, exchanges the authorization code server-side, validates signed ID tokens where applicable, extracts provider identity, enforces organization enablement, and then links or authenticates the Blackcap user.
Blackcap does not store access tokens for these login providers. It stores only the user identity link: provider, subject/account ID, email, verified flag, display name/avatar where available, and login timestamps.
Account matching and linking
Safe default behavior:
- If the provider subject is already linked to a Blackcap user, authenticate that user after checking organization membership and provider enablement.
- If a verified/trusted provider email matches an existing active user in the organization and org policy allows verified-email auto-linking, link the provider to that user.
- If the email is missing or unverified, do not auto-link.
- If no matching user exists, reject login unless the user accepts an invite with that same verified provider email.
Providers such as X may not return an email in the normal profile response. Those providers can be manually linked by an already-authenticated user, but cannot be used to discover or auto-link a Blackcap account by email.
Invite-only, auto-link, and self-link are intentionally separate controls. With invite-only off, auto-link on, and self-link on, the current implementation still does not provide open self-registration: linked identities can log in, verified emails can auto-link to existing users when policy allows it, and already-authenticated users can link providers from My Account. Creating brand-new users from an arbitrary provider login should remain a separate future setting/flow with default role, approval, domain, and audit decisions.
Blackcap MFA policy
Blackcap MFA is TOTP-based and can be enrolled from My Account. Organization admins can require MFA from Configuration → Organization → Login Providers. That requirement protects the user account across internal and external login whenever any active organization membership requires it. System Admin can act inside every organization, so a requirement from any active organization also protects System Admin login, including legacy/bootstrap accounts without membership rows.
There are three related rules:
- If a user has MFA enabled, future internal/password logins for that user require MFA unless a trusted-browser token is valid.
- If a user has MFA enabled and signs in with an external provider, Blackcap requires MFA unless the provider identity response explicitly reports that MFA was already completed for that login.
- If any active organization membership requires MFA, the user must enroll Blackcap MFA before either password or external-provider login can grant a full session. The current/primary organization does not limit this account-level check.
For external-provider login, Blackcap looks for explicit assurance signals such as amr/acr values indicating MFA, OTP, FIDO/WebAuthn, hardware/software key, or similar multi-factor completion. Missing, password-only, or ambiguous provider signals do not satisfy Blackcap MFA. This keeps the behavior conservative while avoiding double MFA when a provider clearly reports that it already challenged the user.
When MFA is required or enabled:
- User completes the primary login factor: password or external provider.
- Blackcap stores only a partial MFA session and does not grant app access yet.
- If no TOTP method is enrolled and the flow requires enrollment, the user is sent to setup.
- User scans the QR code, enters the shorter manual setup key, or copies the setup URI into an authenticator app that supports URI import.
- User enters the current 6-digit code.
- Blackcap enables or verifies MFA, shows recovery codes once when enrolling, and then creates the full session.
- Future logins for that account require the TOTP or a recovery code unless the user chose to trust that browser or the external provider reports MFA for that login.
The full otpauth:// setup URI is intentionally not displayed inline because it is too long to type safely. The setup screen shows a copy button for the URI and a copy button for the shorter manual setup key. The user can cancel setup from the same action row; canceling a partial MFA setup returns to login without granting a full app session.
TOTP stands for Time-based One-Time Password. It is the rotating six-digit code used by authenticator apps. It works well for local/self-hosted Blackcap installs because, after enrollment, it does not require HTTPS, internet access, SMS delivery, or a third-party MFA service.
Trusted browser behavior
After successful MFA setup or challenge, the user can choose Trust this browser for 90 days.
Blackcap then:
- creates a random trusted-browser token;
- stores only a selector plus hashed token metadata in SQLite;
- sets an HttpOnly cookie in that browser;
- uses the token only after a valid primary login factor succeeds;
- limits trust to the same user/browser and Blackcap MFA challenge path;
- expires trust after 90 days;
- revokes trusted-browser tokens when MFA is disabled or reset;
- lets an admin clear trusted-browser tokens without removing the user's MFA enrollment.
Trusted browsers do not bypass password or external-provider authentication. They only skip Blackcap's additional MFA prompt after the primary login factor has succeeded, and only while the account still has an enabled Blackcap MFA method. A stale trusted-browser cookie cannot bypass required enrollment.
Kiosk/display/client token behavior
Kiosk display receiver URLs and remote display-client APIs remain outside the user-login/MFA flow. A valid receiver/client token continues to authorize those display routes even when the organization requires internal-login MFA.
This is intentional because kiosk/wall display clients are unattended devices, not browser user sessions. Rotating or revoking the display/client token remains the control for those endpoints.
Data model
New tables:
organization_auth_provider_settingsorganization_auth_policiesuser_mfa_methodsuser_mfa_recovery_codesuser_mfa_trusted_browsers
Platform provider credentials are stored in existing provider_configs rows using provider types such as auth_google, auth_apple, auth_github, and so on. Secrets are encrypted with the existing Blackcap secret utilities. TOTP secrets are encrypted at rest. Recovery codes are stored only as password hashes. Trusted-browser records store only a selector plus hashed token metadata, never the raw token.
Routes
Platform provider config:
GET /admin/platform/auth-providers→ redirects into ConfigurationPOST /admin/platform/auth-providers/<provider>/configure
Organization provider/MFA policy:
GET /admin/organization/login-providers→ redirects into ConfigurationPOST /admin/organization/login-providers/<provider>/savePOST /admin/organization/security/mfa-policyPOST /admin/users/<user_id>/mfa/reset
Login:
GET /auth/<provider>/startGET|POST /auth/<provider>/callback- Google-compatible aliases remain:
/auth/google/startand/auth/google/callback
MFA/account:
GET|POST /auth/mfa/setupGET /auth/mfa/setup.svgGET|POST /auth/mfa/challengePOST /account/mfa/disablePOST /account/provider/<provider>/unlink
Provider setup notes
Always verify callback URL, scopes, token endpoint requirements, app-review requirements, and production app settings against each provider's current official documentation before enabling a provider in production. Apple, Facebook, Microsoft, LinkedIn, GitHub, and X each have provider-specific configuration requirements and may change behavior over time.
Apple Sign in with Apple commonly requires creating a Services ID and generating the client secret JWT in the Apple developer portal/process. Blackcap stores the resulting client secret value; future work could add Apple private-key/JWT generation as a richer provider-specific config helper.
Runtime/dependency notes
The MFA QR-code setup screen uses the qrcode Python package. Install/update dependencies with:
cd /home/pi/Blackcap-Pi
/home/pi/inky_env/bin/python3 -m pip install -r requirements.txt
For a minimal update on an existing install, install only the new QR-code package:
/home/pi/inky_env/bin/python3 -m pip install qrcode==8.2
The auth-provider/OAuth and MFA services should remain lazy-loaded so normal Home/Admin page loads do not pay the cost of provider/JWT/HTTP imports unless those features are actually used.
Testing checklist
- Home/Admin first page load remains close to pre-auth-provider performance.
- Configuration first load remains lightweight unless Authentication Providers or Login Providers is opened.
- Authentication Providers and Login Providers appear under Configuration, not as standalone sidebar destinations.
- Provider configuration rows show logos, informative tooltips, copyable callback URLs, and row-based controls.
- Floating table headers remain aligned while horizontally scrolling.
- The viewport-level horizontal scrollbar controls the active provider table and hands off cleanly to the table-bottom scrollbar.
- Internal login still works without org MFA enabled.
- Internal login prompts setup/challenge when any organization membership requires MFA.
- A user with MFA enabled is challenged on future internal/password logins even if org-wide MFA is off.
- MFA QR/manual setup verifies a current code.
- Recovery code works once and is marked used.
- User cannot disable MFA if org policy requires it.
- Org admin MFA reset clears the user's MFA method, recovery codes, and trusted-browser tokens.
- Org/admin trusted-browser clear revokes only remembered-browser trust and leaves the MFA method and recovery codes intact.
- Trusted-browser checkbox skips MFA for that same browser on future logins that would otherwise require Blackcap MFA for 90 days.
- External provider login cannot bypass organization-required enrollment. After enrollment, a trustworthy provider MFA signal can avoid a duplicate Blackcap challenge.
- System Admin is forced into enrollment when any active organization requires MFA.
- Password and external-provider invite acceptance run the same required-MFA gate before granting a full session.
- A stale trusted-browser cookie does not bypass enrollment when no MFA method is enabled.
- Kiosk receiver and display-client token routes still work without user-login MFA.
- Existing Google login works from legacy INI config and from provider-config rows.
- Login-page provider buttons are compact logo-only buttons and only appear when platform and org enablement are both true.
?org=org_...filters the login buttons and callback context.- Missing/unverified provider emails are not auto-linked.
- Blank secret save preserves the existing encrypted secret.
- Clear secret removes the stored secret.
- Direct endpoints enforce permissions.
- No OAuth tokens, TOTP secrets, TOTP codes, recovery codes, or raw trusted-browser tokens are logged.
Current safety rules
This patch tightens the Blackcap Pi internal-auth foundation.
Shopping list ownership
Shopping list access now follows this model:
contributor: can create lists and edit/execute only the active shopping list. Cannot delete lists.operator: can edit/delete shopping lists they created, and can also edit/execute the active shopping list like a contributor.data_admin,org_admin, andsystem_admin: can manage any shopping list.
New shopping lists are stamped with creator attribution. Existing legacy/unattributed lists are not assumed to belong to a contributor or operator.
Route and permission hardening
Server-side route checks remain the source of truth. The Shopping Lists UI now receives per-list action flags so buttons match what the server allows.
Blocked global route permissions now create a permission_denied audit event with the endpoint, path, method, and required permission.
CSRF coverage
Auth forms now include a session CSRF token field. Existing Admin and mobile templates already inject CSRF tokens into forms and fetch/XHR calls.
Deactivated users
Deactivated users are blocked on the next request, their session is cleared, and an audit event is recorded.
API tokens for deactivated users remain unusable because API-token lookup requires an active user.
Backup/restore inclusion
Backups now include the auth database and SQLite WAL/SHM sidecar files when present:
data/blackcap.dbdata/blackcap.db-waldata/blackcap.db-shm
Linked-provider password behavior
If a user links an external authentication provider, Blackcap treats that provider as the password authority for that account. Password-change UI is disabled/hidden for that user, and internal/password login returns a provider-linked message instead of accepting the stored password. Unlinking the provider restores local password login/change access. This avoids a mixed state where users believe the external provider controls sign-in while Blackcap still accepts a separate local password.