From hanakai-yaku
Use when implementing authentication in Hanami 2.x, including login, logout, signup, session management, password hashing, and protecting endpoints. Sets up login/logout flows, configures session-based or token-based auth, adds password hashing with bcrypt, and returns correct 401/403 responses for auth failures. Use when a user asks about auth, login, signup, JWT, sessions, passwords, or securing Hanami actions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/hanakai-yaku:setup-authenticationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this workflow when implementing authentication in Hanami 2.x.
Use this workflow when implementing authentication in Hanami 2.x.
Core principle: Authentication is explicit in every Action. No global magic — each Action declares its auth requirements.
inject-dependencies)Deps["authentication"]register-provider)config/providers/authentication.rbstart blockExample — config/providers/authentication.rb:
Hanami.app.register_provider(:authentication) do
start do
require "bcrypt"
register("authentication", MyApp::Authentication::Service.new)
end
end
create-action)Example — minimal auth service:
# app/authentication/service.rb
module MyApp
module Authentication
class Service
def authenticate(email:, password:, session:)
user = UserRepository.new.find_by_email(email)
return false unless user
return false unless BCrypt::Password.new(user.password_digest) == password
session[:user_id] = user.id
true
end
def current_user(session)
return nil unless session[:user_id]
UserRepository.new.find(session[:user_id])
end
def authenticated?(session)
!current_user(session).nil?
end
end
end
end
Example — login Action:
# app/actions/sessions/create.rb
module MyApp
module Actions
module Sessions
class Create < MyApp::Action
include Deps["authentication"]
params do
required(:email).filled(:string)
required(:password).filled(:string)
end
def handle(request, response)
halt 422 unless request.params.valid?
authenticated = authentication.authenticate(
email: request.params[:email],
password: request.params[:password],
session: request.session
)
if authenticated
response.redirect_to "/dashboard"
else
response.status = 401
response.body = { error: "Invalid credentials" }.to_json
end
end
end
end
end
end
Example — protected Action:
# app/actions/dashboard/show.rb
module MyApp
module Actions
module Dashboard
class Show < MyApp::Action
include Deps["authentication"]
def handle(request, response)
halt 401 unless authentication.authenticated?(request.session)
user = authentication.current_user(request.session)
response.body = { user: user.email }.to_json
end
end
end
end
end
handle-errors)After each step, verify the workflow is progressing correctly:
# Verify provider is registered
bundle exec hanami console
> Hanami.app["authentication"] # Should return auth service instance
# Test login endpoint
curl -X POST http://localhost:2300/sessions \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"secret"}'
# Expect: redirect or 200 with session cookie on success; 401 on failure
# Test protected endpoint without auth
curl http://localhost:2300/dashboard
# Expect: 401 Unauthorized
# Test protected endpoint with session cookie
curl http://localhost:2300/dashboard --cookie "<session-cookie>"
# Expect: 200 with user data
| Mistake | Reality |
|---|---|
| "I'll put auth in a base controller" | Hanami has no base controller. Each Action must explicitly check auth. |
| "I'll store passwords in plain text" | Always hash passwords with bcrypt or argon2. |
| "I'll skip session security configuration" | Configure secure sessions with http_only, secure, and same_site. |
| "I'll return specific error messages that reveal valid emails" | Return generic "Invalid credentials" for both missing user and wrong password. |
| Auth logic scattered across Actions | Centralise all auth logic in the auth service; Actions only call it. |
npx claudepluginhub igmarin/hanakai-yakuGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.