Check your daily Valorant store, Night Market, and bundles — without launching the game.
Valorant Store Checker is a production-grade, security-hardened Next.js application that authenticates with Riot's OAuth flow and surfaces your personalized in-game store. It supports multi-step authentication (including MFA and browser-based fallback), multi-account switching, store rotation history, wishlists, inventory browsing, and full profile/rank display — all without ever opening the Valorant client.
Sessions are encrypted at rest using AES-256-GCM, tokens never leave the server, and all Riot cookies are stored server-side only. The project ships with 101 Vitest tests and is designed for self-hosting on Vercel.
| Feature | Description |
|---|---|
| Daily Store | View all 4 daily rotating skins with VP pricing and tier icons |
| Night Market | Check your personalized Night Market discounts when active |
| Bundles | Browse current featured bundles with full item breakdowns and pricing |
| Wallet | See your current VP and Radianite Point balances |
| Store History | Browse past store rotations indexed by date, with repeat and price statistics |
| Wishlist | Bookmark skins you want; get highlighted when they appear in your store |
| Inventory | View all cosmetics you currently own (skins, sprays, cards, etc.) |
| Profile & Rank | Display your Riot ID, account level, current rank, and RR progress |
| Multi-Account | Link and switch between multiple Riot accounts in one session |
| MFA Support | Full multi-factor authentication flow for 2FA-protected accounts |
| Secure Auth | Riot OAuth flow with browser-based Playwright fallback |
| Category | Technology |
|---|---|
| Framework | Next.js 16 (App Router, Server Components) |
| Language | TypeScript 5 (strict + noUncheckedIndexedAccess) |
| Styling | Tailwind CSS v4 |
| UI Primitives | Radix UI, Lucide Icons, CVA |
| Validation | Zod |
| Server DB | LibSQL / Turso (SQLite) |
| Client DB | Dexie.js (IndexedDB — store history) |
| Auth Fallback | Playwright (headless browser cookie extraction) |
| Testing | Vitest + MSW v2 (101 tests) |
┌─────────────────────────────────────────────────────┐
│ Browser (Client) │
│ React 19 · Tailwind v4 · Dexie (IndexedDB history) │
└──────────────────────┬──────────────────────────────┘
│ HTTP (HTTP-only cookie JWT)
┌──────────────────────▼──────────────────────────────┐
│ Next.js App Router (Server) │
│ │
│ RSCs + API Routes │
│ ├── /api/auth ← multi-step Riot OAuth │
│ ├── /api/profile ← rank + identity │
│ ├── /api/inventory ← owned cosmetics │
│ ├── /api/wishlist ← bookmark management │
│ └── /api/accounts ← multi-account switching │
│ │
│ Session Layer │
│ ├── session-store.ts (encrypt-on-write / decrypt) │
│ ├── session-crypto.ts (AES-256-GCM) │
│ └── session-db.ts (LibSQL + hourly cleanup) │
│ │
│ Riot Module │
│ ├── riot-auth.ts (OAuth + MFA + reauth) │
│ ├── riot-store.ts (daily / night market / bundles) │
│ ├── riot-tokens.ts (entitlements extraction) │
│ └── riot-inventory.ts (cosmetics) │
└──────────────────────┬──────────────────────────────┘
│
┌─────────────┴──────────────┐
▼ ▼
LibSQL / Turso Riot Servers
(encrypted sessions) (auth + store APIs)
Key patterns:
- Reference-token sessions — JWT in cookie carries only a session ID; all data stays in SQLite
- RSC deduplication —
React.cache()wrapsgetSession()to deduplicate DB reads per render pass - withSession HOF — all protected API routes wrapped with
withSession(handler)for zero-boilerplate auth - FIFO in-memory cache — capped at 10 entries per cache (profile, store, inventory) to prevent unbounded memory growth
- Section-level error boundaries — each store section fails independently; the rest of the page renders
| Variable | Description |
|---|---|
SESSION_SECRET |
Secret for signing session JWTs. Min 32 chars. Generate: openssl rand -base64 32 |
| Variable | Description |
|---|---|
ENCRYPTION_KEY |
AES-256-GCM key for encrypting Riot cookies at rest. Must be 64 hex chars (32 bytes). Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" |
HENRIK_API_KEY |
HenrikDev API key for rank and level data. The app degrades gracefully without it. |
| Variable | Description |
|---|---|
TURSO_DATABASE_URL |
LibSQL connection URL from Turso (e.g. libsql://...) |
TURSO_AUTH_TOKEN |
Turso auth token for the above database |
SESSION_DB_PATH |
Override local SQLite path (default: .session-data/sessions.db) |
Important: Without
ENCRYPTION_KEY, Riot session cookies are stored in plaintext in the database. Setting this variable is strongly recommended for any deployment accessible to others.
-
Click Deploy with Vercel above, or import the repo at vercel.com/new.
-
Add the following environment variables in the Vercel project settings:
SESSION_SECRET← requiredENCRYPTION_KEY← strongly recommendedHENRIK_API_KEY← optional (rank data)TURSO_DATABASE_URL+TURSO_AUTH_TOKEN← for persistent sessions across deployments
-
Click Deploy.
Every push to
maintriggers an automatic redeployment.
Without a persistent Turso database, sessions are stored in a local SQLite file that is ephemeral on Vercel (wiped on each deployment). To persist sessions:
- Create a free database at turso.tech:
turso db create valorant-store-checker turso db tokens create valorant-store-checker
- Add
TURSO_DATABASE_URLandTURSO_AUTH_TOKENto your Vercel environment variables.
- Node.js 20+
- npm
-
Clone the repository:
git clone https://github.com/yugam23/Valorant-Store-Checker.git cd Valorant-Store-Checker -
Install dependencies:
npm install
-
Create
.env.local:SESSION_SECRET=your-dev-secret-min-32-chars-long ENCRYPTION_KEY=your-64-char-hex-key-here HENRIK_API_KEY=your-henrikdev-api-key
-
Start the development server:
npm run dev
-
Open http://localhost:3000.
npm test # run all tests
npm run test:coverage # run with coverage reportThe test suite uses Vitest + MSW v2 for API mocking. Coverage thresholds are enforced (statements 18%, branches 11%, functions 16%, lines 18%).
src/
├── app/
│ ├── page.tsx # Home / landing
│ ├── login/ # Auth flow (credential entry + MFA)
│ ├── store/ # Daily store, Night Market, bundles (protected)
│ ├── profile/ # Rank, level, identity (protected)
│ ├── inventory/ # Owned cosmetics (protected)
│ ├── history/ # Store rotation history (protected)
│ └── api/
│ ├── auth/ # Multi-step Riot OAuth dispatcher
│ ├── profile/ # Identity + rank
│ ├── inventory/ # Cosmetics
│ ├── wishlist/ # Bookmark management
│ └── accounts/ # Multi-account list + switch
├── components/
│ ├── store/ # StoreCard, StoreGrid, DailyStore, Bundle, NightMarket, Wallet
│ ├── profile/ # PlayerCardBanner, RankDisplay, RRProgressBar, AccountLevelBadge
│ ├── layout/ # Header, MobileNav, AccountSwitcher
│ └── ui/ # Button, LoadingSkeleton, SectionErrorBoundary
└── lib/
├── auth-handlers/ # credentials, mfa, url, cookie, browser, shared
├── schemas/ # Zod schemas (session, riot-auth, storefront, henrik)
├── __tests__/ # 101 Vitest tests across 8 files
├── session.ts # getCachedSession (RSC-safe)
├── session-store.ts # Transparent encrypt/decrypt layer
├── session-crypto.ts # AES-256-GCM primitives
├── session-db.ts # LibSQL client + migrations
├── api-validate.ts # parseBody<T> + withSession HOF
├── riot-auth.ts # Riot OAuth + MFA + SSID refresh
├── riot-store.ts # Storefront API
├── riot-tokens.ts # Token + entitlements extraction
├── riot-inventory.ts # Owned cosmetics
└── valorant-api.ts # HenrikDev integration (rank, skins metadata)
This project is designed with security as a first-class concern:
- HTTP-only cookies — session tokens are never accessible to JavaScript
- Server-side token storage — Riot access tokens and cookies never reach the client
- AES-256-GCM encryption — all Riot cookies are encrypted at rest in the database
- Reference-token sessions — JWTs contain only a session ID, not the session payload
- Zod input validation — all API routes reject malformed requests early with 400 responses
- CSP headers — strict Content Security Policy with no inline scripts in production
- HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy — full security header suite
- Automatic token refresh — SSID-based refresh at 55 minutes; sessions invalidated at 65 minutes
- Session cleanup — expired sessions purged from the database hourly
Disclaimer: This project is not affiliated with Riot Games. Usage is subject to Riot's Terms of Service. Credentials are only used to authenticate directly with Riot's servers — they are never stored or logged.
This project is open source. See LICENSE for details.