Skip to content

Commit e53eb8b

Browse files
woodruffwwebknjaz
andauthored
Clarify the error during OIDC exchange on PRs from forks
This specializes the token retrieval error handling, providing an alternative error message when the error cause is something that we know can't possibly work due to GitHub's own restrictions on PRs from forks. PR #203 Closes #202 Ref python-pillow/Pillow#7616 Co-authored-by: Sviatoslav Sydorenko <[email protected]>
1 parent edfa8f3 commit e53eb8b

File tree

1 file changed

+43
-1
lines changed

1 file changed

+43
-1
lines changed

oidc-exchange.py

+43-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,20 @@
4747
Learn more at https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings.
4848
"""
4949

50+
# Specialization of the token retrieval failure case, when we know that
51+
# the failure cause is use within a third-party PR.
52+
_TOKEN_RETRIEVAL_FAILED_FORK_PR_MESSAGE = """
53+
OpenID Connect token retrieval failed: {identity_error}
54+
55+
The workflow context indicates that this action was called from a
56+
pull request on a fork. GitHub doesn't give these workflows OIDC permissions,
57+
even if `id-token: write` is explicitly configured.
58+
59+
To fix this, change your publishing workflow to use an event that
60+
forks of your repository cannot trigger (such as tag or release
61+
creation, or a manually triggered workflow dispatch).
62+
"""
63+
5064
# Rendered if the package index refuses the given OIDC token.
5165
_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE = """
5266
Token request failed: the server refused the request for the following reasons:
@@ -165,6 +179,29 @@ def _get(name: str) -> str: # noqa: WPS430
165179
)
166180

167181

182+
def event_is_third_party_pr() -> bool:
183+
# Non-`pull_request` events cannot be from third-party PRs.
184+
if os.getenv("GITHUB_EVENT_NAME") != "pull_request":
185+
return False
186+
187+
event_path = os.getenv("GITHUB_EVENT_PATH")
188+
if not event_path:
189+
# No GITHUB_EVENT_PATH indicates a weird GitHub or runner bug.
190+
debug("unexpected: no GITHUB_EVENT_PATH to check")
191+
return False
192+
193+
try:
194+
event = json.loads(Path(event_path).read_bytes())
195+
except json.JSONDecodeError:
196+
debug("unexpected: GITHUB_EVENT_PATH does not contain valid JSON")
197+
return False
198+
199+
try:
200+
return event["pull_request"]["head"]["repo"]["fork"]
201+
except KeyError:
202+
return False
203+
204+
168205
repository_url = get_normalized_input("repository-url")
169206
repository_domain = urlparse(repository_url).netloc
170207
token_exchange_url = f"https://{repository_domain}/_/oidc/mint-token"
@@ -182,7 +219,12 @@ def _get(name: str) -> str: # noqa: WPS430
182219
try:
183220
oidc_token = id.detect_credential(audience=oidc_audience)
184221
except id.IdentityError as identity_error:
185-
die(_TOKEN_RETRIEVAL_FAILED_MESSAGE.format(identity_error=identity_error))
222+
cause_msg_tmpl = (
223+
_TOKEN_RETRIEVAL_FAILED_FORK_PR_MESSAGE if event_is_third_party_pr()
224+
else _TOKEN_RETRIEVAL_FAILED_MESSAGE
225+
)
226+
for_cause_msg = cause_msg_tmpl.format(identity_error=identity_error)
227+
die(for_cause_msg)
186228

187229
# Now we can do the actual token exchange.
188230
mint_token_resp = requests.post(

0 commit comments

Comments
 (0)