Skip to content

feat: add wallet authentication and permissions#3

Merged
ECWireless merged 2 commits into
mainfrom
codex/wallet-auth-permissions
May 29, 2026
Merged

feat: add wallet authentication and permissions#3
ECWireless merged 2 commits into
mainfrom
codex/wallet-auth-permissions

Conversation

@ECWireless
Copy link
Copy Markdown
Member

@ECWireless ECWireless commented May 29, 2026

This pull request introduces a full implementation of wallet-based authentication and authorization for the RaidGuild Accounting dashboard, using Sign-In with Ethereum (SIWE), session management, and permission checks. It includes new API endpoints, a WalletConnect React component for the UI, and supporting infrastructure for session and permission handling. Additionally, it updates environment variables and documentation to reflect the new authentication requirements, and improves UI feedback with toast animations.

Authentication & Session Management

  • Added new API routes for wallet authentication: /api/auth/nonce (generates a SIWE nonce), /api/auth/verify (verifies wallet signature and checks permissions), /api/auth/session (returns session state), and /api/auth/logout (destroys session). These endpoints handle all aspects of SIWE-based authentication and session lifecycle. [1] [2] [3] [4]
  • Introduced the WalletConnect React component, which manages the full wallet sign-in flow (connect, sign SIWE message, verify, show session state, and sign out), and provides user-friendly error handling and UI feedback.

Environment & Documentation Updates

  • Updated .env.example to include new required variables for authentication and permission checks (DAO_SHARE_TOKEN_ADDRESS, DAO_SHARE_THRESHOLD, HATS_CONTRACT_ADDRESS, etc.), and clarified comments for public and server-only configuration. [1] [2]
  • Improved README.md with documentation for the new environment variables and authentication requirements, and added instructions for generating required secrets.

UI/UX Improvements

  • Refactored the home page to show different content for authenticated members and unauthenticated users, leveraging the new session state and wallet authentication. [1] [2] [3] [4]
  • Added custom toast animations in globals.css for smoother feedback on authentication actions.

Dependencies

  • Added new dependencies for authentication and wallet connection: siwe, wagmi, viem, iron-session, and @tanstack/react-query.

Authentication and Session Infrastructure

  • Implemented SIWE-based wallet authentication with new API endpoints for nonce generation, signature verification (including permission checks), session lookup, and logout. [1] [2] [3] [4]
  • Added WalletConnect component for end-to-end wallet connection, authentication, and session management in the UI.

Environment and Documentation

  • Updated .env.example with new required variables for DAO shares, Hats, and authentication, and improved comments for clarity. [1] [2]
  • Enhanced README.md with documentation on authentication, environment variables, and secret generation.

UI/UX Enhancements

  • Refactored the home page to display different views for authenticated and unauthenticated users, integrating wallet authentication into the flow. [1] [2] [3] [4]
  • Added toast notification animations for better feedback during authentication actions.

Dependencies

  • Introduced new packages for authentication and wallet integration: siwe, wagmi, viem, iron-session, and @tanstack/react-query.

Summary by CodeRabbit

  • New Features

    • Wallet-based sign-in authentication via Web3 wallet signing
    • Permission-gated access control with role-based membership verification (member, admin, cleric roles)
    • Toast notification system for user feedback
    • Session management with automatic logout functionality
  • Documentation

    • Updated configuration guide with environment variables for DAO membership thresholds and admin role configuration
    • Enhanced security guidance for encryption and session secret setup

Review Change Stack

Copilot AI review requested due to automatic review settings May 29, 2026 21:53
@vercel
Copy link
Copy Markdown

vercel Bot commented May 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
raidguild-accounting Ready Ready Preview, Comment May 29, 2026 10:22pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@ECWireless, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 32 minutes and 52 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 155b8946-1248-4c55-a96b-1651f0bfa616

📥 Commits

Reviewing files that changed from the base of the PR and between a0a1012 and e250809.

