> ## Documentation Index
> Fetch the complete documentation index at: https://namespace.so/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Authenticate with Namespace

> Use workload identity federation, local credentials, and explicit token sources with the TypeScript Devbox SDK.

Use workload identity federation for CI/CD and other automated workloads that can obtain an OIDC token. Use `devbox login` for local development, or provide an explicit token source when the default credential resolution does not fit your environment.

## Workload identity federation

Federation lets an external workload exchange its OIDC identity for short-lived, scoped Namespace credentials. Prefer this approach for automated workloads instead of storing a shared, long-lived token.

Federation happens before the TypeScript SDK starts. The SDK does not exchange third-party OIDC tokens directly. A provider integration or the Namespace CLI establishes the Namespace credential, then `createDevboxClient()` loads it through the default credential resolution.

The general flow is:

1. Configure a Namespace trust relationship that restricts the accepted OIDC issuer, subject, audience, and grants.
2. Obtain an OIDC token from the workload's identity provider.
3. Exchange that token for short-lived Namespace credentials.
4. Start the TypeScript application and create a client without an explicit `tokenSource`.

<Info>
  Applications running inside a Namespace workload already receive a workload credential. `createDevboxClient()` discovers it automatically through `NSC_TOKEN_FILE` or `/var/run/nsc/token.json`, so no external exchange step is needed.
</Info>

### Exchange an OIDC token

Configure a [trust relationship](/docs/reference/cli/auth-trust-relationships-add), obtain the provider's OIDC token, and exchange it before starting the application:

```bash {1-3} theme={null}
nsc auth exchange-oidc-token \
  --token "$OIDC_TOKEN" \
  --tenant_id "$NAMESPACE_WORKSPACE_ID"
node dist/manage-devboxes.js
```

The exchange stores a short-lived Namespace credential in the standard Namespace token configuration, where `loadDefaults()` can find it.

### Provider-specific setup

See the federation guides for [OpenID Connect](/docs/federation/openid), [GitHub Actions](/docs/federation/github-actions), [CircleCI](/docs/federation/circleci), [Google Cloud](/docs/federation/gcp), [AWS](/docs/federation/aws), and [RWX](/docs/federation/rwx). These guides cover obtaining the provider token and configuring trust with Namespace; the SDK consumes the resulting Namespace credential in the same way for every provider.

## Local development with `devbox login`

