diff --git a/CHANGELOG.md b/CHANGELOG.md index 28d3f0076..6cbb0ea44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ All versions prior to 0.9.0 are untracked. ## [Unreleased] +### Added + +* CLI: `sigstore sign` and `sigstore get-identity-token` now support the + `--oauth-force-oob` option; which has the same behavior as the + preexisting `SIGSTORE_OAUTH_FORCE_OOB` environment variable + ([#667](https://github.com/sigstore/sigstore-python/pull/667)) + ### Changed * `sigstore verify` now performs additional verification of Rekor's inclusion diff --git a/README.md b/README.md index a7e3952da..4dfe67d7f 100644 --- a/README.md +++ b/README.md @@ -134,8 +134,8 @@ Sigstore instance options: usage: sigstore sign [-h] [--identity-token TOKEN] [--oidc-client-id ID] [--oidc-client-secret SECRET] [--oidc-disable-ambient-providers] [--oidc-issuer URL] - [--no-default-files] [--signature FILE] - [--certificate FILE] [--bundle FILE] + [--oauth-force-oob] [--no-default-files] + [--signature FILE] [--certificate FILE] [--bundle FILE] [--output-directory DIR] [--overwrite] [--staging] [--rekor-url URL] [--rekor-root-pubkey FILE] [--fulcio-url URL] [--ctfe FILE] @@ -160,6 +160,9 @@ OpenID Connect options: (e.g. on GitHub Actions) (default: False) --oidc-issuer URL The OpenID Connect issuer to use (conflicts with --staging) (default: https://oauth2.sigstore.dev/auth) + --oauth-force-oob Force an out-of-band OAuth flow and do not + automatically start the default web browser (default: + False) Output options: --no-default-files Don't emit the default output files ({input}.sigstore) diff --git a/sigstore/_cli.py b/sigstore/_cli.py index 5827ce4ce..cdaa3569d 100644 --- a/sigstore/_cli.py +++ b/sigstore/_cli.py @@ -221,6 +221,12 @@ def _add_shared_oidc_options( default=os.getenv("SIGSTORE_OIDC_ISSUER", DEFAULT_OAUTH_ISSUER_URL), help="The OpenID Connect issuer to use (conflicts with --staging)", ) + group.add_argument( + "--oauth-force-oob", + action="store_true", + default=_boolify_env("SIGSTORE_OAUTH_FORCE_OOB"), + help="Force an out-of-band OAuth flow and do not automatically start the default web browser", + ) def _parser() -> argparse.ArgumentParser: @@ -964,7 +970,9 @@ def _get_identity(args: argparse.Namespace) -> Optional[IdentityToken]: args.oidc_client_secret = "" # nosec: B105 token = issuer.identity_token( - client_id=args.oidc_client_id, client_secret=args.oidc_client_secret + client_id=args.oidc_client_id, + client_secret=args.oidc_client_secret, + force_oob=args.oauth_force_oob, ) return token diff --git a/sigstore/oidc.py b/sigstore/oidc.py index 8a03a1c83..f96ddc604 100644 --- a/sigstore/oidc.py +++ b/sigstore/oidc.py @@ -19,7 +19,6 @@ from __future__ import annotations import logging -import os import sys import time import urllib.parse @@ -247,13 +246,19 @@ def staging(cls) -> Issuer: return cls(STAGING_OAUTH_ISSUER_URL) def identity_token( # nosec: B107 - self, client_id: str = "sigstore", client_secret: str = "" + self, + client_id: str = "sigstore", + client_secret: str = "", + force_oob: bool = False, ) -> IdentityToken: """ Retrieves and returns an `IdentityToken` from the current `Issuer`, via OAuth. - This function blocks on user interaction, either via a web browser or an out-of-band - OAuth flow. + This function blocks on user interaction. + + The `force_oob` flag controls the kind of flow performed. When `False` (the default), + this function attempts to open the user's web browser before falling back to + an out-of-band flow. When `True`, the out-of-band flow is always used. """ # This function and the components that it relies on are based off of: @@ -261,8 +266,6 @@ def identity_token( # nosec: B107 from sigstore._internal.oidc.oauth import _OAuthFlow - force_oob = os.getenv("SIGSTORE_OAUTH_FORCE_OOB") is not None - code: str with _OAuthFlow(client_id, client_secret, self) as server: # Launch web browser diff --git a/test/unit/internal/oidc/test_issuer.py b/test/unit/internal/oidc/test_issuer.py index b3458b28c..77d4c4d38 100644 --- a/test/unit/internal/oidc/test_issuer.py +++ b/test/unit/internal/oidc/test_issuer.py @@ -30,8 +30,7 @@ def test_init_url(): @pytest.mark.online def test_get_identity_token_identity_error(monkeypatch): - monkeypatch.setenv("SIGSTORE_OAUTH_FORCE_OOB", "") monkeypatch.setattr("builtins.input", lambda _: "hunter2") with pytest.raises(IdentityError): - Issuer.staging().identity_token() + Issuer.staging().identity_token(force_oob=True)