@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.
@@ -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(User, {
33
- email: request.input("email"),
34
- password: request.input("password"),
35
- }, {
36
- userAgent: request.header("user-agent"),
37
- ip: request.ip,
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: "...", // your client-side device fingerprint
91
- familyId: "...", // pre-existing family for token rotation, usually omitted
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, // access token from the Authorization header
106
- request.input("refreshToken"), // refresh token from the request body
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.input("refreshToken"),
139
- { userAgent: request.header("user-agent"), ip: request.ip },
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) => { /* audit */ });
160
- authEvents.on("login.failed", (credentials, reason) => { /* alert on brute force */ });
161
- authEvents.on("logout", (user) => { /* clear server-side session, if any */ });
162
- authEvents.on("token.refreshed", (user, newPair, oldToken) => { /* track rotation */ });
163
- authEvents.on("cleanup.completed", (count) => { /* metrics */ });
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=/`). Log out with `authService.logout(user, access, refresh)` then `authService.clearSessionCookies(response)`. Resolve pages with `pageSession({ project })` (`web.session`); header beats cookie; `auth.session.maxAge` defaults to `"30d"`. Multi-instance: share the JWT secret and the cache store, keep clocks in sync.
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.