Skip to main content Startseite Ersteller stacksjs bench-review stacks-auth
stacks-auth Use when implementing authentication, authorization, passkeys, TOTP/2FA, RBAC, gates, policies, session auth, token management, email verification, password resets, or rate limiting in a Stacks application. Covers the @stacksjs/auth package, config/auth.ts, app/Gates.ts, and app/Middleware/.
Zur Installation springen Skills Marktplatz Entdecken und erkunden Sie KI-Skills, die von der Community erstellt wurden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Prompt kopierenPrompt-Details anzeigen Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
npx skills add https://github.com/stacksjs/bench-review --skill stacks-authDer Befehl bleibt in einer Zeile. Scrollen Sie horizontal, um ihn vor dem Kopieren vollständig zu prüfen.
Sie bevorzugen eine lokale Kopie? Laden Sie die Dateien herunter, die SkillsMP derzeit vorliegen.
ZIP herunterladen Herunterladen... Mehr aus diesem Repository
Use when integrating AI capabilities into a Stacks application — using Anthropic/OpenAI/Ollama/AWS Bedrock drivers, image generation (DALL-E), vision analysis, RAG/vector search, embeddings, MCP (Model Context Protocol) clients, text summarization, sentiment analysis, content classification, personalization, or the buddy AI assistant. Covers @stacksjs/ai and config/ai.ts.
Verwandte Berufe SOC
Basierend auf der SOC-Berufsklassifikation
name stacks-auth description Use when implementing authentication, authorization, passkeys, TOTP/2FA, RBAC, gates, policies, session auth, token management, email verification, password resets, or rate limiting in a Stacks application. Covers the @stacksjs/auth package, config/auth.ts, app/Gates.ts, and app/Middleware/. license MIT compatibility Bun >= 1.3.0, TypeScript allowed-tools Read Edit Write Bash Grep Glob
Stacks Authentication & Authorization
The @stacksjs/auth package provides comprehensive authentication and authorization for Stacks applications, built on @stacksjs/ts-auth.
Key Paths
Core package source: storage/framework/core/auth/src/
Configuration: config/auth.ts
Security config: config/security.ts
Hashing config: config/hashing.ts
Application gates: app/Gates.ts
Application middleware: app/Middleware/
Middleware aliases: app/Middleware.ts
Auth types:
storage/framework/core/types/src/auth.ts
Source Files auth/src/
├── index.ts # All re-exports
├── authentication.ts # Auth class - core auth logic
├── authenticator.ts # 2FA and personal access client
├── client.ts # Client re-exports
├── middleware.ts # Auth middleware handler
├── rate-limiter.ts # RateLimiter class (5 attempts, 15min lockout)
├── passkey.ts # WebAuthn/Passkey support
├── password/reset.ts # Password reset flow
├── register.ts # User registration
├── user.ts # Auth user helpers
├── tokens.ts # Token CRUD, scopes, refresh tokens, OAuth clients
├── gate.ts # Authorization gates & policies
├── policy.ts # BasePolicy class + discovery
├── authorizable.ts # User authorization mixin
├── rbac.ts # Full RBAC system
├── email-verification.ts # Email verification flow
└── session-auth.ts # Session-based SPA auth
Auth Class (authentication.ts) — Static Methods
Login & Authentication
Auth.attempt(credentials: AuthCredentials): Promise<boolean> — validate credentials without creating token
Auth.validate(credentials: AuthCredentials): Promise<boolean> — alias for attempt
Auth.login(credentials: AuthCredentials, options?: TokenCreateOptions): Promise<{ user, token } | null> — login and create token
Auth.loginUsingId(userId: number, options?: TokenCreateOptions): Promise<{ user, token } | null> — login by user ID
Auth.logout(): Promise<void> — revoke current token
Auth.once(credentials: AuthCredentials): Promise<boolean> — one-time auth without token
Auth.requestToken(credentials, clientId, clientSecret): Promise<{ token } | null> — OAuth token request
User State
Auth.user(): Promise<UserModel | undefined> — get authenticated user from bearer token
Auth.check(): Promise<boolean> — is user authenticated?
Auth.guest(): Promise<boolean> — is user a guest?
Auth.id(): Promise<number | undefined> — get authenticated user ID
Auth.setUser(user: UserModel): void — manually set user
Token Creation
Auth.createTokenForUser(user, options?: TokenCreateOptions): Promise<NewAccessToken>
Auth.createToken(user, name?, abilities?): Promise<AuthToken>
Token Validation
Auth.validateToken(token: string): Promise<boolean> — validate bearer token
Auth.getUserFromToken(token: string): Promise<UserModel | undefined>
Auth.currentAccessToken(): Promise<PersonalAccessToken | undefined>
Token Abilities (Scopes)
Auth.tokenCan(ability: string): Promise<boolean>
Auth.tokenCant(ability: string): Promise<boolean>
Auth.tokenAbilities(): Promise<string[]>
Auth.tokenCanAll(abilities: string[]): Promise<boolean>
Auth.tokenCanAny(abilities: string[]): Promise<boolean>
Token Management
Auth.tokens(userId?: number): Promise<PersonalAccessToken[]>
Auth.revokeToken(token: string): Promise<void>
Auth.revokeTokenById(tokenId: number): Promise<void>
Auth.revokeAllTokens(userId?: number): Promise<void>
Auth.revokeOtherTokens(userId?: number): Promise<void>
Auth.pruneExpiredTokens(): Promise<number>
Auth.pruneRevokedTokens(): Promise<number>
Auth.rotateToken(oldToken: string): Promise<AuthToken | null>
Auth.findToken(tokenId: number): Promise<PersonalAccessToken | null>
Utility
Auth.guard(name?: string): typeof Auth — select guard (returns self)
Auth.viaRemember(): boolean — always false currently
Auth.clearState(): void — clear cached user/token
Token System (tokens.ts)
Access Tokens
tokens(userId: number): Promise<AccessToken[]>
findToken(plainTextToken: string): Promise<AccessToken | null>
currentAccessToken(): Promise<AccessToken | null>
createToken(userId, name?, scopes?, options?): Promise<PersonalAccessTokenResult>
Options: { expiresInMinutes?, withRefreshToken?, refreshExpiresInDays? }
Refresh Tokens
refreshToken(refreshTokenPlain, options?): Promise<RefreshTokenResult>
validateRefreshToken(refreshTokenPlain): Promise<boolean>
revokeRefreshToken(refreshTokenPlain): Promise<void>
revokeAllRefreshTokens(userId): Promise<void>
deleteExpiredRefreshTokens(): Promise<number>
deleteRevokedRefreshTokens(daysOld?): Promise<number>
Token Revocation
revokeToken(plainTextToken): Promise<void>
revokeTokenById(tokenId): Promise<void>
revokeAllTokens(userId): Promise<void>
revokeOtherTokens(userId): Promise<void>
deleteExpiredTokens(): Promise<number>
deleteRevokedTokens(daysOld?): Promise<number>
Token Scopes
tokenCan(scope): Promise<boolean>
tokenCant(scope): Promise<boolean>
tokenCanAll(scopes): Promise<boolean>
tokenCanAny(scopes): Promise<boolean>
tokenAbilities(): Promise<string[]>
parseScopes(scopes: string | string[] | null | undefined): TokenScopes
OAuth Clients
clients(userId): Promise<OAuthClient[]>
findClient(clientId): Promise<OAuthClient | null>
createClient(options: CreateClientOptions): Promise<CreateClientResult>
revokeClient(clientId): Promise<void>
Two-Factor Authentication (authenticator.ts)
generateTwoFactorSecret(): string
generateTwoFactorToken(secret: Secret): Promise<Token>
verifyTwoFactorCode(token: Token, secret: Secret): Promise<boolean>
generateTwoFactorUri(user?, service?, secret?): string
createPersonalAccessClient(): Promise<Result<string, never>>
Re-exported from @stacksjs/ts-auth
generateTOTP, verifyTOTP, generateTOTPSecret, totpKeyUri
Authorization Gates (gate.ts)
Gate Functions
define<T>(ability: string, callback: GateCallback<T>): void
policy(model: string | { name }, policyClass: new () => Policy): void
before(callback): void — run before any gate check
after(callback): void — run after any gate check
allows(ability, user, ...args): Promise<boolean>
denies(ability, user, ...args): Promise<boolean>
can(ability, user, ...args): Promise<boolean>
cannot(ability, user, ...args): Promise<boolean>
any(abilities[], user, ...args): Promise<boolean>
all(abilities[], user, ...args): Promise<boolean>
none(abilities[], user, ...args): Promise<boolean>
authorize(ability, user, ...args): Promise<AuthorizationResponse> — throws on deny
inspect(ability, user, ...args): Promise<AuthorizationResponse> — never throws
has(ability): boolean
hasPolicy(model): boolean
abilities(): string[]
getPolicyFor<T>(model: T): Policy<T> | null
flush(): void — clear all gates
Gate Facade — Gate.define(), Gate.can(), etc
AuthorizationResponse Class
static allow(message?): AuthorizationResponse
static deny(message?, code?): AuthorizationResponse
allowed(): boolean, denied(): boolean
authorize(): void — throws AuthorizationException if denied
Policy Interface Methods: before?, viewAny?, view?, create?, update?, delete?, restore?, forceDelete?
BasePolicy Abstract Class Protected helpers: allow(message?), deny(message?, code?), denyIf(condition), denyUnless(condition), allowIf(condition)
RBAC System (rbac.ts)
Role Management
Rbac.createRole(name, guardName?, description?): Promise<RoleRecord>
Rbac.findRole(name, guardName?): Promise<RoleRecord | null>
Rbac.deleteRole(name, guardName?): Promise<void>
Rbac.getAllRoles(guardName?): Promise<RoleRecord[]>
Permission Management
Rbac.createPermission(name, guardName?, description?): Promise<PermissionRecord>
Rbac.findPermission(name, guardName?): Promise<PermissionRecord | null>
Rbac.deletePermission(name, guardName?): Promise<void>
Rbac.getAllPermissions(guardName?): Promise<PermissionRecord[]>
User-Role Operations
Rbac.getUserRoles(user): Promise<RoleRecord[]>
Rbac.assignRole(user, roleName, guardName?): Promise<void>
Rbac.removeRole(user, roleName, guardName?): Promise<void>
Rbac.removeAllRoles(user): Promise<void>
Rbac.syncRoles(user, roleNames[], guardName?): Promise<void>
Rbac.hasRole(user, roleName, guardName?): Promise<boolean>
Rbac.hasAnyRole(user, roleNames[], guardName?): Promise<boolean>
Rbac.hasAllRoles(user, roleNames[], guardName?): Promise<boolean>
User-Permission Operations
Rbac.getUserPermissions(user): Promise<PermissionRecord[]>
Rbac.givePermission(user, permissionName, guardName?): Promise<void>
Rbac.revokePermission(user, permissionName, guardName?): Promise<void>
Rbac.revokeAllPermissions(user): Promise<void>
Rbac.syncPermissions(user, permissionNames[], guardName?): Promise<void>
Rbac.hasPermission(user, permissionName, guardName?): Promise<boolean>
Rbac.hasAnyPermission(user, permissionNames[], guardName?): Promise<boolean>
Rbac.hasAllPermissions(user, permissionNames[], guardName?): Promise<boolean>
Role-Permission Operations
Rbac.getRolePermissions(roleId): Promise<PermissionRecord[]>
Rbac.givePermissionToRole(roleName, permissionName, guardName?): Promise<void>
Rbac.revokePermissionFromRole(roleName, permissionName, guardName?): Promise<void>
Rbac.syncRolePermissions(roleName, permissionNames[], guardName?): Promise<void>
withRbac Mixin withRbac(user) — adds hasRole(), hasPermission(), assignRole(), givePermission(), etc. to any user object
RBAC Types interface RoleRecord { id, name, guard_name, description?, created_at?, updated_at? }
interface PermissionRecord { id, name, guard_name, description?, created_at?, updated_at? }
interface RbacStore { findRoleByName, createRole, deleteRole, getAllRoles, findPermissionByName, createPermission, ... }
Session Auth (session-auth.ts)
SessionAuth.login(email, password): Promise<{ user, sessionId }>
SessionAuth.logout(sessionId): void
SessionAuth.user(sessionId): Promise<UserModel | undefined>
SessionAuth.check(sessionId): boolean
SessionAuth.refresh(sessionId, ttlMs?): boolean
Internal: in-memory Map with 10k session limit, 5-minute eviction interval, timing-safe password comparison with dummy bcrypt hash.
Email Verification (email-verification.ts)
EmailVerification.isVerified(user): boolean
EmailVerification.send(user): Promise<void>
EmailVerification.verify(userId, token): Promise<EmailVerificationResult>
EmailVerification.resend(user): Promise<EmailVerificationResult>
Password Reset (password/reset.ts) const actions = passwordResets (email)
await actions.sendEmail ()
const valid = await actions.verifyToken (token)
const result = await actions.resetPassword (token, newPassword)
Registration (register.ts)
register(credentials: NewUser): Promise<{ token: AuthToken }>
User Helpers (user.ts)
authUser(): Promise<UserModel | undefined>
check(): Promise<boolean>
id(): Promise<number | undefined>
email(): Promise<string | undefined>
name(): Promise<string | undefined>
isAuthenticated(): Promise<boolean>
logout(): Promise<void>
refresh(): Promise<void>
Passkey/WebAuthn (passkey.ts)
getUserPasskeys(userId): Promise<PasskeyAttribute[]>
getUserPasskey(userId, passkeyId): Promise<PasskeyAttribute | undefined>
setCurrentRegistrationOptions(user, verified): Promise<void>
Re-exported from @stacksjs/ts-auth
generateRegistrationOptions, generateAuthenticationOptions
verifyRegistrationResponse, verifyAuthenticationResponse
startRegistration, startAuthentication (browser)
browserSupportsWebAuthn, browserSupportsWebAuthnAutofill
platformAuthenticatorIsAvailable
Auth Middleware (middleware.ts) export const authMiddlewareHandler = {
name : 'auth' ,
handle : authMiddleware,
}
Rate Limiter (rate-limiter.ts) class RateLimiter {
static MAX_ATTEMPTS = 5
static LOCKOUT_DURATION = 15 * 60 * 1000
static MAX_STORE_SIZE = 10_000
static EVICTION_INTERVAL = 5 * 60 * 1000
static isRateLimited (email): boolean
static recordFailedAttempt (email): void
static resetAttempts (email): void
static validateAttempt (email): void
}
Authorizable Mixin (authorizable.ts) const authUser = withAuthorization (user)
await authUser.can ('edit-post' , post)
await authUser.cannot ('delete-post' , post)
await authUser.canAny (['edit' , 'delete' ], post)
await authUser.canAll (['edit' , 'publish' ], post)
await authUser.authorize ('edit-post' , post)
Configuration
config/auth.ts {
default : 'api' ,
guards : { api : { driver : 'token' , provider : 'users' } },
providers : { users : { driver : 'database' , table : 'users' } },
username : 'email' ,
password : 'password' ,
tokenExpiry : 30 ,
tokenRotation : 7 ,
defaultAbilities : ['*' ],
defaultTokenName : 'auth_token' ,
passwordReset : { expire : 60 , throttle : 60 }
}
config/hashing.ts {
driver : 'bcrypt' ,
bcrypt : { rounds : 12 },
argon2 : { memory : 65536 , time : 3 }
}
config/security.ts {
firewall : {
enabled : true ,
countryCodes : [],
ipAddresses : { allowlist : [], blocklist : [] },
rateLimitPerMinute : 500 ,
useIpReputationLists : true ,
useKnownBadInputsRuleSet : true
}
}
Middleware Aliases (app/Middleware.ts) Available middleware names: maintenance, auth, guest, api, team, logger, abilities, can, throttle, local, development, staging, production, env.local, env.development, env.staging, env.production, role, permission, verified (EnsureEmailIsVerified)
Application Gates Example (app/Gates.ts) Gate .define ('access-admin' , (user ) => user?.email ?.endsWith ('@stacksjs.org' ) ?? false )
Gate .define ('edit-settings' , (user ) => !!user)
Gate .define ('view-dashboard' , (user ) => !!user)
Default API Routes
POST /login → LoginAction (validates email + password)
POST /register → RegisterAction
POST /auth/refresh → RefreshTokenAction
POST /auth/token → CreateTokenAction
GET /auth/tokens → ListTokensAction (auth middleware)
DELETE /auth/tokens/{id} → RevokeTokenAction (auth middleware)
GET /me → GetMeAction (auth middleware)
POST /logout → LogoutAction (auth middleware)
User Model Traits
traits : {
useAuth : { usePasskey : true },
useUuid : true ,
useTimestamps : true ,
useSocials : ['github' ],
}
Gotchas
Auth depends on @stacksjs/ts-auth for TOTP and passkey functions
Password hashing defaults to bcrypt with 12 rounds (config/hashing.ts)
Rate limiter uses in-memory Map, resets on server restart — not shared across workers
Session auth also uses in-memory Map with 10k limit — for SPA cookie auth
Token format is tokenId|plainText — the | separates the encrypted ID from the plain token
The parseToken() helper splits on | to extract both parts
Bearer tokens come from the Authorization: Bearer <token> header
Auth.user() internally calls getBearerToken() → parseToken() → getTokenFromId() → validates hash
RBAC has an internal cache (userRoles, userPermissions, rolePermissions) — call Rbac.flushCache() after direct DB changes
Gate before callbacks can short-circuit — return true to allow, null to continue checking
withRbac() and withAuthorization() return new objects with methods mixed in
The RbacStore interface must be implemented and set via Rbac.setStore() for RBAC to work
Password reset tokens expire after 60 minutes by default
Default token abilities are ['*'] — wildcard access
Token expiry defaults to 30 days
Session auth uses timing-safe bcrypt comparison even for failed lookups (dummy hash prevents timing attacks)
Build cd storage/framework/core/auth && bun build.ts