📒 Files selected for processing (10)
  • README.md
  • src/app/api/auth/logout/route.ts
  • src/app/api/auth/nonce/route.ts
  • src/app/api/auth/session/route.ts
  • src/app/api/auth/verify/route.ts
  • src/app/page.tsx
  • src/components/auth/wallet-connect.tsx
  • src/components/ui/toast.tsx
  • src/db/index.ts
  • src/lib/auth/permissions.ts
📝 Walkthrough

Walkthrough

This pull request implements a complete wallet authentication system for the accounting app. It adds SIWE-based sign-in, iron-session server-side session management, wagmi wallet integration, permission-gated access based on on-chain shares and database cleric roles, and a toast notification system for user feedback.

Changes

Wallet Authentication & Authorization

Layer / File(s) Summary
Authentication type contracts
src/lib/auth/types.ts, src/lib/auth/session.ts, src/lib/auth/permissions.ts
Defines AuthRole, AuthPermissions, and AuthSessionData types; session utilities with Iron Session, SESSION_SECRET validation, and serialization; permission checking combining on-chain ERC-20 share balance, optional hat roles, and database cleric membership.
Authentication API endpoints
src/app/api/auth/logout/route.ts, src/app/api/auth/nonce/route.ts, src/app/api/auth/session/route.ts, src/app/api/auth/verify/route.ts
Four routes handle session lifecycle: logout destroys sessions, nonce generates SIWE nonces for signing, session returns serialized auth state, and verify validates SIWE signatures against nonce and domain, checks wallet permissions, and establishes authenticated sessions.
Client-side wallet infrastructure
src/lib/wagmi.ts, src/components/ui/toast.tsx, src/components/providers.tsx, src/app/globals.css
Wagmi configuration for Gnosis and EVM chains with injected connector; toast provider maintaining in-memory notification list with enter/exit animations; providers component composing wagmi, React Query, and toast context.
Wallet connect component
src/components/auth/wallet-connect.tsx
Client-side React component managing complete wallet sign-in/sign-out flows: hydrates session on mount, orchestrates wagmi connector selection and SIWE message signing, verifies signature via /api/auth/verify, and renders authenticated address with roles or unauthenticated connect button based on session state.
App layout and page routing
src/app/layout.tsx, src/app/page.tsx
RootLayout wraps content with Providers; Home becomes async, fetches auth session, and conditionally renders PublicHome (wallet-gated landing with WalletConnect) or MemberHome (authenticated dashboard).
Configuration and dependencies
.env.example, README.md, package.json, src/db/index.ts
Adds environment variables for on-chain configuration (DAO_SHARE_TOKEN_ADDRESS, DAO_SHARE_THRESHOLD, HATS_CONTRACT_ADDRESS); documents authentication setup, session/encryption key requirements, and database driver selection; adds dependencies (@tanstack/react-query, iron-session, siwe, viem, wagmi); branches database client between local Node-postgres and remote Neon HTTP based on DATABASE_URL host detection.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WalletConnect as WalletConnect UI
  participant GetNonce as /api/auth/nonce
  participant SignMessage as Sign via wagmi
  participant VerifyRoute as /api/auth/verify
  participant Permissions as getWalletPermissions
  participant SessionAPI as /api/auth/session
  User->>WalletConnect: Click "Connect Wallet"
  WalletConnect->>GetNonce: Fetch nonce
  GetNonce->>WalletConnect: Return nonce
  WalletConnect->>SignMessage: Sign SIWE message
  SignMessage->>WalletConnect: Return signature
  WalletConnect->>VerifyRoute: POST message + signature
  VerifyRoute->>VerifyRoute: Verify SIWE signature
  VerifyRoute->>Permissions: Check wallet roles
  Permissions->>VerifyRoute: Return canAccess, roles
  VerifyRoute->>VerifyRoute: Save to session
  VerifyRoute->>WalletConnect: Return authenticated session
  WalletConnect->>SessionAPI: Confirm session state
  SessionAPI->>WalletConnect: Return current auth state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • raid-guild/accounting#2: The wallet permission logic (getWalletPermissionshasClericRole) depends on the Drizzle database schema and clericRoles table introduced in the DB foundation PR.

