Use the sign-in system your team already uses. Artifacts can connect to Google, GitHub, Microsoft Entra ID, Okta, Auth0, Keycloak, and other services that support OAuth 2.0 or OpenID Connect (OIDC), the standard protocols for signing in through another service. That includes company single sign-on (SSO) and identity services you host yourself.
This works through Better Auth, the authentication library built into Artifacts. It connects Artifacts to your chosen sign-in service, keeps users signed in, and handles authorization for coding agents. You configure that connection in your Artifacts deployment; your users continue signing in with their existing accounts. They do not need a Better Auth account.
The provider list is open-ended: Better Auth includes ready-made integrations and a generic OAuth/OIDC integration for other providers. Standard providers can be configured through deployment settings; custom behavior can be added in TypeScript. This works on both Cloudflare and your own servers with celld.
For Cloudflare deployments, Cloudflare Access is an alternative that handles sign-in at Cloudflare's edge. You do not need Access to use Artifacts on Cloudflare.
Your deployment administrator chooses the provider and who can sign in. There is no central Artifacts account, and users do not need Cloudflare or object-store credentials. The default local host needs no sign-in.
| What you want to do | Start here |
|---|---|
| Sign in or connect your coding agent | Connect to an existing deployment |
| Understand personal, team, and public access | Library and URL permissions |
| Set up sign-in for your deployment | Choose an authentication mode |
| Fix a failed sign-in or connection | Troubleshooting |
| Build a custom MCP client | Credential and OAuth reference |
Gallery: open your deployment's URL, such as https://artifacts.example.com,
and sign in with one of its configured providers. The deployment administrator
must admit your account. Choose My library or Team library in the gallery.
Coding agent: add a remote HTTP MCP server using one of these URLs:
https://artifacts.example.com/mcp
https://artifacts.example.com/mcp?library=team&workspace=default
The first selects your personal library and the default workspace. The second selects the shared team library. Use the same hostname as the gallery. Your MCP client must support OAuth: connect, complete provider sign-in in the browser, and approve access when prompted. Client registration is handled by the OAuth flow; you do not need to supply a client ID or secret for a supported public client. Check the connection by asking your agent to list artifacts and scripts without creating or changing anything.
Local host: artifacts host serves http://127.0.0.1:4786 and
http://127.0.0.1:4786/mcp without sign-in in its default local configuration.
For a shared or network deployment, an administrator must configure sign-in;
see the setup below and the celld deployment guide.
Personal libraries are private to the verified user. Team library content
can be read and edited by every user admitted to the deployment. There are no
separate team viewer/editor roles. workspace organizes content inside a library;
it does not grant access. Each deployment represents one team. Infrastructure
administrators can still administer the underlying storage.
Private URLs use the item's current library ownership. Public URLs allow external callers to view an app or invoke a script, without granting management access to its source. Public scripts can verify an application bearer token or webhook signature using their stored secrets. The gateway strips management cookies and Access credentials before calling user code; private routes also strip the management authorization header. See script access.
If Access protects the entire hostname, add an edge bypass for the specific public paths you want external callers to reach. Setting a link to public does not override Cloudflare's policy. Keep management paths protected.
Moving an item between personal and team libraries preserves its data, secrets, schedule, and URL; its public/private URL setting remains unchanged. Management and private-URL permissions follow the new owner.
This section is for the person deploying Artifacts. Choose where sign-in happens:
- In Artifacts: connect your existing sign-in service using the built-in Better Auth library. Follow Set up Better Auth.
- At Cloudflare's edge: use Cloudflare Access for a Cloudflare deployment. Follow Set up Cloudflare Access.
| Mode | Use when | Required setup |
|---|---|---|
better-auth |
You want provider sign-in hosted by Artifacts on Cloudflare or celld | Provider application, admission rule, deployment secret, and persistent AUTH_DB database with migrations |
access |
Cloudflare Access protects your deployment | Access application, user policy, managed OAuth for MCP, ACCESS_TEAM_DOMAIN, and ACCESS_AUD |
The checked-in Wrangler configuration defaults to access. Set AUTH_MODE
explicitly for your deployment. Missing Access configuration returns 503 outside
the local bypass. Better Auth permits provider sign-in only; email/password
registration is disabled.
The following commands run from the repository root for a Cloudflare deployment. For celld, use the same provider and admission settings, with the runtime-specific database and deployment steps in Configure celld.
Merge these values into vars in wrangler.jsonc, replacing the example hostname
and email domain:
{
"ENVIRONMENT": "production",
"AUTH_MODE": "better-auth",
"BETTER_AUTH_URL": "https://artifacts.example.com",
"BETTER_AUTH_ALLOWED_DOMAINS": "example.com",
"BETTER_AUTH_TRUSTED_IP_HEADER": "cf-connecting-ip"
}BETTER_AUTH_URL must be the stable HTTPS origin users and clients actually open,
without a path, query, or fragment. HTTP is allowed only on loopback development
hosts. If you also configure ARTIFACTS_PUBLIC_ORIGIN for generated links, use the
same external origin; that setting does not configure authentication.
Configure at least one admission rule:
| Setting | Admits |
|---|---|
BETTER_AUTH_ALLOWED_EMAILS |
Exact email addresses, separated by commas or whitespace |
BETTER_AUTH_ALLOWED_DOMAINS |
Exact email domains, separated by commas or whitespace |
BETTER_AUTH_ALLOW_ALL_USERS="true" |
Every identity authenticated by the configured providers |
Email/domain matches are case-insensitive and require a verified provider email. Either allowlist may admit a user; domain rules do not implicitly include subdomains. With no admission rule, nobody is admitted. Use allow-all only when the provider already enforces your intended membership boundary. Provider hints such as Google's hosted domain do not replace Artifacts admission rules.
Choose the provider your users already use. Artifacts passes provider settings to Better Auth rather than maintaining its own list of supported providers:
| Provider integration | Configure it with |
|---|---|
| A social provider supported by the installed Better Auth version, such as Google or GitHub | BETTER_AUTH_SOCIAL_PROVIDERS |
| Company SSO or a self-hosted identity service using OIDC, such as Microsoft Entra ID, Okta, Auth0, or Keycloak | BETTER_AUTH_OIDC_PROVIDERS |
| A provider needing custom OAuth endpoints, profile mapping, callbacks, or a plugin | Better Auth's generic OAuth options or the TypeScript configuration in teamProviderOptions |
You can configure multiple providers. Users choose from them on the sign-in page. The examples below show the configuration shape; use your provider's application registration instructions to obtain a client ID and client secret.
Create an OAuth application with your identity provider. Register the callback matching its configured provider ID:
https://artifacts.example.com/api/auth/callback/google
https://artifacts.example.com/api/auth/callback/github
https://artifacts.example.com/api/auth/callback/company
For Google or GitHub, store BETTER_AUTH_SOCIAL_PROVIDERS as a Worker secret
containing JSON in Better Auth's native provider format. Choose one or combine both:
{
"google": { "clientId": "<google-client-id>", "clientSecret": "<google-client-secret>" },
"github": { "clientId": "<github-client-id>", "clientSecret": "<github-client-secret>" }
}bun x wrangler secret put BETTER_AUTH_SOCIAL_PROVIDERSPaste the provider JSON at the prompt. Other social providers supported by the installed Better Auth version use the same setting.
For a discovery-based OpenID Connect provider, store this JSON array in
BETTER_AUTH_OIDC_PROVIDERS instead. The company provider ID corresponds to the
third callback above:
[
{
"providerId": "company",
"name": "Company SSO",
"discoveryUrl": "https://id.example.com/.well-known/openid-configuration",
"clientId": "<oidc-client-id>",
"clientSecret": "<oidc-client-secret>",
"scopes": ["openid", "profile", "email"],
"requireIdTokenVerification": true
}
]bun x wrangler secret put BETTER_AUTH_OIDC_PROVIDERSBoth provider settings can coexist. The sign-in page discovers configured
providers; it has no fixed provider list. For custom profile mapping, callbacks,
or additional provider plugins, extend
teamProviderOptions in ordinary TypeScript.
Generate a deployment-specific secret, then paste it at Wrangler's prompt:
openssl rand -base64 32
bun x wrangler secret put BETTER_AUTH_SECRETThe secret must contain at least 32 random characters. Keep it and provider
credentials out of committed configuration. Better Auth encrypts stored provider
access and refresh tokens and manages its RS256 signing keys in AUTH_DB.
Create the database once:
bun x wrangler d1 create artifacts-auth --binding AUTH_DBAdd its returned ID to the d1_databases array in wrangler.jsonc:
{
"d1_databases": [
{
"binding": "AUTH_DB",
"database_name": "artifacts-auth",
"database_id": "<database-id-returned-by-wrangler>",
"migrations_dir": "cloudflare/migrations"
}
]
}Apply migrations before serving Better Auth traffic, then deploy:
bun x wrangler d1 migrations apply AUTH_DB --remote
bun run deploy:cloudflareThis database stores users, sessions, OAuth grants, signing keys, and auth rate limits. It is separate from each artifact's runtime SQLite database. Startup does not automatically detect or repair a missing or outdated auth schema.
Open the configured public origin and sign in with an admitted account. Then
connect an OAuth-capable MCP client using the URLs in
Connect to an existing deployment.
An unauthenticated /mcp request should return 401 with a WWW-Authenticate
challenge pointing to this deployment's resource metadata:
curl -i https://artifacts.example.com/mcpA successful /health response checks the runtime only; it does not verify auth
configuration, database migrations, or access to your library.
Keep the AUTH_DB binding above and apply migrations to workerd's local database:
bun x wrangler d1 migrations apply AUTH_DB --localCreate an ignored .dev.vars file with these settings, substituting real local
provider credentials and a generated secret:
AUTH_MODE="better-auth"
BETTER_AUTH_URL="http://127.0.0.1:4785"
BETTER_AUTH_SECRET="<generated-secret-at-least-32-characters>"
BETTER_AUTH_ALLOWED_EMAILS="you@example.com"
BETTER_AUTH_SOCIAL_PROVIDERS='{"github":{"clientId":"<client-id>","clientSecret":"<client-secret>"}}'Register http://127.0.0.1:4785/api/auth/callback/github with that provider, then
run bun run dev:cloudflare. Use this exact host and port for the gallery, MCP,
and callback; localhost and 127.0.0.1 are different origins.
Use this alternative when Cloudflare Access protects your deployment. It replaces the Better Auth setup above; you do not need to configure both.
- Protect every hostname that reaches the Worker, including
workers.dev, custom domains, and preview URLs. Cover the gallery, assets, and/mcp. - Configure an identity provider and an Access policy admitting the intended users. Cloudflare account membership and Artifacts access are separate.
- Enable Access managed OAuth for the application so MCP clients can complete OAuth sign-in. In Zero Trust, open Access controls > Applications, edit the application, and enable Managed OAuth under Advanced settings. Allow the redirect URIs used by your clients, including localhost/loopback callbacks for desktop clients.
- Set these
varsinwrangler.jsonc, then redeploy:
{
"ENVIRONMENT": "production",
"AUTH_MODE": "access",
"ACCESS_TEAM_DOMAIN": "your-team.cloudflareaccess.com",
"ACCESS_AUD": "<access-application-audience>"
}The domain and audience are identifiers, not secrets. Access mode needs no
AUTH_DB or Better Auth provider configuration. Artifacts verifies the forwarded
Cf-Access-Jwt-Assertion signature, issuer, audience, subject, and expiry. Managed
OAuth resolves the client's token at Cloudflare's edge; Artifacts does not validate
that opaque token itself. An arbitrary bearer token or caller-supplied identity
header cannot replace the signed assertion.
Use the celld deployment guide for preparing the Worker,
deploying to bucket storage, and running nodes. Better Auth uses the same public
origin, providers, secret, and admission rules on celld. Configure a persistent
AUTH_DB D1-compatible binding and apply the checked-in SQL migrations to that
runtime's database before enabling traffic. Wrangler's --remote command above
migrates Cloudflare D1; it does not migrate celld storage.
Generated celld configs carry an AUTH_DB binding from wrangler.jsonc, but set
ENVIRONMENT=local for development. Set it to production in the prepared
deployment config. artifacts host is the loopback development host; it does not
provision a production fleet or configure provider sign-in for you.
Access mode requires Cloudflare Access in front of celld and a verified assertion
on protected requests. A different reverse proxy does not automatically supply
that credential. For Better Auth, set BETTER_AUTH_TRUSTED_IP_HEADER only to a
header your trusted proxy replaces. Otherwise omit it; auth requests share a
per-path rate-limit bucket instead of trusting caller-supplied IP headers.
Keep the prepared project's migrations_dir inside that project and copy
cloudflare/migrations there. After deploying the configured Worker and starting
a node, apply its auth migrations before admitting application traffic. Follow
the pinned celld runtime's D1 operations reference
for the migration command and bucket configuration.
dev:celld does not apply local auth migrations. celld's migration command targets
deployed bucket storage, and workerd's local D1 is a separate database. Use the
workerd local setup for routine auth development;
the celld auth integration fixture bootstraps its own temporary schema.
| Symptom | Check or next action |
|---|---|
| Access mode returns 503 | Set ACCESS_TEAM_DOMAIN to the complete *.cloudflareaccess.com domain and ACCESS_AUD to the correct application's audience; redeploy. |
| Access mode returns 401 or an edge login page instead of MCP | Confirm Access covers the exact hostname and /mcp, and managed OAuth is enabled. Artifacts needs a signed assertion from the edge. |
| Better Auth returns 503 / authentication unavailable | Check AUTH_DB, applied migrations, the public origin, the deployment secret, and provider JSON. Server logs contain initialization details. |
| The sign-in page has no providers | Configure BETTER_AUTH_SOCIAL_PROVIDERS or BETTER_AUTH_OIDC_PROVIDERS; for OIDC, check discovery availability and verification metadata. |
| Provider rejects the redirect URI | Match the public origin and /api/auth/callback/<provider-id> exactly, including the local host and port. |
| Provider sign-in succeeds but Artifacts denies access | Check admission rules and whether the provider reports a verified email. With no admission rule, nobody is admitted. |
| Gallery works but MCP returns 401 | Complete the MCP client's OAuth flow. A browser session cookie is not an MCP bearer token. Reconnect if its attached session expired or was signed out. |
MCP returns 403 insufficient_scope |
Request artifacts scope and the canonical /mcp resource, then authorize again. |
MCP token does not work on /api/tools or a private URL |
These routes use browser sessions in Better Auth mode. Use /mcp for OAuth programmatic management. |
| A public URL still prompts for Access sign-in | Add an Access bypass for that public path. The Artifacts link setting cannot override the edge. |
Management requests return 403 Origin is not allowed |
Use the deployment's own origin. The management API rejects cross-origin browser requests. |
| My personal library looks empty after changing auth mode | Private identities differ between Access and Better Auth; see the migration note below. |
Switching modes does not migrate personal libraries. Access identities use their issuer and subject; Better Auth identities use its user ID. Existing data is retained, but the application does not infer that two identities are the same person. Plan an explicit data migration if users need to retain their personal libraries. The shared team-library key stays unchanged, so users admitted under the new mode can access the existing team content. Use separate deployments and admission policies for unrelated teams.
Apply future auth schema migrations with the runtime's migration tooling before
serving the updated code. Keep migration history; do not regenerate the initial
migration to replace it. Maintainers can generate a new schema delta with
bun run scripts/generate-auth-migration.ts cloudflare/migrations/0002_description.sql.
Authentication configuration and token verification live in
better-auth.ts; Access verification lives in
auth.ts.
These details are for custom clients and integrations. For normal use, connect your MCP client and complete the browser sign-in described above.
| Surface | Better Auth mode | Cloudflare Access mode |
|---|---|---|
Gallery and management routes such as /api/tools |
Browser session cookie from provider sign-in | Valid signed Access assertion forwarded by Cloudflare |
/mcp |
OAuth access token issued by this deployment for its MCP resource | Managed OAuth token validated by Access, which forwards a signed assertion |
| Private app or script URL | Browser session, with the current owner's library permissions | Valid Access assertion, with the current owner's library permissions |
| Public app or script URL | No Artifacts sign-in; the handler can require application credentials | No Artifacts sign-in; the Access edge policy must also permit the path |
Artifacts does not currently issue personal API keys or support a client-credentials
grant for unattended management. In Better Auth mode, sending an MCP access token
to /api/tools does not authenticate that request: OAuth bearer authentication is
implemented only on /mcp. Use the MCP OAuth flow for programmatic management.
Provider access tokens and BETTER_AUTH_SECRET are not Artifacts API credentials.
In Better Auth mode, a custom MCP client should request the artifacts scope and
resource https://artifacts.example.com/mcp. The resource excludes library/workspace query
parameters. Discovery, authorization-code flow with PKCE, and refresh tokens are
supported. Access tokens expire after five minutes; clients should refresh them.
Signing out of the Better Auth session also invalidates its MCP access tokens.
Existing clients using the legacy canvas scope continue to work.
The artifacts scope does not distinguish read-only from write access. Clients
using DPoP-bound tokens must send the corresponding proof with each MCP request.
The default local host bypasses sign-in only with ENVIRONMENT=local, Access
mode, and a loopback request hostname. Better Auth always requires sign-in,
including during local development.