Authsec staging - #47
Merged
Merged
Conversation
… into authsec-staging
adityaauthnull250401
force-pushed
the
main
branch
from
July 3, 2026 06:29
5772f65 to
62a33fa
Compare
… into authsec-staging
#1 One-transaction grant: GrantAssignment now creates the connector assignment + broker-RS registration (approved) + connector-executor role binding on the client's service account in a single transaction (repo.GrantAssignmentTx). RevokeAssignment tears down the registration + binding only when it's the client's last assignment. Replaces the manual 4-table / raw-SQL enablement with one API call. #2 last_seen_at: service_accounts.last_seen_at is now updated on M2M token issuance and on a successful broker action (was NULL forever). #3 Agent activity: GET /uflow/admin/agents/:id/activity returns the agent lens over connector_action_audit (matched by actor_client_id / subject_id / actor_spiffe_id) for Agent 360. Build/vet/gofmt clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EnsureBrokerResourceServer only wired the connector:execute oauth_scope + its permission link inside the create branch, so a broker RS provisioned before that wiring existed never got the link — scope resolution then failed with "no scopes granted" forever (the manual INSERT we needed last session). Re-assert the (idempotent) scope + link on the already-exists path too, so a fresh service account resolves connector:execute with zero manual SQL. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…undwork) RegisterAgentClient (POST /authsec/agents) minted an XAA/A2A client with no service_accounts row. Broker authorization binds connector:execute via role_bindings.service_account_id, so such an agent's actor could never hold broker scopes — a delegated XAA token acting on the broker had nothing to bind to. Create an active service_account linked to the new client (idempotent via uq_sa_client). Grants are unchanged (still authorization_code/refresh/ token-exchange — this is not a client_credentials M2M client); the SA link is what lets Phase 6 delegated agents carry connector:execute. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng, refresh-lock, F8 audit F7 (owner always): owner_email required at service-account creation (threaded through CreateServiceAccount; implicit-create sites default to the acting admin). Audit now records actor ALWAYS (act.client_id, else authenticating client — fixes direct-M2M no-actor) and stamps the accountable owner into every connector_action_audit row. D6: reject a foreign-workspace client_id at grant time with a clear error (resolveClientPrincipals now returns the SA workspace; GrantAssignmentTx guards it). Cross-workspace stays the deferred A2A/XAA case. #2a schema hardening — connector_connections: workspace_id + composite FK, scope->binding_type, auth_type->auth_method, subject_user_id->uuid, binding/ status/auth CHECK constraints, NULL-safe partial unique indexes (a plain UNIQUE let duplicate workspace rows through), and lifecycle cols (version, external_account_*, refresh_expires_at, last_used_at, revoked_at). #2b refresh-under-lock — Refresh takes a non-blocking pg advisory lock on the connection id; a loser re-reads the rotated token instead of racing, the winner does a version CAS and increments version. Kills the thundering-herd refresh-token rotation race. #2c F8 audit triad — split the overloaded outcome/http_status into authz_outcome / broker_status / provider_status / action_outcome, so an authorized-but-provider-failed call is no longer logged as success. Schema change requires wipe+rebootstrap on deploy. Build/vet/gofmt clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…put constraints (F3)
F1 — GitHub App support. The GitHub connector can now use an org-installed
GitHub App (bot identity, no human attached) instead of an OAuth App that
impersonates whoever authorized it.
- connector_provider_apps gains app_kind ('oauth2'|'github_app') + github_app_id;
connector_connections.auth_method CHECK now allows 'github_app'.
- internal/connectoradapters/githubapp.go: signs the App JWT (RS256, <=10m) and
exchanges it at /app/installations/:id/access_tokens for a ~1h installation
token, cached per installation (re-mint within 5m of expiry). No refresh flow.
- Broker runAction mints the installation token on demand for github_app
connections and injects it (never a static Vault token).
- Endpoints: POST /providers/github/app-github (set App id + key PEM -> Vault);
POST /:id/connections/github-app (bind installation id, no OAuth dance).
Realizes D2 (connector-per-org-installation).
F3 — action-input constraints. An assignment can now bound WHERE an action
runs, not just WHICH action.
- connector_assignments gains input_constraints (jsonb): a per-field predicate
{"owner":{"equals":"acme-eng"},"repo":{"glob":"release-*"}} with equals /
one_of / glob rules (AND across fields), injection-safe glob (only *).
- Enforced as Gate 3 in runAction, AFTER input-schema validation and BEFORE the
provider call; a violation is a policy_deny (403), fail-closed on malformed
constraints or missing/non-string fields.
- Gate 2 now uses MatchingAssignment (action-specific row wins over all-actions)
so the correct row's constraints apply. Threaded through the grant API/service/tx.
Schema change requires wipe+rebootstrap (or additive ADD COLUMNs) on deploy.
Build/vet/gofmt clean; Gin routes verified conflict-free.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ctions
An authenticated end-user can now connect their OWN provider account; the
broker resolves that connection when a delegated (XAA) token carries sub=this
user. Unblocks the local per-user agent topology (T2) and per-user Google.
- StartUser: refactored Start onto a shared startConnect; the user path sets
binding_type=user + subject_user_id (the caller's own UUID from their session,
never supplied) on the OAuth state.
- HandleCallback branches on binding_type: user connections use a per-user Vault
path (.../users/{subject}) and upsert keyed by (connector, subject).
- Repo: ListUserConnectionsBySubject + RevokeUserConnection (status=revoked +
revoked_at, best-effort Vault secret delete); service wrappers.
- Endpoints (bound to caller identity via ResolveUserID):
POST /:id/connections/user/oauth/start · GET /connections/me ·
DELETE /:id/connections/me. Revoke -> broker fails closed on next action.
No new schema (reuses the hardened connector_connections + oauth_states).
Build/vet/gofmt clean; Gin routes verified conflict-free.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The broker now gates WHICH TEAM an agent may act FOR, not just which agent
calls — enforced inside the broker chain, not at the agent's own front door.
- connectors.allowed_subject_groups (uuid[]): group ids the on-behalf-of user
must belong to; empty = no restriction.
- Gate 4 in runAction: for a delegated (XAA) call, the token subject must be a
member of an allowed group (SubjectInAnyGroup over user_groups). A connector
with a group policy but no human subject (M2M) is denied — the policy is
meaningless without a subject.
- PUT /authsec/connectors/:id/subject-groups {group_ids} (connector:assign).
Closes Track A (design-review D5): #1 owner/D6, #2 schema-harden+refresh-lock+
F8, #3 GitHub App (F1), #4 input constraints (F3), #5 user consent (R4), #6
this. Additive column; rides the existing rebootstrap window. Build/vet/gofmt
clean; routes conflict-free.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… into authsec-staging
Previously a revoked client registration was a permanent dead end — the agent got "client not authorized" with no way for the admin to re-approve. Now: 1. authorize flow: revoked registration flips to pending_approval so the admin sees it in the Connections page 2. approval function: also matches 'revoked' status so the admin's approve action works on revoked registrations (covers both authorize and XAA access_request paths) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… into authsec-staging
updated the Jenkins file. Signed-off-by: sriram <ram@authnull.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Changes
Testing
go test -short ./tests/unit/)go vet ./...cleanrun-integrationto this PR to trigger integration tests in CI (requires a live DB)Checklist