capauth 0.3.12__tar.gz → 0.3.13__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.
- {capauth-0.3.12 → capauth-0.3.13}/.github/workflows/ci.yml +5 -0
- capauth-0.3.13/.github/workflows/image.yml +90 -0
- {capauth-0.3.12 → capauth-0.3.13}/CHANGELOG.md +31 -0
- {capauth-0.3.12 → capauth-0.3.13}/Dockerfile +4 -0
- {capauth-0.3.12/src/capauth.egg-info → capauth-0.3.13}/PKG-INFO +3 -1
- {capauth-0.3.12 → capauth-0.3.13}/README.md +2 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/CAPAUTH_OIDC_IDP.md +8 -1
- capauth-0.3.13/docs/SIGN_CHALLENGE.md +80 -0
- {capauth-0.3.12 → capauth-0.3.13}/scripts/capauth-backup.sh +10 -2
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/cli.py +41 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/clients.py +9 -1
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/provider.py +39 -11
- capauth-0.3.13/src/capauth/sign_challenge.py +192 -0
- {capauth-0.3.12 → capauth-0.3.13/src/capauth.egg-info}/PKG-INFO +3 -1
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth.egg-info/SOURCES.txt +7 -0
- capauth-0.3.13/src/capauth.egg-info/scm_file_list.json +408 -0
- capauth-0.3.13/tests/test_backup_fallback.py +66 -0
- capauth-0.3.13/tests/test_dockerfile_version.py +17 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_integration.py +4 -1
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_oidc_hardening.py +92 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_oidc_idp.py +30 -0
- capauth-0.3.13/tests/test_sign_challenge.py +229 -0
- {capauth-0.3.12 → capauth-0.3.13}/urllib.error +1 -1
- {capauth-0.3.12 → capauth-0.3.13}/.cursorrules +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.dockerignore +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.github/workflows/docs-check.yml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.github/workflows/nextcloud-appstore.yml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.github/workflows/publish.yml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.github/workflows/pytest.yml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.github/workflows/secret-scan.yml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.gitignore +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.gitleaks-baseline.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/.gitleaks.toml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/AGENTS.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/AI-ADVOCATE.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/ARCHITECTURE.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/CLAUDE.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/CODE_OF_CONDUCT.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/CONTRIBUTING.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/Dockerfile.authentik-capauth +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/LICENSE +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/MANIFEST.in +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/MISSION.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/SECURITY.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/SKILL.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/SOP.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/authentik-custom/capauth_migrate.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/authentik-custom/imghdr.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/authentik-custom/user_settings.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/bin/cli.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/.gitignore +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/README.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/RELEASING.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/background.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/content_scripts/autofill.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/content_scripts/cross_origin_signing.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/content_scripts/detector.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/content_scripts/provider.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/content_scripts/provider_bridge.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/icons/icon.svg +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/icons/icon128.png +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/icons/icon16.png +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/icons/icon48.png +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/lib/bunker-e2e.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/lib/keyqr.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/lib/keyvault.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/lib/openpgp.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/lib/signer-backends.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/manifest.firefox.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/manifest.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/native-host/capauth_signer.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/native-host/com.capauth.signer.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/native-host/install.sh +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/options/options.html +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/options/options.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/package-lock.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/package.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/popup/popup.css +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/popup/popup.html +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/popup/popup.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/scripts/build.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/scripts/generate-icons.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/stages/AuthenticatorSelectionStage.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/stages/CapAuthStage.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/stages/QRDisplayStage.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/stages/SignatureVerificationStage.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/stages/StagePipeline.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/submission/STORE_LISTING.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/submission/privacy-policy.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/fixtures/test_key.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/background.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/bunker_e2e.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/detector.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/keyqr.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/keyvault.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/openpgp.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/provider_api.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/provider_tierb.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/remote_bunker.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/tests/unit/signer_backends.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/browser-extension/vitest.config.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/deploy/capauth-service/.env.example +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/deploy/capauth-service/deploy.sh +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/deploy/capauth-service/docker-compose.yml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/deploy/capauth-service/systemd/capauth-backup.service +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/deploy/capauth-service/systemd/capauth-backup.timer +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/deploy/forgejo-capauth/.env.example +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/deploy/forgejo-capauth/docker-compose.yml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/deploy/forgejo-capauth/preflight.sh +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/AIRGAPPED_SIGNING_WORKSTATION.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/ARCHITECTURE.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/AUTHENTIK_CUSTOM_STAGE.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/AUTHENTIK_DEPLOYMENT_SKSSO.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/AUTHENTIK_FORGEJO_DEPLOYMENT.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/AUTHENTIK_STRATEGY_DECISION.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/CAPAUTH-WEBAUTHN-05-QUALIFICATION.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/CAPAUTH_BUNKER_REMOTE_SIGNER.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/CLAIMS.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/COLD_MACHINE_BOOTSTRAP_AND_DR.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/CRYPTO_SPEC.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/DELEGATED_CAPABILITIES.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/DOWNSTREAM_DURABLE_BACKENDS.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/ENTERPRISE_MANAGED_KEYS.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/HUMAN_IDENTITY_SETUP_AND_ROTATION.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/IDENTITY_ESTATE.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/INTEGRATION_BLUEPRINT.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/M0-capauth-api-v1.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/M0-capauth-importer-inventory.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/PASSPHRASE_ROTATION.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/PQC_CAPAUTH_REVISIT.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/PQC_ROOT_MIGRATION.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/PROTOCOL.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/ROOT_ROTATION_CEREMONY.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/SKDASHBOARD_CONTROL_PLANE_POLICY.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/TRUST_GRAPH_COORD_PROJECTION.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/ZERO_KNOWLEDGE_PROFILE.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/audits/BLAST-RADIUS-CAPAUTH-01.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/audits/BLAST-RADIUS-CAPAUTH-01.sha256 +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/authentik-capauth.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/deploy-plan/capauth-bulletproof-deploy.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/evidence/capauth/SKL-CAPAUTH-SIGNER-P3P-MUTATION-ENVELOPE-2026-08-25.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/docs/evidence/capauth/SKL-CAPAUTH-SIGNER-P3P-ROSTER-2026-08-25.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/index.d.ts +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/index.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/openclaw-plugin.archived-2026-04-23/package.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/openclaw-plugin.archived-2026-04-23/src/index.ts +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/openclaw-plugin.archived-2026-04-23/src/openclaw.plugin.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/package.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/app.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/icons/icon-192.png +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/icons/icon-512.png +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/index.html +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/lib/bunker-e2e.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/lib/bunker-signer.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/lib/canonical.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/lib/keyqr.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/lib/keyvault.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/manifest.webmanifest +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/sw.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/vendor/openpgp.min.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/phone-signer/vendor/qrcode-generator.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/pyproject.toml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/ruff-0.15.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/scripts/build_authentik_capauth_image.sh +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/scripts/capauth-restore.sh +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/scripts/gitleaks-scope.sh +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/scripts/migrate_canonical_subjects.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/scripts/pqc_ceremony_dryrun.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/scripts/provision_agent_profiles.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/setup.cfg +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/skill.yaml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/agent_identity.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/apps.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authentik/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authentik/api.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authentik/blueprints/capauth-pgp-login.yaml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authentik/claims_mapper.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authentik/nonce_store.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authentik/stage.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authentik/urls.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authentik/verifier.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/authz.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/control_plane.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/control_plane_authorizer.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/control_plane_session.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/crypto/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/crypto/base.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/crypto/gnupg_backend.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/crypto/pgpy_backend.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/crypto/sequoia_backend.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/crypto/skpgp_backend.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/custody.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/delegated.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/did.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/discovery/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/discovery/base.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/discovery/file_discovery.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/discovery/mdns.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/discovery/syncthing.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/estate.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/exceptions.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/identity.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/identity_class.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integration.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/auth_flow.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/cli.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/config.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/forgejo_api.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/oidc_provider.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/tests/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/tests/test_auth_flow.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/tests/test_cli.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/tests/test_config.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/tests/test_forgejo_api.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/forgejo/tests/test_oidc_provider.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/.gitignore +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/.nextcloudignore +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/CSR-PR.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/INFO-XML-CHANGES.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/PUBLISHING.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/README.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/appinfo/info.xml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/appinfo/routes.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/build-release.sh +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/certificates/.gitignore +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/certificates/capauth.crt +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/certificates/capauth.csr +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/composer.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/css/login.css +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/js/login.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/krankerl.toml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/AlternativeLogin/CapAuthLogin.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/AppInfo/Application.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/BackgroundJob/PruneExpiredNonces.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Controller/LoginController.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Controller/TokenController.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Db/KeyRegistry.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Exception/CapAuthUnauthorizedException.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Middleware/PgpVerificationMiddleware.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Migration/Version000300Date20260301000000.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Provider/CapAuthProvider.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Service/ChallengeService.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Service/GroupSyncService.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Service/UserProvisioningService.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/Service/VerifierService.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/lib/User/Backend.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/phpunit.xml +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/screenshots/README.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/screenshots/login.png +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/templates/login.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/bootstrap.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/integration/FullAuthFlowTest.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/oidc/oidc_e2e_login_test.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/unit/Controller/LoginControllerTest.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/unit/Db/KeyRegistryTest.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/unit/Provider/CapAuthProviderTest.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/unit/Service/ChallengeServiceTest.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/unit/Service/VerifierServiceTest.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/integrations/nextcloud/tests/unit/User/BackendTest.php +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/key_transparency.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/login.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/manifest.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/mesh.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/migrations/0001_initial.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/migrations/0002_widen_fingerprint_to_64.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/migrations/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/models.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pairing/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pairing/canonicalize.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pairing/kernel.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pairing/operator_session.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pairing/proof.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pairing/records.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pairing/store.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pairing/window.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pma.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pqc_confidentiality.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pqc_identity.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pqc_root_identity.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/pqc_tokens.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/profile.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/provisioning.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/registry.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/rotation.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/seal.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/app.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/bunker.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/bunker_e2e.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/keystore.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/clients.example.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/passkey.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/signing_key.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/static/bunker/lib/bunker-e2e.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/static/bunker/lib/keyvault.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/static/bunker/vendor/openpgp.min.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/oidc/store.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/push.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/service/server.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/subject.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/sync.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/testing.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/tokens.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/trust/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/trust/calibration.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth/trust/graph.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth.egg-info/dependency_links.txt +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth.egg-info/entry_points.txt +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth.egg-info/requires.txt +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/src/capauth.egg-info/top_level.txt +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/stages/ak-stage-capauth/.gitignore +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/stages/ak-stage-capauth/package-lock.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/stages/ak-stage-capauth/package.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/stages/ak-stage-capauth/src/CapAuthStage.test.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/stages/ak-stage-capauth/src/CapAuthStage.vue +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/stages/ak-stage-capauth/src/index.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/stages/ak-stage-capauth/vite.config.js +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/__init__.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/conftest.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/fixtures/bunker_e2e_v1_vector.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/fixtures/canonical_nonce_v2_vector.json +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_agent_identity.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_agentrun_capabilities.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_audience_mint_helper.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_audience_tokens.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_audience_ttl_seconds.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_authz.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_authz_legacy_grace.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_authz_signature_gate.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_backup_restore.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_bunker.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_bunker_e2e.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_canonicalize.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_capauth_service_deploy.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_change_capabilities.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_claims_mapper.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_control_plane.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_control_plane_authorizer.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_control_plane_capabilities.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_control_plane_session.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_crypto.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_custody.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_delegated_capabilities.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_device_key_proof.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_did_capability_relationships.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_enrollment_proof_helper.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_estate.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_fingerprint_v6.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_forgejo_preflight.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_home_resolution.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_identity.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_identity_class.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_identity_ttl.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_installed_bunker_assets.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_key_revocation_expiry.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_key_transparency.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_login.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_manifest.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_mesh.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_models_pqc_q0.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_nextcloud_app.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_no_subapp_dependency.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_nonce_store.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_oidc_durable_store.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_oidc_idp_e2e.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_oidc_refresh_family.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_operator_session.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_pairing.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_pairing_base_dir_env.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_passkey.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_passkey_enrollment_ux.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_pma.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_pqc_confidentiality.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_pqc_identity.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_pqc_root_integration.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_pqc_t3_gate.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_pqc_tokens.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_profile.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_provisioning.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_push.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_py313_compat.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_qr_login.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_real_pgp_e2e.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_register_cli.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_registry.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_rotation.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_seal.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_sequoia_backend.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_service.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_service_authz_decide.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_service_v6_fingerprint.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_skpgp_backend.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_subject.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_sync.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_syncthing_discovery.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_testing_helper.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_token_gc.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_token_store_flag.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_tokens.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_trust_calibration.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_trust_graph.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_trust_graph_coord_audit.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_verifier.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_version_honesty.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tests/test_zk_profile.py +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/tools/build-sq.sh +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/web/README.md +0 -0
- {capauth-0.3.12 → capauth-0.3.13}/web/stages/capauth/CapAuthStage.ts +0 -0
|
@@ -37,11 +37,16 @@ jobs:
|
|
|
37
37
|
# `import skcapstone` succeeds without it while
|
|
38
38
|
# `from skcapstone.coordination import Board` does not. --no-deps means we
|
|
39
39
|
# have to name it ourselves.
|
|
40
|
+
# skcapstone requires Python 3.11+, capauth supports 3.10: on 3.10 the
|
|
41
|
+
# integration tests skip (pytest.importorskip); on 3.11+ the import is
|
|
42
|
+
# asserted here so those tests can never skip silently.
|
|
40
43
|
- name: Install skcapstone (optional integration backbone) from source
|
|
44
|
+
if: matrix.python-version != '3.10'
|
|
41
45
|
run: |
|
|
42
46
|
pip install --no-deps --no-cache-dir \
|
|
43
47
|
"skcapstone @ git+https://github.com/smilinTux/skcapstone" \
|
|
44
48
|
"skcoord @ git+https://github.com/smilinTux/skcoord"
|
|
49
|
+
python -c "from skcapstone.scheduler_jobs import load_jobs_with_dropins"
|
|
45
50
|
- name: Run tests with coverage
|
|
46
51
|
run: python -m pytest tests/ -v --tb=short --cov=capauth --cov-report=xml --cov-report=term-missing
|
|
47
52
|
- name: Upload coverage
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
name: Container image
|
|
2
|
+
|
|
3
|
+
# Publishes the CapAuth service image (the OIDC IdP and verify service) for a
|
|
4
|
+
# release tag as ghcr.io/smilintux/capauth:<version>, and records its digest.
|
|
5
|
+
# Consumers pin <version>@sha256:<digest> (SKStacks sksso.CAPAUTH_IMAGE refuses
|
|
6
|
+
# anything else). A published version is never overwritten.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
tags: ["v*"]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
inputs:
|
|
12
|
+
tag:
|
|
13
|
+
description: "Existing release tag to (re)build if its image is missing, e.g. v0.3.13"
|
|
14
|
+
required: true
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
packages: write
|
|
19
|
+
|
|
20
|
+
concurrency:
|
|
21
|
+
group: image-${{ inputs.tag || github.ref }}
|
|
22
|
+
cancel-in-progress: false
|
|
23
|
+
|
|
24
|
+
env:
|
|
25
|
+
IMAGE: ghcr.io/smilintux/capauth
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
image:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
with:
|
|
33
|
+
ref: ${{ inputs.tag || github.ref }}
|
|
34
|
+
fetch-depth: 0
|
|
35
|
+
fetch-tags: true
|
|
36
|
+
- name: Resolve the version and refuse a tag that is not on main
|
|
37
|
+
id: v
|
|
38
|
+
env:
|
|
39
|
+
TAG: ${{ inputs.tag || github.ref_name }}
|
|
40
|
+
run: |
|
|
41
|
+
set -euo pipefail
|
|
42
|
+
case "$TAG" in v[0-9]*.[0-9]*.[0-9]*) ;; *) echo "::error::not a release tag: $TAG"; exit 1 ;; esac
|
|
43
|
+
version="${TAG#v}"
|
|
44
|
+
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
45
|
+
echo "::error::not a release version: $version"; exit 1
|
|
46
|
+
fi
|
|
47
|
+
git fetch --no-tags origin main:refs/remotes/origin/main
|
|
48
|
+
sha="$(git rev-parse "refs/tags/$TAG^{commit}")"
|
|
49
|
+
git merge-base --is-ancestor "$sha" refs/remotes/origin/main || {
|
|
50
|
+
echo "::error::$TAG ($sha) is not on main"; exit 1; }
|
|
51
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
52
|
+
echo "sha=$sha" >> "$GITHUB_OUTPUT"
|
|
53
|
+
- name: Log in to ghcr.io
|
|
54
|
+
env:
|
|
55
|
+
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
56
|
+
run: echo "$TOKEN" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
|
|
57
|
+
- name: Refuse to overwrite a published version
|
|
58
|
+
run: |
|
|
59
|
+
if docker manifest inspect "$IMAGE:${{ steps.v.outputs.version }}" >/dev/null 2>&1; then
|
|
60
|
+
echo "::error::$IMAGE:${{ steps.v.outputs.version }} already exists; versions are immutable"
|
|
61
|
+
exit 1
|
|
62
|
+
fi
|
|
63
|
+
- name: Build
|
|
64
|
+
run: |
|
|
65
|
+
docker build \
|
|
66
|
+
--build-arg CAPAUTH_VERSION="${{ steps.v.outputs.version }}" \
|
|
67
|
+
--label org.opencontainers.image.source="https://github.com/${{ github.repository }}" \
|
|
68
|
+
--label org.opencontainers.image.revision="${{ steps.v.outputs.sha }}" \
|
|
69
|
+
--label org.opencontainers.image.version="${{ steps.v.outputs.version }}" \
|
|
70
|
+
-t "$IMAGE:${{ steps.v.outputs.version }}" .
|
|
71
|
+
- name: Smoke test (version, service answers)
|
|
72
|
+
run: |
|
|
73
|
+
set -euo pipefail
|
|
74
|
+
v="${{ steps.v.outputs.version }}"
|
|
75
|
+
docker run --rm "$IMAGE:$v" capauth --version | tee /dev/stderr | grep -F "$v"
|
|
76
|
+
docker run -d --name smoke -p 18420:8420 "$IMAGE:$v"
|
|
77
|
+
for i in $(seq 1 30); do
|
|
78
|
+
curl -fsS http://127.0.0.1:18420/capauth/v1/status >/dev/null && break
|
|
79
|
+
sleep 2
|
|
80
|
+
done
|
|
81
|
+
curl -fsS http://127.0.0.1:18420/capauth/v1/status >/dev/null
|
|
82
|
+
docker rm -f smoke
|
|
83
|
+
- name: Push and record the digest
|
|
84
|
+
run: |
|
|
85
|
+
set -euo pipefail
|
|
86
|
+
v="${{ steps.v.outputs.version }}"
|
|
87
|
+
docker push "$IMAGE:$v"
|
|
88
|
+
ref="$(docker inspect --format '{{index .RepoDigests 0}}' "$IMAGE:$v")"
|
|
89
|
+
echo "Pinned reference: \`$IMAGE:$v@${ref#*@}\`" >> "$GITHUB_STEP_SUMMARY"
|
|
90
|
+
echo "pinned: $IMAGE:$v@${ref#*@}"
|
|
@@ -6,6 +6,37 @@ All notable changes to `capauth` are documented here. The format is based on
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
- Release tags (`v*`) now also publish the service image
|
|
10
|
+
`ghcr.io/smilintux/capauth:<version>` (`.github/workflows/image.yml`): the
|
|
11
|
+
tag must be on main, a published version is never overwritten, the image is
|
|
12
|
+
smoke-tested (`capauth --version`, `/capauth/v1/status`) before the push,
|
|
13
|
+
and the job summary records the digest to pin (`<version>@sha256:...`).
|
|
14
|
+
|
|
15
|
+
- `capauth sign-challenge`: reads the CapAuth login challenge from the
|
|
16
|
+
clipboard (or `--stdin`), refuses anything that is not a complete
|
|
17
|
+
`CAPAUTH_NONCE_V1`/`V2` challenge, signs it with gpg (gpg-agent's pinentry
|
|
18
|
+
asks for the passphrase; the tool never handles it) and puts the armored
|
|
19
|
+
signature back on the clipboard (or `--stdout`), printing only
|
|
20
|
+
`signed, paste it now`. Key: `--key`, `$CAPAUTH_SIGN_KEY`, or the profile.
|
|
21
|
+
The OIDC login page gains a "Paste signature and continue" button, names
|
|
22
|
+
the command in its steps and remembers the fingerprint of the last manual
|
|
23
|
+
login, so a repeat login is copy, sign, paste. Docs: `docs/SIGN_CHALLENGE.md`.
|
|
24
|
+
|
|
25
|
+
- Service image builds from a plain checkout again: the Dockerfile takes
|
|
26
|
+
`--build-arg CAPAUTH_VERSION=<version>` (default `0.0.0+docker`) for
|
|
27
|
+
setuptools-scm, because `.dockerignore` keeps `.git` out of the context.
|
|
28
|
+
|
|
29
|
+
- OIDC IdP: a static client may set `require_nonce: false` so stock
|
|
30
|
+
Authentik's OAuth source, which never sends an OIDC `nonce`, can use the
|
|
31
|
+
authorization code flow. State, PKCE S256 and the confidential client secret
|
|
32
|
+
stay mandatory; the default is unchanged (nonce required), and a nonce that
|
|
33
|
+
is sent must still be well formed.
|
|
34
|
+
|
|
35
|
+
- CI: green on main again. `ruff format` (pinned 0.15.4) applied to
|
|
36
|
+
`tests/test_backup_fallback.py`; the Python 3.10 job no longer installs
|
|
37
|
+
skcapstone (it requires 3.11+), and the skcapstone integration tests skip
|
|
38
|
+
there while 3.11+ asserts the import so they cannot skip silently.
|
|
39
|
+
|
|
9
40
|
- Raise operator proof absolute and idle ceilings to 24 hours while keeping
|
|
10
41
|
access capabilities bounded to five minutes (card `4af0ae7f`).
|
|
11
42
|
|
|
@@ -16,6 +16,10 @@ COPY src/ ./src/
|
|
|
16
16
|
# resolves these relative to the package root → /app/phone-signer.
|
|
17
17
|
COPY phone-signer/ ./phone-signer/
|
|
18
18
|
|
|
19
|
+
# The build context has no .git (.dockerignore), so setuptools-scm cannot
|
|
20
|
+
# derive a version: pass one with --build-arg CAPAUTH_VERSION=<version>.
|
|
21
|
+
ARG CAPAUTH_VERSION=0.0.0+docker
|
|
22
|
+
ENV SETUPTOOLS_SCM_PRETEND_VERSION=${CAPAUTH_VERSION}
|
|
19
23
|
RUN pip install --no-cache-dir -e ".[service]"
|
|
20
24
|
RUN pip install --no-cache-dir python-multipart>=0.0.6
|
|
21
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: capauth
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
4
4
|
Summary: Capability-based Authentication — decentralized, PGP-based identity and authorization
|
|
5
5
|
Author-email: smilinTux <admin@smilintux.org>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -254,6 +254,7 @@ capauth pqc-report # live PQC posture per surface
|
|
|
254
254
|
|
|
255
255
|
# Auth & integration
|
|
256
256
|
capauth login <service_url> [--no-claims] # passwordless PGP login (caches OIDC token)
|
|
257
|
+
capauth sign-challenge # sign the copied browser login challenge, paste the result
|
|
257
258
|
capauth setup forgejo --capauth-url <url> # generate Forgejo OIDC app.ini block
|
|
258
259
|
|
|
259
260
|
# Mesh & membership
|
|
@@ -294,6 +295,7 @@ rides in the payload `event` field so routing stays severity-based.
|
|
|
294
295
|
| **[Integration Blueprint](docs/INTEGRATION_BLUEPRINT.md)** | third-party integration guide |
|
|
295
296
|
| **[Cold-Machine Bootstrap & DR](docs/COLD_MACHINE_BOOTSTRAP_AND_DR.md)** | standing capauth back up on a blank box + disaster recovery: the restore-not-regenerate rule, the ordered restore chain, and the operator checklist |
|
|
296
297
|
| **[authentik-capauth](docs/authentik-capauth.md)** | the custom Authentik image with the CapAuth PGP stage baked in — build (`AK_VERSION`, version-agnostic venv install, frontend rebuild), SKStacks deploy + `lifecycle.migrate` override, and the four build/migrate gotchas |
|
|
298
|
+
| **[Sign a login: copy, sign, paste](docs/SIGN_CHALLENGE.md)** | `capauth sign-challenge`: the four-step browser login with your gpg key, one-time setup, desktop shortcut |
|
|
297
299
|
| **[AI Advocate](AI-ADVOCATE.md)** | how AI advocates manage a sovereign profile on your behalf |
|
|
298
300
|
|
|
299
301
|
## Why it matters
|
|
@@ -191,6 +191,7 @@ capauth pqc-report # live PQC posture per surface
|
|
|
191
191
|
|
|
192
192
|
# Auth & integration
|
|
193
193
|
capauth login <service_url> [--no-claims] # passwordless PGP login (caches OIDC token)
|
|
194
|
+
capauth sign-challenge # sign the copied browser login challenge, paste the result
|
|
194
195
|
capauth setup forgejo --capauth-url <url> # generate Forgejo OIDC app.ini block
|
|
195
196
|
|
|
196
197
|
# Mesh & membership
|
|
@@ -231,6 +232,7 @@ rides in the payload `event` field so routing stays severity-based.
|
|
|
231
232
|
| **[Integration Blueprint](docs/INTEGRATION_BLUEPRINT.md)** | third-party integration guide |
|
|
232
233
|
| **[Cold-Machine Bootstrap & DR](docs/COLD_MACHINE_BOOTSTRAP_AND_DR.md)** | standing capauth back up on a blank box + disaster recovery: the restore-not-regenerate rule, the ordered restore chain, and the operator checklist |
|
|
233
234
|
| **[authentik-capauth](docs/authentik-capauth.md)** | the custom Authentik image with the CapAuth PGP stage baked in — build (`AK_VERSION`, version-agnostic venv install, frontend rebuild), SKStacks deploy + `lifecycle.migrate` override, and the four build/migrate gotchas |
|
|
235
|
+
| **[Sign a login: copy, sign, paste](docs/SIGN_CHALLENGE.md)** | `capauth sign-challenge`: the four-step browser login with your gpg key, one-time setup, desktop shortcut |
|
|
234
236
|
| **[AI Advocate](AI-ADVOCATE.md)** | how AI advocates manage a sovereign profile on your behalf |
|
|
235
237
|
|
|
236
238
|
## Why it matters
|
|
@@ -175,10 +175,17 @@ Add an entry to your CapAuth client registry for Authentik:
|
|
|
175
175
|
"client_secret": "<long-random-secret>",
|
|
176
176
|
"redirect_uris": ["https://<authentik-host>/source/oauth/callback/capauth/"],
|
|
177
177
|
"name": "Authentik",
|
|
178
|
-
"scopes": ["openid", "profile", "email", "groups"]
|
|
178
|
+
"scopes": ["openid", "profile", "email", "groups"],
|
|
179
|
+
"require_nonce": false
|
|
179
180
|
}
|
|
180
181
|
```
|
|
181
182
|
|
|
183
|
+
`require_nonce: false` is needed because Authentik's OAuth source never sends
|
|
184
|
+
an OIDC `nonce` (checked against Authentik 2025.12). Without it `/oidc/authorize`
|
|
185
|
+
answers `400 invalid_nonce`. The flow stays bound by `state`, PKCE S256 and the
|
|
186
|
+
client secret. Set the source's **PKCE** field to **S256** (Authentik 2025.2+;
|
|
187
|
+
older Authentik has no source PKCE and cannot use this IdP).
|
|
188
|
+
|
|
182
189
|
> The redirect URI Authentik uses for an OAuth source is
|
|
183
190
|
> `https://<authentik-host>/source/oauth/callback/<source-slug>/`. If you name
|
|
184
191
|
> the source slug `capauth`, the URI is the one above. Confirm the exact value
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Signing a CapAuth login: copy, sign, paste
|
|
2
|
+
|
|
3
|
+
`capauth sign-challenge` is the simple way to log in with your PGP key when an
|
|
4
|
+
application (for example skmesh, through Authentik) offers "Sign in with
|
|
5
|
+
CapAuth". You never type a key, a fingerprint (after the first time) or a gpg
|
|
6
|
+
command.
|
|
7
|
+
|
|
8
|
+
## The four steps
|
|
9
|
+
|
|
10
|
+
1. On the login page click **Sign in with CapAuth** (the button next to the
|
|
11
|
+
normal username and password form). You land on the CapAuth page; open
|
|
12
|
+
"Other sign-in and recovery options" if it is closed.
|
|
13
|
+
2. Click **Copy message**. (The first time only: type your key's fingerprint
|
|
14
|
+
into the fingerprint box first. The page remembers it in this browser, so
|
|
15
|
+
next time the section is already open with a fresh message.)
|
|
16
|
+
3. Run **`capauth sign-challenge`** in a terminal, or press your desktop
|
|
17
|
+
shortcut (below). gpg shows its own passphrase window; type the key's
|
|
18
|
+
passphrase there. The terminal prints `signed, paste it now`.
|
|
19
|
+
4. Click **Paste signature and continue** (or paste into the box with Ctrl+V
|
|
20
|
+
and click Verify & Continue). You are logged in.
|
|
21
|
+
|
|
22
|
+
The message you copy is only valid for a few minutes. If it expired, reload
|
|
23
|
+
the page and start again at step 2.
|
|
24
|
+
|
|
25
|
+
## What the command does, and what it does not
|
|
26
|
+
|
|
27
|
+
- Reads the copied text from the clipboard (Wayland `wl-paste`, X11 `xclip`
|
|
28
|
+
or `xsel`, macOS `pbpaste`), or from stdin with `--stdin`.
|
|
29
|
+
- Refuses anything that is not a complete CapAuth login challenge
|
|
30
|
+
(`CAPAUTH_NONCE_V1` or `CAPAUTH_NONCE_V2` with every field in order). It
|
|
31
|
+
never signs arbitrary clipboard text.
|
|
32
|
+
- Signs with `gpg --armor --detach-sign --local-user <key>`. The passphrase
|
|
33
|
+
goes from you to gpg-agent's pinentry dialog; the command never sees,
|
|
34
|
+
stores or prints it.
|
|
35
|
+
- Puts the ASCII-armored signature on the clipboard (or prints it with
|
|
36
|
+
`--stdout`) and prints only `signed, paste it now`.
|
|
37
|
+
|
|
38
|
+
Which key: `--key <fingerprint>`, else `$CAPAUTH_SIGN_KEY`, else the key of
|
|
39
|
+
your CapAuth profile (`capauth profile show`).
|
|
40
|
+
|
|
41
|
+
## One-time setup
|
|
42
|
+
|
|
43
|
+
1. Install capauth (`pip install capauth`) and a clipboard tool
|
|
44
|
+
(`sudo apt install wl-clipboard` on Wayland, `xclip` on X11).
|
|
45
|
+
2. gpg must hold the private key. A CapAuth profile keeps it in
|
|
46
|
+
`<CapAuth home>/identity/private.asc` (home: `~/.skcapstone/capauth`, or
|
|
47
|
+
`capauth --home`). Import it once; gpg asks for its passphrase in the
|
|
48
|
+
pinentry window and keeps it protected with that passphrase:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
gpg --import ~/.skcapstone/capauth/identity/private.asc
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This is a second protected copy of the key (in `~/.gnupg`). If the key's
|
|
55
|
+
passphrase is ever changed, change it in gpg too (`gpg --edit-key <fp>`,
|
|
56
|
+
then `passwd`), or delete the gpg copy and import again.
|
|
57
|
+
3. The key must be enrolled and approved in CapAuth and linked to your user
|
|
58
|
+
(the admin does the approval and the link; you send them your **public**
|
|
59
|
+
key: `capauth export-pubkey`).
|
|
60
|
+
|
|
61
|
+
### Desktop shortcut
|
|
62
|
+
|
|
63
|
+
Bind a keyboard shortcut (GNOME: Settings, Keyboard, Custom Shortcuts;
|
|
64
|
+
Cinnamon and KDE have the same) to:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
capauth sign-challenge
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
gpg-agent needs a graphical pinentry for this (`pinentry-gnome3` or
|
|
71
|
+
`pinentry-qt`, the default on desktop installs). Then the flow is: Copy
|
|
72
|
+
message, shortcut, passphrase window, Paste signature and continue.
|
|
73
|
+
|
|
74
|
+
## Later upgrade: sign on the phone
|
|
75
|
+
|
|
76
|
+
The CapAuth Bunker phone signer ([CAPAUTH_BUNKER_REMOTE_SIGNER.md](CAPAUTH_BUNKER_REMOTE_SIGNER.md))
|
|
77
|
+
keeps the key on your phone and signs by scanning a QR code ("Sign from
|
|
78
|
+
another device (QR)" on the same page). It needs the `/bunker/` routes and a
|
|
79
|
+
relay configured on the server side, so it is a follow-up to this copy and
|
|
80
|
+
paste flow.
|
|
@@ -152,8 +152,16 @@ backup_keystore() {
|
|
|
152
152
|
# Online backup: consistent snapshot of a live DB, source untouched.
|
|
153
153
|
sqlite3 "$DB_PATH" ".backup '$dst'"
|
|
154
154
|
else
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
log "sqlite3 CLI not found; using Python SQLite online backup"
|
|
156
|
+
python3 - "$DB_PATH" "$dst" <<'PY'
|
|
157
|
+
import sqlite3
|
|
158
|
+
import sys
|
|
159
|
+
|
|
160
|
+
source, destination = sys.argv[1:]
|
|
161
|
+
with sqlite3.connect(f"file:{source}?mode=ro", uri=True) as live:
|
|
162
|
+
with sqlite3.connect(destination) as backup:
|
|
163
|
+
live.backup(backup)
|
|
164
|
+
PY
|
|
157
165
|
fi
|
|
158
166
|
chmod 600 "$dst"
|
|
159
167
|
manifest "keys.db: source=$DB_PATH bytes=$(stat -c%s "$dst" 2>/dev/null || stat -f%z "$dst") sha256=$(sha "$dst")"
|
|
@@ -646,6 +646,47 @@ def export_pubkey(ctx: click.Context, output: Optional[str]) -> None:
|
|
|
646
646
|
raise SystemExit(1)
|
|
647
647
|
|
|
648
648
|
|
|
649
|
+
@main.command("sign-challenge")
|
|
650
|
+
@click.option(
|
|
651
|
+
"--key",
|
|
652
|
+
default=None,
|
|
653
|
+
help="Fingerprint to sign with (default: $CAPAUTH_SIGN_KEY, then your CapAuth profile).",
|
|
654
|
+
)
|
|
655
|
+
@click.option("--stdin", "from_stdin", is_flag=True, help="Read the challenge from stdin.")
|
|
656
|
+
@click.option("--stdout", "to_stdout", is_flag=True, help="Print the signature instead.")
|
|
657
|
+
@click.pass_context
|
|
658
|
+
def sign_challenge_cmd(
|
|
659
|
+
ctx: click.Context, key: Optional[str], from_stdin: bool, to_stdout: bool
|
|
660
|
+
) -> None:
|
|
661
|
+
"""Sign the CapAuth login challenge on the clipboard.
|
|
662
|
+
|
|
663
|
+
On the login page click "Copy message", run this, then paste: the
|
|
664
|
+
signature replaces the challenge on the clipboard. gpg-agent asks for the
|
|
665
|
+
key's passphrase in its own dialog; this command never sees it.
|
|
666
|
+
"""
|
|
667
|
+
from .sign_challenge import (
|
|
668
|
+
ChallengeError,
|
|
669
|
+
normalize_challenge,
|
|
670
|
+
read_clipboard,
|
|
671
|
+
resolve_key,
|
|
672
|
+
write_clipboard,
|
|
673
|
+
)
|
|
674
|
+
from .sign_challenge import sign as gpg_sign
|
|
675
|
+
|
|
676
|
+
try:
|
|
677
|
+
fp = resolve_key(key, ctx.obj.get("home"))
|
|
678
|
+
text = click.get_text_stream("stdin").read() if from_stdin else read_clipboard()
|
|
679
|
+
signature = gpg_sign(normalize_challenge(text), fp)
|
|
680
|
+
if to_stdout:
|
|
681
|
+
click.echo(signature, nl=False)
|
|
682
|
+
else:
|
|
683
|
+
write_clipboard(signature)
|
|
684
|
+
click.echo("signed, paste it now")
|
|
685
|
+
except ChallengeError as exc:
|
|
686
|
+
click.echo(f"Error: {exc}", err=True)
|
|
687
|
+
raise SystemExit(1)
|
|
688
|
+
|
|
689
|
+
|
|
649
690
|
@main.command("verify")
|
|
650
691
|
@click.option(
|
|
651
692
|
"--pubkey",
|
|
@@ -17,10 +17,16 @@ Each client entry:
|
|
|
17
17
|
"client_secret": "change-me",
|
|
18
18
|
"redirect_uris": ["https://authentik.example/source/oauth/callback/capauth/"],
|
|
19
19
|
"name": "Authentik",
|
|
20
|
-
"scopes": ["openid", "profile", "email", "groups"]
|
|
20
|
+
"scopes": ["openid", "profile", "email", "groups"],
|
|
21
|
+
"require_nonce": true
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
``redirect_uris`` is matched EXACTLY (no wildcard) per OAuth2 security guidance.
|
|
25
|
+
|
|
26
|
+
``require_nonce`` (default true) may be set to false for a relying party that
|
|
27
|
+
never sends an OIDC ``nonce``, such as stock Authentik's OAuth source. The code
|
|
28
|
+
flow stays bound by state, PKCE S256 and the confidential client secret; a
|
|
29
|
+
nonce that is sent must still be well formed and is echoed in the ID token.
|
|
24
30
|
"""
|
|
25
31
|
|
|
26
32
|
from __future__ import annotations
|
|
@@ -44,6 +50,7 @@ class OIDCClient:
|
|
|
44
50
|
redirect_uris: list[str]
|
|
45
51
|
name: str = ""
|
|
46
52
|
scopes: list[str] = field(default_factory=lambda: list(DEFAULT_SCOPES))
|
|
53
|
+
require_nonce: bool = True
|
|
47
54
|
|
|
48
55
|
def redirect_uri_allowed(self, redirect_uri: str) -> bool:
|
|
49
56
|
"""Return True if *redirect_uri* exactly matches a registered URI."""
|
|
@@ -93,6 +100,7 @@ class ClientRegistry:
|
|
|
93
100
|
redirect_uris=list(entry.get("redirect_uris", [])),
|
|
94
101
|
name=entry.get("name", entry["client_id"]),
|
|
95
102
|
scopes=list(entry.get("scopes", DEFAULT_SCOPES)),
|
|
103
|
+
require_nonce=entry.get("require_nonce", True) is not False,
|
|
96
104
|
)
|
|
97
105
|
|
|
98
106
|
# ------------------------------------------------------------------
|
|
@@ -162,7 +162,7 @@ _LOGIN_PAGE = """<!DOCTYPE html>
|
|
|
162
162
|
<head>
|
|
163
163
|
<meta charset="UTF-8"/>
|
|
164
164
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
165
|
-
<title>CapAuth
|
|
165
|
+
<title>CapAuth: Sign in with PGP</title>
|
|
166
166
|
<style>
|
|
167
167
|
*{{box-sizing:border-box;margin:0;padding:0}}
|
|
168
168
|
body{{font-family:'Segoe UI',system-ui,sans-serif;background:#0f0f1a;color:#e2e8f0;
|
|
@@ -212,7 +212,7 @@ _LOGIN_PAGE = """<!DOCTYPE html>
|
|
|
212
212
|
<details id="manual-pgp">
|
|
213
213
|
<summary>Other sign-in and recovery options</summary>
|
|
214
214
|
<p class="sub" style="font-size:.74rem;margin:.6rem 0">The text below is the message to sign. It is not a signature. Paste only a fresh ASCII-armored PGP signature into the signature box.</p>
|
|
215
|
-
<div class="step">1
|
|
215
|
+
<div class="step">1 - Your PGP fingerprint (remembered after the first login)</div>
|
|
216
216
|
<label for="fp">Fingerprint (40- or 64-hex chars)</label>
|
|
217
217
|
<input id="fp" type="text" maxlength="64" placeholder="ABCDEF0123..." autocomplete="off"/>
|
|
218
218
|
|
|
@@ -220,9 +220,13 @@ _LOGIN_PAGE = """<!DOCTYPE html>
|
|
|
220
220
|
<div class="nonce-box" id="nonce">Enter your fingerprint to load the complete signed payload.</div>
|
|
221
221
|
<button class="copy" type="button" onclick="copyPayload()">Copy message</button>
|
|
222
222
|
|
|
223
|
-
<div class="step">3 -
|
|
224
|
-
<
|
|
225
|
-
|
|
223
|
+
<div class="step">3 - Sign it: run <code>capauth sign-challenge</code> on your computer</div>
|
|
224
|
+
<p class="sub" style="font-size:.74rem;margin:.2rem 0 .8rem">It signs the copied message with your key (gpg asks for your passphrase in its own window) and puts the signature on the clipboard.</p>
|
|
225
|
+
|
|
226
|
+
<div class="step">4 - Paste the signature</div>
|
|
227
|
+
<button class="copy" type="button" onclick="pasteAndSubmit()">Paste signature and continue</button>
|
|
228
|
+
<label for="sig">or paste it here yourself (ASCII armor)</label>
|
|
229
|
+
<textarea id="sig" placeholder="-----BEGIN PGP SIGNATURE----- ... -----END PGP SIGNATURE-----"></textarea>
|
|
226
230
|
|
|
227
231
|
<p class="sub">This fingerprint must already have an approved CapAuth enrollment.</p>
|
|
228
232
|
|
|
@@ -236,7 +240,7 @@ _LOGIN_PAGE = """<!DOCTYPE html>
|
|
|
236
240
|
</div>
|
|
237
241
|
|
|
238
242
|
<p style="margin-top:1rem;font-size:.74rem;color:#475569">
|
|
239
|
-
|
|
243
|
+
Without the capauth tool: copy the complete message, then use <code>gpg --armor --sign</code> for an inline signed message
|
|
240
244
|
or <code>gpg --armor --detach-sign</code> for a detached signature. The browser extension can sign automatically.
|
|
241
245
|
</p>
|
|
242
246
|
</details>
|
|
@@ -282,7 +286,7 @@ refreshBrowserAuth();
|
|
|
282
286
|
async function passkeyLogin(){{
|
|
283
287
|
document.getElementById("err").style.display="none";
|
|
284
288
|
if(!window.capauthWebAuthn || !window.capauthWebAuthn.available()){{
|
|
285
|
-
return setErr("This browser has no passkey support
|
|
289
|
+
return setErr("This browser has no passkey support. Use PGP below.");
|
|
286
290
|
}}
|
|
287
291
|
try{{
|
|
288
292
|
const redirect = await window.capauthWebAuthn.login(BASE, REQUEST_ID);
|
|
@@ -321,14 +325,14 @@ document.getElementById("fp").addEventListener("blur", async function(){{
|
|
|
321
325
|
"service="+ch.service, "expires="+ch.expires].join("\\n");
|
|
322
326
|
document.getElementById("nonce").textContent=currentPayload;
|
|
323
327
|
// window.capauth provider: auto-sign with Tier B origin-binding. The
|
|
324
|
-
// extension injects origin=window.location.origin and signs in-extension
|
|
328
|
+
// extension injects origin=window.location.origin and signs in-extension -
|
|
325
329
|
// the private key never reaches this page. Falls back to manual paste.
|
|
326
330
|
if(window.capauth && window.capauth.isCapAuth){{
|
|
327
331
|
try{{
|
|
328
332
|
const res=await window.capauth.signChallenge(ch);
|
|
329
333
|
document.getElementById("sig").value=res.signature;
|
|
330
334
|
submitSig();
|
|
331
|
-
}}catch(e){{ /* denied/locked
|
|
335
|
+
}}catch(e){{ /* denied/locked: leave the paste flow available */ }}
|
|
332
336
|
}}
|
|
333
337
|
}}catch(e){{ document.getElementById("nonce").textContent="Error: "+e.message; }}
|
|
334
338
|
}});
|
|
@@ -338,7 +342,7 @@ async function submitSig(){{
|
|
|
338
342
|
const fp=document.getElementById("fp").value.trim().toUpperCase().replace(/\\s/g,"");
|
|
339
343
|
const sig=document.getElementById("sig").value.trim();
|
|
340
344
|
if(![40,64].includes(fp.length)) return setErr("Fingerprint must be 40- or 64-hex characters.");
|
|
341
|
-
if(!currentNonce) return setErr("No challenge loaded
|
|
345
|
+
if(!currentNonce) return setErr("No challenge loaded. Enter your fingerprint first.");
|
|
342
346
|
if(!sig) return setErr("Paste your PGP signature.");
|
|
343
347
|
|
|
344
348
|
const body={{request_id:REQUEST_ID, fingerprint:fp, nonce:currentNonce,
|
|
@@ -347,9 +351,33 @@ async function submitSig(){{
|
|
|
347
351
|
method:"POST", headers:{{"Content-Type":"application/json"}}, body: JSON.stringify(body)
|
|
348
352
|
}});
|
|
349
353
|
if(!r.ok){{ const b=await r.json().catch(()=>({{}})); return setErr(b.detail || b.error || "Login failed."); }}
|
|
354
|
+
try{{ localStorage.setItem("capauth_manual_fp",fp); }}catch(e){{}}
|
|
350
355
|
const d=await r.json();
|
|
351
356
|
window.location.href=d.redirect_to;
|
|
352
357
|
}}
|
|
358
|
+
|
|
359
|
+
async function pasteAndSubmit(){{
|
|
360
|
+
document.getElementById("err").style.display="none";
|
|
361
|
+
let text="";
|
|
362
|
+
try{{ text=await navigator.clipboard.readText(); }}
|
|
363
|
+
catch(e){{ return setErr("This browser did not allow reading the clipboard. Paste the signature into the box (Ctrl+V), then click Verify & Continue."); }}
|
|
364
|
+
if(!/BEGIN PGP (SIGNATURE|MESSAGE)/.test(text)){{
|
|
365
|
+
return setErr("The clipboard holds no signature yet. Click Copy message, run capauth sign-challenge, then try again.");
|
|
366
|
+
}}
|
|
367
|
+
document.getElementById("sig").value=text.trim();
|
|
368
|
+
submitSig();
|
|
369
|
+
}}
|
|
370
|
+
|
|
371
|
+
// the fingerprint of the last successful manual login: open this section
|
|
372
|
+
// with a fresh challenge loaded, so the next login is copy, sign, paste
|
|
373
|
+
(function(){{
|
|
374
|
+
let fp="";
|
|
375
|
+
try{{ fp=(localStorage.getItem("capauth_manual_fp")||"").trim(); }}catch(e){{}}
|
|
376
|
+
if(![40,64].includes(fp.length)||!/^[0-9A-F]+$/.test(fp)) return;
|
|
377
|
+
document.getElementById("fp").value=fp;
|
|
378
|
+
document.getElementById("manual-pgp").open=true;
|
|
379
|
+
document.getElementById("fp").dispatchEvent(new Event("blur"));
|
|
380
|
+
}})();
|
|
353
381
|
</script>
|
|
354
382
|
</body>
|
|
355
383
|
</html>
|
|
@@ -1067,7 +1095,7 @@ def build_oidc_router(
|
|
|
1067
1095
|
raise HTTPException(status_code=400, detail="invalid_code_challenge")
|
|
1068
1096
|
if not (_OPAQUE_MIN_LENGTH <= len(state) <= 512):
|
|
1069
1097
|
raise HTTPException(status_code=400, detail="invalid_state")
|
|
1070
|
-
if not (_OPAQUE_MIN_LENGTH <= len(nonce) <= 512):
|
|
1098
|
+
if (nonce or client.require_nonce) and not (_OPAQUE_MIN_LENGTH <= len(nonce) <= 512):
|
|
1071
1099
|
raise HTTPException(status_code=400, detail="invalid_nonce")
|
|
1072
1100
|
scopes = scope.split()
|
|
1073
1101
|
if (
|