Repository navigation
Close the SSO offboarding gap: session revocation, SCIM provisioning, reconciliation #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ed95764
b32f9f8
132735c
53aeab6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| -- migrate:up | ||
|
|
||
| -- SCIM clients (Okta, Azure AD, Google Workspace) authenticate with a single | ||
| -- static bearer token per org, per the SCIM 2.0 RFC - there is no user | ||
| -- session involved. Modeled directly on api_keys' hashing scheme | ||
| -- (internal/api/api_keys.go: GenerateAPIKey/HashAPIKey/GetKeyPrefix), but | ||
| -- kept as its own table rather than reusing api_keys since a SCIM token is | ||
| -- org-bound with no owning user. | ||
| CREATE TABLE IF NOT EXISTS scim_tokens ( | ||
| id BIGSERIAL PRIMARY KEY, | ||
| org_id BIGINT NOT NULL REFERENCES orgs(id) ON DELETE CASCADE, | ||
| token_hash VARCHAR(64) NOT NULL UNIQUE, | ||
| token_prefix VARCHAR(16) NOT NULL, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: warning Verify Suggestions:
|
||
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | ||
| expires_at TIMESTAMPTZ, | ||
| revoked_at TIMESTAMPTZ, | ||
| created_by_user_id BIGINT REFERENCES users(id) ON DELETE SET NULL | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: warning Consider adding a Suggestions:
|
||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS idx_scim_tokens_org_id ON scim_tokens(org_id); | ||
|
|
||
| -- migrate:down | ||
| DROP TABLE IF EXISTS scim_tokens; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| -- migrate:up | ||
|
|
||
| -- Tracks which IdP subject (OIDC `sub` / SAML NameID) maps to which | ||
| -- LiveReview user, per org and provider. Needed so an incoming OIDC | ||
| -- back-channel logout token or SAML LogoutRequest - which only carries the | ||
| -- IdP's own subject identifier, not a LiveReview user id - can be resolved | ||
| -- to a user whose sessions should be revoked. Also holds the encrypted OIDC | ||
| -- refresh token used by the optional reconciliation poll (see | ||
| -- org_sso_configs.reconciliation_enabled) to silently re-check "is this | ||
| -- account still valid at the IdP" between logins. | ||
| CREATE TABLE IF NOT EXISTS sso_identities ( | ||
| id BIGSERIAL PRIMARY KEY, | ||
| org_id BIGINT NOT NULL REFERENCES orgs(id) ON DELETE CASCADE, | ||
| user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, | ||
| provider_type VARCHAR(16) NOT NULL CHECK (provider_type IN ('oidc', 'saml')), | ||
| subject VARCHAR(512) NOT NULL, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: warning A Suggestions:
|
||
| oidc_refresh_token_enc TEXT, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: critical Ensure strong encryption is used for Suggestions:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: warning Consider if Suggestions:
|
||
| last_checked_at TIMESTAMPTZ, | ||
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | ||
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | ||
| UNIQUE (org_id, provider_type, subject) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: info The unique constraint on |
||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS idx_sso_identities_user_id ON sso_identities(user_id); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: info An index on |
||
|
|
||
| CREATE OR REPLACE FUNCTION set_sso_identities_updated_at() | ||
| RETURNS TRIGGER AS $$ | ||
| BEGIN | ||
| NEW.updated_at = NOW(); | ||
| RETURN NEW; | ||
| END; | ||
| $$ LANGUAGE plpgsql; | ||
|
|
||
| CREATE TRIGGER trg_sso_identities_updated_at | ||
| BEFORE UPDATE ON sso_identities | ||
| FOR EACH ROW | ||
| EXECUTE FUNCTION set_sso_identities_updated_at(); | ||
|
|
||
| -- migrate:down | ||
| DROP TRIGGER IF EXISTS trg_sso_identities_updated_at ON sso_identities; | ||
| DROP FUNCTION IF EXISTS set_sso_identities_updated_at(); | ||
| DROP TABLE IF EXISTS sso_identities; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| -- migrate:up | ||
|
|
||
| -- Fallback deprovisioning for orgs that configure OIDC login but never wire | ||
| -- up SCIM at their IdP (a separate integration step many admins skip). Only | ||
| -- meaningful for provider_type='oidc' - SAML has no standing credential to | ||
| -- silently re-check, so the settings UI only exposes this toggle for OIDC. | ||
| ALTER TABLE org_sso_configs | ||
| ADD COLUMN IF NOT EXISTS reconciliation_enabled BOOLEAN NOT NULL DEFAULT FALSE, | ||
| ADD COLUMN IF NOT EXISTS reconciliation_interval TEXT NOT NULL DEFAULT 'daily' | ||
| CHECK (reconciliation_interval IN ('hourly', 'daily', 'weekly')), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: info The |
||
| ADD COLUMN IF NOT EXISTS last_reconciled_at TIMESTAMPTZ; | ||
|
|
||
| -- migrate:down | ||
| ALTER TABLE org_sso_configs | ||
| DROP COLUMN IF EXISTS reconciliation_enabled, | ||
| DROP COLUMN IF EXISTS reconciliation_interval, | ||
| DROP COLUMN IF EXISTS last_reconciled_at; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,7 @@ Generated from `internal/auditlog/catalog.go`. | |
| | `auth.logout` | `auth` | session | info | Signed out | A user ended a session. | | ||
| | `auth.password.change` | `auth` | user | warning | Password changed | A user changed their password. | | ||
| | `auth.password.reset.complete` | `auth` | user | warning | Password reset completed | A user completed a required password reset. | | ||
| | `auth.session.revoke` | `auth` | session | warning | Sessions revoked | A user's active sessions and refresh tokens were revoked, typically following deactivation. | | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: critical Ensure Suggestions:
|
||
| | `auth.sso.provision` | `auth` | user | info | SSO account provisioned | An account was provisioned through SSO. | | ||
| | `auth.token.refresh` | `auth` | session | info | Session refreshed | A session token was refreshed. | | ||
| | `auto_approve.policy.create` | `settings` | policy | warning | Auto-approve policy created | An auto-approve policy was created. | | ||
|
|
@@ -106,6 +107,12 @@ Generated from `internal/auditlog/catalog.go`. | |
| | `ruleset.create` | `repository` | ruleset | warning | Ruleset created | A CI ruleset was created. | | ||
| | `ruleset.delete` | `repository` | ruleset | warning | Ruleset deleted | A CI ruleset was deleted. | | ||
| | `ruleset.update` | `repository` | ruleset | warning | Ruleset updated | A CI ruleset was changed. | | ||
| | `scim.token.create` | `auth` | scim_token | warning | SCIM token created | An org's SCIM bearer token was generated or rotated. | | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Severity: info The new SCIM audit events are crucial for tracking identity lifecycle management and detecting suspicious activity. |
||
| | `scim.token.revoke` | `auth` | scim_token | warning | SCIM token revoked | An org's SCIM bearer token was revoked. | | ||
| | `scim.user.create` | `auth` | user | info | SCIM user provisioned | An identity provider created or linked a user via SCIM. | | ||
| | `scim.user.deactivate` | `auth` | user | warning | SCIM user deactivated | An identity provider deactivated a user via SCIM. | | ||
| | `scim.user.reactivate` | `auth` | user | warning | SCIM user reactivated | An identity provider reactivated a user via SCIM. | | ||
| | `scim.user.update` | `auth` | user | info | SCIM user updated | An identity provider updated a user's attributes via SCIM. | | ||
| | `settings.auto_approve.update` | `settings` | setting | warning | Auto-approve updated | The auto-approve policy was changed. | | ||
| | `settings.managed_ai.update` | `settings` | setting | critical | Managed AI updated | LiveReview-managed AI configuration was changed. | | ||
| | `settings.prompts.update` | `settings` | prompt | warning | Prompts updated | Review prompts were changed. | | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Severity: critical
Ensure
token_hashuses a strong, salted hashing algorithm. This is critical for security.Suggestions: