Skip to content

Commit d2842de

Browse files
Trusted Publishers: use the hf CLI for the OIDC exchange (#2546)
* Trusted Publishers: use the hf CLI for the OIDC exchange * nit
1 parent 0e632ed commit d2842de

1 file changed

Lines changed: 41 additions & 58 deletions

File tree

docs/hub/trusted-publishers.md

Lines changed: 41 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -52,60 +52,40 @@ jobs:
5252

5353
steps:
5454
- uses: actions/checkout@v4
55-
- uses: actions/setup-python@v5
56-
with:
57-
python-version: "3.11"
58-
- run: pip install --upgrade "huggingface_hub>=1.18.0"
5955

60-
- name: Exchange GitHub OIDC token for a Hub token
56+
- name: Install the hf CLI
6157
run: |
62-
set -euo pipefail
58+
curl -LsSf https://hf.co/cli/install.sh | bash
59+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
6360
61+
- name: Upload checkpoint
62+
env:
6463
# The HF repo to publish to. For non-model repos, prefix accordingly:
6564
# datasets/acme/awesome-dataset, spaces/acme/awesome-space, kernels/acme/awesome-kernel
66-
RESOURCE="acme/awesome-model"
67-
68-
ID_TOKEN=$(curl -sSf \
69-
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
70-
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://huggingface.co" \
71-
| jq -r .value)
72-
73-
HEADERS=$(mktemp); BODY=$(mktemp)
74-
STATUS=$(curl -sS -D "$HEADERS" -o "$BODY" -w '%{http_code}' \
75-
-X POST "https://huggingface.co/oauth/token" \
76-
-H "Content-Type: application/json" \
77-
-d "$(jq -n --arg t "$ID_TOKEN" --arg r "$RESOURCE" \
78-
'{
79-
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
80-
subject_token_type: "urn:ietf:params:oauth:token-type:id_token",
81-
subject_token: $t,
82-
resource: $r
83-
}')")
84-
85-
if [ "$STATUS" -ne 200 ]; then
86-
REQUEST_ID=$(grep -i '^x-request-id:' "$HEADERS" | sed 's/.*: *//' | tr -d '\r')
87-
echo "::error::Token exchange failed with HTTP $STATUS (x-request-id: ${REQUEST_ID:-unknown})"
88-
cat "$BODY"
89-
exit 1
90-
fi
91-
92-
HF_TOKEN=$(jq -r .access_token "$BODY")
93-
echo "::add-mask::$HF_TOKEN"
94-
# Safe way to write to env
95-
DELIM="EOF_$(openssl rand -hex 16)"
96-
{
97-
echo "HF_TOKEN<<$DELIM"
98-
echo "$HF_TOKEN"
99-
echo "$DELIM"
100-
} >> "$GITHUB_ENV"
101-
102-
- name: Upload checkpoint
103-
run: |
104-
hf upload acme/awesome-model ./checkpoint . \
105-
--commit-message "Publish from ${GITHUB_SHA::7}"
65+
HF_OIDC_RESOURCE: acme/awesome-model
66+
run: hf upload acme/awesome-model ./checkpoint . --commit-message "Publish from ${GITHUB_SHA::7}"
10667
```
10768
108-
That's it — `huggingface_hub` picks up `HF_TOKEN` from the environment.
69+
That's it. On GitHub Actions, the `hf` CLI (`huggingface_hub>=1.19.0`) detects the provider, performs the exchange, and uses the resulting token automatically. You only set `HF_OIDC_RESOURCE`.
70+
71+
> [!TIP]
72+
> Publishing to several repos in one run (e.g. a model **and** a dataset)? Set `HF_OIDC_RESOURCE` per step so each token is scoped to the repo that step pushes to.
73+
74+
#### Other CI providers
75+
76+
The `hf` CLI mints the ID token natively on GitHub Actions only for now. On GitLab, CircleCI, Bitbucket, or any other provider, mint the ID token yourself (see [Supported CI providers](#supported-ci-providers)) and pass it via `HF_OIDC_ID_TOKEN` — the CLI exchanges it directly:
77+
78+
```yaml
79+
# GitLab CI (.gitlab-ci.yml)
80+
publish:
81+
id_tokens:
82+
HF_ID_TOKEN:
83+
aud: https://huggingface.co
84+
script:
85+
- curl -LsSf https://hf.co/cli/install.sh | bash
86+
- export PATH="$HOME/.local/bin:$PATH"
87+
- HF_OIDC_ID_TOKEN="$HF_ID_TOKEN" HF_OIDC_RESOURCE="acme/awesome-model" hf upload acme/awesome-model ./checkpoint .
88+
```
10989

11090
Complete working examples:
11191

@@ -123,19 +103,22 @@ Both tokens expire after 60 minutes. You need the **Write** role on a Hub repo t
123103

124104
### Accessing gated repos from CI
125105

126-
If you only need to *read* gated repos (e.g. download a model from a job), configure a publisher on your account under [**Authentication settings → CI/CD Access**](https://huggingface.co/settings/authentication#ci-cd-access) instead of on a specific repo, then pass your **username** as `resource`:
106+
If you only need to *read* gated repos (e.g. download a model from a job), configure a publisher on your account under [**Authentication settings → CI/CD Access**](https://huggingface.co/settings/authentication#ci-cd-access) instead of on a specific repo, then use your **username** as the resource.
107+
108+
On **GitHub Actions**, the `hf` CLI does the exchange for you, just set `HF_OIDC_RESOURCE`:
109+
110+
```yaml
111+
- name: Download a gated model
112+
env:
113+
HF_OIDC_RESOURCE: your-hf-username
114+
run: hf download acme/gated-model
115+
```
116+
117+
On **other providers**, mint the ID token yourself (see [Other CI providers](#other-ci-providers)) and pass it via `HF_OIDC_ID_TOKEN`:
127118

128119
```bash
129-
HF_TOKEN=$(curl -sSf -X POST "https://huggingface.co/oauth/token" \
130-
-H "Content-Type: application/json" \
131-
-d "$(jq -n --arg t "$ID_TOKEN" --arg r "your-hf-username" \
132-
'{
133-
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
134-
subject_token_type: "urn:ietf:params:oauth:token-type:id_token",
135-
subject_token: $t,
136-
resource: $r
137-
}')" \
138-
| jq -r .access_token)
120+
# $ID_TOKEN is the OIDC token your provider minted (e.g. $HF_ID_TOKEN on GitLab)
121+
HF_OIDC_ID_TOKEN="$ID_TOKEN" HF_OIDC_RESOURCE="your-hf-username" hf download acme/gated-model
139122
```
140123

141124
The resulting token can read gated repos you have access to and uses your account's rate limits. It **cannot** write anything, and **cannot** read your private repos.

0 commit comments

Comments
 (0)