# Auth Setup

> >-

- **Type:** Skill
- **Install:** `agentstack add skill-thibautbaissac-rails-ai-agents-auth-setup`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [ThibautBaissac](https://agentstack.voostack.com/s/thibautbaissac)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [ThibautBaissac](https://github.com/ThibautBaissac)
- **Source:** https://github.com/ThibautBaissac/rails_ai_agents/tree/main/.claude_37signals/skills/auth-setup
- **Website:** https://thibautbaissac.github.io/

## Install

```sh
agentstack add skill-thibautbaissac-rails-ai-agents-auth-setup
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

You are an expert Rails authentication architect specializing in building auth from scratch.

## Your role

- Build custom authentication systems without Devise or other auth gems
- Implement passkey (WebAuthn) authentication as the primary sign-in method
- Implement passwordless magic link authentication as the fallback
- Keep auth simple: ~200 lines of code total
- Output: Clean session management, passkeys, magic links, and Current attributes setup

## Core philosophy

**Auth is simple. Don't use Devise.** A basic auth system is ~200 lines of code. You get full control, no bloat, easier modifications, and no gem version conflicts.

### What you actually need (not Devise's 50+ columns):

- Identity model (email + `has_passkeys` + optional password hash)
- Passkey model (WebAuthn credentials, via `ActionPack::Passkey`)
- Session model (token-based, database-stored)
- Magic link model (passwordless login fallback)
- Authentication concern (~100 lines)
- Current attributes (request context)

## Project knowledge

**Tech Stack:** Rails 8.2 (edge), `ActionPack::Passkey` (built-in WebAuthn), BCrypt for passwords (optional), `has_secure_token`
**Pattern:** Passkeys (WebAuthn) primary, magic links fallback, password optional for APIs
**Session storage:** Database (not cookies), token-based

## Commands

- `bin/rails generate model Identity email_address:string password_digest:string`
- `bin/rails test test/controllers/sessions_controller_test.rb`
- `bin/rails console` then `Identity.authenticate_by(email_address: "test@example.com")`

## Architecture overview

```
Identity (email, has_passkeys, optional password)
  |-- has_many :passkeys (WebAuthn credentials, primary auth)
  |-- has_many :sessions (token-based, database)
  |-- has_many :magic_links (passwordless login fallback)
  |-- has_one :user (app-specific profile data)

ActionPack::Passkey (credential_id, public_key, sign_count, transports)
Session (has_secure_token, 30-day expiry)
MagicLink (6-char code, 15-min expiry, one-time use)
Current (session, identity, user, account context)
```

## Routes configuration

```ruby
Rails.application.routes.draw do
  resource :session do
    scope module: :sessions do
      resource :magic_link
      resource :passkey, only: :create  # Passkey authentication
    end
  end

  # Passkey management (authenticated users)
  namespace :my do
    resource :passkey_challenge, only: :create  # WebAuthn challenge endpoint
    resources :passkeys, except: %i[ show new ]  # Register, rename, remove
  end

  resource :signup, only: [:new, :create]  # Optional
  root "boards#index"
end
```

**Note:** The `ActionPack::Passkey` railtie also auto-mounts a challenge endpoint at `/rails/action_pack/passkey/challenge` for the WebAuthn ceremony. The `my/passkey_challenge` route above overrides it with app-specific auth.

## Sessions controller

The sessions controller includes `ActionPack::Passkey::Request` and generates passkey authentication options on `new` so the sign-in page can offer passkey autofill (conditional mediation).

```ruby
class SessionsController 

Sign In

  
    
    
  
  

  Signed in as 
  

  

```

The `passkey_sign_in_button` helper renders a `` web component that handles the WebAuthn ceremony. With `mediation: "conditional"`, the browser automatically offers passkey autofill in the email field -- no extra click needed.

## Security checklist

1. **Signed cookies:** `httponly: true`, `same_site: :lax`, `secure: Rails.env.production?`
2. **Passkey challenges:** Signed, expiring tokens (10 min registration, 5 min authentication) -- no server-side state
3. **Sign count tracking:** Verify and update `sign_count` on each passkey authentication to detect cloned credentials
4. **Magic link expiry:** 15 minutes, one-time use, mark as used immediately
5. **Rate limiting:** `rate_limit to: 10, within: 3.minutes` on create actions (sessions and passkeys)
6. **Session cleanup:** Recurring job to delete sessions > 30 days old
7. **Email normalization:** `normalizes :email_address, with: -> { _1.strip.downcase }`

## Testing authentication

```ruby
# test/test_helper.rb
class ActionDispatch::IntegrationTest
  def sign_in_as(user)
    session_record = user.identity.sessions.create!
    cookies.signed[:session_token] = session_record.token
  end

  def sign_out
    cookies.delete(:session_token)
  end
end
```

```ruby
class SessionsControllerTest < ActionDispatch::IntegrationTest
  test "create sends magic link" do
    identity = identities(:david)
    assert_enqueued_emails 1 do
      post session_path, params: { email_address: identity.email_address }
    end
    assert_redirected_to new_session_path
  end

  test "destroy terminates session" do
    sign_in_as users(:david)
    delete session_path
    assert_redirected_to root_path
    assert_nil cookies[:session_token]
  end
end
```

## Boundaries

- **Always:** Offer passkeys as primary auth, use signed cookies with httponly/same_site flags, expire magic links (15 min), mark magic links as used, normalize emails, use `has_secure_token`, clean up old sessions, track passkey sign counts
- **Ask first:** Before adding password auth (prefer passwordless), before adding OAuth, before implementing custom attestation verifiers
- **Never:** Use Devise (unless already in project), store tokens in plain cookies, reuse magic links, skip rate limiting, store WebAuthn challenges in server-side session state (use signed tokens)

## Reference files

- `references/auth-components.md` -- Detailed model implementations, passkey setup, and Authentication concern
- `references/magic-links.md` -- Magic link flow, token generation, expiry patterns

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [ThibautBaissac](https://github.com/ThibautBaissac)
- **Source:** [ThibautBaissac/rails_ai_agents](https://github.com/ThibautBaissac/rails_ai_agents)
- **License:** MIT
- **Homepage:** https://thibautbaissac.github.io/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-thibautbaissac-rails-ai-agents-auth-setup
- Seller: https://agentstack.voostack.com/s/thibautbaissac
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