Poem

🐰 A wallet hops in with SIWE's delight,
Iron sessions guard the treasured right,
Wagmi wires the chains all around,
Permissions bloom where shares are found,
Toast cheer echoes—auth's now in sight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add wallet authentication and permissions' directly and concisely summarizes the main objective of the PR, which implements SIWE wallet-based authentication, session management, and permission checks across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/wallet-auth-permissions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds wallet-based authentication/authorization to the RaidGuild Accounting dashboard using SIWE + cookie-backed sessions, and wires the UI to support connect/verify/logout flows.

Changes:

  • Introduces SIWE auth API endpoints (nonce, verify, session, logout) backed by iron-session, plus server-side permission checks (DAO shares, Hats, DB cleric role).
  • Adds a WalletConnect client component and app-wide client providers (Wagmi + React Query + toast UI feedback).
  • Updates runtime DB initialization to use node-postgres for localhost DB URLs, and updates docs/env examples for new required secrets/vars.

Reviewed changes

Copilot reviewed 18 out of 20 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/lib/wagmi.ts Wagmi client config for supported chains/connectors.
src/lib/auth/types.ts Shared types for session/permissions/roles.
src/lib/auth/session.ts Iron-session setup + session serialization helper.
src/lib/auth/permissions.ts Server-side permission evaluation (shares/hat/DB role).
src/db/index.ts Runtime DB driver selection for local vs Neon URLs.
src/components/ui/toast.tsx Toast provider for lightweight UI feedback.
src/components/providers.tsx App-level client providers (Wagmi/React Query/Toast).
src/components/auth/wallet-connect.tsx End-to-end wallet connect + SIWE sign/verify + logout UI.
src/app/page.tsx Public vs member home rendering based on session state.
src/app/layout.tsx Wraps app in Providers to enable client auth/UX infra.
src/app/globals.css Toast enter/exit animations.
src/app/api/auth/verify/route.ts SIWE verification + permission checks + session persistence.
src/app/api/auth/session/route.ts Returns serialized session state.
src/app/api/auth/nonce/route.ts Issues and stores SIWE nonce in session.
src/app/api/auth/logout/route.ts Destroys session on logout.
README.md Documents new auth/env requirements and secret generation.
public/raidguild-full-logo.svg Adds logo used on public home.
package.json Adds wagmi/viem/siwe/iron-session/react-query deps.
pnpm-lock.yaml Lockfile updates for added deps.
.env.example Adds required auth/permission env vars (incl. SESSION_SECRET).
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/app/api/auth/verify/route.ts Outdated
Comment thread src/components/auth/wallet-connect.tsx
Comment thread src/components/ui/toast.tsx Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
src/app/api/auth/logout/route.ts (1)

11-18: 💤 Low value

Error message may be misleading.

The catch block returns "Session configuration is missing" for any error, but the actual failure could be unrelated to session configuration (e.g., network issues, unexpected exceptions). Consider returning a more generic message like "Logout failed" or implementing error mapping similar to getPublicErrorMessage in the verify route (lines 16-49 of src/app/api/auth/verify/route.ts) to provide more accurate feedback while still avoiding information leakage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/auth/logout/route.ts` around lines 11 - 18, The catch block in
the logout route currently always returns the specific string "Session
configuration is missing" which can be misleading; update the handler in
src/app/api/auth/logout/route.ts so that the catch uses a generic public message
(e.g. "Logout failed") or reuse the same error-mapping pattern as verify route's
getPublicErrorMessage (from src/app/api/auth/verify/route.ts) to translate the
caught error before returning it via NextResponse.json, and keep logging the
full error with console.error for diagnostics.
src/app/api/auth/nonce/route.ts (1)

13-20: 💤 Low value

Error message may be misleading.

The catch block returns "Session configuration is missing" for any error, but the actual failure could be unrelated to session configuration. Consider using a more generic message like "Nonce generation failed" or implementing error mapping similar to the verify route's getPublicErrorMessage approach.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/auth/nonce/route.ts` around lines 13 - 20, The catch in
src/app/api/auth/nonce/route.ts currently returns a misleading fixed message
("Session configuration is missing") for all errors; update the handler in the
nonce route's try/catch to return a generic public message like "Nonce
generation failed" (or reuse the existing getPublicErrorMessage mapping used by
the verify route) and keep the original error logged via console.error or
processLogger so internal details are preserved. Locate the catch block in the
nonce route handler and replace the hardcoded session message with the
generic/publicized message and/or call getPublicErrorMessage(error) before
returning the JSON response with status 500.
src/app/api/auth/session/route.ts (1)

