# Node.js (Traditional Web App)

Build a secure server‑rendered web app using BlitzWare OAuth 2.0 Authorization Code + PKCE with the BlitzWare Node SDK.

{% hint style="info" %}
This tutorial is based on the [example app](https://github.com/LanderDK/blitzware-node-web-sdk/tree/master/examples).
{% endhint %}

1. [**Configure BlitzWare**](#configure-blitzware)
2. [**Install the BlitzWare Node Web SDK**](#id-2-install-the-blitzware-node-web-sdk)
3. [**Configure environment**](#id-3-configure-environment)
4. [**Express setup**](#id-4-express-setup)
5. [**Koa setup**](#id-5-koa-setup)
6. [**How it works**](#id-6-how-it-works)

## 1) Configure BlitzWare <a href="#configure-blitzware" id="configure-blitzware"></a>

### Get Your Application Keys <a href="#get-your-application-keys" id="get-your-application-keys"></a>

You will need some details about your application to communicate with BlitzWare. You can get these details from the Application Settings section in the BlitzWare dashboard.

<figure><img src="https://3971581513-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FYjb41by6V7VaMjHbf8VN%2Fuploads%2FwQUuApkYQcdaekNmrQIy%2Fspa_settings.png?alt=media&#x26;token=33781bdf-97e2-4ad5-aae8-d767017b70a6" alt=""><figcaption></figcaption></figure>

You need the **Client ID** and **Client Secret** (given when app was created).

### Configure Redirect URIs

A redirect URI is a URL in your application where BlitzWare redirects the user after they have authenticated. The redirect URI for your app must be added to the **Redirect URIs** list in your **Application Settings** under the **Security** tab. If this is not set, users will be unable to log in to the application and will get an error.

## 2) Install the BlitzWare Node Web SDK

Run the following command within your project directory to install the [BlitzWare Node Web SDK](https://www.npmjs.com/package/blitzware-node-web-sdk):

{% code title="npm" overflow="wrap" lineNumbers="true" %}

```
npm install blitzware-node-web-sdk
```

{% endcode %}

{% code title="yarn" lineNumbers="true" %}

```
yarn add blitzware-node-web-sdk
```

{% endcode %}

## 3) Configure environment

Create a `.env` file:

```bash
BLITZWARE_CLIENT_ID=your-client-id
BLITZWARE_CLIENT_SECRET=your-client-secret
BLITZWARE_REDIRECT_URI=http://localhost:3000/callback
SESSION_SECRET=replace-with-a-strong-secret
# Optional: override auth base (self-hosted/staging)
# BLITZWARE_BASE_URL=https://auth.blitzware.xyz/api/auth
```

## 4) Express setup

{% code lineNumbers="true" %}

```javascript
const path = require("path");
require("dotenv").config({ path: path.join(__dirname, "../.env") });
const express = require("express");
const session = require("express-session");
const { expressAuth, expressRequiresAuth } = require("blitzware-node-web-sdk");

const app = express();
const port = process.env.PORT || 3000;

// BlitzWare configuration
const config = {
  authRequired: false, // Don't require auth for all routes
  clientId: process.env.BLITZWARE_CLIENT_ID || "your-client-id",
  clientSecret: process.env.BLITZWARE_CLIENT_SECRET || "your-client-secret",
  redirectUri:
    process.env.BLITZWARE_REDIRECT_URI || `http://localhost:${port}/callback`,
  secret: process.env.SESSION_SECRET || "LONG_RANDOM_STRING",
  // baseUrl: process.env.BLITZWARE_BASE_URL, // Optional: custom auth server
};

// Session middleware (required for auth middleware)
app.use(
  session({
    secret: config.secret,
    resave: false,
    saveUninitialized: false,
  })
);

// Parse JSON bodies
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// BlitzWare auth router attaches /login, /logout, and /callback routes
app.use(expressAuth(config));

// Home route - req.session.user is provided from the auth router
app.get("/", (req, res) => {
  res.send(`
    <html>
      <head><title>BlitzWare Express Example</title></head>
      <body>
        <h1>BlitzWare Express Example</h1>
        ${
          req.session.user
            ? `
            <p>✅ <strong>Logged in as ${req.session.user.username}</strong></p>
            <p><a href="/profile">View Profile</a></p>
            <p><a href="/logout">Logout</a></p>
          `
            : `
            <p>❌ Not logged in</p>
            <p><a href="/login">Login</a></p>
          `
        }
      </body>
    </html>
  `);
});

// Protected profile route - expressRequiresAuth() middleware
app.get("/profile", expressRequiresAuth(), (req, res) => {
  res.send(`
    <html>
      <head><title>Profile</title></head>
      <body>
        <h1>Profile</h1>
        <pre>${JSON.stringify(req.session.user, null, 2)}</pre>
        <p><a href="/">← Back to Home</a></p>
      </body>
    </html>
  `);
});

app.listen(port, () => {
  console.log(`
🚀 BlitzWare Express Example running at http://localhost:${port}

🔗 Routes:
   • GET /         - Home page
   • GET /profile  - Protected profile page  
   • GET /login    - Login (automatic)
   • GET /logout   - Logout (automatic)

📝 Setup:
   1. Set BLITZWARE_CLIENT_ID and BLITZWARE_CLIENT_SECRET in .env
   2. Visit http://localhost:${port}/login to authenticate
  `);
});

module.exports = app;
```

{% endcode %}

## 5) Koa setup

{% code lineNumbers="true" %}

```javascript
const path = require("path");
require("dotenv").config({ path: path.join(__dirname, "../.env") });
const Koa = require("koa");
const Router = require("@koa/router");
const KoaSession = require("koa-session");
const session =
  KoaSession && KoaSession.default ? KoaSession.default : KoaSession;
const bodyParser = require("koa-bodyparser");
const { koaAuth, koaRequiresAuth } = require("blitzware-node-web-sdk");

const app = new Koa();
const router = new Router();
const port = process.env.PORT || 3001;

// BlitzWare configuration
const config = {
  authRequired: false, // Don't require auth for all routes
  clientId: process.env.BLITZWARE_CLIENT_ID || "your-client-id",
  clientSecret: process.env.BLITZWARE_CLIENT_SECRET || "your-client-secret",
  redirectUri:
    process.env.BLITZWARE_REDIRECT_URI || `http://localhost:${port}/callback`,
  secret: process.env.SESSION_SECRET || "LONG_RANDOM_STRING",
  // baseUrl: process.env.BLITZWARE_BASE_URL, // Optional: custom auth server
};

// Koa requires signing keys for sessions
app.keys = [config.secret];

// Session middleware
app.use(session(app));

app.use(bodyParser());

// BlitzWare auth router attaches /login, /logout, and /callback routes
app.use(koaAuth(config));

// Home route - ctx.session.user is provided from the auth router
router.get("/", async (ctx) => {
  ctx.type = "html";
  ctx.body = `
    <html>
      <head><title>BlitzWare Koa Example</title></head>
      <body>
        <h1>BlitzWare Koa Example</h1>
        ${
          ctx.session.user
            ? `
            <p>✅ <strong>Logged in as ${ctx.session.user.username}</strong></p>
            <p><a href="/profile">View Profile</a></p>
            <p><a href="/logout">Logout</a></p>
          `
            : `
            <p>❌ Not logged in</p>
            <p><a href="/login">Login</a></p>
          `
        }
      </body>
    </html>
  `;
});

// Protected profile route - koaRequiresAuth() middleware
router.get("/profile", koaRequiresAuth(), async (ctx) => {
  ctx.type = "html";
  ctx.body = `
    <html>
      <head><title>Profile</title></head>
      <body>
        <h1>Profile</h1>
        <pre>${JSON.stringify(ctx.session.user, null, 2)}</pre>
        <p><a href="/">← Back to Home</a></p>
      </body>
    </html>
  `;
});

app.use(router.routes());
app.use(router.allowedMethods());

app.listen(port, () => {
  console.log(`
🚀 BlitzWare Koa Example running at http://localhost:${port}

🔗 Routes:
   • GET /         - Home page
   • GET /profile  - Protected profile page
   • GET /login    - Login (automatic)
   • GET /logout   - Logout (automatic)

📝 Setup:
   1. Set BLITZWARE_CLIENT_ID and BLITZWARE_CLIENT_SECRET in .env
   2. Visit http://localhost:${port}/login to authenticate
  `);
});
```

{% endcode %}

### 6) How it works

* PKCE + state: The SDK generates a `state` and PKCE verifier/challenge.
  * `state` defends against CSRF
  * PKCE protects the code exchange

#### Automatic Routes

When you use `expressAuth()` or `koaAuth()`, the following routes are created automatically:

* `GET /login` - Initiates OAuth login flow
* `GET /logout` - Logs out user and clears session
* `GET /callback` - OAuth callback handler

#### Protection

The SDK provides middleware to protect routes and enforce authorization:

**Authentication Middleware**

* `expressRequiresAuth()` / `koaRequiresAuth()` - Ensures a user is logged in before accessing a route. Redirects to `/login` if not authenticated.

**Express Example:**

```js
app.get("/profile", expressRequiresAuth(), (req, res) => {
  res.json({ user: req.session.user });
});
```

**Koa Example:**

```js
router.get("/profile", koaRequiresAuth(), async (ctx) => {
  ctx.body = { user: ctx.session.user };
});
```

**Role-Based Authorization Middleware**

* `expressRequiresRole(role)` / `koaRequiresRole(role)` - Ensures a user has a specific role. Returns 403 Forbidden if the user doesn't have the required role.

**Express Example:**

```js
const { expressRequiresAuth, expressRequiresRole } = require("blitzware-node-sdk");

// Admin-only route
app.get("/admin", 
  expressRequiresAuth(), 
  expressRequiresRole("admin"), 
  (req, res) => {
    res.send("Admin Dashboard");
  }
);
```

**Koa Example:**

```js
const { koaRequiresAuth, koaRequiresRole } = require("blitzware-node-sdk");

// Admin-only route
router.get("/admin",
  koaRequiresAuth(),
  koaRequiresRole("admin"),
  async (ctx) => {
    ctx.body = "Admin Dashboard";
  }
);
```

**Note:** These middleware functions check for roles stored in `user.roles` array. They do not perform token introspection by default.

#### Logout (front-channel)

The SDK performs a front-channel logout: it serves a small HTML page that POSTs to the auth service (so auth-service cookies are sent) and then redirects back to your app.

***

If you need additional features — token introspection on each request, automatic refresh using `session.refreshToken`, or other behavior — open an issue or PR and I can add an opt-in option such as `requiresAuth({ validateToken: true })`.

***

License: MIT
