docs: make required OAuth scopes discoverable before a 403, not after - #109
Open
jackvaughanjr wants to merge 1 commit into
Open
docs: make required OAuth scopes discoverable before a 403, not after#109jackvaughanjr wants to merge 1 commit into
jackvaughanjr wants to merge 1 commit into
Conversation
Three gaps made the scope model hard to reason about before running into it at runtime. 1. The scope-to-tool table had drifted from TOOL_SCOPE_REGISTRY. get_login_failures was absent from the README entirely, and confirm_delete_group and confirm_delete_application were missing from their rows. The table is now verified against the registry. 2. The README claimed a token with '*.manage' implicitly covers '*.read'. The code has never done that: scope_guard matches exact strings, so declaring only okta.users.manage silently removes list_users, get_user, and get_user_profile_attributes from tools/list. They do not error, they are simply absent. The docs now describe the real behavior, and prune_tools_by_scope logs a warning naming the missing '.read' scope and the tools it cost. get_scope_status reports the same under manage_without_read_gaps. The same false claim in scope_registry.py's own docstring is corrected. Exact matching is kept deliberately: OKTA_SCOPES stays an auditable declaration of intent rather than something a reader has to expand mentally. 3. OKTA_SCOPES declares what you want; the Okta Admin Console controls what you are granted. Startup pruning only sees the former, so a scope declared but not granted produces a runtime 403 instead of a hidden tool. Documented, with a troubleshooting entry keyed on the literal error text and ready-made read-only and full scope profiles. Relates to okta#104.
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.
Independent of #107 and #108; no shared code beyond three lines in
server.py.Relates to #104.
Three separate gaps made the scope model hard to reason about until it failed at runtime.
1. The scope-to-tool table had drifted
get_login_failureswas absent from the README entirely, despite being registered inTOOL_SCOPE_REGISTRY.confirm_delete_groupandconfirm_delete_applicationwere missing from their rows. The table was regenerated from the registry and diffed programmatically rather than checked by eye.2. The README promised a scope hierarchy the code does not implement
It stated that a token with
*.manageimplicitly covers*.read.scope_guardhas always matched exact strings:So declaring only
okta.users.managesilently removeslist_users,get_user, andget_user_profile_attributesfromtools/list. They do not 403; they are simply absent, and the operator has no way to find out why.Two things could be fixed here: the doc, or the code. This PR fixes the doc and keeps exact matching, because
OKTA_SCOPESis more useful as an exact, auditable declaration of intent than as something a reader has to expand mentally. Okta's API genuinely does grant read via.manage, so the README now names that difference explicitly rather than leaving it as a surprise.What changes in the code is that the consequence is no longer silent.
prune_tools_by_scopenow emits a warning naming the missing.readscope and the exact tools it cost:get_scope_statusreports the same under a new additivemanage_without_read_gapskey, so the model can tell a user what to add. The sibling scope is derived from the string, not a hardcoded resource list, andokta.logs.read(which has no.managecounterpart) produces no spurious warning.The same false claim in
scope_registry.py's own module docstring is corrected.3. Configured and granted scopes were conflated
OKTA_SCOPESdeclares what you want. The Okta Admin Console controls what you are granted. Startup pruning only sees the former, so a scope declared but never granted passes preflight and fails at call time with an HTTP 403 instead of being hidden. Now documented with a table of the three states, a troubleshooting entry keyed on the literal error text so it is greppable by someone who just hit it, and a note that a missing admin-role assignment is a second possible cause of a 403.Also adds copy-pasteable read-only and full
OKTA_SCOPESprofiles, generated from the registry rather than typed by hand. The read-only profile is the minimum set for a compliance audit.Testing
10 tests: manage-only triggers the warning naming the scope and the three tools, both-scopes and read-only produce no warning,
okta.logs.readalone neither warns nor crashes,get_scope_statusreports the condition and stays clean when absent, and module state resets between calls. Assertions are on captured loguru records, since this codebase uses loguru directly andcaplogdoes not intercept it.One test asserts the README no longer contains the false claim, as a cheap guard against the doc drifting back.
No behavior changes for a correctly configured server. Full suite: 539 passed, up from 529 on
main.