10-17: 💤 Low value

Error message may be misleading.

The catch block returns "Session configuration is missing" for any error, but the actual failure could be unrelated to session configuration. Consider using a more generic message like "Session lookup failed" or implementing error mapping similar to the verify route's getPublicErrorMessage approach.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/auth/session/route.ts` around lines 10 - 17, The catch block in
the session route currently always returns the specific message "Session
configuration is missing" which can be misleading; update the error handling in
the route (the catch surrounding the session lookup) to return a generic public
error (for example "Session lookup failed") or reuse the existing
getPublicErrorMessage mapping used by the verify route to map internal errors to
safe client messages, and ensure the NextResponse.json call uses that mapped
message while preserving logging of the original error via console.error.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 59-60: Add documentation for HATS_CONTRACT_ADDRESS alongside
DAO_SHARE_TOKEN_ADDRESS, DAO_SHARE_THRESHOLD, and ANGRY_DWARF_HAT_ID in the
README: state that HATS_CONTRACT_ADDRESS is the Ethereum address of the Hats
protocol contract used for hats-based permission checks, show the expected
format (0x-prefixed hex address), give a short example value, and note which
network(s) to use (matching the deployed Hats contract or local test address);
ensure this aligns with the .env.example requirement and mention that it is
required for hats-based permissions to function.

In `@src/app/page.tsx`:
- Around line 87-88: Home() currently fetches the auth session but discards it,
causing WalletConnect to hydrate with an empty session and flicker; thread the
server-fetched session into WalletConnect by passing a serialized session prop
(for example serialize with JSON.stringify/parse or use a safe serializer) so
WalletConnect receives initial state and skips the extra fetch; update all
places where <WalletConnect /> is rendered (including the other occurrences
noted) to accept and forward that prop and adjust WalletConnect’s
props/signature to use the incoming initialSession instead of assuming
emptySession.

In `@src/components/auth/wallet-connect.tsx`:
- Around line 232-236: The signOut function currently waits for both the server
logout (fetch("/api/auth/logout")) and disconnectAsync to succeed before
clearing UI state, so a failed disconnectAsync can leave stale authenticated UI;
change the flow so the local session is cleared and router.refresh is called
regardless of disconnectAsync failure: perform the server logout first, then
attempt disconnectAsync inside a try/catch (log or ignore errors) and ensure
setSession(emptySession) and router.refresh() run in a finally block or
immediately after the server logout success so a rejected disconnectAsync cannot
block clearing the client state (update function signOut, referencing
disconnectAsync, setSession, and router.refresh).

In `@src/components/ui/toast.tsx`:
- Around line 27-44: Replace the collision-prone Date.now() ID generation in
showToast with a collision-safe ID (e.g., use crypto.randomUUID() or an internal
monotonic counter) so each toast gets a unique key; update the creation site
where setToasts adds { id, message, state: "entering" } and ensure the later
timeout comparisons (the toast.id checks in the leaving and filter callbacks)
use that new unique ID generation method to avoid duplicate keys and incorrect
toast mutations.

