What Authentication Methods Does Multica Support? JWT, PAT, OAuth, and Daemon Tokens Explained
Multica supports four distinct authentication mechanisms: JWT tokens for user sessions, Personal Access Tokens (PAT) for API access, Google OAuth for third-party sign-in, and Daemon Tokens for machine-to-machine authentication.
The multica-ai/multica repository implements a comprehensive authentication layer in its Go backend that covers interactive users, programmatic clients, and background daemon processes. Understanding what authentication methods Multica supports is essential for securely integrating with its REST API, whether you are building a web frontend, automating CI/CD pipelines, or deploying persistent agent daemons.
JSON Web Tokens (JWT)
Multica uses JWT (JSON Web Token) as its default session mechanism for authenticated users. These tokens are signed with HMAC-SHA256 and contain claims for the user ID, email, name, expiration (exp), and issued-at timestamp (iat).
JWT Generation
After successful email-code verification or Google OAuth login, the backend issues a JWT through the issueJWT function in server/internal/handler/auth.go:
func (h *Handler) issueJWT(user db.User) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": uuidToString(user.ID), // user ID
"email": user.Email,
"name": user.Name,
"exp": time.Now().Add(30 * 24 * time.Hour).Unix(),
"iat": time.Now().Unix(),
})
return token.SignedString(auth.JWTSecret())
}
The signing secret originates from the JWT_SECRET environment variable (with a development fallback).
JWT Validation
Incoming requests are validated by the Auth middleware in server/internal/middleware/auth.go. The middleware extracts the Authorization: Bearer <token> header, parses the JWT using the shared secret, and injects X-User-ID and X-User-Email headers into the request context:
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, jwt.ErrSignatureInvalid
}
return auth.JWTSecret(), nil
})
Personal Access Tokens (PAT)
For programmatic API access, Multica implements Personal Access Tokens (PAT). These are long-lived credentials that allow scripts and integrations to authenticate without user sessions.
PAT Structure and Generation
PATs are random 40-character hexadecimal strings prefixed with mul_. The GeneratePATToken function in server/internal/auth/jwt.go creates these tokens using cryptographic randomness:
func GeneratePATToken() (string, error) {
b := make([]byte, 20) // 20 bytes → 40 hex chars
if _, err := rand.Read(b); err != nil {
return "", fmt.Errorf("generate PAT token: %w", err)
}
return "mul_" + hex.EncodeToString(b), nil
}
Secure Storage with SHA-256 Hashing
Security is enforced by never storing the raw token. Instead, the HashToken function computes a SHA-256 hash for database storage in the personal_access_tokens table:
func HashToken(token string) string {
h := sha256.Sum256([]byte(token))
return hex.EncodeToString(h[:])
}
PAT Validation in Middleware
When a request includes a mul_ prefixed token, the middleware in server/internal/middleware/auth.go hashes the presented token and queries the database for a matching hash:
if strings.HasPrefix(tokenString, "mul_") {
hash := auth.HashToken(tokenString)
pat, err := queries.GetPersonalAccessTokenByHash(r.Context(), hash)
// …
}
Google OAuth Integration
Multica supports Google OAuth as a third-party authentication provider, enabling users to sign in with their Google accounts.
OAuth Flow Implementation
The GoogleLogin handler in server/internal/handler/auth.go manages the server-side exchange:
// Exchange the one‑time code for an access token.
tokenResp, err := http.PostForm("https://oauth2.googleapis.com/token", url.Values{
"code": {req.Code},
"client_id": {clientID},
"client_secret": {clientSecret},
"redirect_uri": {redirectURI},
"grant_type": {"authorization_code"},
})
After exchanging the authorization code for an access token, the handler fetches the user's Google profile, creates or updates the local user record, and issues a standard JWT for session management.
Daemon Tokens
For machine-to-machine authentication between background agents and the API, Multica uses Daemon Tokens prefixed with mdt_.
Daemon Token Generation
Generated similarly to PATs via GenerateDaemonToken in server/internal/auth/jwt.go, these tokens use a distinct prefix to trigger separate validation logic:
func GenerateDaemonToken() (string, error) {
b := make([]byte, 20)
if _, err := rand.Read(b); err != nil {
return "", fmt.Errorf("generate daemon token: %w", err)
}
return "mdt_" + hex.EncodeToString(b), nil
}
Workspace-Scoped Validation
The DaemonAuth middleware in server/internal/middleware/daemon_auth.go validates these tokens. It checks not only the token's validity against the daemon_tokens table but also enforces that the daemon can only access its assigned workspace:
if strings.HasPrefix(tokenString, "mdt_") {
// look up token, verify workspace, then set X-Workspace-ID, X-User-ID …
}
Practical Code Examples
Authenticating with a JWT
curl -H "Authorization: Bearer <jwt-token>" \
https://api.multica.ai/v1/issues
Using a Personal Access Token
curl -H "Authorization: Bearer mul_abcdef1234..." \
https://api.multica.ai/v1/projects
Daemon Token Usage
curl -H "Authorization: Bearer mdt_1234abcd..." \
-H "X-Workspace-ID: workspace-abc123" \
https://api.multica.ai/v1/daemon/poll
Summary
- JWT tokens provide temporary user sessions after email or OAuth login, validated by HMAC-SHA256 signatures in
server/internal/middleware/auth.go. - Personal Access Tokens offer long-lived API credentials using
mul_prefixed hashes stored in thepersonal_access_tokenstable. - Google OAuth enables third-party sign-in via the
GoogleLoginhandler, which exchanges codes for profiles and issues standard JWTs. - Daemon Tokens facilitate secure machine-to-machine authentication with workspace scoping via the
DaemonAuthmiddleware.
Frequently Asked Questions
Can I use Multica without Google OAuth?
Yes. Multica supports email-based authentication with verification codes, which issues standard JWT tokens upon successful validation. OAuth is optional and implemented alongside the native email-code flow.
How are tokens revoked in Multica?
PATs and Daemon Tokens are revoked by deleting their SHA-256 hash entries from the respective database tables (personal_access_tokens or daemon_tokens). JWTs expire automatically based on their exp claim and cannot be individually revoked before expiration.
What is the difference between a PAT and a Daemon Token?
While both use similar cryptographic generation in server/internal/auth/jwt.go, PATs (mul_ prefix) are validated by the standard Auth middleware for user-level API access, whereas Daemon Tokens (mdt_ prefix) are processed by the DaemonAuth middleware which additionally enforces workspace isolation for background processes.
Is the JWT secret configurable?
Yes. The JWT_SECRET environment variable configures the HMAC-SHA256 signing key. If not provided, the system uses a development default, which should not be used in production environments.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →