Version: closed alpha

Authentication

Three gates and a per-slice signing key — no auth service to run.

Drift gives you authentication without an auth provider. There's no JWKS endpoint to host, no session store to scale, no OAuth callback to wire up. A function declares how it's protected with one keyword in its @atomic directive, and the SDK's drift.JWT primitive mints and verifies tokens with a signing key the platform manages for you and never exposes to your code.

The three gates

The auth keyword gates a function at the platform edge, before your code runs:

ModeBehaviour
auth=nonePublic — anyone can call it. The default.
auth=apikeyRequires a valid X-API-Key header. Missing → 401, wrong → 403. Manage keys with drift atomic auth set/list/revoke.
auth=jwtRequires a valid Bearer token in the Authorization header, verified against the slice's own JWT signing key. Missing or invalid (bad signature, expired, wrong issuer) → 401.

apikey is for machine-to-machine calls; jwt is for user sessions, where you want expiry, identity, and roles. For full control — optional auth, custom error bodies, or reading claims — leave the gate at auth=none and verify the token yourself inside the handler (shown below).

JWT: issue & verify

Tokens are HS256, signed with a 32-byte key unique to your slice. You never see, set, or rotate the key — the platform holds it and uses it on both sides. The issuer (iss) is stamped as your slice's identity automatically and checked on every verify, so a token minted by one slice can't be replayed against another.

CallDoes
drift.JWT.Issue(claims)Mints a signed token. You set sub, exp (required), optional nbf/aud/jti, and a free-form custom map (roles, display name, anything JSON).
drift.JWT.Verify(token, opts)Verifies the signature and rejects an expired (exp), not-yet-valid (nbf), wrong-issuer, wrong-audience, or non-HS256 token. Returns the decoded claims, including your custom map.
Always set exp. Verification rejects a token with no expiry — an un-expiring session token is a liability, so Drift won't issue one silently.

A login → protected-route flow

The login function is public (auth=none): it checks credentials and mints a token. The protected function declares auth=jwt, so the edge verifies the token before your code runs; inside, you call Verify again only to read the claims.

Go

// @atomic http=post:login auth=none
func PostLogin(body map[string]any, req drift.Request) (int, string, any, map[string]string) {
    user, ok := checkPassword(body["email"], body["password"])  // your check, against Backbone
    if !ok {
        return 401, "Unauthorized", map[string]any{"error": "bad credentials"}, nil
    }
    token, _ := drift.JWT.Issue(drift.JWTClaims{
        Sub:    user.ID,
        Exp:    time.Now().Add(24 * time.Hour).Unix(),
        Custom: map[string]any{"role": user.Role},
    })
    return 200, "OK", map[string]any{"token": token}, nil
}

// @atomic http=get:me auth=jwt            // edge verifies the Bearer token first
func GetMe(req drift.Request) (int, string, any, map[string]string) {
    token := strings.TrimPrefix(req.Headers["Authorization"], "Bearer ")
    claims, _ := drift.JWT.Verify(token, drift.JWTVerifyOptions{})   // read claims
    return 200, "OK", claims.Custom, nil
}

Python

# @atomic http=post:login auth=none
def post_login(body, req):
    user = check_password(body["email"], body["password"])
    if not user:
        return 401, "Unauthorized", {"error": "bad credentials"}
    token = drift.jwt.issue({
        "sub": user["id"],
        "exp": int(time.time()) + 86400,        # 24h
        "custom": {"role": user["role"]},
    })
    return 200, "OK", {"token": token}

# @atomic http=get:me auth=jwt
def get_me(req):
    token  = req["headers"]["Authorization"].removeprefix("Bearer ")
    claims = drift.jwt.verify(token)
    return 200, "OK", claims["custom"]

The token rides in the standard Authorization: Bearer <token> header — a browser frontend served from Canvas calls /api/me same-origin and adds that header, no CORS in the way. Store the token however your client prefers.

Atomic guide → · SDK reference →