In `@src/db/index.ts`:
- Around line 11-18: The isLocalDatabaseUrl function misdetects IPv6 localhost
because URL().hostname returns bracketed addresses like "[::1]"; update the
check in isLocalDatabaseUrl to handle bracketed IPv6 by normalizing hostname
(strip surrounding square brackets if present) or by checking both bracketed and
unbracketed forms before comparing to "::1" (also keep existing checks for
"localhost" and "127.0.0.1" intact) so local IPv6 DSNs are correctly identified.

In `@src/lib/auth/permissions.ts`:
- Line 84: The code currently defaults DAO_SHARE_THRESHOLD to "100"; instead
require an explicit env var and throw when missing: check
process.env.DAO_SHARE_THRESHOLD and if undefined throw an Error (similar to the
DAO_SHARE_TOKEN_ADDRESS handling), then call parseUnits on the provided string
with the existing decimals to set threshold; reference the parseUnits call, the
threshold const, and the DAO_SHARE_THRESHOLD env var so the change is applied
where threshold is created.

---

Nitpick comments:
In `@src/app/api/auth/logout/route.ts`:
- Around line 11-18: The catch block in the logout route currently always
returns the specific string "Session configuration is missing" which can be
misleading; update the handler in src/app/api/auth/logout/route.ts so that the
catch uses a generic public message (e.g. "Logout failed") or reuse the same
error-mapping pattern as verify route's getPublicErrorMessage (from
src/app/api/auth/verify/route.ts) to translate the caught error before returning
it via NextResponse.json, and keep logging the full error with console.error for
diagnostics.

In `@src/app/api/auth/nonce/route.ts`:
- Around line 13-20: The catch in src/app/api/auth/nonce/route.ts currently
returns a misleading fixed message ("Session configuration is missing") for all
errors; update the handler in the nonce route's try/catch to return a generic
public message like "Nonce generation failed" (or reuse the existing
getPublicErrorMessage mapping used by the verify route) and keep the original
error logged via console.error or processLogger so internal details are
preserved. Locate the catch block in the nonce route handler and replace the
hardcoded session message with the generic/publicized message and/or call
getPublicErrorMessage(error) before returning the JSON response with status 500.

In `@src/app/api/auth/session/route.ts`:
- Around line 10-17: The catch block in the session route currently always
returns the specific message "Session configuration is missing" which can be
misleading; update the error handling in the route (the catch surrounding the
session lookup) to return a generic public error (for example "Session lookup
failed") or reuse the existing getPublicErrorMessage mapping used by the verify
route to map internal errors to safe client messages, and ensure the
NextResponse.json call uses that mapped message while preserving logging of the
original error via console.error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06b670be-6828-4ae4-b608-a84fbcb1a350

📥 Commits

Reviewing files that changed from the base of the PR and between 83ce562 and a0a1012.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • public/raidguild-full-logo.svg is excluded by !**/*.svg
📒 Files selected for processing (18)
  • .env.example
  • README.md
  • package.json
  • src/app/api/auth/logout/route.ts
  • src/app/api/auth/nonce/route.ts
  • src/app/api/auth/session/route.ts
  • src/app/api/auth/verify/route.ts
  • src/app/globals.css
  • src/app/layout.tsx
  • src/app/page.tsx
  • src/components/auth/wallet-connect.tsx
  • src/components/providers.tsx
  • src/components/ui/toast.tsx
  • src/db/index.ts
  • src/lib/auth/permissions.ts
  • src/lib/auth/session.ts
  • src/lib/auth/types.ts
  • src/lib/wagmi.ts

Comment thread README.md Outdated
Comment thread src/app/page.tsx Outdated
Comment thread src/components/auth/wallet-connect.tsx Outdated
Comment thread src/components/ui/toast.tsx
Comment thread src/db/index.ts
Comment thread src/lib/auth/permissions.ts Outdated
@ECWireless ECWireless merged commit 50bdfdc into main May 29, 2026
5 checks passed
@ECWireless ECWireless deleted the codex/wallet-auth-permissions branch May 29, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants