Documentation
¶
Index ¶
- Constants
- Variables
- func AgentIDFromContext(ctx interface{ ... }) uuid.UUID
- func AgentMiddleware(jwtSecret string) func(http.Handler) http.Handler
- func AudienceContains(aud jwt.ClaimStrings, target string) bool
- func BearerToken(header string) (string, error)
- func CheckPassword(hash, password string) error
- func GenerateTempPassword() (string, error)
- func HashIntegrationToken(token string) []byte
- func HashPassword(password string) (string, error)
- func IssueAgentToken(secret string, agentID uuid.UUID) (string, error)
- func IssueOAuthAccessToken(secret string, userID uuid.UUID, ...) (string, error)
- func IssueRefreshToken(secret string, userID uuid.UUID, email, displayName, tenantRole string, ...) (string, error)
- func IssueToken(secret string, userID uuid.UUID, email, displayName, tenantRole string, ...) (string, error)
- func IssueTokenWithDuration(secret string, userID uuid.UUID, email, displayName, tenantRole string, ...) (string, error)
- func Middleware(jwtSecret string) func(http.Handler) http.Handler
- func NewIntegrationToken() (string, []byte, error)
- func RequireTenantRole(minRole Role) func(http.Handler) http.Handler
- func ScopeContains(scope, required string) bool
- func UserIDFromContext(ctx context.Context) uuid.UUID
- func ValidatePasswordStrength(password string, userInputs []string) error
- type AgentClaims
- type Claims
- type Role
Constants ¶
const ( AccessTokenDuration = 15 * time.Minute RefreshTokenDuration = 7 * 24 * time.Hour )
const MinPasswordScore = 3
MinPasswordScore is the minimum acceptable zxcvbn strength score (0–4) for a user-chosen password. The frontend meter enforces the same threshold via @zxcvbn-ts, so "acceptable" means the same thing on both sides and a password the meter shows as green is one the backend will accept. 3 ("safely unguessable") is the floor for an internet-facing login.
Variables ¶
var ErrWeakPassword = errors.New("password is too weak: choose a longer or less predictable password")
ErrWeakPassword is returned by ValidatePasswordStrength for passwords below MinPasswordScore.
Functions ¶
func AgentIDFromContext ¶
AgentIDFromContext returns the agent UUID from context, set by AgentMiddleware.
func AgentMiddleware ¶
AgentMiddleware validates the agent JWT Bearer token and injects AgentClaims into context.
func AudienceContains ¶
func AudienceContains(aud jwt.ClaimStrings, target string) bool
AudienceContains reports whether `target` is one of the audience values in the claim. Helper for the MCP endpoint's audience check.
func BearerToken ¶ added in v0.4.0
BearerToken extracts a strict Authorization bearer token.
func CheckPassword ¶
CheckPassword compares a plaintext password with a bcrypt hash. Returns nil on success, error on mismatch.
func GenerateTempPassword ¶ added in v0.4.0
GenerateTempPassword returns a random, high-entropy temporary password (lowercase base32, 24 chars). Used for admin-provisioned users and the `airlock auth reset` break-glass; the recipient is forced to change it or register a passkey on first login. It comfortably clears MinPasswordScore.
func HashIntegrationToken ¶ added in v0.4.0
HashIntegrationToken validates and hashes an integration token for lookup. Invalid token shapes return nil so they cannot match a database row.
func HashPassword ¶
HashPassword hashes a plaintext password using bcrypt.
func IssueAgentToken ¶
IssueAgentToken creates a signed JWT for an agent container (100-year expiry).
func IssueOAuthAccessToken ¶ added in v0.4.0
func IssueOAuthAccessToken(secret string, userID uuid.UUID, email, tenantRole, clientID, scope, audience string) (string, error)
IssueOAuthAccessToken mints a JWT with the OAuth-side claims set (ClientID + Scope + Audience). 15-min lifetime, same HS256 secret as the web-login tokens.
This is the SOLE call site in the codebase that sets ClientID. Anything else producing a JWT must not — the MCP endpoint uses ClientID's presence to decide whether the audience check should run. The web-login IssueToken / IssueTokenWithDuration paths deliberately keep ClientID empty.
audience is the canonical resource URL, e.g. "https://airlock.example.com/api/agent/<uuid>/mcp". scope is the space-delimited scope string ("mcp" in v1).
func IssueRefreshToken ¶
func IssueRefreshToken(secret string, userID uuid.UUID, email, displayName, tenantRole string, mustChangePassword bool) (string, error)
IssueRefreshToken creates a signed refresh token (7 days).
func IssueToken ¶
func IssueToken(secret string, userID uuid.UUID, email, displayName, tenantRole string, mustChangePassword bool) (string, error)
IssueToken creates a signed access token (15 min).
func IssueTokenWithDuration ¶
func IssueTokenWithDuration(secret string, userID uuid.UUID, email, displayName, tenantRole string, mustChangePassword bool, duration time.Duration) (string, error)
IssueTokenWithDuration creates a signed token with a custom duration.
func Middleware ¶
Middleware validates the JWT Bearer token and injects claims into context. It returns 401 for missing, invalid, or expired tokens.
func NewIntegrationToken ¶ added in v0.4.0
NewIntegrationToken returns a build-scoped bearer token and the hash Airlock stores. The plaintext token exists only in the codegen toolserver environment.
func RequireTenantRole ¶
RequireTenantRole returns middleware that checks the user has at least the given role. Uses hierarchy: admin > manager > user. Returns 403 if not authorized.
func ScopeContains ¶ added in v0.4.0
ScopeContains reports whether `required` is present in a space-delimited scope string. OAuth 2.0 scopes are case-sensitive (RFC 6749 §3.3); we match exactly.
func UserIDFromContext ¶
UserIDFromContext returns the user ID from the JWT claims.
func ValidatePasswordStrength ¶ added in v0.4.0
ValidatePasswordStrength rejects weak passwords using zxcvbn. userInputs are context strings (email, display name) that count against the score when the password contains them. Returns nil when the password is strong enough.
Types ¶
type AgentClaims ¶
type AgentClaims struct {
jwt.RegisteredClaims
AgentID string `json:"agent_id"`
}
AgentClaims are the JWT claims for agent tokens.
func ValidateAgentToken ¶
func ValidateAgentToken(secret, tokenString string) (*AgentClaims, error)
ValidateAgentToken validates a JWT and returns the agent claims. Rejects tokens that do not have an AgentID claim.
type Claims ¶
type Claims struct {
jwt.RegisteredClaims
Email string `json:"email"`
// DisplayName is the user's display name, carried so proxied agent
// requests can forward it (X-User-Name) without a DB lookup. Display
// claim only — never used for authorization. Empty for OAuth/MCP tokens.
DisplayName string `json:"name,omitempty"`
TenantRole string `json:"tenant_role"`
ClientID string `json:"client_id,omitempty"`
Scope string `json:"scope,omitempty"`
// MustChangePassword mirrors users.must_change_password. When true the
// /api/v1 secured-account gate restricts the token to the account-securing
// endpoints until the user sets a strong password or registers a passkey,
// then receives a fresh token with the flag cleared. Web-login tokens carry
// the live value at issue; OAuth/MCP tokens never set it.
MustChangePassword bool `json:"must_change_password,omitempty"`
}
Claims are the JWT claims for Airlock tokens.
ClientID and Scope are populated ONLY by IssueOAuthAccessToken (the MCP server-side OAuth flow). Web-login tokens never carry them — the presence of ClientID is the discriminator the MCP endpoint uses to branch between "OAuth access token, verify aud+scope+client" and "web-login JWT, accept as-is". Anything else in this codebase that sets ClientID would break that discrimination — see the Test_UserJWTHasNoClientID invariant in jwt_test.go.
Audience comes from jwt.RegisteredClaims and binds an OAuth access token to a specific agent's MCP URL (RFC 8707 resource indicators).
func ClaimsFromContext ¶
ClaimsFromContext retrieves the JWT claims from the context.
func ValidateToken ¶
ValidateToken validates a JWT and returns the claims.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package lockout implements per-(email, ip) login throttling for the airlock auth path.
|
Package lockout implements per-(email, ip) login throttling for the airlock auth path. |
|
Package passkey wraps go-webauthn for airlock's human login.
|
Package passkey wraps go-webauthn for airlock's human login. |