@seamless-auth/express 0.0.1 → 0.0.2-beta.10
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/LICENSE +79 -0
- package/LICENSE.md +26 -0
- package/README.md +99 -122
- package/dist/index.d.ts +190 -7
- package/dist/index.js +501 -5
- package/package.json +31 -14
- package/dist/createServer.d.ts +0 -4
- package/dist/createServer.d.ts.map +0 -1
- package/dist/createServer.js +0 -120
- package/dist/index.d.ts.map +0 -1
- package/dist/internal/authFetch.d.ts +0 -9
- package/dist/internal/authFetch.d.ts.map +0 -1
- package/dist/internal/authFetch.js +0 -41
- package/dist/internal/cookie.d.ts +0 -11
- package/dist/internal/cookie.d.ts.map +0 -1
- package/dist/internal/cookie.js +0 -28
- package/dist/internal/getSeamlessUser.d.ts +0 -11
- package/dist/internal/getSeamlessUser.d.ts.map +0 -1
- package/dist/internal/getSeamlessUser.js +0 -32
- package/dist/internal/refreshAccessToken.d.ts +0 -10
- package/dist/internal/refreshAccessToken.d.ts.map +0 -1
- package/dist/internal/refreshAccessToken.js +0 -44
- package/dist/internal/verifyCookieJwt.d.ts +0 -2
- package/dist/internal/verifyCookieJwt.d.ts.map +0 -1
- package/dist/internal/verifyCookieJwt.js +0 -13
- package/dist/internal/verifySignedAuthResponse.d.ts +0 -6
- package/dist/internal/verifySignedAuthResponse.d.ts.map +0 -1
- package/dist/internal/verifySignedAuthResponse.js +0 -23
- package/dist/middleware/ensureCookies.d.ts +0 -8
- package/dist/middleware/ensureCookies.d.ts.map +0 -1
- package/dist/middleware/ensureCookies.js +0 -84
- package/dist/middleware/requireAuth.d.ts +0 -9
- package/dist/middleware/requireAuth.d.ts.map +0 -1
- package/dist/middleware/requireAuth.js +0 -74
- package/dist/middleware/requireRole.d.ts +0 -9
- package/dist/middleware/requireRole.d.ts.map +0 -1
- package/dist/middleware/requireRole.js +0 -37
- package/dist/types.d.ts +0 -9
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc.
|
|
5
|
+
<https://fsf.org/>
|
|
6
|
+
Everyone is permitted to copy and distribute verbatim copies
|
|
7
|
+
of this license document, but changing it is not allowed.
|
|
8
|
+
|
|
9
|
+
This license is identical to the GNU General Public License, except that it also
|
|
10
|
+
ensures that software running as a network service makes its source code
|
|
11
|
+
available to users.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
TERMS AND CONDITIONS
|
|
16
|
+
|
|
17
|
+
0. Definitions.
|
|
18
|
+
|
|
19
|
+
“This License” refers to version 3 of the GNU Affero General Public License.
|
|
20
|
+
|
|
21
|
+
“Copyright” also means copyright-like laws that apply to other kinds of works,
|
|
22
|
+
such as semiconductor masks.
|
|
23
|
+
|
|
24
|
+
The “Program” refers to any copyrightable work licensed under this License.
|
|
25
|
+
Each licensee is addressed as “you”.
|
|
26
|
+
|
|
27
|
+
To “modify” a work means to copy from or adapt all or part of the work in a
|
|
28
|
+
fashion requiring copyright permission, other than the making of an exact copy.
|
|
29
|
+
|
|
30
|
+
To “propagate” a work means to do anything with it that, without permission,
|
|
31
|
+
would make you directly or secondarily liable for infringement under applicable
|
|
32
|
+
copyright law, except executing it on a computer or modifying a private copy.
|
|
33
|
+
|
|
34
|
+
To “convey” a work means any kind of propagation that enables other parties to
|
|
35
|
+
make or receive copies.
|
|
36
|
+
|
|
37
|
+
An interactive user interface displays “Appropriate Legal Notices” to the extent
|
|
38
|
+
that it includes a convenient and prominently visible feature that displays an
|
|
39
|
+
appropriate copyright notice, and tells the user that there is no warranty for
|
|
40
|
+
the work (except to the extent that warranties are provided), that licensees may
|
|
41
|
+
convey the work under this License, and how to view a copy of this License.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
13. Remote Network Interaction; Use with the GNU General Public License.
|
|
46
|
+
|
|
47
|
+
Notwithstanding any other provision of this License, if you modify the Program,
|
|
48
|
+
your modified version must prominently offer all users interacting with it
|
|
49
|
+
remotely through a computer network an opportunity to receive the Corresponding
|
|
50
|
+
Source of your version by providing access to the Corresponding Source from a
|
|
51
|
+
network server at no charge, through some standard or customary means of
|
|
52
|
+
facilitating copying of software.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
15. Disclaimer of Warranty.
|
|
57
|
+
|
|
58
|
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
59
|
+
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
|
|
60
|
+
PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
61
|
+
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
62
|
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
16. Limitation of Liability.
|
|
67
|
+
|
|
68
|
+
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
|
|
69
|
+
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
|
|
70
|
+
PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
|
71
|
+
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
|
|
72
|
+
THE PROGRAM.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
END OF TERMS AND CONDITIONS
|
|
77
|
+
|
|
78
|
+
You should have received a copy of the GNU Affero General Public License along
|
|
79
|
+
with this program. If not, see <https://www.gnu.org/licenses/>.
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# License
|
|
2
|
+
|
|
3
|
+
Seamless Auth Server - Express ("@seamlessauth/express") is licensed under the **GNU Affero General Public License v3.0 (AGPL-3.0-only)**.
|
|
4
|
+
|
|
5
|
+
- SPDX: `AGPL-3.0-only`
|
|
6
|
+
|
|
7
|
+
## What this means (high level)
|
|
8
|
+
|
|
9
|
+
- You are free to **use**, **modify**, and **self-host** this software.
|
|
10
|
+
- If you **modify** this software and **run it as a network service** (for example, hosting it for others to use), you must **make the complete corresponding source code of your modified version available** to users of that service, under the AGPL.
|
|
11
|
+
|
|
12
|
+
This summary is not legal advice and does not replace the license text.
|
|
13
|
+
|
|
14
|
+
## Full license text
|
|
15
|
+
|
|
16
|
+
The full license text is available here:
|
|
17
|
+
|
|
18
|
+
- https://www.gnu.org/licenses/agpl-3.0.html
|
|
19
|
+
|
|
20
|
+
You should include a copy of the AGPLv3 license in your distribution. If this repository does not contain the full license text yet, add it as `LICENSE` or `LICENSE.txt` (recommended), and keep this `LICENSE.md` as the human-friendly summary.
|
|
21
|
+
|
|
22
|
+
## Commercial licensing
|
|
23
|
+
|
|
24
|
+
If you would like to embed Seamless Auth API into a proprietary product, redistribute it under different terms, or offer it as a managed service without AGPL obligations, commercial licensing may be available.
|
|
25
|
+
|
|
26
|
+
Contact: support@seamlessauth.com
|
package/README.md
CHANGED
|
@@ -1,78 +1,92 @@
|
|
|
1
|
-
# @seamless-auth/
|
|
1
|
+
# @seamless-auth/express
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@seamless-auth/express)
|
|
4
|
+
[](#testing)
|
|
5
|
+
[](#license)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
### Seamless Auth – Express Adapter
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
A secure, passwordless **server-side adapter** for Express that connects your API to a private **Seamless Auth Server**.
|
|
10
|
+
|
|
11
|
+
This package:
|
|
12
|
+
|
|
13
|
+
- Proxies authentication flows
|
|
14
|
+
- Manages signed, HttpOnly session cookies
|
|
15
|
+
- Enforces authentication and authorization in your API
|
|
16
|
+
- Handles all API ↔ Auth Server communication via short-lived service tokens
|
|
8
17
|
|
|
9
18
|
> **npm:** https://www.npmjs.com/package/@seamless-auth/express
|
|
10
19
|
> **Docs:** https://docs.seamlessauth.com
|
|
11
20
|
> **Repo:** https://github.com/fells-code/seamless-auth-server
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
> Or get a full starter application with https://github.com/fells-code/create-seamless
|
|
22
|
+
Pair this with:
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
- **React SDK:** https://github.com/fells-code/seamless-auth-react
|
|
25
|
+
- **Starter app:** https://github.com/fells-code/create-seamless
|
|
18
26
|
|
|
19
27
|
---
|
|
20
28
|
|
|
21
29
|
## Installation
|
|
22
30
|
|
|
23
31
|
```bash
|
|
24
|
-
npm install @seamless-auth/
|
|
32
|
+
npm install @seamless-auth/express
|
|
25
33
|
# or
|
|
26
|
-
yarn add @seamless-auth/
|
|
34
|
+
yarn add @seamless-auth/express
|
|
27
35
|
```
|
|
28
36
|
|
|
29
|
-
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
30
40
|
|
|
31
41
|
```ts
|
|
32
42
|
import express from "express";
|
|
33
43
|
import cookieParser from "cookie-parser";
|
|
34
|
-
import
|
|
44
|
+
import {
|
|
45
|
+
createSeamlessAuthServer,
|
|
35
46
|
requireAuth,
|
|
36
47
|
requireRole,
|
|
37
|
-
} from "@seamless-auth/
|
|
48
|
+
} from "@seamless-auth/express";
|
|
38
49
|
|
|
39
50
|
const app = express();
|
|
51
|
+
app.use(express.json());
|
|
40
52
|
app.use(cookieParser());
|
|
41
53
|
|
|
42
|
-
//
|
|
54
|
+
// Mount Seamless Auth routes
|
|
43
55
|
app.use(
|
|
44
56
|
"/auth",
|
|
45
|
-
createSeamlessAuthServer({
|
|
57
|
+
createSeamlessAuthServer({
|
|
58
|
+
authServerUrl: process.env.AUTH_SERVER_URL!,
|
|
59
|
+
}),
|
|
46
60
|
);
|
|
47
61
|
|
|
48
|
-
// Everything
|
|
62
|
+
// Everything below requires authentication
|
|
49
63
|
app.use(requireAuth());
|
|
50
64
|
|
|
51
|
-
app.get("/api/me", (req, res) =>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
65
|
+
app.get("/api/me", (req, res) => {
|
|
66
|
+
res.json({ user: req.user });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
app.get("/admin", requireRole("admin"), (req, res) => {
|
|
70
|
+
res.json({ message: "Welcome admin!" });
|
|
71
|
+
});
|
|
55
72
|
|
|
56
|
-
app.listen(5000, () => console.log("
|
|
73
|
+
app.listen(5000, () => console.log("API running on http://localhost:3000"));
|
|
57
74
|
```
|
|
58
75
|
|
|
59
76
|
---
|
|
60
77
|
|
|
61
|
-
# Full Documentation
|
|
62
|
-
|
|
63
78
|
## Overview
|
|
64
79
|
|
|
65
|
-
`@seamless-auth/express` lets your backend API act as
|
|
80
|
+
`@seamless-auth/express` lets your backend API act as the **security boundary** for authentication and authorization.
|
|
66
81
|
|
|
67
|
-
|
|
82
|
+
Your API:
|
|
68
83
|
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
- Internal Auth Server communication (JWKS + service tokens)
|
|
84
|
+
- owns user session cookies
|
|
85
|
+
- verifies identity locally
|
|
86
|
+
- asserts identity upstream using service tokens
|
|
87
|
+
- never forwards browser cookies to the Auth Server
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
This keeps trust boundaries clean and auditable.
|
|
76
90
|
|
|
77
91
|
---
|
|
78
92
|
|
|
@@ -84,9 +98,9 @@ Everything happens securely between your API and a private Seamless Auth Server.
|
|
|
84
98
|
▼
|
|
85
99
|
[Your Express API]
|
|
86
100
|
├─ createSeamlessAuthServer() ← mounts /auth routes
|
|
87
|
-
├─ requireAuth() ← verifies
|
|
88
|
-
├─ requireRole(
|
|
89
|
-
└─ getSeamlessUser() ←
|
|
101
|
+
├─ requireAuth() ← verifies access cookie
|
|
102
|
+
├─ requireRole("admin") ← role-based guard
|
|
103
|
+
└─ getSeamlessUser() ← optional user hydration
|
|
90
104
|
│
|
|
91
105
|
▼
|
|
92
106
|
[Private Seamless Auth Server]
|
|
@@ -96,13 +110,14 @@ Everything happens securely between your API and a private Seamless Auth Server.
|
|
|
96
110
|
|
|
97
111
|
## Environment Variables
|
|
98
112
|
|
|
99
|
-
| Variable
|
|
100
|
-
|
|
|
101
|
-
| `AUTH_SERVER_URL`
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
113
|
+
| Variable | Description | Example |
|
|
114
|
+
| -------------------- | ----------------------------------------- | ------------------------------------------------------ |
|
|
115
|
+
| `AUTH_SERVER_URL` | Base URL of your Seamless Auth Server | `https://auth.client.com` |
|
|
116
|
+
| `COOKIE_SIGNING_KEY` | Secret for signing API session cookies | `local-dev-secret` |
|
|
117
|
+
| `API_SERVICE_TOKEN` | API → Auth Server service secret | `shared-m2m-value` |
|
|
118
|
+
| `APP_ORIGIN` | Your site URL (or localhost in demo mode) | `https://myapp.com` |
|
|
119
|
+
| `DATABASE_URL` | Database URL for your API | `postgres://myuser:mypassword@localhost:5432/seamless` |
|
|
120
|
+
| `DB_NAME` | Name of your database | `seamless` |
|
|
106
121
|
|
|
107
122
|
---
|
|
108
123
|
|
|
@@ -110,35 +125,35 @@ Everything happens securely between your API and a private Seamless Auth Server.
|
|
|
110
125
|
|
|
111
126
|
### `createSeamlessAuthServer(options)`
|
|
112
127
|
|
|
113
|
-
Mounts an Express router
|
|
128
|
+
Mounts an Express router that exposes the full Seamless Auth flow under `/auth`.
|
|
129
|
+
|
|
130
|
+
Routes include:
|
|
114
131
|
|
|
115
132
|
- `/auth/login/start`
|
|
116
133
|
- `/auth/login/finish`
|
|
117
|
-
- `/auth/webauthn
|
|
118
|
-
- `/auth/registration
|
|
119
|
-
- `/auth/me`
|
|
134
|
+
- `/auth/webauthn/*`
|
|
135
|
+
- `/auth/registration/*`
|
|
136
|
+
- `/auth/users/me`
|
|
120
137
|
- `/auth/logout`
|
|
121
138
|
|
|
122
139
|
**Options**
|
|
123
140
|
|
|
124
141
|
```ts
|
|
125
142
|
{
|
|
126
|
-
authServerUrl: string;
|
|
127
|
-
cookieDomain?: string;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
};
|
|
143
|
+
authServerUrl: string; // required
|
|
144
|
+
cookieDomain?: string; // optional (defaults to host)
|
|
145
|
+
accessCookieName?: string;
|
|
146
|
+
registrationCookieName?: string;
|
|
147
|
+
refreshCookieName?: string;
|
|
148
|
+
preAuthCookieName?: string;
|
|
133
149
|
}
|
|
134
150
|
```
|
|
135
151
|
|
|
136
152
|
---
|
|
137
153
|
|
|
138
|
-
### `requireAuth(
|
|
154
|
+
### `requireAuth(options?)`
|
|
139
155
|
|
|
140
|
-
|
|
141
|
-
and attaches the decoded user payload to `req.user`.
|
|
156
|
+
Express middleware that verifies a signed access cookie and attaches the decoded user payload to `req.user`.
|
|
142
157
|
|
|
143
158
|
```ts
|
|
144
159
|
app.get("/api/profile", requireAuth(), (req, res) => {
|
|
@@ -148,10 +163,11 @@ app.get("/api/profile", requireAuth(), (req, res) => {
|
|
|
148
163
|
|
|
149
164
|
---
|
|
150
165
|
|
|
151
|
-
### `requireRole(role: string
|
|
166
|
+
### `requireRole(role: string)`
|
|
167
|
+
|
|
168
|
+
Role-based authorization middleware.
|
|
152
169
|
|
|
153
|
-
|
|
154
|
-
Blocks non-matching roles with HTTP 403.
|
|
170
|
+
Blocks requests when the authenticated user does not have the required role.
|
|
155
171
|
|
|
156
172
|
```ts
|
|
157
173
|
app.get("/admin", requireRole("admin"), (req, res) => {
|
|
@@ -163,106 +179,67 @@ app.get("/admin", requireRole("admin"), (req, res) => {
|
|
|
163
179
|
|
|
164
180
|
### `getSeamlessUser(req, authServerUrl, cookieName?)`
|
|
165
181
|
|
|
166
|
-
|
|
167
|
-
|
|
182
|
+
Optional helper that calls the Auth Server to retrieve the **fully hydrated user object**.
|
|
183
|
+
|
|
184
|
+
This does **not** enforce authentication.
|
|
168
185
|
|
|
169
186
|
```ts
|
|
170
|
-
const user = await getSeamlessUser(req, process.env.AUTH_SERVER_URL
|
|
187
|
+
const user = await getSeamlessUser(req, process.env.AUTH_SERVER_URL);
|
|
171
188
|
```
|
|
172
189
|
|
|
173
|
-
|
|
190
|
+
Returned shape (example):
|
|
174
191
|
|
|
175
192
|
```ts
|
|
176
193
|
{
|
|
177
194
|
id: string;
|
|
178
195
|
email: string;
|
|
179
196
|
phone: string;
|
|
180
|
-
roles: string[]
|
|
197
|
+
roles: string[];
|
|
181
198
|
}
|
|
182
199
|
```
|
|
183
200
|
|
|
201
|
+
---
|
|
202
|
+
|
|
184
203
|
## End-to-End Flow
|
|
185
204
|
|
|
186
205
|
1. **Frontend** → `/auth/login/start`
|
|
187
|
-
|
|
188
|
-
→ sets short-lived pre-auth cookie.
|
|
206
|
+
API proxies request and sets a short-lived _pre-auth_ cookie.
|
|
189
207
|
|
|
190
208
|
2. **Frontend** → `/auth/webauthn/finish`
|
|
191
|
-
|
|
209
|
+
API verifies response and sets a signed access cookie.
|
|
192
210
|
|
|
193
|
-
3. **
|
|
194
|
-
|
|
195
|
-
→ Role routes use `requireRole()`.
|
|
211
|
+
3. **API routes** → `/api/*`
|
|
212
|
+
`requireAuth()` verifies the cookie and attaches `req.user`.
|
|
196
213
|
|
|
197
214
|
---
|
|
198
215
|
|
|
199
216
|
## Local Development
|
|
200
217
|
|
|
201
|
-
In order to develop with your Seamless Auth server instance, you will need to have:
|
|
202
|
-
|
|
203
|
-
- Created an account @ https://dashboard.seamlessauth.com
|
|
204
|
-
- Created a new Seamless Auth application
|
|
205
|
-
|
|
206
|
-
Example env:
|
|
207
|
-
|
|
208
218
|
```bash
|
|
209
|
-
AUTH_SERVER_URL=http://
|
|
210
|
-
|
|
211
|
-
|
|
219
|
+
AUTH_SERVER_URL=http://localhost:5312
|
|
220
|
+
SEAMLESS_SERVICE_TOKEN=generated-secret
|
|
221
|
+
COOKIE_SIGNING_KEY=local-dev-key
|
|
222
|
+
FRONTEND_URL=https://localhost:5001
|
|
212
223
|
```
|
|
213
224
|
|
|
214
225
|
---
|
|
215
226
|
|
|
216
|
-
## Example Middleware Stack
|
|
217
|
-
|
|
218
|
-
```ts
|
|
219
|
-
const AUTH_SERVER_URL = process.env.AUTH_SERVER_URL!;
|
|
220
|
-
app.use(cors({ origin: "http://localhost:5001", credentials: true }));
|
|
221
|
-
app.use(express.json());
|
|
222
|
-
app.use(cookieParser());
|
|
223
|
-
app.use("/auth", createSeamlessAuthServer({ authServerUrl: AUTH_SERVER_URL }));
|
|
224
|
-
app.use(requireAuth());
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
## Security Model
|
|
230
|
-
|
|
231
|
-
| Layer | Auth Mechanism | Signed By |
|
|
232
|
-
| --------------------- | ------------------------------------- | ------------------ |
|
|
233
|
-
| **Frontend ↔ API** | Signed JWT in HttpOnly cookie (HS256) | Client API |
|
|
234
|
-
| **API ↔ Auth Server** | Bearer Service JWT (RS256) | API’s private key |
|
|
235
|
-
| **Auth Server** | Validates service tokens via JWKS | Seamless Auth JWKS |
|
|
236
|
-
|
|
237
|
-
All tokens and cookies are stateless and cryptographically verifiable.
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
227
|
## Testing
|
|
242
228
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
Example:
|
|
229
|
+
This package includes **Express smoke tests** using `supertest`.
|
|
246
230
|
|
|
247
|
-
|
|
248
|
-
import { requireAuth } from "@seamless-auth/server-express";
|
|
249
|
-
app.get("/api/test", requireAuth(), (req, res) => res.json({ ok: true }));
|
|
250
|
-
```
|
|
231
|
+
Core authentication logic is tested separately in `@seamless-auth/core`.
|
|
251
232
|
|
|
252
233
|
---
|
|
253
234
|
|
|
254
|
-
##
|
|
235
|
+
## License
|
|
255
236
|
|
|
256
|
-
|
|
257
|
-
| -------------------------------------------- | ----------- |
|
|
258
|
-
| JWKS-verified response signing | ✅ |
|
|
259
|
-
| OIDC discovery & SSO readiness | planned |
|
|
260
|
-
| Federation (Google / Okta) | future |
|
|
261
|
-
| Multi-framework adapters (Next.js / Fastify) | coming soon |
|
|
237
|
+
**AGPL-3.0-only** © 2026 Fells Code LLC
|
|
262
238
|
|
|
263
|
-
|
|
239
|
+
This license ensures:
|
|
264
240
|
|
|
265
|
-
|
|
241
|
+
- transparency of security-critical code
|
|
242
|
+
- freedom to self-host and modify
|
|
243
|
+
- sustainability of the managed service offering
|
|
266
244
|
|
|
267
|
-
|
|
268
|
-
Part of the **Seamless Auth** ecosystem.
|
|
245
|
+
See [`LICENSE`](./LICENSE) for details.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,190 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { Router, Request, Response, NextFunction, RequestHandler } from 'express';
|
|
2
|
+
|
|
3
|
+
type SeamlessAuthServerOptions = {
|
|
4
|
+
authServerUrl: string;
|
|
5
|
+
cookieSecret: string;
|
|
6
|
+
serviceSecret: string;
|
|
7
|
+
jwksKid?: string;
|
|
8
|
+
cookieDomain?: string;
|
|
9
|
+
accessCookieName?: string;
|
|
10
|
+
registrationCookieName?: string;
|
|
11
|
+
refreshCookieName?: string;
|
|
12
|
+
preAuthCookieName?: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Creates an Express Router that proxies all authentication traffic to a Seamless Auth server.
|
|
16
|
+
*
|
|
17
|
+
* This helper wires your API backend to a Seamless Auth instance running in
|
|
18
|
+
* "server mode." It automatically forwards login, registration, WebAuthn,
|
|
19
|
+
* logout, token refresh, and session validation routes to the auth server
|
|
20
|
+
* and handles all cookie management required for a seamless login flow.
|
|
21
|
+
*
|
|
22
|
+
* ### Responsibilities
|
|
23
|
+
* - Proxies all `/auth/*` routes to the upstream Seamless Auth server
|
|
24
|
+
* - Manages `access`, `registration`, `pre-auth`, and `refresh` cookies
|
|
25
|
+
* - Normalizes cookie settings for cross-domain or same-domain deployments
|
|
26
|
+
* - Ensures authentication routes behave consistently across environments
|
|
27
|
+
* - Provides shared middleware for auth flows
|
|
28
|
+
*
|
|
29
|
+
* ### Cookie Types
|
|
30
|
+
* - **accessCookie** – long-lived session cookie for authenticated API requests
|
|
31
|
+
* - **registrationCookie** – ephemeral cookie used during registration and OTP/WebAuthn flows
|
|
32
|
+
* - **preAuthCookie** – short-lived cookie used during login initiation
|
|
33
|
+
* - **refreshCookie** – opaque refresh token cookie used to rotate session tokens
|
|
34
|
+
*
|
|
35
|
+
* All cookie names and their domains may be customized via the `opts` parameter.
|
|
36
|
+
*
|
|
37
|
+
* ### Example
|
|
38
|
+
* ```ts
|
|
39
|
+
* app.use("/auth", createSeamlessAuthServer({
|
|
40
|
+
* authServerUrl: "https://identifier.seamlessauth.com",
|
|
41
|
+
* cookieDomain: "mycompany.com",
|
|
42
|
+
* cookieSecret: "someLongRandomValue"
|
|
43
|
+
* serviceSecret: "someLongRandomValueToo"
|
|
44
|
+
* jwksKid: "dev-main"
|
|
45
|
+
* accessCookieName: "sa_access",
|
|
46
|
+
* registrationCookieName: "sa_registration",
|
|
47
|
+
* refreshCookieName: "sa_refresh",
|
|
48
|
+
* }));
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @param opts - Configuration options for the Seamless Auth proxy:
|
|
52
|
+
* - `authServerUrl` — Base URL of your Seamless Auth instance (required)
|
|
53
|
+
* - `cookieSecret` — The value to encode your cookies secrets with (required)
|
|
54
|
+
* - `serviceSecret` - An machine to machine shared secret that matches your auth servers (required)
|
|
55
|
+
* - `jwksKid` - The active jwks KID
|
|
56
|
+
* - `cookieDomain` — Domain attribute applied to all auth cookies
|
|
57
|
+
* - `accessCookieName` — Name of the session access cookie
|
|
58
|
+
* - `registrationCookieName` — Name of the ephemeral registration cookie
|
|
59
|
+
* - `refreshCookieName` — Name of the refresh token cookie
|
|
60
|
+
* - `preAuthCookieName` — Name of the cookie used during login initiation
|
|
61
|
+
*
|
|
62
|
+
* @returns An Express `Router` preconfigured with all Seamless Auth routes.
|
|
63
|
+
*/
|
|
64
|
+
declare function createSeamlessAuthServer(opts: SeamlessAuthServerOptions): Router;
|
|
65
|
+
|
|
66
|
+
interface RequireAuthOptions {
|
|
67
|
+
cookieName?: string;
|
|
68
|
+
cookieSecret: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Express middleware that enforces authentication using Seamless Auth cookies.
|
|
72
|
+
*
|
|
73
|
+
* This guard verifies the signed access cookie generated by the Seamless Auth
|
|
74
|
+
* server. If the access cookie is valid and unexpired, the decoded session
|
|
75
|
+
* payload is attached to `req.user` and the request proceeds.
|
|
76
|
+
*
|
|
77
|
+
* If the access cookie is expired or missing *but* a valid refresh cookie is
|
|
78
|
+
* present, the middleware automatically attempts a silent token refresh using
|
|
79
|
+
* the Seamless Auth server. When successful, new session cookies are issued and
|
|
80
|
+
* the request continues with an updated `req.user`.
|
|
81
|
+
*
|
|
82
|
+
* If neither the access token nor refresh token can validate the session,
|
|
83
|
+
* the middleware returns a 401 Unauthorized error and prevents further
|
|
84
|
+
* route execution.
|
|
85
|
+
*
|
|
86
|
+
* ### Responsibilities
|
|
87
|
+
* - Validates the Seamless Auth session access cookie
|
|
88
|
+
* - Attempts refresh-token–based session renewal when necessary
|
|
89
|
+
* - Populates `req.user` with the verified session payload
|
|
90
|
+
* - Handles all cookie rewriting during refresh flows
|
|
91
|
+
* - Acts as a request-level authentication guard for API routes
|
|
92
|
+
*
|
|
93
|
+
* ### Cookie Parameters
|
|
94
|
+
* - **cookieName** — Name of the access cookie that holds the signed session JWT
|
|
95
|
+
* - **refreshCookieName** — Name of the refresh cookie used for silent token refresh
|
|
96
|
+
* - **cookieDomain** — Domain or path value applied to issued cookies
|
|
97
|
+
*
|
|
98
|
+
* ### Example
|
|
99
|
+
* ```ts
|
|
100
|
+
* // Protect a route
|
|
101
|
+
* app.get("/api/me", requireAuth(), (req, res) => {
|
|
102
|
+
* res.json({ user: req.user });
|
|
103
|
+
* });
|
|
104
|
+
*
|
|
105
|
+
* // Custom cookie names (if your Seamless Auth server uses overrides)
|
|
106
|
+
* app.use(
|
|
107
|
+
* "/internal",
|
|
108
|
+
* requireAuth("sa_access", "sa_refresh", "mycompany.com"),
|
|
109
|
+
* internalRouter
|
|
110
|
+
* );
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @param cookieName - The access cookie name. Defaults to `"seamless-access"`.
|
|
114
|
+
* @param refreshCookieName - The refresh cookie name used for session rotation. Defaults to `"seamless-refresh"`.
|
|
115
|
+
* @param cookieDomain - Domain or path used when rewriting cookies. Defaults to `"/"`.
|
|
116
|
+
*
|
|
117
|
+
* @returns An Express middleware function that enforces Seamless Auth
|
|
118
|
+
* authentication on incoming requests.
|
|
119
|
+
*/
|
|
120
|
+
interface RequireAuthOptions {
|
|
121
|
+
cookieName?: string;
|
|
122
|
+
cookieSecret: string;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Express middleware that enforces authentication
|
|
126
|
+
* using an already-issued Seamless Auth access cookie.
|
|
127
|
+
*
|
|
128
|
+
* NOTE:
|
|
129
|
+
* - This middleware does NOT attempt token refresh.
|
|
130
|
+
* - Refresh is handled upstream by ensureCookies().
|
|
131
|
+
*/
|
|
132
|
+
declare function requireAuth(opts: RequireAuthOptions): (req: Request, res: Response, next: NextFunction) => void;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Express middleware that enforces role-based authorization for Seamless Auth.
|
|
136
|
+
*
|
|
137
|
+
* This middleware assumes `requireAuth()` has already:
|
|
138
|
+
* - authenticated the request
|
|
139
|
+
* - populated `req.user` with the authenticated session payload
|
|
140
|
+
*
|
|
141
|
+
* `requireRole` performs **authorization only**. It does not inspect cookies,
|
|
142
|
+
* verify tokens, or read environment variables.
|
|
143
|
+
*
|
|
144
|
+
* If any of the required roles are present on the user, access is granted.
|
|
145
|
+
* Otherwise, a 403 Forbidden response is returned.
|
|
146
|
+
*
|
|
147
|
+
* * ### Example
|
|
148
|
+
* ```ts
|
|
149
|
+
* // Require a single role
|
|
150
|
+
* app.get("/admin/users",
|
|
151
|
+
* requireAuth(),
|
|
152
|
+
* requireRole("admin"),
|
|
153
|
+
* (req, res) => {
|
|
154
|
+
* res.send("Welcome admin!");
|
|
155
|
+
* }
|
|
156
|
+
* );
|
|
157
|
+
*
|
|
158
|
+
* // Allow any of multiple roles
|
|
159
|
+
* app.post("/settings",
|
|
160
|
+
* requireAuth(),
|
|
161
|
+
* requireRole(["admin", "supervisor"]),
|
|
162
|
+
* updateSettingsHandler
|
|
163
|
+
* );
|
|
164
|
+
*
|
|
165
|
+
* @param requiredRoles - A role or list of roles required to access the route
|
|
166
|
+
*/
|
|
167
|
+
declare function requireRole(requiredRoles: string | string[]): RequestHandler;
|
|
168
|
+
|
|
169
|
+
interface EnsureCookiesMiddlewareOptions {
|
|
170
|
+
authServerUrl: string;
|
|
171
|
+
cookieDomain?: string;
|
|
172
|
+
accessCookieName: string;
|
|
173
|
+
registrationCookieName: string;
|
|
174
|
+
refreshCookieName: string;
|
|
175
|
+
preAuthCookieName: string;
|
|
176
|
+
cookieSecret: string;
|
|
177
|
+
serviceSecret: string;
|
|
178
|
+
issuer: string;
|
|
179
|
+
audience: string;
|
|
180
|
+
keyId: string;
|
|
181
|
+
}
|
|
182
|
+
declare function createEnsureCookiesMiddleware(opts: EnsureCookiesMiddlewareOptions): (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
183
|
+
|
|
184
|
+
declare function getSeamlessUser(req: Request, opts: {
|
|
185
|
+
authServerUrl: string;
|
|
186
|
+
cookieSecret: string;
|
|
187
|
+
cookieName?: string;
|
|
188
|
+
}): Promise<any>;
|
|
189
|
+
|
|
190
|
+
export { type SeamlessAuthServerOptions, createEnsureCookiesMiddleware, createSeamlessAuthServer as default, getSeamlessUser, requireAuth, requireRole };
|