DeepBlue Dynamics / Docs / Authentication

Authentication

One identity, every service. auth.nuts.services issues tokens that work across GrubCrawler, Ferricula, Shivvr, and anything else in the fleet that needs auth.

Get a token at auth.nuts.services/dashboard — sign in with email magic-link, Google, or GitHub, then generate an ahp_ API token. The same token works against every service that points GNOSIS_AUTH_URL at https://auth.nuts.services.

Token shapes

Three kinds of tokens, all issued and validated by auth.nuts.services:

Token Shape Lifetime Use
Browser JWTeyJ... (3-part b64)24 hoursWeb app sessions after magic-link / OAuth login
API tokenahp_<opaque>1 yearScripts, AI agents, MCP clients, CI
Internal HMAC<b64>.<b64> (2-part)SecondsPre-signed query-string URLs (service-internal)

API token flow (scripts & agents)

Generate once, use forever (or until you revoke it). Pass as the Bearer header on every request:

curl -X POST https://grub.nuts.services/api/crawl \
  -H "Authorization: Bearer ahp_yourtoken" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

The receiving service exchanges your ahp_ token for a fresh JWT at POST auth.nuts.services/auth, extracts your email from the JWT claims, and uses it to partition storage. You don't need to manage refresh — the exchange happens server-side on every authenticated request.

Browser login flow (web apps)

For services with a web UI, redirect users to auth.nuts.services with a return_url back to your callback. nuts-auth handles email magic-link, Google, and GitHub — you handle the token storage afterwards.

  1. Redirect to:
    https://auth.nuts.services/login?return_url=https://your-service.com/auth/callback
  2. User signs in (email magic-link, Google, or GitHub).
  3. auth.nuts.services redirects back to your return_url with the JWT in the query string:
    https://your-service.com/auth/callback?token=eyJ...
  4. Stash the token (typically localStorage.nuts_session_token) and send it as the Bearer header on subsequent API calls.
// On your login button
function login() {
  const returnUrl = encodeURIComponent(window.location.origin + '/auth/callback');
  window.location.href = `https://auth.nuts.services/login?return_url=${returnUrl}`;
}

// On your /auth/callback page
const token = new URLSearchParams(location.search).get('token');
if (token) {
  localStorage.setItem('nuts_session_token', token);
  history.replaceState({}, '', '/dashboard');
}

Cross-origin tip: localStorage is per-origin. To open the auth.nuts.services dashboard from another service without forcing a re-login, append your JWT to the URL: https://auth.nuts.services/dashboard?token=<your-jwt>. The auth dashboard auto-captures it.

Validating tokens server-side

Services validate by routing based on token shape. Use the N.U.T.S. integration guide for the validation library, or hand-roll:

Token starts with Validate via
ahp_POST auth.nuts.services/auth with token=<ahp_...> form body. Returns a JWT with the user's email in the sub claim.
eyJ (3 dots)GET auth.nuts.services/api/verify with Authorization: Bearer <jwt>. Returns the decoded claims if valid; 401 if expired.

Self-hosted: disabling auth

When running services on a private network or behind a corporate firewall, set DISABLE_AUTH=true to skip token validation entirely. Pass a customer_id in the request body to scope storage:

docker run -p 6792:6792 -e DISABLE_AUTH=true deepbluedynamics/grubcrawler

Don't expose DISABLE_AUTH=true services to the public internet. There's no rate limiting or access control when auth is disabled — anyone who reaches the endpoint can use it.

Per-service auth details: GrubCrawler · N.U.T.S.