feat(bfabric): OAuth service accounts and RFC 7592 client management - #622
Open
Caushi wants to merge 6 commits into
Open
feat(bfabric): OAuth service accounts and RFC 7592 client management#622Caushi wants to merge 6 commits into
Caushi wants to merge 6 commits into
Conversation
Caushi
force-pushed
the
feature/oauth-service-accounts-on-union
branch
2 times, most recently
from
August 25, 2026 14:24
5649796 to
6ff6d8d
Compare
Adds an `auth_method: client_credentials` environment that authenticates from an inline `client_secret`, so cron jobs and scripts connect without a browser or a cached token, and `bfabric-cli auth service-account` to record one. Registration responses can now be saved with `auth register --save-env`, including the RFC 7592 `registration_access_token` / `registration_client_uri`. That makes `auth client-show` / `client-update` / `client-delete` possible, so a client registered with a wrong redirect URI is corrected in place rather than re-registered. Two write paths need care, both verified against the live TEST instance: B-Fabric rotates *both* the client secret and the registration access token on every successful update and revokes the old ones immediately (confirmed: the previous token returns 401). Since the config writer replaces every auth-owned key at once, writing only the rotated keys would drop the untouched ones and strand the client unmanageable. `merge_auth_owned_keys` carries the rest along. Rotation is detected by comparing against the stored value rather than testing for presence, so a server echoing an unchanged credential reports nothing. `--save-env` into an existing environment would likewise replace its stored credentials, so it now refuses unless `--force` is passed. Also refuses `api delete` / `api update` without a terminal to confirm on: unattended service-account runs would otherwise hang or die on a prompt.
A client_credentials grant has no authorization step to inherit a scope from, so the token request is the only place one can be expressed. The service account passed scope="" and dropped the scope the operator recorded, taking whatever the server granted by default while `auth status` displayed the narrower scope as though it applied. Carry `scope` on ConfigData (through the file reader and the JSON export, so it survives BFABRICPY_CONFIG_OVERRIDE) and pass it to the provider. An empty scope omits the parameter, keeping the server default as the fallback. The interactive path keeps scope="": its scope is fixed at authorization time and a refresh inherits it, so re-requesting one could only narrow the token below what the user consented to. A regression test pins that.
`auth service-account` rewrote auth_method on an existing environment with no prompt, so every later connect() authenticated as the service account instead of whoever was configured. The superseded login/password stayed behind in the file, unreachable but still a stored secret. Ask before converting, refuse without a terminal (matching _confirm_repoint), and drop the superseded credentials on the way. A re-run against an environment that is already a service account does not prompt — that is the documented path for storing a rotated secret. Also correct save_registration's guard docstring: it claimed the write would strip the credentials an existing login authenticates with, which merge_auth_owned_keys made false. The real hazard is client_id being replaced while auth_method stays "oauth", leaving the token cache (keyed on client_id) unable to match. Pinned by a test.
The feature was written against merge_auth_owned_keys, which the refactor replaced with an explicit auth mode. The four call sites move to write_environment_to_config(auth="merge") and read_environment_auth_keys, so the read-modify-write sandwich and its extra file reads are gone. auth client-delete now also clears auth_method when the environment was a service account: validate_writable_environment refuses to persist client_credentials without a secret, which is exactly the state deleting the client leaves behind.
…ve path save_registration and _resolve_token_from_config took a plain str, so a BaseUrl was cast back down to str at the two call sites and then re-normalised inside save_registration. Type both on BaseUrl and normalise once, where the CLI argument enters. Also drops an inconsistency in cmd_login_register, which passed the normalised URL to register_client but the raw one to save_registration.
save_registration re-read the environment it had just written to decide whether to warn about missing registration credentials. The merge cannot add keys the server did not return, so the response already answers the question. Also repoint the service-account tests at from_config_data, replacing the _connect_oauth_from_config alias dropped in the parent branch. Claude-Session: https://claude.ai/code/session_01RZNr7BRtYpGSP4EXNgp8hk
Caushi
force-pushed
the
feature/oauth-service-accounts-on-union
branch
from
August 25, 2026 14:29
6ff6d8d to
2eeec95
Compare
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.
Stacked on #621 — review that first; this PR's diff is only the service-account work on top of it. Replaces #619, which targets
mainand predates the auth-method union.auth_method: client_credentials, so an environment authenticates a service account from its inlineclient_secretandBfabric.connect()works unattended, with no browser and no cached token.bfabric-cli auth service-accountto record one, prompting for the secret rather than taking it on the command line.read_client,update_clientanddelete_client, and theauth client-show/client-update/client-deletecommands, so a client registered through the CLI can be corrected or revoked later using its registration access token.--save-envtoauth registerandregister-webapp, recording the registration credentials needed to edit the client later. A webapp is never recorded as a service account, even when--service-usergave it the grant.scopeto be requested on every token request, narrowing the token; without one the server applies the client's default. Interactive OAuth is unaffected.auth service-accountto ask before converting an existing environment to a service account, and to refuse non-interactively.auth client-deleteto clearauth_methodalong with the dead credentials, since aclient_credentialsenvironment without its secret cannot authenticate.auth logoutto clear a stored client secret, andauth status/auth listto reportclient_credentialsenvironments.