@warlock.js/auth 5.25.0 → 5.26.0
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/CHANGELOG.md +19 -9
- package/esm/contracts/index.d.mts +1 -1
- package/esm/contracts/request-locals.d.mts +1 -1
- package/esm/contracts/types.d.mts +8 -3
- package/esm/contracts/types.d.mts.map +1 -1
- package/esm/contracts/types.mjs.map +1 -1
- package/esm/index.d.mts +3 -3
- package/esm/index.mjs +2 -2
- package/esm/services/auth-config.mjs +3 -1
- package/esm/services/auth-config.mjs.map +1 -1
- package/esm/services/auth.service.d.mts +9 -4
- package/esm/services/auth.service.d.mts.map +1 -1
- package/esm/services/auth.service.mjs +35 -8
- package/esm/services/auth.service.mjs.map +1 -1
- package/esm/services/index.d.mts +1 -1
- package/esm/services/index.mjs +1 -1
- package/esm/services/resolve-request-user.d.mts +3 -2
- package/esm/services/resolve-request-user.d.mts.map +1 -1
- package/esm/services/resolve-request-user.mjs +8 -2
- package/esm/services/resolve-request-user.mjs.map +1 -1
- package/esm/session/page-session.d.mts +3 -1
- package/esm/session/page-session.d.mts.map +1 -1
- package/esm/session/page-session.mjs +2 -1
- package/esm/session/page-session.mjs.map +1 -1
- package/llms-full.txt +39 -20
- package/package.json +8 -8
- package/skills/handle-login-and-logout/SKILL.md +36 -21
|
@@ -29,13 +29,17 @@ import { type RequestHandler } from "@warlock.js/core";
|
|
|
29
29
|
import { User } from "app/users/models/user/user.model";
|
|
30
30
|
|
|
31
31
|
export const loginController: RequestHandler = async ({ request, response }) => {
|
|
32
|
-
const result = await authService.login(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
const result = await authService.login(
|
|
33
|
+
User,
|
|
34
|
+
{
|
|
35
|
+
email: request.input("email"),
|
|
36
|
+
password: request.input("password"),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
userAgent: request.header("user-agent"),
|
|
40
|
+
ip: request.ip,
|
|
41
|
+
},
|
|
42
|
+
);
|
|
39
43
|
|
|
40
44
|
if (!result) {
|
|
41
45
|
return response.unauthorized({ error: "Invalid credentials" });
|
|
@@ -87,8 +91,8 @@ The optional `deviceInfo` carries metadata into the refresh token row:
|
|
|
87
91
|
authService.login(User, credentials, {
|
|
88
92
|
userAgent: request.header("user-agent"),
|
|
89
93
|
ip: request.ip,
|
|
90
|
-
deviceId: "...",
|
|
91
|
-
familyId: "...",
|
|
94
|
+
deviceId: "...", // your client-side device fingerprint
|
|
95
|
+
familyId: "...", // pre-existing family for token rotation, usually omitted
|
|
92
96
|
});
|
|
93
97
|
```
|
|
94
98
|
|
|
@@ -102,8 +106,8 @@ import { type RequestHandler } from "@warlock.js/core";
|
|
|
102
106
|
export const logoutController: RequestHandler = async ({ request, response }) => {
|
|
103
107
|
await authService.logout(
|
|
104
108
|
request.locals.user!,
|
|
105
|
-
request.authorizationValue,
|
|
106
|
-
request.input("refreshToken"),
|
|
109
|
+
request.authorizationValue, // access token from the Authorization header
|
|
110
|
+
request.input("refreshToken"), // refresh token from the request body
|
|
107
111
|
);
|
|
108
112
|
|
|
109
113
|
return response.success({ message: "Logged out" });
|
|
@@ -111,6 +115,7 @@ export const logoutController: RequestHandler = async ({ request, response }) =>
|
|
|
111
115
|
```
|
|
112
116
|
|
|
113
117
|
The contract:
|
|
118
|
+
|
|
114
119
|
- **Pass the access token** → that specific access-token row is deleted.
|
|
115
120
|
- **Pass the refresh token** → that specific refresh-token row is revoked.
|
|
116
121
|
- **Omit refresh token** → behavior depends on `config.auth.refreshToken.logoutWithoutToken`:
|
|
@@ -134,10 +139,10 @@ Useful for "logout from all devices" buttons. Fires `token.revoked` per token +
|
|
|
134
139
|
import { type RequestHandler } from "@warlock.js/core";
|
|
135
140
|
|
|
136
141
|
export const refreshController: RequestHandler = async ({ request, response }) => {
|
|
137
|
-
const tokens = await authService.refreshTokens(
|
|
138
|
-
request.
|
|
139
|
-
|
|
140
|
-
);
|
|
142
|
+
const tokens = await authService.refreshTokens(request.input("refreshToken"), {
|
|
143
|
+
userAgent: request.header("user-agent"),
|
|
144
|
+
ip: request.ip,
|
|
145
|
+
});
|
|
141
146
|
|
|
142
147
|
if (!tokens) {
|
|
143
148
|
return response.unauthorized({ error: "Invalid refresh token" });
|
|
@@ -156,11 +161,21 @@ Returns a new token pair or `null` (token expired, revoked, or replay-detected).
|
|
|
156
161
|
```ts
|
|
157
162
|
import { authEvents } from "@warlock.js/auth";
|
|
158
163
|
|
|
159
|
-
authEvents.on("login.success", (user, tokens, deviceInfo) => {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
authEvents.on("
|
|
163
|
-
|
|
164
|
+
authEvents.on("login.success", (user, tokens, deviceInfo) => {
|
|
165
|
+
/* audit */
|
|
166
|
+
});
|
|
167
|
+
authEvents.on("login.failed", (credentials, reason) => {
|
|
168
|
+
/* alert on brute force */
|
|
169
|
+
});
|
|
170
|
+
authEvents.on("logout", (user) => {
|
|
171
|
+
/* clear server-side session, if any */
|
|
172
|
+
});
|
|
173
|
+
authEvents.on("token.refreshed", (user, newPair, oldToken) => {
|
|
174
|
+
/* track rotation */
|
|
175
|
+
});
|
|
176
|
+
authEvents.on("cleanup.completed", (count) => {
|
|
177
|
+
/* metrics */
|
|
178
|
+
});
|
|
164
179
|
```
|
|
165
180
|
|
|
166
181
|
Full event list: `login.attempt`, `login.success`, `login.failed`, `logout`, `logout.all`, `logout.failsafe`, `token.created`, `token.refreshed`, `token.revoked`, `token.expired`, `token.familyRevoked`, `session.created`, `session.destroyed`, `cleanup.completed`.
|
|
@@ -180,4 +195,4 @@ Full event list: `login.attempt`, `login.success`, `login.failed`, `logout`, `lo
|
|
|
180
195
|
|
|
181
196
|
## Browser sessions for Web pages
|
|
182
197
|
|
|
183
|
-
For cookie sessions read by `@warlock.js/web` pages, log in with `authService.loginWithSessionCookies(request, response, Model, credentials)`. It requires a same-origin `Origin`/`Referer` even without cookies (login CSRF) and sets the access and refresh cookies (`auth.cookie.name`, `auth.cookie.refreshName`, `Path=/`).
|
|
198
|
+
For cookie sessions read by `@warlock.js/web` pages, log in with `authService.loginWithSessionCookies(request, response, Model, credentials)`. It requires a same-origin `Origin`/`Referer` even without cookies (login CSRF) and sets the access and refresh cookies (`auth.cookie.name`, `auth.cookie.refreshName`, `Path=/`). To share a session across subdomains, configure `auth.cookie.cookieDomain(request)` to return (for example) `".example.com"`; return `undefined` for host-only cookies. With `cookieDomain` configured, always pass the request to cookie helpers so they select the same Domain: `authService.setSessionCookies(response, tokens, request)`, `authService.clearSessionCookies(response, request)`, and `authService.clearAuthCookie(response, options, request)`. Omitting it throws `CookieDomainRequestRequiredError`. Domain clears also remove the legacy host-only names. Resolve pages with `pageSession({ project })` (`web.session`); header beats cookie; `auth.session.maxAge` defaults to `"30d"`. A page session with multiple `userTypes` cannot renew its cookie: the resolver only supplies a renewal user type when exactly one type is allowed, so it reads the session but skips automatic renewal. Multi-instance: share the JWT secret and the cache store, keep clocks in sync.
|