authsome 0.4.1__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- authsome-0.4.2/.github/release-please-manifest.json +3 -0
- {authsome-0.4.1 → authsome-0.4.2}/CHANGELOG.md +20 -0
- {authsome-0.4.1 → authsome-0.4.2}/PKG-INFO +1 -1
- {authsome-0.4.1 → authsome-0.4.2}/pyproject.toml +1 -1
- {authsome-0.4.1 → authsome-0.4.2}/skills/authsome/SKILL.md +10 -5
- {authsome-0.4.1 → authsome-0.4.2}/skills/authsome/references/adding-provider.md +1 -1
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/__init__.py +3 -3
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/audit/__init__.py +2 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/flows/pkce.py +1 -1
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/models/config.py +3 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/models/connection.py +6 -2
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/models/provider.py +2 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/sessions.py +1 -1
- authsome-0.4.2/src/authsome/cli/admin.py +193 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/cli/client.py +40 -24
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/cli/context.py +4 -7
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/cli/daemon_control.py +1 -1
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/cli/helpers.py +6 -12
- authsome-0.4.2/src/authsome/cli/main.py +616 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/identity/__init__.py +6 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/identity/local.py +53 -4
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/identity/principal.py +2 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/paths.py +2 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/proxy/server.py +15 -8
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/credential_service.py +2 -2
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/auth.py +7 -4
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/health.py +1 -21
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/ui.py +1 -1
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/schemas.py +3 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/ui/pages.py +48 -8
- authsome-0.4.2/src/authsome/store/__init__.py +5 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/store/interfaces.py +1 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/store/local.py +1 -1
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/vault/__init__.py +7 -48
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/vault/crypto.py +0 -92
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/test_client_signing.py +62 -1
- authsome-0.4.2/tests/cli/test_daemon.py +87 -0
- authsome-0.4.2/tests/cli/test_doctor.py +63 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/test_get.py +27 -34
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/test_identity.py +6 -13
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/test_import_env.py +12 -34
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/test_init.py +6 -9
- authsome-0.4.2/tests/cli/test_list.py +81 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/test_login.py +16 -26
- authsome-0.4.2/tests/cli/test_logout.py +35 -0
- authsome-0.4.2/tests/cli/test_register.py +94 -0
- authsome-0.4.2/tests/cli/test_revoke.py +39 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/test_whoami.py +9 -28
- {authsome-0.4.1 → authsome-0.4.2}/tests/identity/test_identity.py +50 -3
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_pop_auth.py +0 -33
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_ui_sessions.py +1 -1
- {authsome-0.4.1 → authsome-0.4.2}/uv.lock +3 -1
- authsome-0.4.1/.github/release-please-manifest.json +0 -3
- authsome-0.4.1/src/authsome/cli/main.py +0 -1198
- authsome-0.4.1/src/authsome/store/__init__.py +0 -1
- authsome-0.4.1/tests/cli/test_daemon.py +0 -99
- authsome-0.4.1/tests/cli/test_doctor.py +0 -79
- authsome-0.4.1/tests/cli/test_list.py +0 -198
- authsome-0.4.1/tests/cli/test_logout.py +0 -49
- authsome-0.4.1/tests/cli/test_register.py +0 -133
- authsome-0.4.1/tests/cli/test_revoke.py +0 -53
- authsome-0.4.1/tests/cli/test_ui.py +0 -23
- authsome-0.4.1/tests/vault/test_rekey.py +0 -167
- {authsome-0.4.1 → authsome-0.4.2}/.claude/commands/run-evals.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.claude-plugin/marketplace.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/dependabot.yml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/pull_request_template.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/release-please-config.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/workflows/pr-title.yml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/workflows/publish-rc.yml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/workflows/publish.yml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/workflows/release-please.yml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.github/workflows/test.yml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.gitignore +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/.pre-commit-config.yaml +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/AGENTS.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/CLAUDE.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/CONTEXT.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/CONTRIBUTING.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/LICENSE +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/README.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/TODOS.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/assets/authsome-how-it-works-dark.svg +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/assets/authsome-how-it-works-light.svg +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/assets/authsome-logo-dark.svg +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/assets/authsome-logo-light.svg +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/UBIQUITOUS_LANGUAGE.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/adr/0001-provider-client-record-server-scope.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/adr/0002-server-registered-identities.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/adr/0003-principal-owned-vault.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/adr/0003-proxy-unmatched-pass-through.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/agents/domain.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/agents/issue-tracker.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/agents/triage-labels.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/internal/authsome-design.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/internal/cli-design-review.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/internal/manual-testing.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/refactor.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/register-provider.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/README.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/changelog.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/compared.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/concepts/architecture.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/concepts/credential-storage.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/concepts/profiles-vs-connections.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/concepts/provider-registry.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/concepts/proxy-injection.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/concepts/the-daemon.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/docs.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/favicon.svg +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/guides/custom-providers.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/guides/headless-device-code.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/guides/login-with-oauth.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/guides/multiple-connections.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/guides/profiles.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/guides/run-agents-with-proxy.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/guides/use-api-keys.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/images/login-github-authsome.png +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/index.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/installation.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/anthropic-sdk.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/claude-code.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/codex.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/cowork.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/cursor.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/hermes.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/index.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/langchain.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/llamaindex.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/nanoclaw.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/openai-agents-sdk.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/opencode.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/agents/python.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/ahrefs.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/apollo.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/ashby.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/beehiiv.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/brevo.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/buffer.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/calendly.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/clearbit.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/dub.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/g2.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/hunter.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/index.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/instantly.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/intercom.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/keywords-everywhere.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/klaviyo.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/lemlist.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/livestorm.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/mailchimp.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/mention-me.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/openai.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/optimizely.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/postmark.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/resend.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/rewardful.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/savvycal.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/semrush.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/sendgrid.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/tolt.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/typeform.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/wistia.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/api-key/zapier.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/atlassian.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/discord.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/github.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/gitlab.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/google.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/hubspot.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/index.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/klaviyo-oauth.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/linear.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/microsoft.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/notion-dcr.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/notion.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/postiz.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/slack.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/integrations/oauth/x.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/logo/dark.svg +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/logo/light.svg +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/quickstart.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/reference/audit-log.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/reference/bundled-providers.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/reference/cli.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/reference/daemon-api.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/reference/environment-variables.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/reference/file-layout.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/reference/provider-schema.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/reference/python-library.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/roadmap.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/security/daemon-trust-boundary.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/security/disclosure.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/security/encryption.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/security/hosted-deployment.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/security/threat-model.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/snippets/masked-input-note.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/snippets/multi-connections-cta.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/snippets/whats-next-apikey.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/snippets/whats-next-oauth.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/troubleshooting/auth-errors.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/troubleshooting/daemon-issues.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/troubleshooting/doctor.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/troubleshooting/oauth-callbacks.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/troubleshooting/proxy-networking.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/docs/site/troubleshooting/token-refresh.mdx +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/evals/.gitignore +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/evals/evals.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/evals/generate_report.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/skills/authsome/references/feedback.md +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/ahrefs.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/apollo.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/ashby.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/atlassian.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/beehiiv.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/brevo.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/buffer.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/calendly.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/clearbit.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/discord.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/dub.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/g2.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/github.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/gitlab.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/google.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/hubspot.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/hunter.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/instantly.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/intercom.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/keywords-everywhere.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/klaviyo-oauth.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/klaviyo.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/lemlist.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/linear.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/livestorm.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/mailchimp.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/mention-me.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/microsoft.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/notion.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/notion_dcr.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/openai.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/optimizely.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/postiz.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/postmark.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/resend.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/rewardful.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/savvycal.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/semrush.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/sendgrid.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/slack.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/tolt.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/typeform.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/wistia.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/x.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/bundled_providers/zapier.json +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/flows/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/flows/api_key.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/flows/base.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/flows/dcr_pkce.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/flows/device_code.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/input_provider.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/models/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/models/enums.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/auth/utils.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/cli/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/cli/client_config.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/errors.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/identity/proof.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/identity/registry.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/proxy/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/proxy/certs.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/proxy/router.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/proxy/runner.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/py.typed +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/analytics.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/app.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/daemon.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/dependencies.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/hosted_auth.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/identity_bootstrap.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/ownership.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/proxy_catalog.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/registries.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/_deps.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/connections.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/identities.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/providers.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/routes/proxy.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/ui/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/ui/web_theme.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/ui_sessions.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/server/urls.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/static/app.js +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/static/style.css +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/_app_detail_shell.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/_layout.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/app_detail_apikey.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/app_detail_disconnected.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/app_detail_managed.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/app_detail_oauth.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/app_provider.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/applications.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/connections.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/identity.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/ui/templates/overview.html +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/src/authsome/utils.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/auth/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/auth/test_flows.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/auth/test_models.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/auth/test_service.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/auth/test_service_provider_clients.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/auth/test_url_template.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/conftest.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/cli/test_helpers.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/common/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/common/test_audit.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/common/test_errors.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/common/test_logging.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/common/test_utils.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/conftest.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/identity/test_proof.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/identity/test_registry.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/proxy/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/proxy/test_proxy.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_analytics.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_auth_sessions.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_hosted_auth.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_identity_bootstrap.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_ownership.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_provider_operation_policy.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/server/test_ui_dashboard.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/vault/__init__.py +0 -0
- {authsome-0.4.1 → authsome-0.4.2}/tests/vault/test_crypto.py +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.2](https://github.com/agentrhq/authsome/compare/authsome-v0.4.1...authsome-v0.4.2) (2026-05-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add device flow verification fields to auth schemas and CLI JSON output ([c24779c](https://github.com/agentrhq/authsome/commit/c24779c6df5939602f06bc3b18c5e270963cbdfc))
|
|
9
|
+
* auto-create env handle identities ([72e1b35](https://github.com/agentrhq/authsome/commit/72e1b350cd13cd9923eb8e2f408c599cfaf98497))
|
|
10
|
+
* env backed identity design ([e1662b5](https://github.com/agentrhq/authsome/commit/e1662b51963a6dac66d6d6f30f654ad04fc96736))
|
|
11
|
+
* improve copy-to-clipboard functionality with browser fallback and UI feedback ([02daa81](https://github.com/agentrhq/authsome/commit/02daa8182ed9d22abe75670db09286979844223d))
|
|
12
|
+
* restructure CLI commands under provider and admin namespaces ([7b4b31a](https://github.com/agentrhq/authsome/commit/7b4b31ad4fbc5bb8ffc2c1f60e0d69268844558e))
|
|
13
|
+
* restructure CLI commands under provider and admin namespaces ([a326e3f](https://github.com/agentrhq/authsome/commit/a326e3f1c43d2037124f0ba49751c8a5af0daa81))
|
|
14
|
+
* simplify Client ID label and implement robust cross-browser copy-to-clipboard logic ([d763f7f](https://github.com/agentrhq/authsome/commit/d763f7f7dc8bb33b7049c27be78d0cdd08195ede))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* expose actionable session details in login --json ([87ae0b7](https://github.com/agentrhq/authsome/commit/87ae0b7aeee76b69abae13d488e9e0829031d0c8))
|
|
20
|
+
* normalize cli error handling for json output ([fb2754b](https://github.com/agentrhq/authsome/commit/fb2754bded00bcdea76a7433736f59ae60f01cff))
|
|
21
|
+
* update daemon process command to include admin subcommand ([7855c77](https://github.com/agentrhq/authsome/commit/7855c77ceec670887c1cd4947e4202aaeff3a9b2))
|
|
22
|
+
|
|
3
23
|
## [0.4.1](https://github.com/agentrhq/authsome/compare/authsome-v0.4.0...authsome-v0.4.1) (2026-05-25)
|
|
4
24
|
|
|
5
25
|
|
|
@@ -40,9 +40,9 @@ to set any auth headers.
|
|
|
40
40
|
- Run `authsome login <provider>` via Bash yourself — do NOT ask the user to
|
|
41
41
|
run this. The browser opens on their machine; they complete OAuth without touching
|
|
42
42
|
the terminal.
|
|
43
|
-
- Poll the status of the login using `authsome list` after a few seconds
|
|
43
|
+
- Poll the status of the login using `authsome provider list` after a few seconds
|
|
44
44
|
- **If connected** — proceed normally.
|
|
45
|
-
- **If not yet connected** — display the authentication URL to the user, ask them to complete the login in their browser, and report back once done. Once the user confirms, run `authsome list` again to verify the connection before continuing.
|
|
45
|
+
- **If not yet connected** — display the authentication URL to the user, ask them to complete the login in their browser, and report back once done. Once the user confirms, run `authsome provider list` again to verify the connection before continuing.
|
|
46
46
|
|
|
47
47
|
---
|
|
48
48
|
|
|
@@ -56,16 +56,16 @@ Only when uv tool install fails, use fallback: `pipx install authsome` or `uvx a
|
|
|
56
56
|
|
|
57
57
|
### If you get an authentication error (401, 403) follow this decision tree:
|
|
58
58
|
|
|
59
|
-
**1. Run `authsome list` to see all providers and their connections**
|
|
59
|
+
**1. Run `authsome provider list` to see all providers and their connections**
|
|
60
60
|
|
|
61
61
|
**2. If relevant provider exists but it has no connections → start the [login flow](#login-flow)**
|
|
62
62
|
|
|
63
|
-
If there is a login error due to wrong client id/client secret you can delete the provider via `authsome remove <provider>` and start the [login flow](#login-flow)
|
|
63
|
+
If there is a login error due to wrong client id/client secret you can delete the provider via `authsome provider remove <provider>` and start the [login flow](#login-flow)
|
|
64
64
|
|
|
65
65
|
**3. If relevant provider exists and it is connected**
|
|
66
66
|
|
|
67
67
|
For 401 error → you need to re-login, creds have expired
|
|
68
|
-
- revoke the creds using `authsome revoke <provider>`
|
|
68
|
+
- revoke the creds using `authsome provider revoke <provider>`
|
|
69
69
|
- then start the [login flow](#login-flow)
|
|
70
70
|
|
|
71
71
|
For 403 error → you need to re-login, with the correct scopes, or missing permissions
|
|
@@ -89,6 +89,9 @@ If you are unsure of the correct command syntax, need to check available flags,
|
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
91
|
authsome --help
|
|
92
|
+
authsome provider --help
|
|
93
|
+
authsome connections --help
|
|
94
|
+
authsome admin --help
|
|
92
95
|
authsome run --help
|
|
93
96
|
```
|
|
94
97
|
|
|
@@ -104,6 +107,8 @@ authsome run --help
|
|
|
104
107
|
- **Never** suggest the user open Gmail/Calendar/GitHub in their browser
|
|
105
108
|
when they ask you to read or interact with those services. You have API
|
|
106
109
|
access. Use it.
|
|
110
|
+
- **Never** use `authsome export`, `--show-secret`, or any workflow that prints
|
|
111
|
+
tokens or API keys to the terminal. Use `authsome run -- ...` instead.
|
|
107
112
|
- If the gateway returns a policy error (403 with a JSON body), respect
|
|
108
113
|
the block. Do not retry or circumvent it.
|
|
109
114
|
- If the skill fails, the goal took too many steps, the CLI behaved unexpectedly,
|
|
@@ -15,5 +15,5 @@ When the provider isn't in the bundled list, do this before writing any config:
|
|
|
15
15
|
|
|
16
16
|
4. **Write and register the provider JSON** — follow the [provider registration guide](https://raw.githubusercontent.com/agentrhq/authsome/main/docs/register-provider.md) to write the provider JSON. Save the file to a local path (e.g. `/tmp/<provider>.json`), then register it:
|
|
17
17
|
```bash
|
|
18
|
-
authsome register /tmp/<provider>.json
|
|
18
|
+
authsome provider register /tmp/<provider>.json
|
|
19
19
|
```
|
|
@@ -12,6 +12,9 @@ Usage:
|
|
|
12
12
|
local proxy.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
from importlib.metadata import PackageNotFoundError as _PkgNotFoundError
|
|
16
|
+
from importlib.metadata import version as _pkg_version
|
|
17
|
+
|
|
15
18
|
from loguru import logger as _logger
|
|
16
19
|
|
|
17
20
|
from authsome.auth.models.connection import ConnectionRecord, Sensitive
|
|
@@ -39,9 +42,6 @@ from authsome.vault import Vault
|
|
|
39
42
|
_logger.disable("authsome")
|
|
40
43
|
|
|
41
44
|
try:
|
|
42
|
-
from importlib.metadata import PackageNotFoundError as _PkgNotFoundError
|
|
43
|
-
from importlib.metadata import version as _pkg_version
|
|
44
|
-
|
|
45
45
|
__version__ = _pkg_version("authsome")
|
|
46
46
|
except _PkgNotFoundError:
|
|
47
47
|
__version__ = "unknown"
|
|
@@ -66,6 +66,7 @@ def _serialize_event(event: AuditEvent) -> str:
|
|
|
66
66
|
return json.dumps(payload, separators=(",", ":"))
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
# TODO: Better to use an audit library: otel or something similar
|
|
69
70
|
def log(event_type: str, **kwargs: Any) -> None:
|
|
70
71
|
"""Append a structured server event to the configured log file."""
|
|
71
72
|
if _log_path is None:
|
|
@@ -78,6 +79,7 @@ def log(event_type: str, **kwargs: Any) -> None:
|
|
|
78
79
|
handle.write("\n")
|
|
79
80
|
|
|
80
81
|
|
|
82
|
+
# FIXME: Why is there a log and alog ?
|
|
81
83
|
async def alog(event_type: str, **kwargs: Any) -> None:
|
|
82
84
|
"""Async wrapper around structured server event logging."""
|
|
83
85
|
log(event_type, **kwargs)
|
|
@@ -7,6 +7,7 @@ from importlib.metadata import PackageNotFoundError, version
|
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
# TODO: This is generic package level function, move it there
|
|
10
11
|
def current_spec_version() -> int:
|
|
11
12
|
"""Return the config spec version derived from authsome's minor package version."""
|
|
12
13
|
try:
|
|
@@ -22,12 +23,14 @@ def current_spec_version() -> int:
|
|
|
22
23
|
return 0
|
|
23
24
|
|
|
24
25
|
|
|
26
|
+
# TODO: This isn't a property of auth module
|
|
25
27
|
class EncryptionConfig(BaseModel):
|
|
26
28
|
"""Vault encryption backend settings for the daemon."""
|
|
27
29
|
|
|
28
30
|
mode: str = "auto"
|
|
29
31
|
|
|
30
32
|
|
|
33
|
+
# TODO: This isn't a property of auth module
|
|
31
34
|
class ServerConfig(BaseModel):
|
|
32
35
|
"""Daemon-owned server configuration."""
|
|
33
36
|
|
|
@@ -15,7 +15,11 @@ from pydantic import BaseModel, Field
|
|
|
15
15
|
|
|
16
16
|
from authsome.auth.models.enums import AuthType, ConnectionStatus
|
|
17
17
|
|
|
18
|
+
# TODO: Auth module shouldn't worry about storage. Remove all key references
|
|
19
|
+
# TODO: Remvoe hardcoded schema versions everywhere
|
|
18
20
|
|
|
21
|
+
|
|
22
|
+
# TODO: Pydantic as secretstr, why not just use that?
|
|
19
23
|
class Sensitive:
|
|
20
24
|
"""Marker annotation: field contains a secret that must be redacted before display."""
|
|
21
25
|
|
|
@@ -73,8 +77,8 @@ class ProviderMetadataRecord(BaseModel):
|
|
|
73
77
|
Stored at key: vault:<vault_id>:<provider>:metadata
|
|
74
78
|
"""
|
|
75
79
|
|
|
76
|
-
schema_version: int = 2
|
|
77
|
-
identity: str | None = None
|
|
80
|
+
schema_version: int = 2 # TODO: Hardcoded schema version?
|
|
81
|
+
identity: str | None = None # TODO: Why is identity / principal_id / vault_id in provider metadata?
|
|
78
82
|
principal_id: str | None = None
|
|
79
83
|
vault_id: str | None = None
|
|
80
84
|
provider: str
|
|
@@ -34,7 +34,7 @@ class AuthSession(BaseModel):
|
|
|
34
34
|
session_id: str
|
|
35
35
|
provider: str
|
|
36
36
|
identity: str | None = None
|
|
37
|
-
principal_id: str | None = None
|
|
37
|
+
principal_id: str | None = None # TODO: Why is this optional ?
|
|
38
38
|
connection_name: str
|
|
39
39
|
flow_type: str
|
|
40
40
|
state: str = AuthSessionStatus.PENDING
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""Administrative CLI commands for authsome."""
|
|
2
|
+
|
|
3
|
+
import json as json_lib
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from authsome.cli.context import ContextObj
|
|
11
|
+
from authsome.cli.daemon_control import (
|
|
12
|
+
DaemonAlreadyRunningError,
|
|
13
|
+
DaemonUnavailableError,
|
|
14
|
+
daemon_status,
|
|
15
|
+
is_daemon_responsive,
|
|
16
|
+
is_port_occupied,
|
|
17
|
+
start_daemon,
|
|
18
|
+
stop_daemon,
|
|
19
|
+
wait_for_daemon_ready,
|
|
20
|
+
)
|
|
21
|
+
from authsome.cli.helpers import auth_command
|
|
22
|
+
from authsome.paths import get_client_log_path, get_server_log_path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@click.group(name="admin")
|
|
26
|
+
def admin() -> None:
|
|
27
|
+
"""Manage operator-facing daemon and maintenance commands."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@admin.command(name="log")
|
|
31
|
+
@click.option("-n", "--lines", default=50, metavar="COUNT", help="Number of entries to show.")
|
|
32
|
+
@click.option("--raw", is_flag=True, help="Show raw client debug log instead of structured audit entries.")
|
|
33
|
+
@auth_command
|
|
34
|
+
async def log_cmd(ctx_obj: ContextObj, lines: int, raw: bool) -> None:
|
|
35
|
+
"""View structured audit entries or the raw client debug log."""
|
|
36
|
+
home = Path(os.environ.get("AUTHSOME_HOME", str(Path.home() / ".authsome")))
|
|
37
|
+
|
|
38
|
+
if raw:
|
|
39
|
+
log_path = get_client_log_path(home)
|
|
40
|
+
try:
|
|
41
|
+
raw_lines = log_path.read_text(encoding="utf-8", errors="replace").splitlines()[-lines:]
|
|
42
|
+
except FileNotFoundError:
|
|
43
|
+
raw_lines = []
|
|
44
|
+
ctx_obj.print_json({"log_file": str(log_path), "entries": raw_lines})
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
audit_path = get_server_log_path(home)
|
|
48
|
+
try:
|
|
49
|
+
raw_lines = audit_path.read_text(encoding="utf-8", errors="replace").splitlines()[-lines:]
|
|
50
|
+
except FileNotFoundError:
|
|
51
|
+
raw_lines = []
|
|
52
|
+
|
|
53
|
+
parsed: list[dict] = []
|
|
54
|
+
for line in raw_lines:
|
|
55
|
+
line = line.strip()
|
|
56
|
+
if not line:
|
|
57
|
+
continue
|
|
58
|
+
try:
|
|
59
|
+
parsed.append(json_lib.loads(line))
|
|
60
|
+
except Exception:
|
|
61
|
+
parsed.append({"raw": line})
|
|
62
|
+
|
|
63
|
+
ctx_obj.print_json({"log_file": str(audit_path), "entries": parsed})
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@admin.group(name="daemon")
|
|
67
|
+
def daemon() -> None:
|
|
68
|
+
"""Manage the local Authsome daemon."""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@daemon.command(name="serve")
|
|
72
|
+
@click.option("--host", default="127.0.0.1", show_default=True, metavar="HOST", help="Host interface to bind.")
|
|
73
|
+
@click.option("--port", default=7998, type=int, show_default=True, metavar="PORT", help="TCP port to listen on.")
|
|
74
|
+
@click.option("--reload", is_flag=True, help="Enable auto-reload on code changes.")
|
|
75
|
+
def daemon_serve(host: str, port: int, reload: bool) -> None:
|
|
76
|
+
"""Run the daemon in the foreground."""
|
|
77
|
+
from authsome.server.daemon import serve
|
|
78
|
+
|
|
79
|
+
serve(host=host, port=port, reload=reload)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@daemon.command(name="start")
|
|
83
|
+
@auth_command
|
|
84
|
+
async def daemon_start(ctx_obj: ContextObj) -> None:
|
|
85
|
+
"""Start the local daemon in the background."""
|
|
86
|
+
if await is_daemon_responsive():
|
|
87
|
+
ctx_obj.print_json({"status": "already_running", "message": "Daemon is already running."})
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
if is_port_occupied(7998):
|
|
91
|
+
ctx_obj.print_json(
|
|
92
|
+
{
|
|
93
|
+
"status": "port_occupied",
|
|
94
|
+
"message": "Port 7998 is occupied by an unrelated process. We did not start a new process.",
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
start_daemon()
|
|
101
|
+
await wait_for_daemon_ready(timeout=5)
|
|
102
|
+
ctx_obj.print_json({"status": "started", "message": "Daemon started successfully."})
|
|
103
|
+
except DaemonAlreadyRunningError as exc:
|
|
104
|
+
pid_str = f" (PID: {exc.pid})" if exc.pid else ""
|
|
105
|
+
ctx_obj.print_json({"status": "already_running", "message": f"Daemon is already running{pid_str}."})
|
|
106
|
+
except DaemonUnavailableError as exc:
|
|
107
|
+
ctx_obj.print_json({"error": exc.__class__.__name__, "message": str(exc)})
|
|
108
|
+
sys.exit(1)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@daemon.command(name="stop")
|
|
112
|
+
@auth_command
|
|
113
|
+
async def daemon_stop(ctx_obj: ContextObj) -> None:
|
|
114
|
+
"""Stop the local daemon."""
|
|
115
|
+
stopped, message = await stop_daemon()
|
|
116
|
+
status = "stopped" if stopped else "not_stopped"
|
|
117
|
+
ctx_obj.print_json({"status": status, "message": message})
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@daemon.command(name="restart")
|
|
121
|
+
@auth_command
|
|
122
|
+
async def daemon_restart(ctx_obj: ContextObj) -> None:
|
|
123
|
+
"""Restart the local daemon."""
|
|
124
|
+
stopped, message = await stop_daemon()
|
|
125
|
+
|
|
126
|
+
if await is_daemon_responsive():
|
|
127
|
+
ctx_obj.print_json(
|
|
128
|
+
{
|
|
129
|
+
"status": "already_running",
|
|
130
|
+
"message": "Daemon is already running on port 7998. We did not start a new process.",
|
|
131
|
+
"stop_message": message,
|
|
132
|
+
"stopped": stopped,
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
if is_port_occupied(7998):
|
|
138
|
+
ctx_obj.print_json(
|
|
139
|
+
{
|
|
140
|
+
"status": "port_occupied",
|
|
141
|
+
"message": "Port 7998 is occupied by an unrelated process. We did not start a new process.",
|
|
142
|
+
"stop_message": message,
|
|
143
|
+
"stopped": stopped,
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
return
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
start_daemon()
|
|
150
|
+
await wait_for_daemon_ready(timeout=5)
|
|
151
|
+
ctx_obj.print_json(
|
|
152
|
+
{
|
|
153
|
+
"status": "restarted" if stopped else "started",
|
|
154
|
+
"message": "Daemon restarted successfully." if stopped else "New daemon started.",
|
|
155
|
+
"stop_message": message,
|
|
156
|
+
"stopped": stopped,
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
except DaemonAlreadyRunningError as exc:
|
|
160
|
+
pid_str = f" (PID: {exc.pid})" if exc.pid else ""
|
|
161
|
+
ctx_obj.print_json(
|
|
162
|
+
{
|
|
163
|
+
"status": "already_running",
|
|
164
|
+
"message": f"Daemon is already running{pid_str}.",
|
|
165
|
+
"stop_message": message,
|
|
166
|
+
"stopped": stopped,
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
except DaemonUnavailableError as exc:
|
|
170
|
+
ctx_obj.print_json({"error": exc.__class__.__name__, "message": str(exc)})
|
|
171
|
+
sys.exit(1)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@daemon.command(name="status")
|
|
175
|
+
@auth_command
|
|
176
|
+
async def daemon_status_cmd(ctx_obj: ContextObj) -> None:
|
|
177
|
+
"""Show daemon status."""
|
|
178
|
+
status = await daemon_status()
|
|
179
|
+
ctx_obj.print_json(status)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@daemon.command(name="logs")
|
|
183
|
+
@click.option("-n", "--lines", default=80, metavar="COUNT", help="Number of lines to show.")
|
|
184
|
+
@auth_command
|
|
185
|
+
async def daemon_logs(ctx_obj: ContextObj, lines: int) -> None:
|
|
186
|
+
"""Show daemon log output."""
|
|
187
|
+
from authsome.cli.daemon_control import LOG_FILE
|
|
188
|
+
|
|
189
|
+
if not LOG_FILE.exists():
|
|
190
|
+
ctx_obj.print_json({"log_file": str(LOG_FILE), "entries": []})
|
|
191
|
+
return
|
|
192
|
+
entries = LOG_FILE.read_text(encoding="utf-8", errors="replace").splitlines()[-lines:]
|
|
193
|
+
ctx_obj.print_json({"log_file": str(LOG_FILE), "entries": entries})
|
|
@@ -14,12 +14,12 @@ from urllib.parse import urlparse
|
|
|
14
14
|
import requests
|
|
15
15
|
|
|
16
16
|
from authsome.identity import (
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
IdentitySource,
|
|
18
|
+
load_runtime_identity,
|
|
19
19
|
mark_claimed,
|
|
20
20
|
mark_registered,
|
|
21
21
|
)
|
|
22
|
-
from authsome.identity.local import
|
|
22
|
+
from authsome.identity.local import RuntimeIdentity, load_identity
|
|
23
23
|
from authsome.identity.proof import POP_AUTH_SCHEME, create_proof_jwt
|
|
24
24
|
from authsome.server.urls import DEFAULT_SERVER_BASE_URL
|
|
25
25
|
|
|
@@ -77,17 +77,6 @@ def raise_for_error(response: requests.Response) -> None:
|
|
|
77
77
|
raise exc
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
def _selected_identity_handle(home: Path, env: Mapping[str, str] | None = None) -> str | None:
|
|
81
|
-
"""Return the acting identity override, falling back to client config."""
|
|
82
|
-
from authsome.cli.client_config import load_client_config
|
|
83
|
-
|
|
84
|
-
values = env if env is not None else os.environ
|
|
85
|
-
override = values.get(IDENTITY_OVERRIDE_ENV, "").strip()
|
|
86
|
-
if override:
|
|
87
|
-
return override
|
|
88
|
-
return load_client_config(home).active_identity
|
|
89
|
-
|
|
90
|
-
|
|
91
80
|
class AuthsomeApiClient:
|
|
92
81
|
"""Small typed wrapper around the daemon API."""
|
|
93
82
|
|
|
@@ -126,11 +115,16 @@ class AuthsomeApiClient:
|
|
|
126
115
|
raise_for_error(response)
|
|
127
116
|
return response.json()
|
|
128
117
|
|
|
118
|
+
def _runtime_identity(self) -> RuntimeIdentity:
|
|
119
|
+
return load_runtime_identity(self._home)
|
|
120
|
+
|
|
121
|
+
def _runtime_for_handle(self, handle: str) -> RuntimeIdentity:
|
|
122
|
+
return load_runtime_identity(self._home, env={IDENTITY_OVERRIDE_ENV: handle})
|
|
123
|
+
|
|
129
124
|
async def _proof_headers(self, method: str, path: str, body: bytes) -> dict[str, str | bytes]:
|
|
130
125
|
identity = await self.ensure_identity_ready()
|
|
131
|
-
private_key = load_private_key(self._home, identity.handle)
|
|
132
126
|
token = create_proof_jwt(
|
|
133
|
-
private_key=
|
|
127
|
+
private_key=identity.signer,
|
|
134
128
|
issuer=identity.did,
|
|
135
129
|
subject=identity.handle,
|
|
136
130
|
method=method,
|
|
@@ -139,16 +133,19 @@ class AuthsomeApiClient:
|
|
|
139
133
|
)
|
|
140
134
|
return {"Authorization": f"{POP_AUTH_SCHEME} {token}"}
|
|
141
135
|
|
|
142
|
-
async def ensure_identity_ready(self) ->
|
|
136
|
+
async def ensure_identity_ready(self) -> RuntimeIdentity:
|
|
143
137
|
"""Ensure the acting identity is registered and, in hosted mode, claimed."""
|
|
144
|
-
|
|
138
|
+
runtime = self._runtime_identity()
|
|
139
|
+
if runtime.source is IdentitySource.ENV:
|
|
140
|
+
return await self._ensure_env_identity_ready(runtime)
|
|
145
141
|
|
|
146
142
|
status: dict[str, Any] | None = None
|
|
143
|
+
identity = load_identity(self._home, runtime.handle)
|
|
147
144
|
if not identity.registered:
|
|
148
145
|
status = await self.register_identity(identity.handle, identity.did)
|
|
149
146
|
identity = mark_registered(self._home, identity.handle)
|
|
150
147
|
elif identity.claimed:
|
|
151
|
-
return
|
|
148
|
+
return runtime
|
|
152
149
|
else:
|
|
153
150
|
try:
|
|
154
151
|
status = await self.get_identity_status(identity.handle)
|
|
@@ -167,11 +164,33 @@ class AuthsomeApiClient:
|
|
|
167
164
|
except Exception:
|
|
168
165
|
pass
|
|
169
166
|
await self._poll_claim_completion(identity.handle)
|
|
170
|
-
|
|
167
|
+
identity = mark_claimed(self._home, identity.handle)
|
|
168
|
+
return self._runtime_for_handle(identity.handle)
|
|
171
169
|
|
|
172
170
|
if registration_status in {"claimed", "registered"}:
|
|
173
|
-
|
|
171
|
+
identity = mark_claimed(self._home, identity.handle)
|
|
172
|
+
return self._runtime_for_handle(identity.handle)
|
|
174
173
|
|
|
174
|
+
return self._runtime_for_handle(identity.handle)
|
|
175
|
+
|
|
176
|
+
async def _ensure_env_identity_ready(self, identity: RuntimeIdentity) -> RuntimeIdentity:
|
|
177
|
+
try:
|
|
178
|
+
status = await self.get_identity_status(identity.handle)
|
|
179
|
+
except Exception:
|
|
180
|
+
status = await self.register_identity(identity.handle, identity.did)
|
|
181
|
+
|
|
182
|
+
registration_status = status.get("registration_status", "registered")
|
|
183
|
+
if registration_status == "claim_required":
|
|
184
|
+
claim_url = status.get("claim_url")
|
|
185
|
+
if not claim_url:
|
|
186
|
+
status = await self.register_identity(identity.handle, identity.did)
|
|
187
|
+
claim_url = status.get("claim_url")
|
|
188
|
+
if claim_url:
|
|
189
|
+
try:
|
|
190
|
+
webbrowser.open(claim_url)
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
193
|
+
await self._poll_claim_completion(identity.handle)
|
|
175
194
|
return identity
|
|
176
195
|
|
|
177
196
|
async def _poll_claim_completion(self, handle: str, *, timeout_seconds: int = 300) -> dict[str, Any]:
|
|
@@ -264,9 +283,6 @@ class AuthsomeApiClient:
|
|
|
264
283
|
async def whoami(self) -> dict[str, Any]:
|
|
265
284
|
return await self._get("/whoami")
|
|
266
285
|
|
|
267
|
-
async def rekey(self) -> dict[str, Any]:
|
|
268
|
-
return await self._post("/rekey", {})
|
|
269
|
-
|
|
270
286
|
async def doctor(self) -> dict[str, Any]:
|
|
271
287
|
return await self.ready()
|
|
272
288
|
|
|
@@ -54,33 +54,30 @@ class ContextObj:
|
|
|
54
54
|
return
|
|
55
55
|
if self.no_color:
|
|
56
56
|
color = None
|
|
57
|
-
click.secho(message, err=err, fg=color, nl=nl)
|
|
57
|
+
click.secho(message, err=err or self.json_output, fg=color, nl=nl)
|
|
58
58
|
|
|
59
59
|
def emit(self, message: str, color: str | None = None, nl: bool = True) -> None:
|
|
60
60
|
"""Print primary data output. Never suppressed by --quiet; respects --no-color."""
|
|
61
61
|
if self.no_color:
|
|
62
62
|
color = None
|
|
63
|
-
click.secho(message, fg=color, nl=nl)
|
|
63
|
+
click.secho(message, err=self.json_output, fg=color, nl=nl)
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
pass_ctx = click.make_pass_decorator(ContextObj)
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
def common_options(f):
|
|
70
|
-
@click.option("--json", "json_output", is_flag=True, help="Output in machine-readable JSON format.")
|
|
71
70
|
@click.option("--quiet", is_flag=True, help="Suppress non-essential output.")
|
|
72
71
|
@click.option("--no-color", is_flag=True, help="Disable ANSI colors.")
|
|
73
72
|
@functools.wraps(f)
|
|
74
73
|
def wrapper(*args, **kwargs):
|
|
75
|
-
json_output = kwargs.pop("json_output", False)
|
|
76
74
|
quiet = kwargs.pop("quiet", False)
|
|
77
75
|
no_color = kwargs.pop("no_color", False)
|
|
78
76
|
ctx = click.get_current_context()
|
|
79
77
|
if getattr(ctx, "obj", None) is None:
|
|
80
|
-
ctx.obj = ContextObj(
|
|
78
|
+
ctx.obj = ContextObj(True, quiet, no_color)
|
|
81
79
|
else:
|
|
82
|
-
|
|
83
|
-
ctx.obj.json_output = True
|
|
80
|
+
ctx.obj.json_output = True
|
|
84
81
|
if quiet:
|
|
85
82
|
ctx.obj.quiet = True
|
|
86
83
|
if no_color:
|
|
@@ -99,7 +99,7 @@ def start_daemon() -> None:
|
|
|
99
99
|
DAEMON_DIR.mkdir(parents=True, exist_ok=True)
|
|
100
100
|
log = LOG_FILE.open("ab")
|
|
101
101
|
process = subprocess.Popen(
|
|
102
|
-
[sys.executable, "-m", "authsome.cli.main", "daemon", "serve"],
|
|
102
|
+
[sys.executable, "-m", "authsome.cli.main", "admin", "daemon", "serve"],
|
|
103
103
|
stdout=log,
|
|
104
104
|
stderr=log,
|
|
105
105
|
start_new_session=True,
|
|
@@ -17,7 +17,7 @@ from authsome.utils import format_error_code
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def handle_errors(func):
|
|
20
|
-
"""Catch exceptions and
|
|
20
|
+
"""Catch exceptions and return structured JSON errors."""
|
|
21
21
|
|
|
22
22
|
@functools.wraps(func)
|
|
23
23
|
def wrapper(ctx_obj: ContextObj, *args, **kwargs):
|
|
@@ -29,10 +29,7 @@ def handle_errors(func):
|
|
|
29
29
|
return asyncio.run(func(ctx_obj, *args, **kwargs))
|
|
30
30
|
return func(ctx_obj, *args, **kwargs)
|
|
31
31
|
except Exception as exc:
|
|
32
|
-
|
|
33
|
-
ctx_obj.print_json({"error": exc.__class__.__name__, "message": str(exc)})
|
|
34
|
-
else:
|
|
35
|
-
ctx_obj.echo(f"Error: {exc}", err=True, color="red")
|
|
32
|
+
ctx_obj.print_json({"error": exc.__class__.__name__, "message": str(exc)})
|
|
36
33
|
sys.exit(format_error_code(exc))
|
|
37
34
|
|
|
38
35
|
return wrapper
|
|
@@ -65,7 +62,7 @@ def setup_logging(verbose: bool, log_file: Path | None) -> None:
|
|
|
65
62
|
)
|
|
66
63
|
|
|
67
64
|
|
|
68
|
-
def _validate_provider_endpoints(definition: Any
|
|
65
|
+
def _validate_provider_endpoints(definition: Any) -> list[tuple[str, str, bool]]:
|
|
69
66
|
"""Extract and validate provider endpoints for security."""
|
|
70
67
|
endpoints_to_check: list[tuple[str, str, bool]] = []
|
|
71
68
|
if definition.oauth:
|
|
@@ -86,21 +83,18 @@ def _validate_provider_endpoints(definition: Any, ctx_obj: ContextObj) -> list[t
|
|
|
86
83
|
if "://" in val:
|
|
87
84
|
parsed = urllib.parse.urlparse(val)
|
|
88
85
|
if parsed.scheme != "https":
|
|
89
|
-
|
|
90
|
-
sys.exit(1)
|
|
86
|
+
raise click.ClickException(f"{name} must use HTTPS scheme ({val})")
|
|
91
87
|
host = parsed.hostname
|
|
92
88
|
else:
|
|
93
89
|
host = val
|
|
94
90
|
|
|
95
91
|
if host in ("localhost", "127.0.0.1", "::1"):
|
|
96
|
-
|
|
97
|
-
sys.exit(1)
|
|
92
|
+
raise click.ClickException(f"{name} cannot be localhost ({val})")
|
|
98
93
|
|
|
99
94
|
if host:
|
|
100
95
|
try:
|
|
101
96
|
ipaddress.ip_address(host)
|
|
102
|
-
|
|
103
|
-
sys.exit(1)
|
|
97
|
+
raise click.ClickException(f"{name} cannot be a bare IP address ({val})")
|
|
104
98
|
except ValueError:
|
|
105
99
|
pass
|
|
106
100
|
|