Skip to content

fix(authserver): map deliberate provisioning refusal to access_denied - #6441

Merged
reyortiz3 merged 1 commit into
mainfrom
fix/authserver-unprovisioned-identity-access-denied
Aug 27, 2026
Merged

fix(authserver): map deliberate provisioning refusal to access_denied#6441
reyortiz3 merged 1 commit into
mainfrom
fix/authserver-unprovisioned-identity-access-denied

Conversation

@reyortiz3

@reyortiz3 reyortiz3 commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Under a deployment where user accounts are provisioned out-of-band (e.g. SCIM) rather than auto-created on first login, an unprovisioned upstream identity's OAuth login is correctly denied — but the denial currently surfaces to the client as error=server_error, not error=access_denied. server_error is semantically wrong (nothing failed internally) and OAuth clients treat it as a retryable/transient failure rather than a definitive denial.

Root cause: CallbackHandler's ResolveUser error branch in pkg/authserver/server/handlers/callback.go maps every resolution failure — including a storage layer's deliberate refusal to provision — to the same generic fosite.ErrServerError.

  • Added an exported sentinel storage.ErrUserNotProvisioned that a UserStorage.CreateUser implementation can return to mean "deliberately not provisioned, deny the login" instead of raising an internal error.
  • Updated CallbackHandler to check errors.Is(err, storage.ErrUserNotProvisioned) and respond with fosite.ErrAccessDenied in that case; every other resolution error keeps mapping to fosite.ErrServerError, unchanged.
  • Updated the UserStorage.CreateUser doc comment to document the new contract.

Type of change

  • Bug fix
  • New feature
  • Refactoring (no behavior change)
  • Dependency update
  • Documentation
  • Other (describe):

Test plan

  • Unit tests (task test)
  • E2E tests (task test-e2e)
  • Linting (task lint-fix)
  • Manual testing (describe below)

Added two new tests in callback_test.go:

  • TestCallbackHandler_UserResolutionFailure_UserNotProvisioned_DeniesAccess — asserts error=access_denied when CreateUser returns storage.ErrUserNotProvisioned.
  • TestCallbackHandler_UserResolutionFailure_OtherError_ServerError — asserts the existing error=server_error behavior is unchanged for unrelated CreateUser failures.

Ran go build ./pkg/authserver/..., go test ./pkg/authserver/..., go vet ./pkg/authserver/..., gofmt -l, and golangci-lint run against the changed packages — all pass.

Does this introduce a user-facing change?

Yes: an unprovisioned identity's login attempt (in deployments that provision users out-of-band and opt into returning storage.ErrUserNotProvisioned) now redirects with error=access_denied instead of error=server_error. No change for deployments whose UserStorage.CreateUser never returns this sentinel.

Special notes for reviewers

This is additive/opt-in: a UserStorage implementation must explicitly return the new sentinel to get the new behavior. Existing implementations that just auto-provision on CreateUser are unaffected.

CreateUser can return storage.ErrUserNotProvisioned to signal a deployment
that provisions users out-of-band (e.g. SCIM) is deliberately refusing to
auto-create an unbound identity, rather than hitting an internal failure.
The callback handler now maps that specific error to fosite.ErrAccessDenied
instead of folding it into the generic server_error response every other
resolution failure still gets.
@github-actions github-actions Bot added size/S Small PR: 100-299 lines changed and removed size/S Small PR: 100-299 lines changed labels Aug 27, 2026
@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.95%. Comparing base (28dd90a) to head (337621c).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6441      +/-   ##
==========================================
- Coverage   78.00%   77.95%   -0.06%     
==========================================
  Files         766      766              
  Lines       74069    74072       +3     
==========================================
- Hits        57780    57744      -36     
- Misses      16284    16323      +39     
  Partials        5        5              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jhrozek jhrozek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed via automated multi-agent pass (security + Go code quality). Small, well-scoped change: storage.ErrUserNotProvisioned is correctly propagated through ResolveUsercreateUserWithIdentity (wrapped with %w) and mapped to access_denied only on that specific path, leaving every other resolution failure as server_error. No information-disclosure risk (the branch only fires after a successful upstream token exchange, so the subject is attacker-proven, not attacker-supplied) and no auth/authz bypass. Both the new path and the server_error regression are covered by tests. LGTM.

@reyortiz3
reyortiz3 merged commit b6302d2 into main Aug 27, 2026
50 checks passed
@reyortiz3
reyortiz3 deleted the fix/authserver-unprovisioned-identity-access-denied branch August 27, 2026 12:35
@github-actions github-actions Bot mentioned this pull request Aug 27, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/S Small PR: 100-299 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants