@seamless-auth/express 0.0.0 → 0.0.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +258 -7
- package/package.json +2 -5
package/README.md
CHANGED
|
@@ -1,13 +1,264 @@
|
|
|
1
1
|
# @seamless-auth/server-express
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
### Seamless Auth Express Adapter
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A secure, passwordless **server-side adapter** that connects your Express API to a private Seamless Auth Server.
|
|
6
|
+
|
|
7
|
+
It proxies all authentication flows, manages signed cookies, and gives you out-of-the-box middleware for verifying users and enforcing roles.
|
|
8
|
+
|
|
9
|
+
> **npm:** https://www.npmjs.com/package/@seamless-auth/express
|
|
10
|
+
> **Docs:** https://docs.seamlessauth.com
|
|
11
|
+
> **Repo:** https://github.com/fells-code/seamless-auth-server
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
> Couple with https://github.com/fells-code/seamless-auth/react for an end to end seamless experience
|
|
15
|
+
|
|
16
|
+
> Or get a full starter application with https://github.com/fells-code/create-seamless
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @seamless-auth/server-express
|
|
26
|
+
# or
|
|
27
|
+
yarn add @seamless-auth/server-express
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## Quick Example
|
|
32
|
+
|
|
33
|
+
```ts
|
|
6
34
|
import express from "express";
|
|
7
|
-
import
|
|
35
|
+
import cookieParser from "cookie-parser";
|
|
36
|
+
import createSeamlessAuthServer, { requireAuth, requireRole } from "@seamless-auth/server-express";
|
|
8
37
|
|
|
9
38
|
const app = express();
|
|
10
|
-
app.use(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}));
|
|
39
|
+
app.use(cookieParser());
|
|
40
|
+
|
|
41
|
+
// Public Seamless Auth endpoints
|
|
42
|
+
app.use("/auth", createSeamlessAuthServer({ authServerUrl: process.env.AUTH_SERVER_URL! }));
|
|
43
|
+
|
|
44
|
+
// Everything after this line requires authentication
|
|
45
|
+
app.use(requireAuth());
|
|
46
|
+
|
|
47
|
+
app.get("/api/me", (req, res) => res.json({ user: (req as any).user }));
|
|
48
|
+
app.get("/admin", requireRole("admin"), (req, res) => res.json({ message: "Welcome admin!" }));
|
|
49
|
+
|
|
50
|
+
app.listen(5000, () => console.log("Portal API running on :5000"));
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
# Full Documentation
|
|
56
|
+
|
|
57
|
+
## Overview
|
|
58
|
+
|
|
59
|
+
`@seamless-auth/express` lets your backend API act as an authentication and authorization server using Seamless Auth.
|
|
60
|
+
|
|
61
|
+
It transparently proxies and validates authentication flows so your frontend can use a single API endpoint for:
|
|
62
|
+
|
|
63
|
+
- Login / Registration / Logout
|
|
64
|
+
- User introspection (`/auth/me`)
|
|
65
|
+
- Session cookies (signed JWTs)
|
|
66
|
+
- Role & permission guards
|
|
67
|
+
- Internal Auth Server communication (JWKS + service tokens)
|
|
68
|
+
|
|
69
|
+
Everything happens securely between your API and a private Seamless Auth Server.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Architecture
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
[Frontend App]
|
|
79
|
+
│
|
|
80
|
+
▼
|
|
81
|
+
[Your Express API]
|
|
82
|
+
├─ createSeamlessAuthServer() ← mounts /auth routes
|
|
83
|
+
├─ requireAuth() ← verifies signed cookie JWT
|
|
84
|
+
├─ requireRole('admin') ← role-based guard
|
|
85
|
+
└─ getSeamlessUser() ← calls Auth Server
|
|
86
|
+
│
|
|
87
|
+
▼
|
|
88
|
+
[Private Seamless Auth Server]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Environment Variables
|
|
94
|
+
|
|
95
|
+
| Variable | Description | Example |
|
|
96
|
+
|-----------|--------------|----------|
|
|
97
|
+
| `AUTH_SERVER_URL` | Base URL of your Seamless Auth Server | `https://auth.client.com` |
|
|
98
|
+
| `SEAMLESS_COOKIE_SIGNING_KEY` | Secret key for signing JWT cookies | `base64:...` |
|
|
99
|
+
| `SERVICE_JWT_PRIVATE_KEY` | Private key for API → Auth Server JWTs | RSA PEM |
|
|
100
|
+
| `SERVICE_JWT_KEYID` | Key ID for JWKS | `service-main` |
|
|
101
|
+
| `COOKIE_DOMAIN` | Domain for cookies | `.client.com` |
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## API Reference
|
|
106
|
+
|
|
107
|
+
### `createSeamlessAuthServer(options)`
|
|
108
|
+
|
|
109
|
+
Mounts an Express router exposing the full Seamless Auth flow:
|
|
110
|
+
|
|
111
|
+
- `/auth/login/start`
|
|
112
|
+
- `/auth/login/finish`
|
|
113
|
+
- `/auth/webauthn/...`
|
|
114
|
+
- `/auth/registration/...`
|
|
115
|
+
- `/auth/me`
|
|
116
|
+
- `/auth/logout`
|
|
117
|
+
|
|
118
|
+
**Options**
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
{
|
|
122
|
+
authServerUrl: string; // required
|
|
123
|
+
cookieDomain?: string;
|
|
124
|
+
cookieNameOverrides?: {
|
|
125
|
+
preauth?: string;
|
|
126
|
+
registration?: string;
|
|
127
|
+
access?: string;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### `requireAuth(cookieName?: string)`
|
|
135
|
+
|
|
136
|
+
Middleware that validates the signed access cookie (`seamless_auth_access` by default)
|
|
137
|
+
and attaches the decoded user payload to `req.user`.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
app.get("/api/profile", requireAuth(), (req, res) => {
|
|
141
|
+
res.json({ user: req.user });
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### `requireRole(role: string, cookieName?: string)`
|
|
148
|
+
|
|
149
|
+
Role-based authorization guard.
|
|
150
|
+
Blocks non-matching roles with HTTP 403.
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
app.get("/admin", requireRole("admin"), (req, res) => {
|
|
154
|
+
res.json({ message: "Welcome admin!" });
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### `getSeamlessUser(req, authServerUrl, cookieName?)`
|
|
161
|
+
|
|
162
|
+
Calls the Auth Server’s `/internal/session/introspect` endpoint using a signed service JWT
|
|
163
|
+
and returns the Seamless user object.
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const user = await getSeamlessUser(req, process.env.AUTH_SERVER_URL!);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
User shape
|
|
170
|
+
```ts
|
|
171
|
+
{
|
|
172
|
+
id: string;
|
|
173
|
+
email: string;
|
|
174
|
+
phone: string;
|
|
175
|
+
roles: string[]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## End-to-End Flow
|
|
180
|
+
|
|
181
|
+
1. **Frontend** → `/auth/login/start`
|
|
182
|
+
→ API proxies to Seamless Auth Server
|
|
183
|
+
→ sets short-lived pre-auth cookie.
|
|
184
|
+
|
|
185
|
+
2. **Frontend** → `/auth/webauthn/finish`
|
|
186
|
+
→ API proxies, validates, sets access cookie (`seamless_auth_access`).
|
|
187
|
+
|
|
188
|
+
3. **Subsequent API calls** → `/api/...`
|
|
189
|
+
→ `requireAuth()` verifies cookie and attaches user.
|
|
190
|
+
→ Role routes use `requireRole()`.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Local Development
|
|
195
|
+
|
|
196
|
+
In order to develop with your Seamless Auth server instance, you will need to have:
|
|
197
|
+
|
|
198
|
+
- Created an account @ https://dashboard.seamlessauth.com
|
|
199
|
+
- Created a new Seamless Auth application
|
|
200
|
+
|
|
201
|
+
Example env:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
AUTH_SERVER_URL=http://https://<identifier>.seamlessauth.com # Found in the portal
|
|
205
|
+
COOKIE_DOMAIN=localhost # Or frontend domain in prod
|
|
206
|
+
SEAMLESS_COOKIE_SIGNING_KEY=local-secret-key # Found in the portal
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Example Middleware Stack
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
const AUTH_SERVER_URL = process.env.AUTH_SERVER_URL!;
|
|
215
|
+
app.use(cors({ origin: "https://localhost:5001", credentials: true }));
|
|
216
|
+
app.use(express.json());
|
|
217
|
+
app.use(cookieParser());
|
|
218
|
+
app.use("/auth", createSeamlessAuthServer({ authServerUrl: AUTH_SERVER_URL }));
|
|
219
|
+
app.use(requireAuth());
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Security Model
|
|
225
|
+
|
|
226
|
+
| Layer | Auth Mechanism | Signed By |
|
|
227
|
+
|--------|----------------|------------|
|
|
228
|
+
| **Frontend ↔ API** | Signed JWT in HttpOnly cookie (HS256) | Client API |
|
|
229
|
+
| **API ↔ Auth Server** | Bearer Service JWT (RS256) | API’s private key |
|
|
230
|
+
| **Auth Server** | Validates service tokens via JWKS | Seamless Auth JWKS |
|
|
231
|
+
|
|
232
|
+
All tokens and cookies are stateless and cryptographically verifiable.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Testing
|
|
237
|
+
|
|
238
|
+
You can mock `requireAuth` and test Express routes via `supertest`.
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { requireAuth } from "@seamless-auth/server-express";
|
|
244
|
+
app.get("/api/test", requireAuth(), (req, res) => res.json({ ok: true }));
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Roadmap
|
|
250
|
+
|
|
251
|
+
| Feature | Status |
|
|
252
|
+
|----------|---------|
|
|
253
|
+
| JWKS-verified response signing | ✅ |
|
|
254
|
+
| OIDC discovery & SSO readiness | planned |
|
|
255
|
+
| Federation (Google / Okta) | future |
|
|
256
|
+
| Multi-framework adapters (Next.js / Fastify) | coming soon |
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
MIT © 2025 Fells Code LLC
|
|
263
|
+
Part of the **Seamless Auth** ecosystem.
|
|
264
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seamless-auth/express",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.1-beta.1",
|
|
4
4
|
"description": "Express adapter for Seamless Auth passwordless authentication",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -26,12 +26,9 @@
|
|
|
26
26
|
"cookie-parser": "^1.4.6",
|
|
27
27
|
"jose": "^6.1.1",
|
|
28
28
|
"jsonwebtoken": "^9.0.2",
|
|
29
|
-
"node-fetch": "^3.3.2"
|
|
30
|
-
"version-packages": "changeset version",
|
|
31
|
-
"release": "changeset publish"
|
|
29
|
+
"node-fetch": "^3.3.2"
|
|
32
30
|
},
|
|
33
31
|
"devDependencies": {
|
|
34
|
-
"@changesets/cli": "^2.29.7",
|
|
35
32
|
"@types/cookie-parser": "^1.4.10",
|
|
36
33
|
"@types/jsonwebtoken": "^9.0.10",
|
|
37
34
|
"typescript": "^5.5.0"
|