Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion docs/source/guides/private.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<Tip>

Due to the possibility of leaking access tokens to users of your website or web application, we only support accessing private/gated models from server-side environments (e.g., Node.js) that have access to the process' environment variables.
The `env.DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN` field makes the provided token visible to every user of your application. Only use this when the user is explicitly entering their own token. Do not embed your own token with this field and use it at your own risk.

</Tip>

Expand Down Expand Up @@ -61,3 +61,13 @@ process.env.HF_TOKEN = 'hf_...';

// ... rest of your code
```

In browser environments where environment variables are not available, you can set the token at runtime using the `env` object:

```js
import { env } from '@huggingface/transformers';

// WARNING: This exposes the token to every user.
// Only use when the user provides their own token.
env.DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN = 'hf_...';
```
5 changes: 5 additions & 0 deletions src/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ const localModelPath = RUNNING_LOCALLY
* @property {Object} customCache The custom cache to use. Defaults to `null`. Note: this must be an object which
* implements the `match` and `put` functions of the Web Cache API. For more information, see https://developer.mozilla.org/en-US/docs/Web/API/Cache.
* If you wish, you may also return a `Promise<string>` from the `match` function if you'd like to use a file path instead of `Promise<Response>`.
* @property {string|null} [DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN=null] Access token used when making requests to the Hugging Face Hub.
* This value is visible to every user of your application. Only set it when the user is explicitly providing
* their own token (e.g., via an input field). Do not use it for any other purpose and use at your own risk.
*/

/** @type {TransformersEnvironment} */
Expand Down Expand Up @@ -155,6 +158,8 @@ export const env = {
useCustomCache: false,
customCache: null,
//////////////////////////////////////////////////////

DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN: null,
}


Expand Down
17 changes: 8 additions & 9 deletions src/utils/hub.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,23 +229,22 @@ export async function getFile(urlOrPath) {
const headers = new Headers();
headers.set('User-Agent', `transformers.js/${version}; is_ci/${IS_CI};`);

// Check whether we are making a request to the Hugging Face Hub.
const isHFURL = isValidUrl(urlOrPath, ['http:', 'https:'], ['huggingface.co', 'hf.co']);
if (isHFURL) {
// If an access token is present in the environment variables,
// we add it to the request headers.
// NOTE: We keep `HF_ACCESS_TOKEN` for backwards compatibility (as a fallback).
const token = process.env?.HF_TOKEN ?? process.env?.HF_ACCESS_TOKEN;
const token = env.DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN ?? process.env?.HF_TOKEN ?? process.env?.HF_ACCESS_TOKEN;
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
}
return fetch(urlOrPath, { headers });
} else {
// Running in a browser-environment, so we use default headers
// NOTE: We do not allow passing authorization headers in the browser,
// since this would require exposing the token to the client.
return fetch(urlOrPath);
const headers = new Headers();
const isHFURL = isValidUrl(urlOrPath, ['http:', 'https:'], ['huggingface.co', 'hf.co']);
if (isHFURL && env.DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN) {
headers.set('Authorization', `Bearer ${env.DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN}`);
}
const options = headers.size > 0 ? { headers } : undefined;
return fetch(urlOrPath, options);
}
}

Expand Down
28 changes: 28 additions & 0 deletions tests/utils/hub-auth.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { env } from "../../src/transformers.js";
import { getFile } from "../../src/utils/hub.js";
import { jest } from "@jest/globals";

describe("Hub authorization", () => {
it("Attaches Authorization header when env.DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN is set", async () => {
const originalFetch = global.fetch;
const mockFetch = jest.fn(() => Promise.resolve(new Response(null)));
global.fetch = mockFetch;

const originalHFToken = process.env.HF_TOKEN;
const originalHFAccessToken = process.env.HF_ACCESS_TOKEN;
delete process.env.HF_TOKEN;
delete process.env.HF_ACCESS_TOKEN;

env.DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN = "hf_dummy";

await getFile("https://huggingface.co/any/model");

const headers = mockFetch.mock.calls[0][1].headers;
expect(headers.get("Authorization")).toBe("Bearer hf_dummy");

env.DANGEROUSLY_AVAILABLE_TO_EVERY_USER_HF_TOKEN = null;
global.fetch = originalFetch;
if (originalHFToken !== undefined) process.env.HF_TOKEN = originalHFToken; else delete process.env.HF_TOKEN;
if (originalHFAccessToken !== undefined) process.env.HF_ACCESS_TOKEN = originalHFAccessToken; else delete process.env.HF_ACCESS_TOKEN;
});
});