feat: interactive browser login (OAuth2 authorization code flow)#15
Merged
Conversation
Add an `innoactive-portal auth` command group so users can authenticate interactively instead of manually managing access tokens. `auth login` runs the OAuth2 authorization code flow with PKCE: it opens the browser to Portal's /oauth/authorize/ endpoint, catches the redirect on a local loopback server, validates state, exchanges the code at /oauth/token/ and persists the access token to ~/.config/innoactive-portal/credentials.json (0600, honoring XDG_CONFIG_HOME). Supports both public clients (PKCE only) and confidential clients (client secret via HTTP Basic). The OAuth client can be configured via CLI flags, the PORTAL_BACKEND_* env vars, or the config file, resolved in that order of precedence. get_bearer_authorization_header() now falls back to the stored token, so existing commands work after one login; explicit env vars still win. Adds `auth logout` and `auth status`, and covers the flow with tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set `tool.uv.package = true` again so `uv run innoactive-portal` runs this repository's code instead of falling through to a globally installed copy. This makes the documented `innoactive-portal ...` commands work in a dev checkout and matches what pip-installed users get. Pin `[tool.setuptools] packages = ["portal_client"]` so the build does not rely on flat-layout auto-discovery, which fails when other top-level directories (tests, specs, ...) are present. The uv.lock diff is mostly format noise from a newer uv (revision 1 -> 3 and added upload-time fields); the only semantic change is portal-client now being recorded as an editable source. CI installs uv unpinned and runs `uv sync --locked`, so the regenerated lock is required and compatible. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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.
What
Adds an
innoactive-portal authcommand group so users can authenticate interactively via the browser instead of manually obtaining and exporting an access token.auth loginruns the OAuth2 authorization code flow with PKCE:code_verifier/code_challenge(S256) and a randomstate./oauth/authorize/.http://localhost:8723/callbackby default).state, then exchanges the code at/oauth/token/.~/.config/innoactive-portal/credentials.json(mode0600, honoringXDG_CONFIG_HOME).Supports both public clients (PKCE only) and confidential clients (client secret sent via HTTP Basic).
Also adds
auth logout(clears stored credentials) andauth status(shows current login + token expiry).Configuring the OAuth client
Each setting resolves with precedence CLI flag → env var → config file → default:
PORTAL_BACKEND_CLIENT_ID,PORTAL_BACKEND_CLIENT_SECRET,PORTAL_BACKEND_REDIRECT_URI,PORTAL_BACKEND_OAUTH_SCOPE~/.config/innoactive-portal/config.json(client_id,client_secret,redirect_uri,scope)With either configured,
innoactive-portal auth loginneeds no arguments.How existing commands benefit
utils.get_bearer_authorization_header()now falls back to the stored token whenPORTAL_BACKEND_ACCESS_TOKENis unset, so every existing command works after a single login. Explicit env vars still take precedence.Notes for reviewers
/oauth/authorize/,/oauth/token/) target Django OAuth Toolkit, relative toPORTAL_BACKEND_ENDPOINT.http://localhost:8723/callback) must be registered on the OAuth client in Portal, or the flow fails — override via--redirect-uri/env/config.Testing
tests/test_auth.py(PKCE, URL building, token exchange for public/confidential clients, credential persistence, settings-resolution precedence, full login flow incl. state-mismatch + error handling, header fallback).ruffclean on changed files.🤖 Generated with Claude Code