# Getting Started

This page walks from installation to a first login, verification, and logout in a runnable Express app.

## Prerequisites

| Item | Requirement |
|---|---|
| Node.js | 20 or higher (`package.json` `engines`) |
| Express | 4.18 or higher (peer dependency) |
| Redis | A reachable Redis server |
| Cookie parsing | `cookie-parser` or equivalent; the package reads tokens from `req.cookies` |
| Keys | An EC P-256 key pair; the signing algorithm is fixed to ES256 |

## Installation

```bash
npm install @pardnchiu/jwt-auth express cookie-parser
```

## Generate ES256 Keys

```bash
mkdir -p keys
openssl ecparam -name prime256v1 -genkey -noout -out keys/private.pem
openssl ec -in keys/private.pem -pubout -out keys/public.pem
```

RSA or HMAC keys cannot sign with ES256, so `CreateJWT()` throws.

## First App

```typescript
import express, { Request, Response, NextFunction } from "express";
import cookieParser from "cookie-parser";
import { JWTAuth } from "@pardnchiu/jwt-auth";

const app = express();
app.use(express.json());
app.use(cookieParser());

async function requireAuth(req: Request, res: Response, next: NextFunction) {
  try {
    const result = await JWTAuth.VerifyJWT(req, res);
    if (!result.isAuth) {
      // isError true means a suspicious request; otherwise a plain guest
      return res.status(result.isError ? 400 : 401).json({ error: "unauthorized" });
    }
    res.locals.user = result.data;
    next();
  } catch (err) {
    res.status(500).json({ error: (err as Error).message });
  }
}

app.post("/login", async (req, res) => {
  try {
    // Issue tokens after verifying credentials
    const result = await JWTAuth.CreateJWT(req, res, {
      id: "user123",
      name: "John",
      email: "john@example.com",
    });
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: (err as Error).message });
  }
});

app.get("/me", requireAuth, (req, res) => {
  res.json({ user: res.locals.user });
});

app.post("/logout", async (req, res) => {
  await JWTAuth.RevokeJWT(req, res);
  res.json({ ok: true });
});

async function main() {
  await JWTAuth.init({
    privateKeyPath: "./keys/private.pem",
    publicKeyPath: "./keys/public.pem",
    accessTokenExpires: 900,
    refreshTokenExpires: 604800,
    isProd: false,
    AccessTokenCookieKey: "access_token",
    RefreshTokenCookieKey: "refresh_id",
    redis: { host: "localhost", port: 6379 },
    checkUserExists: async (userId) => true,
  });
  app.listen(3000);
}

main().catch((err) => {
  // Key loading failed or Redis is unreachable
  console.error(err);
  process.exit(1);
});
```

## Verify It Works

```bash
curl -i -c jar.txt -X POST http://localhost:3000/login
curl -i -b jar.txt http://localhost:3000/me
curl -i -b jar.txt -X POST http://localhost:3000/logout
curl -i -b jar.txt http://localhost:3000/me
```

With `isProd: false` the cookie `domain` is fixed to `localhost`, so use `http://localhost` rather than `127.0.0.1`; otherwise neither browsers nor curl send the cookie back.

## Next Steps

- When tokens refresh and when IDs rotate: [Refresh Token Rotation](/token-refresh)
- Every config field: [Configuration](/configuration)
- Mobile apps and other non-browser clients: [Client Integration](/client-integration)
