Releases: Pageloom/weft-id
Releases · Pageloom/weft-id
v1.8.0
Immutable
release. Only release title and notes can be modified.
Fixed
- Outbound SCIM group membership no longer silently drops members at spec-compliant receivers. WeftID was using its own UUID as the SCIM
idinGroup.members[].valueand in PUT/PATCH/DELETE paths. Receivers that mint their own server-assignedidper RFC 7644 §3.3 (Authentik, most spec-compliant SCIM 2.0 sources) resolve group members against that server id, so the WeftID-UUID references never matched and members landed in zero groups downstream. The worker now captures the receiver'sidfrom the first POST response, stores it in a new mapping table, and uses it for every subsequent PUT/PATCH/DELETE and for group member references. Affects any tenant whose downstream SCIM receiver does not happen to conflateidwithexternalId - 404 on
DELETE /Users/<id>orDELETE /Groups/<id>no longer dead-letters. Removing a group grant for users the receiver never saw used to flood the sync log with false failures. The Generic adapter now treats 404 on DELETE as success-like ("resource is already gone"), drains the queue row, and surfaces the outcome as an amber Skipped badge in the Sync activity panel. 404 on POST/PUT/PATCH continues to behave as before - 404 on PUT against a stale id self-heals. If a downstream resource is recreated (or our recorded id otherwise drifts), the next push gets a 404, WeftID clears the stale mapping, and the attempt after that POSTs cleanly to remint the mapping. No operator intervention required
Changed
- The Atlassian quirk's "404 is permanent (already gone)" override was removed in favor of the new general policy (404 on DELETE = success, 404 on other verbs = permanent). Behavior is unchanged for the common deprovisioning case
- GitHub Enterprise Cloud's SCIM quirk opts out of PUT-on-Groups (GitHub returns 405) via a new per-vendor
GROUP_UPDATE_VERBcapability flag. Behavior for GitHub tenants is preserved: groups still go through POST only, with the same 409-on-duplicate semantics as before
Added
- New
sp_scim_remote_idstable (tenant-scoped with RLS) records WeftID-id → receiver-id mappings. Migration0041_scim_remote_ids.sqlis purely additive and applied automatically on self-hosted upgrades. Rows that pre-date the table self-heal on the next push via a fallback path; no resync action is required - New audit events
scim_remote_id_mapped(first time a receiver mints an id for a resource) andscim_remote_id_invalidated(a 404 cleared a stale mapping). Both are operational tier, visible in the audit log when operational events are shown - The SCIM admin guide gained a "Resource ID mapping" section explaining when mappings are created, used, and cleared, plus updated status meanings (the new amber Skipped badge) and worker reason codes (
already_absent,remote_id_invalidated) - New
dev/scim-testbed.shbootstrap script (andmake scim-testbed-{up,down,destroy,status,logs,info}targets) spins up a local Authentik instance for end-to-end outbound-SCIM testing. The Authentik runtime lives outside the WeftID checkout by default (~/.local/share/weft-id/scim-testbed/authentik/) so generated secrets and volumes can't leak into source. Seedev/scim-testbed.md. Authentik is a separate MIT-licensed project; WeftID does not bundle or redistribute it - Dev-only:
host.docker.internalis allowed as a SCIM target URL whenIS_DEV=true, so WeftID's containers can reach a SCIM receiver running on the Docker Desktop host. Production builds reject it as before
v1.7.1
Immutable
release. Only release title and notes can be modified.
Added
- Outbound SCIM credentials can now be imported from the downstream application in addition to being generated by WeftID. Use the new Import existing token button on the SP's SCIM tab for receivers that mint the bearer token on their side (e.g. Authentik, some self-hosted SCIM servers). Generated and imported tokens are stored and pushed identically; only the source differs
- New additive
POST /api/v1/service-providers/{sp_id}/scim/credentials/importendpoint (super_admin, shares the 10/min rate-limit bucket with the existingPOST /scim/credentialsmint endpoint) and a distinctscim_token_importedaudit event so imported credentials are auditable separately from generated ones
v1.7.0
Immutable
release. Only release title and notes can be modified.
Added
- Outbound SCIM 2.0 provisioning: WeftID can now push user and group changes to downstream applications, closing the gap that pure SAML cannot (a user removed from WeftID no longer retains access to downstream SaaS)
- SCIM 2.0 push client with per-vendor quirk modules: day-one support for Slack (Enterprise Grid), GitHub Enterprise Cloud, Atlassian (Guard / Access), and GitLab.com; a spec-correct Generic SCIM 2.0 path covers everything else
- Admin UI under each SP's SCIM tab: target URL, application type, group membership mode (effective / direct), sync activity retention (3 / 6 / 12 / 24 months / forever), and bearer-credential management
- Bearer-credential lifecycle: tokens minted by WeftID, displayed in plaintext exactly once, Fernet-encrypted at rest; rotation with a 24-hour overlap window so in-flight pushes complete cleanly; instant revoke
- Sync activity panel: live pending and dead-lettered counters, per-status filtering, and a "Retry dead-lettered" action that re-enqueues every dead row for the SP
- Per-tenant background worker with retry, exponential backoff, dead-letter on retry-budget exhaustion, and per-SP sequential fan-out within a tenant slice to avoid hammering a single downstream
- Event-log-driven dispatch: mutations tagged with a
scim_triggerannotation in the event-type registry enqueue work automatically; eager fan-out at trigger time for group / membership changes so queue depth is a meaningful "work remaining" metric - Coalescing outbox keyed
UNIQUE(sp_id, resource_type, resource_id): re-enqueues bumpenqueued_atand reset attempts; the worker re-fetches current resource state at push time so "last state wins" is automatic and deprovision is just "user no longer in scope" - Two-log model: admin actions (token create / rotate / revoke, config edits) go to the main audit log with indefinite retention; per-push outcomes go to a dedicated
scim_sync_logwith per-SP retention (default 3 months, configurable to 6 / 12 / 24 months or forever for regulated tenants) - API endpoints under
/api/v1/service-providers/{sp_id}/scim: config GET/PUT, credentials CRUD, sync-log paginated read, queue status, and retry-dead-lettered POST - Documentation: full admin guide at
docs/admin-guide/service-providers/scim.mdwith per-vendor walkthroughs (Slack / GitHub / Atlassian / GitLab), credential lifecycle, sync panel reference, and a troubleshooting section - Inline help link from the SP detail SCIM tab to the docs page
Fixed
- OAuth2 authorization endpoint:
auth_request_idsingle-use replay protection now works correctly under Starlette 1.0+. The previous nested-mutation pattern silently failed to mark the session modified, so a reusedauth_request_idcould redirect with a fresh code instead of showing the error page
Security
- Bumped
starletteto 1.0.1 (PYSEC-2026-161 / GHSA-86qp-5c8j-p5mr: Host header URL reconstruction) - Bumped
fastapito 0.136.1,python-multipartto 0.0.29,psycopg-poolto 3.3.1,ua-parserto 1.0.2, and refreshedidnato 3.15 in production requirements
v1.6.0
Immutable
release. Only release title and notes can be modified.
Added
- Standard user attributes: a 14-attribute registry (contact, professional, location, profile categories) with per-tenant configuration for enable/required/mirror-from-IdP/locked-for-users/send-to-SPs flags
- Tenant attribute configuration settings page (
/admin/settings/user-attributes) with per-row toggles and category bulk enable/disable - Self-service profile attribute editing on the user profile page and admin attribute editing on the user detail tab, grouped by category
- Admin-only "Connected IdP attributes" panel showing the per-IdP mirror snapshot for each user
- IdP-driven attribute mirroring: SAML logins extract registry-keyed values via the per-IdP attribute mapping and mirror them into canonical user profiles
- Downstream SAML assertion emission for enabled tenant attributes, with a per-SP "available, not sent" view and per-row SAML OID toggle in the admin attribute tab
- Tenant-required attribute enforcement: dashboard banner for missing user-fixable fields, Admin Todo view listing every user with any missing required attribute, and a bulk force-profile-completion action that gates navigation until missing unlocked-required fields are filled
- Opt-in scrub of mirrored attributes when disconnecting an IdP: web checkbox on the danger tab and
?scrub_mirrored_attributes=trueon the DELETE API. Diverged values are preserved - API endpoints:
GET/PUT /api/v1/tenant/attribute-config,/api/v1/users/{id}/attributes, and/api/v1/me/attributesfor canonical and IdP-mirror reads/writes - Audit events:
user_idp_attribute_mirror_failed(admin tier) andtenant_attribute_config_read_failedfor visibility into mirror-write and config-read failures during SSO
Changed
- Per-IdP
attribute_mappingnow accepts registry keys (jobTitle, phoneWork, etc.) alongside the existing fixed email/first_name/last_name/groups - New SPs are seeded with the tenant's default sendable attribute set; existing SPs are untouched
mirror_from_idpdefaults to true on newly-enabled attributes so enabling an attribute does the obvious thing; tenants who want IdP values held only as read-only diagnostic info can turn the flag offuser_profile_updatedevent metadata now records an action per key (added/updated/cleared) instead of{old, new}values, keeping phone/mobile/address/postal-code/employee-ID values out of the event stream- Copy: unified "Profile attributes" naming, softer forced-mode banner, clarified flag tooltips, "Send to new SPs" replaces "Send to SPs by default", and a new docs page for user attributes
- Documentation: scrub-on-disconnect section in the SAML setup guide, expanded audit event-type table, and seven admin/user guide pages updated for the new feature
Fixed
- Removed the misleading "Enable all in " checkbox on the tenant attribute settings page (it overwrote partial selections)
- Profile attribute save errors and successes now render as flash banners instead of silently appearing in the URL
- Passkey E2E tests no longer race the auto-ceremony redirect on slow runs and leave the shared test tenant with a lingering passkey
- Several pre-release correctness fixes on the user attributes feature (self-edit 403 on non-string user IDs, duplicate
displayNameSAML attribute when SPs had no mapping, stale event-log diffs, RLS-safe set-based mirror scrub)
Security
- Bumped
cryptography46.0.7 → 48.0.0 anduvicorn0.42.0 → 0.47.0 - Bumped
python-multipart,pyopenssl,certifi,urllib3, andpipto clear the dependency CVE backlog - Removed raw PII (phone, mobile, address, postal code, employee ID) from
user_profile_updatedaudit event metadata. The audit signal (who changed which key, when, how) is preserved - IdP attribute mirror-write failures and tenant attribute config read failures during SSO are now surfaced as admin-tier audit events instead of only container logs
v1.5.0
Immutable
release. Only release title and notes can be modified.
Added
- Passkey authentication (FIDO2/WebAuthn): register, list, rename, and delete passkeys from the two-step verification page
- Passkey login with a scoped ceremony and clone detection on used authenticators
- Admin passkey management: per-user passkey view/revoke and auth-method filter on the user list
- Tenant authentication strength policy with forced TOTP enrollment and platform-MFA requirement
- Passkey enrollment accepted under the enhanced auth policy
- API endpoints for SAML IdP reimport-xml and SAML debug entry retrieval (with response schemas)
- API endpoint to clear all group relationships in one call
- Admin passkey API endpoints (list, revoke)
Changed
- Renamed "two-step verification" copy where it was inaccurate now that passkeys are available
- Passkey-related copy aligned across templates and emails (revoke terminology, MFA reset docs)
- Tenant auth strength selector switched from a dropdown to radio buttons
- Passkey clone detection raises a typed exception instead of matching error strings
Fixed
- Enforce
require_platform_mfaon SAML IdP login (policy was not applied on that path) - Enhanced auth policy bypass via email OTP closed
- TOCTOU race in passkey
complete_authentication - Passkey-existence oracle on the login page (revealed whether a user had a passkey registered)
- Migration
0032conflict with baseline schema on fresh installs
Security
- WebAuthn RP ID now derived from the tenant record, not request headers (prevents RP ID spoofing via
Host/Originmanipulation) - Require user verification (UV) in all WebAuthn ceremonies (registration, authentication, reauth)
- Bounded request body size and tightened WebAuthn input schemas
- Rate limits on passkey registration and enrollment endpoints
- Plain admins can no longer revoke super_admin passkeys (privilege escalation blocked)
- Updated
lxmlandpython-multipartto fix CVEs - Minor dependency bumps:
click,werkzeug,python-dotenv,ua-parser-builtins,watchfiles
v1.4.1
Immutable
release. Only release title and notes can be modified.
Security
- Updated Pillow to 12.2.0 to fix GZIP decompression bomb vulnerability
- Updated pytest to 9.0.3 to fix insecure tmpdir handling
v1.4.0
Immutable
release. Only release title and notes can be modified.
Added
- SAML assertion debug log for troubleshooting authentication failures, accessible at Audit > SAML Debug with optional verbose logging for successful assertions
- SAML assertion replay prevention via Memcached (each assertion ID is cached and rejected on resubmission within its validity window)
- SAML assertion attribute resilience: missing optional attributes (first name, last name) no longer block sign-in. Existing user values are preserved.
- Automatic user attribute sync from the upstream IdP on each SAML sign-in (first name, last name updated when they differ)
- Failed SAML authentication attempts are now always logged with full diagnostic details
- SLO URL editing for metadata-imported service providers (previously read-only)
Changed
- IdP-assigned users skip the password-setting step during onboarding and are directed to sign in through their identity provider
- SAML Debug Log moved from Settings to the Audit section in navigation
- Profile editing policy (
allow_users_edit_profile) now enforced in the service layer, covering both web UI and API
Security
- Added
max_lengthconstraints to allForm()parameters to prevent oversized input attacks (e.g., CPU exhaustion via Argon2 with megabyte-length passwords) - Fixed XSS in assertion attribute preview where user-controlled data was interpolated via innerHTML without escaping
- Blocked SSRF via redirect following in SAML metadata URL fetch
- Fixed open redirect via unvalidated SAML RelayState parameter
- Replaced sequential integer nonces with cryptographically random tokens for email verification and password-reset links
- Removed unauthenticated check-email API endpoint (user enumeration vector)
- Removed super admin self-reactivation bypass; all reactivations now require admin approval
- Restricted B2B OAuth2 client management to super admins (was admin+)
- Rate-limited the account reactivation endpoint
- Certificate rotation grace period bounded to 0-90 days
- PII redacted from verbose SAML assertion event log metadata
- Docker containers now run as a non-root user
- Install script generates a random database password for the application user
- Install script sets
.envfile permissions to 600 (owner-only read/write) - On-demand TLS certificate issuance restricted to registered tenant subdomains
v1.3.0
Immutable
release. Only release title and notes can be modified.
Added
- Per-SP AES-256-GCM assertion encryption (opt-in) for SAML IdP connections, replacing CBC where enabled
- Auto-download of the Tailwind CSS binary on first use (no manual install required)
Changed
- Renamed remaining "Loom Identity Platform" references to "WeftID" in X.509 signing certificates and the API schema title
- Role values now display as "Super Admin" / "Admin" / "User" instead of raw database values across all templates
- Consolidated repo root: moved compose files, shell scripts, and project management files into dedicated directories; retired shell scripts in favor of
maketargets
Security
- Fixed CBC padding oracle vulnerability on SAML ACS endpoints
- Scoped the SAML ACS rate limit key per tenant to prevent cross-tenant denial-of-service via shared egress IPs
- Updated cryptography from 46.0.6 to 46.0.7
v1.2.0
Immutable
release. Only release title and notes can be modified.
Added
- Streamlined sign-in flow that routes directly to auth method without email verification, with tenant opt-in setting to preserve the old discovery flow
- Bulk user operations from the user list: inactivate/reactivate, add to group, add secondary emails, and change primary email with dry-run SP assertion impact preview
- User audit export as password-encrypted XLSX (Users, Group Memberships, App Access sheets)
- Audit log XLSX export with optional date range, replacing the JSON export
- Audit event visibility tiers (security, admin, operational, system) with color-coded UI toggles and API filter support
- Resend invitation email for pending users with nonce-based link invalidation
- Branded email headers with tenant logo and name across all 15 outbound emails, plus a Pageloom footer
- User list filter panel redesigned as floating popover with IS/IS NOT toggle, filter negation, group hierarchy inclusion, and tinted active-state borders
- Contextual documentation links on admin pages (information-circle icon linking to relevant docs)
- Icons on action bar buttons
Changed
- Email management is now admin-only; self-service email add/remove/promote/verify removed from user accounts
- Sign-in flow defaults to skipping email verification (old behavior available via
require_email_verification_for_logintenant setting) - Consolidated tenant name and site title into a single field (
tenants.name) - Standardized product name to "WeftID" across all user-facing copy
- Renamed "MFA" to "two-step verification" in emails and exports
- Authorization denial logs moved from tenant audit trail to application logs
- Removed
weftidmanagement script in favor of documented Docker Compose commands
Fixed
- Fixed XSS in bulk email template where user-controlled names were interpolated via innerHTML
- Fixed group picker missing group_type data and modal backdrop issues
- Fixed export file passwords persisting in the database after file expiry (now redacted)
- Fixed flaky test_claim_next_task in parallel test runs
Security
- Set-password and invitation links are now one-time use via nonce-based invalidation (migration 0023)
- Bounded bulk operation list fields to max 5000 items to prevent resource exhaustion
- Export file passwords are redacted from the database after the download window expires
v1.1.0
Immutable
release. Only release title and notes can be modified.
Added
- Group assertion scope setting to control which groups are shared in SAML assertions (access-relevant, trunk, or all) with per-SP override and consent screen disclosure
- Email deliverability verification CLI (
python -m app.cli.verify_email) for checking SPF, DKIM, and DMARC before tenant provisioning - Self-hosting upgrade and backup documentation with full rollback procedure
Changed
- Restructured self-hosting guide as a numbered first-setup flow with install directory guidance
- Self-hosting docs now emphasize that SECRET_KEY and POSTGRES_PASSWORD are irrecoverable
- Standardized password error messages across all password templates
- Rebuilt documentation site with Zensical 0.0.28
Fixed
- Fixed production Docker image showing "dev" as the version when the build arg was not explicitly passed
- Fixed incorrect role list in self-hosting backup documentation (removed unused migrator role)
Security
- Fixed LIKE wildcard injection in search queries where %, _, and \ in search terms were interpreted as SQL wildcards instead of matching literally
- Added rate limiting to password change endpoints (5 per user per hour, 10 per IP per hour)
- Fixed content injection via unvalidated query parameters in password-related templates