# Secure A Frontend App

> Use when wiring auth/session, protecting routes, or handling secrets in a frontend app — OIDC login via NextAuth + server session store + access-token refresh & injection, route guards + permission-matrix RBAC, SSR cookie propagation + hydration, and vault secrets + public/private config split. React/Next.js reference, framework-flexible.

- **Type:** Skill
- **Install:** `agentstack add skill-kennguyen887-agent-foundation-secure-a-frontend-app`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [kennguyen887](https://agentstack.voostack.com/s/kennguyen887)
- **Installs:** 0
- **Category:** [Communication](https://agentstack.voostack.com/c/communication)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [kennguyen887](https://github.com/kennguyen887)
- **Source:** https://github.com/kennguyen887/agent-foundation/tree/main/skills/secure-a-frontend-app

## Install

```sh
agentstack add skill-kennguyen887-agent-foundation-secure-a-frontend-app
```

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

## About

# Secure a frontend app

Auth, session, access control, and secrets for a web app. App structure: `structure-a-frontend-app`;
feature code: `write-frontend-code`.

## 1. Auth & session
- **OIDC via NextAuth.** Configure the provider (with PKCE/nonce checks) in the auth route; store the
  session **server-side** (a session-store adapter, e.g. Redis) rather than a fat JWT cookie.
- **Refresh in the session callback.** On each `session` resolve, check expiry and rotate the access
  token before handing it to the client:
  ```ts
  async session({ session, user }) {
    const acct = await store.getAccount(user.id);
    if (Date.parse(acct.expires_at)  ({ cookies: parseCookies(ctx.req), origin: ctx.req.headers.host });
  // _app: …
  ```
- **Guard token init behind hydration** — don't read the session until the router/session is ready:
  ```ts
  useEffect(() => {
    if (!router.isReady || status === 'loading') return;
    if (session) BaseHttp.saveToken(session.accessToken);
  }, [router.isReady, status]);
  ```
▸ *Other stacks:* read auth cookies server-side, hand to a provider; gate client token use on "ready/mounted".

## 3. Route protection & RBAC
- **Roles + permissions come from one profile hook** (`useMyProfile` → memoized role booleans + a
  `permissions: string[]`). Don't re-derive roles ad hoc per component.
- **Permission matrix as `feature.action` strings.** Gate UI with a checker, routes with a guard:
  ```ts
  // component-level visibility
  can({ feature: 'listing', action: 'create' });   // → permissions.includes('listing.create')
  // route-level (a GuardContainer wrapping the app)
  const allowed = routesForPermissions(permissions).includes(currentRoute);
  if (!allowed && role) { confirm('Access denied'); router.replace(Routes.Home); }
  ```
- **`middleware.ts`** handles edge redirects/rewrites (e.g. a cookie-driven path → canonical route);
  keep it **thin** — real auth/role logic lives in the guard + matrix, not middleware.
▸ *Other stacks:* a route-guard wrapper + a permission map; render-gate by `feature.action`, redirect on deny.

## 4. Secrets & config safety
- **Server secrets from a vault, never the bundle.** Load server-only env from a mounted secrets path
  at boot (fall back to `.env` locally):
  ```js
  // next.config.js
  const paths = ['/vault/secrets/global', '/vault/secrets/app'];
  paths.every(fs.existsSync) ? paths.forEach((p) => dotenv.config({ path: p })) : dotenv.config();
  ```
- **Public vs private split in one Config module** — only `NEXT_PUBLIC_*` (client-exposed) values go in
  the public group; secrets (client secret, store password, service keys) stay server-only. Mark
  server-only vars in `.env.example` ("do not use `NEXT_PUBLIC_` — never expose to the browser").
- A value reaches the browser bundle **only** if it is `NEXT_PUBLIC_`-prefixed AND in the public Config
  group — audit both sides.
▸ *Other stacks:* the framework's public-env prefix + a settings module separating server secrets;
pull secrets from a vault/secret-manager, not committed env.

## Verification
- Login goes through the OIDC provider; tokens are stored server-side; the session callback refreshes
  expiring tokens; the client injects the token once; a 401 triggers one queued refresh + replay.
- Roles/permissions come from the profile hook; UI gates on `feature.action`; a route guard redirects
  on deny; `middleware.ts` stays thin.
- `grep -rn "NEXT_PUBLIC_" src` shows only client-safe values; secrets load from the vault path; no
  secret sits in the public Config group or the client bundle.

## Related
- `write-frontend-code` — the `BaseHttp` 401-refresh queue, SSR guards, config module.
- `structure-a-frontend-app` · `code-conventions` · `release-safety` (backend release/secrets).

## Source & license

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

- **Author:** [kennguyen887](https://github.com/kennguyen887)
- **Source:** [kennguyen887/agent-foundation](https://github.com/kennguyen887/agent-foundation)
- **License:** MIT

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-kennguyen887-agent-foundation-secure-a-frontend-app
- Seller: https://agentstack.voostack.com/s/kennguyen887
- 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%.
