Authentication
Give the client a credential explicitly, or let it detect one from the environment, and know which wins.
The Gigadrive Network client resolves a credential in its constructor, before it sends anything. Pass one in, or set environment variables and let the client find them.
Credentials you pass in
Four combinations are recognized, each selecting a different OAuth 2.0 flow.
| Config fields | Flow | Use it for |
|---|---|---|
clientId + clientSecret | client credentials | API keys: servers, CI, scripts |
bearerToken | none, the token is sent as-is | a token you already hold |
clientId + refreshToken | refresh token, against the IDP | a stored user session |
clientId + onAuthorizationUrl | authorization code with PKCE | interactive CLIs and desktop apps |
Client credentials are the common case:
import { GigadriveClient } from '@gigadrive/sdk';
const client = new GigadriveClient({
clientId: process.env.GIGADRIVE_CLIENT_ID,
clientSecret: process.env.GIGADRIVE_CLIENT_SECRET,
});
const { items } = await client.applications.list();The client id is an API key ID and the secret is the string that key returned at creation. Both come from API keys. The client posts them to https://api.gigadrive.network/oauth2/token with HTTP Basic auth, then caches the access token it gets back.
Access tokens last 300 seconds. The client refreshes 30 seconds before expiry, or earlier for shorter tokens, since the margin is capped at half the lifetime. Concurrent callers share one in-flight refresh rather than each triggering their own.
Never put a client secret in browser code
A secret shipped to a browser is a secret you have published. Mint the credential on your server, do the work there, or hand the browser a short-lived upload URL instead.
Every constructor option
| Option | Type | Default |
|---|---|---|
clientId | string | GIGADRIVE_CLIENT_ID |
clientSecret | string | GIGADRIVE_CLIENT_SECRET |
bearerToken | string | GIGADRIVE_BEARER_TOKEN |
refreshToken | string | GIGADRIVE_REFRESH_TOKEN |
applicationId | string | GIGADRIVE_APPLICATION_ID |
baseUrl | string | GIGADRIVE_API_BASE_URL, else https://api.gigadrive.network |
idpIssuerUrl | string | GIGADRIVE_IDP_ISSUER_URL, else https://idp.gigadrive.de |
onAuthorizationUrl | (url: string) => Promise<string> | none, and no environment equivalent |
redirectUri | string | urn:ietf:wg:oauth:2.0:oob |
scopes | string[] | offline_access openid profile email |
fetch | typeof globalThis.fetch | globalThis.fetch, bound to globalThis |
scopes applies to the authorization-code flow only, and it replaces the default set rather than adding to it. Requesting ['network:applications:read'] alone drops openid, profile, email and offline_access, so list the identity scopes again if you still want them. The names come from Scopes.
applicationId is not a credential. It supplies the default application for client.storage, which addresses buckets without repeating the application in every call.
Credentials detected from the environment
With no matching config field, the client walks this list and takes the first match.
| Order | Source | Result |
|---|---|---|
| 1 | config bearerToken | the token, sent unchanged |
| 2 | config clientId and clientSecret | client credentials |
| 3 | config refreshToken and clientId | refresh token |
| 4 | config onAuthorizationUrl and clientId | authorization code with PKCE |
| 5 | GIGADRIVE_BEARER_TOKEN | the token, sent unchanged |
| 6 | GIGADRIVE_CLIENT_ID and GIGADRIVE_CLIENT_SECRET | client credentials |
| 7 | GIGADRIVE_REFRESH_TOKEN and GIGADRIVE_CLIENT_ID | refresh token |
| 8 | nothing matched | throws AuthenticationError |
Config always beats the environment, and the authorization-code flow has no environment branch: a callback is a function, so it can only be passed in code.
These are the seven variables the client reads:
| Variable | Purpose |
|---|---|
GIGADRIVE_CLIENT_ID | OAuth client id, which is the API key ID |
GIGADRIVE_CLIENT_SECRET | OAuth client secret, which is the API key secret |
GIGADRIVE_BEARER_TOKEN | an access token you already hold, sent as-is and never refreshed |
GIGADRIVE_REFRESH_TOKEN | refresh token, exchanged at the IDP and rotated automatically |
GIGADRIVE_IDP_ISSUER_URL | issuer used for OIDC discovery in the refresh and PKCE flows |
GIGADRIVE_API_BASE_URL | API base URL, which also determines the token endpoint |
GIGADRIVE_APPLICATION_ID | default application context for client.storage |
A bearer token is never renewed. When it expires, requests fail with AuthenticationError and you have to build a new client.
Inside a deployed Function
Every deployed Function gets its own OAuth client, with GIGADRIVE_CLIENT_ID, GIGADRIVE_CLIENT_SECRET and GIGADRIVE_APPLICATION_ID injected at start. Row 6 of that table picks them up, so the constructor takes no arguments:
import { GigadriveClient } from '@gigadrive/sdk';
const client = new GigadriveClient();
export async function saveAvatar(userId: string, file: Blob): Promise<string> {
const { url } = await client.storage.upload({
bucket: 'avatars',
key: `avatars/${userId}.png`,
data: file,
});
return url;
}That credential is scoped to the Function's own application and carries storage, sticky-session and runtime-cache permissions only. It cannot create applications or read environment variables. OIDC federation lists the exact scope set and what the platform injects.
On your own machine
gigadrive setup links the directory to an application, provisions an application-scoped key, and writes GIGADRIVE_CLIENT_ID, GIGADRIVE_CLIENT_SECRET and GIGADRIVE_API_BASE_URL into .env.local. Pass --rotate to revoke the previous key and mint a fresh one.
That key is deliberately narrow: it grants network:env_vars:read and nothing else. For anything beyond reading configuration, create a key with the scopes you need through client.apiKeys.create, described in Resources.