For local, interactive development, [install the Devbox CLI](/docs/devbox#install-the-devbox-cli) and authenticate in the browser:

```bash theme={null}
devbox login
```

The SDK reads the credentials written by `devbox login` from the platform-specific user configuration directory. You do not need to load them explicitly:

```typescript {3} theme={null}
import { createDevboxClient } from "@namespacelabs/sdk/devbox";

const client = createDevboxClient();
const devboxes = await client.devboxes.list();
```

## Default credential resolution

`createDevboxClient()` uses `loadDefaults` when `tokenSource` is omitted. Credentials are resolved in this order:

1. The token file named by `NSC_TOKEN_FILE`.
2. The workload token at `/var/run/nsc/token.json`, when that file exists.
3. The current user's Namespace token file.

## `loadDefaults()`

Loads the first available token source using the default resolution order above.

### Example

Pass the function itself to defer file access until the client first needs a token:

```typescript {1,5} theme={null}
import { loadDefaults } from "@namespacelabs/sdk/auth";
import { createDevboxClient } from "@namespacelabs/sdk/devbox";

const client = createDevboxClient({
  tokenSource: loadDefaults,
});
```

You can also call and await `loadDefaults()` to load credentials immediately.

### API reference

```typescript theme={null}
loadDefaults(): Promise<TokenSource>
```

#### Arguments and options

This function does not accept arguments.

#### Return value

Returns a promise that resolves to the first available `TokenSource`.

***

## `loadUserToken()`

Loads credentials for the user who ran `devbox login`. Use this when an application must not fall back to workload credentials.

### Example

```typescript {1,5} theme={null}
import { loadUserToken } from "@namespacelabs/sdk/auth";
import { createDevboxClient } from "@namespacelabs/sdk/devbox";

const client = createDevboxClient({
  tokenSource: loadUserToken,
});
```

The provider is evaluated lazily. Pass `await loadUserToken()` instead when you want missing or invalid credentials to fail during client setup.

### API reference

```typescript theme={null}
loadUserToken(): Promise<TokenSource>
```

#### Arguments and options

This function does not accept arguments.

#### Return value

Returns a promise that resolves to the current user's `TokenSource`.

***

## `loadWorkloadToken()`

Loads a workload bearer token from `NSC_TOKEN_FILE`, or from `/var/run/nsc/token.json` when the environment variable is unset. It does not fall back to user credentials.

### Example

```typescript {1,5} theme={null}
import { loadWorkloadToken } from "@namespacelabs/sdk/auth";
import { createDevboxClient } from "@namespacelabs/sdk/devbox";

const client = createDevboxClient({
  tokenSource: loadWorkloadToken,
});
```

Workload token files must contain a `bearer_token`. Workload bearer tokens are returned directly and are not refreshed through a user session.

### API reference

```typescript theme={null}
loadWorkloadToken(): Promise<TokenSource>
```

#### Arguments and options

This function does not accept arguments.

#### Return value

Returns a promise that resolves to a workload `TokenSource`.

***

## `fromBearerToken()`

Creates a token source from an existing bearer token. Use this when another trusted component supplies the credential.

### Example

```typescript {1,5} theme={null}
import { fromBearerToken } from "@namespacelabs/sdk/auth";
import { createDevboxClient } from "@namespacelabs/sdk/devbox";

const client = createDevboxClient({
  tokenSource: fromBearerToken(process.env.NAMESPACE_TOKEN!),
});
```

The SDK does not renew an explicitly supplied bearer token. The application is responsible for supplying a token that remains valid for its operations.

<Warning>
  Prefer workload identity federation when the environment supports OIDC. If federation is unavailable, use a scoped, expiring [revokable token](/docs/reference/cli/token-create) and keep it out of source control.
</Warning>

### API reference

```typescript theme={null}
fromBearerToken(token: string): TokenSource
```

#### Arguments and options

<ResponseField name="token" type="string" required>
  The bearer token used to authenticate Namespace requests.
</ResponseField>

#### Return value

Returns a `TokenSource` that issues the supplied bearer token.

## `TokenSource`

A token source issues a bearer token with at least the requested remaining lifetime.

```typescript theme={null}
interface TokenSource {
  issueToken(minDuration: number, force?: boolean): Promise<string>;
}
```

<ResponseField name="minDuration" type="number" required>
  The requested minimum remaining token lifetime in milliseconds.
</ResponseField>

<ResponseField name="force" type="boolean">
  Requests a fresh token instead of a cached token. A custom source should honor this when it can refresh credentials.
</ResponseField>

`DevboxClientOptions.tokenSource` accepts either a `TokenSource` or a zero-argument function that returns one, synchronously or asynchronously. The SDK invokes provider functions on first use and retries resolution on a later request if the provider rejects.

The client keeps issued tokens in memory and reuses a token while its JWT `exp` claim satisfies the requested lifetime. Tokens without an `exp` claim are treated as non-expiring. Concurrent requests that need a token share a compatible in-flight issuance request. Namespace RPC, gateway, and SSH authentication request at least five minutes of remaining validity.

User credentials created by `devbox login` include a session credential. The SDK uses it to issue short-lived bearer tokens, caches those bearer tokens beside the user token file, and refreshes them before they have too little validity for an operation. Explicit and workload bearer tokens have no session-based refresh.

***

## `bearerAuthInterceptor()`

Create a Connect interceptor that authenticates Devbox RPCs with tokens from a `TokenSource`. Use it when supplying a custom transport through `DevboxClientOptions.transport`.

### Example

When `DevboxClientOptions.transport` is set, the SDK uses that Connect transport as-is. Add `bearerAuthInterceptor()` to authenticate Devbox RPCs. Continue to pass the same `tokenSource`, because the SDK also uses it to authenticate gateway and SSH connections.

```typescript {6-11,14-16} theme={null}
import { createConnectTransport } from "@connectrpc/connect-node";
import { bearerAuthInterceptor } from "@namespacelabs/sdk/api";
import { loadDefaults } from "@namespacelabs/sdk/auth";
import { createDevboxClient } from "@namespacelabs/sdk/devbox";

const tokenSource = await loadDefaults();
const transport = createConnectTransport({
  httpVersion: "1.1",
  baseUrl: "https://private-api.iad.namespaceapis.com",
  useBinaryFormat: false,
  interceptors: [bearerAuthInterceptor(tokenSource)],
});

const client = createDevboxClient({
  transport,
  tokenSource,
});
```

### API reference

```typescript theme={null}
bearerAuthInterceptor(source: TokenSourceInput, minDuration?: number): Interceptor
```

#### Arguments and options

<ResponseField name="source" type="TokenSourceInput" required>
  A `TokenSource`, or a zero-argument function that returns one synchronously or asynchronously. The interceptor wraps it with the SDK's in-memory token cache. Passing a source already wrapped by the SDK is a no-op.
</ResponseField>

<ResponseField name="minDuration" type="number" default="300000">
  The minimum remaining token validity requested for every RPC, in milliseconds. The default is the SDK-wide five-minute minimum.
</ResponseField>

#### Return value

Returns a Connect `Interceptor` that obtains a token before each request and adds it as an `Authorization: Bearer` header. Valid tokens are reused, and concurrent requests share a compatible in-flight issuance request.

The interceptor does not refresh a token and retry a request after the server rejects its authentication. The resulting Connect error is returned to the caller.

Omitting the interceptor leaves RPCs unauthenticated. Omitting `tokenSource` from the client makes gateway authentication fall back to default credentials rather than the credentials used by the custom transport.

## Authentication errors

`loadDefaults()` and `loadUserToken()` reject with `NotLoggedInError` when the user token file is missing. Prompt the user to run `devbox login` or handle the error by name:

```typescript {5-7} theme={null}
import { NotLoggedInError, loadUserToken } from "@namespacelabs/sdk/auth";

try {
  await loadUserToken();
} catch (error) {
  if (error instanceof NotLoggedInError) {
    console.error("Run `devbox login` and try again.");
  } else {
    throw error;
  }
}
```

Malformed token files, token issuance failures, and rejected RPC authentication surface as their underlying file, JSON, or Connect errors. Gateway HTTP failures, including rejected gateway credentials, surface as `DevboxGatewayError`. See [Handle errors and timeouts](/docs/reference/typescript-sdk/errors).

## Related documentation

<Columns cols={2}>
  <Card title="TypeScript SDK overview" icon="code-xml" href="/docs/reference/typescript-sdk">
    Install the SDK and create a Devbox client.
  </Card>

  <Card title="Handle errors and timeouts" icon="triangle-alert" href="/docs/reference/typescript-sdk/errors">
    Handle authentication, RPC, gateway, timeout, and abort failures.
  </Card>
</Columns>
