sentri 1.1.2 → 2.1.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.
Files changed (64) hide show
  1. package/README.md +268 -448
  2. package/dist/cli.d.ts +0 -2
  3. package/dist/cli.js +113 -107
  4. package/dist/index.d.ts +545 -11
  5. package/dist/index.js +1 -5
  6. package/package.json +9 -7
  7. package/dist/cli.d.ts.map +0 -1
  8. package/dist/cli.js.map +0 -1
  9. package/dist/client.d.ts +0 -160
  10. package/dist/client.d.ts.map +0 -1
  11. package/dist/client.js +0 -45
  12. package/dist/client.js.map +0 -1
  13. package/dist/errors/AuthError.d.ts +0 -99
  14. package/dist/errors/AuthError.d.ts.map +0 -1
  15. package/dist/errors/AuthError.js +0 -97
  16. package/dist/errors/AuthError.js.map +0 -1
  17. package/dist/index.d.ts.map +0 -1
  18. package/dist/index.js.map +0 -1
  19. package/dist/libs/config.d.ts +0 -62
  20. package/dist/libs/config.d.ts.map +0 -1
  21. package/dist/libs/config.js +0 -97
  22. package/dist/libs/config.js.map +0 -1
  23. package/dist/libs/hash.d.ts +0 -17
  24. package/dist/libs/hash.d.ts.map +0 -1
  25. package/dist/libs/hash.js +0 -22
  26. package/dist/libs/hash.js.map +0 -1
  27. package/dist/libs/token.d.ts +0 -46
  28. package/dist/libs/token.d.ts.map +0 -1
  29. package/dist/libs/token.js +0 -118
  30. package/dist/libs/token.js.map +0 -1
  31. package/dist/middleware/authorize.d.ts +0 -18
  32. package/dist/middleware/authorize.d.ts.map +0 -1
  33. package/dist/middleware/authorize.js +0 -30
  34. package/dist/middleware/authorize.js.map +0 -1
  35. package/dist/middleware/errorHandler.d.ts +0 -71
  36. package/dist/middleware/errorHandler.d.ts.map +0 -1
  37. package/dist/middleware/errorHandler.js +0 -74
  38. package/dist/middleware/errorHandler.js.map +0 -1
  39. package/dist/middleware/permit.d.ts +0 -62
  40. package/dist/middleware/permit.d.ts.map +0 -1
  41. package/dist/middleware/permit.js +0 -61
  42. package/dist/middleware/permit.js.map +0 -1
  43. package/dist/middleware/protect.d.ts +0 -31
  44. package/dist/middleware/protect.d.ts.map +0 -1
  45. package/dist/middleware/protect.js +0 -54
  46. package/dist/middleware/protect.js.map +0 -1
  47. package/dist/middleware/router.d.ts +0 -34
  48. package/dist/middleware/router.d.ts.map +0 -1
  49. package/dist/middleware/router.js +0 -264
  50. package/dist/middleware/router.js.map +0 -1
  51. package/dist/services/auth.d.ts +0 -85
  52. package/dist/services/auth.d.ts.map +0 -1
  53. package/dist/services/auth.js +0 -173
  54. package/dist/services/auth.js.map +0 -1
  55. package/dist/types/auth.d.ts +0 -450
  56. package/dist/types/auth.d.ts.map +0 -1
  57. package/dist/types/auth.js +0 -21
  58. package/dist/types/auth.js.map +0 -1
  59. package/templates/drizzle/adapter.ts +0 -154
  60. package/templates/drizzle/auth.ts +0 -82
  61. package/templates/drizzle/schema.ts +0 -47
  62. package/templates/prisma/adapter.ts +0 -122
  63. package/templates/prisma/auth.ts +0 -85
  64. package/templates/prisma/schema.prisma +0 -56
@@ -1,264 +0,0 @@
1
- import { Router } from 'express';
2
- import { SentriError } from '../errors/AuthError.js';
3
- import { register, login, refresh, logout, logoutAll, assignRoles } from '../services/auth.js';
4
- import { resolveConfig, parseExpiry } from '../libs/config.js';
5
- import { protect } from './protect.js';
6
- import { authorize } from './authorize.js';
7
- const MIN_PASSWORD_LENGTH = 8;
8
- // bcrypt silently truncates input beyond 72 bytes. Enforcing a cap makes the
9
- // truncation boundary explicit so two passwords that share the same first 72
10
- // bytes cannot be treated as identical.
11
- const MAX_PASSWORD_LENGTH = 72;
12
- const MAX_IDENTIFIER_LENGTH = 255;
13
- function badRequest(message) {
14
- return new SentriError('VALIDATION_ERROR', message);
15
- }
16
- function ok(response, statusCode, message, data) {
17
- response.status(statusCode).json({ error: false, statusCode, message, data });
18
- }
19
- function fail(response, error) {
20
- response.status(error.statusCode).json({ error: true, statusCode: error.statusCode, message: error.message, data: null });
21
- }
22
- function parseBody(body) {
23
- if (body === null || body === undefined || typeof body !== 'object' || Array.isArray(body)) {
24
- throw new SentriError('VALIDATION_ERROR', 'Request body is missing or not a JSON object. Did you apply express.json()?');
25
- }
26
- return body;
27
- }
28
- // Read a single cookie from the raw Cookie header — no cookie-parser needed.
29
- function readCookie(cookieHeader, name) {
30
- if (!cookieHeader)
31
- return undefined;
32
- const pair = cookieHeader
33
- .split(';')
34
- .map((segment) => segment.trim())
35
- .find((segment) => segment.startsWith(`${name}=`));
36
- return pair !== undefined ? pair.slice(name.length + 1) : undefined;
37
- }
38
- function getCookieName(config) {
39
- return config.cookie?.name ?? 'refresh_token';
40
- }
41
- function setCookie(response, token, config) {
42
- const cookieConfig = config.cookie ?? {};
43
- const resolved = resolveConfig(config);
44
- const maxAge = parseExpiry(resolved.refreshExpiresIn);
45
- response.cookie(getCookieName(config), token, {
46
- httpOnly: cookieConfig.httpOnly ?? true,
47
- secure: cookieConfig.secure ?? false,
48
- sameSite: cookieConfig.sameSite ?? 'strict',
49
- path: cookieConfig.path ?? '/',
50
- maxAge,
51
- });
52
- }
53
- function clearCookie(response, config) {
54
- const cookieConfig = config.cookie ?? {};
55
- response.clearCookie(getCookieName(config), { path: cookieConfig.path ?? '/' });
56
- }
57
- /**
58
- * Validate the `X-Api-Key` header when `config.apiKey` is set.
59
- * Throws `SentriError` with code `UNAUTHORIZED` on mismatch.
60
- */
61
- function validateApiKey(request, config) {
62
- if (!config.apiKey)
63
- return;
64
- const provided = request.headers['x-api-key'];
65
- if (typeof provided !== 'string' || provided !== config.apiKey) {
66
- throw new SentriError('UNAUTHORIZED', 'Invalid or missing API key');
67
- }
68
- }
69
- /**
70
- * Creates a pre-built Express Router with all standard auth endpoints.
71
- *
72
- * Mount it once and all routes are ready:
73
- *
74
- * ```
75
- * POST /register — register a new user (protected by X-Api-Key when config.apiKey is set)
76
- * POST /login — authenticate and get tokens
77
- * POST /refresh — rotate refresh token
78
- * POST /logout — invalidate current session; access tokens issued before logout become invalid
79
- * POST /logout-all — invalidate all sessions for the authenticated user
80
- * GET /me — return the currently authenticated user
81
- * POST /users/:userId/roles — assign roles (admin only)
82
- * ```
83
- *
84
- * Requires `express.json()` to be applied before the router.
85
- *
86
- * When `cookie` is set in config, the refresh token is stored in an httpOnly
87
- * cookie automatically — no `cookie-parser` needed.
88
- *
89
- * When `apiKey` is set in config, `POST /register` requires the caller to send
90
- * an `X-Api-Key: <key>` header matching the configured value.
91
- *
92
- * When `router` is set in config, individual service functions can be replaced
93
- * while the router still handles validation and response formatting.
94
- *
95
- * @example
96
- * app.use(express.json());
97
- * app.use('/auth', auth.router());
98
- */
99
- export function createAuthRouter(config) {
100
- const router = Router();
101
- // Resolve service functions — use custom override from config.router when provided, else fall back to the built-in service.
102
- const baseConfig = config;
103
- const registerFn = config.router?.register ?? ((input) => register(input, baseConfig));
104
- const loginFn = config.router?.login ?? ((input) => login(input, baseConfig));
105
- const refreshFn = config.router?.refresh ?? ((token) => refresh(token, baseConfig));
106
- const logoutFn = config.router?.logout ?? ((token) => token !== undefined ? logout(token, baseConfig) : Promise.resolve());
107
- const logoutAllFn = config.router?.logoutAll ?? ((userId) => logoutAll(userId, baseConfig));
108
- const assignRolesFn = config.router?.assignRoles ?? ((userId, roles) => assignRoles(userId, roles, baseConfig));
109
- /**
110
- * POST /register
111
- *
112
- * Register a new user. Does **not** issue tokens — call `/login` after registration.
113
- *
114
- * When `config.apiKey` is set the caller must supply the matching value in the
115
- * `X-Api-Key` header, preventing arbitrary users from self-registering as admins.
116
- */
117
- router.post('/register', async (request, response, next) => {
118
- try {
119
- validateApiKey(request, config);
120
- const body = parseBody(request.body);
121
- const { identifier, password, roles } = body;
122
- if (typeof identifier !== 'string' || identifier.trim().length === 0) {
123
- throw badRequest('identifier is required and must be a non-empty string');
124
- }
125
- if (identifier.length > MAX_IDENTIFIER_LENGTH) {
126
- throw badRequest(`identifier must not exceed ${MAX_IDENTIFIER_LENGTH} characters`);
127
- }
128
- if (typeof password !== 'string' || password.length < MIN_PASSWORD_LENGTH) {
129
- throw badRequest(`password is required and must be at least ${MIN_PASSWORD_LENGTH} characters`);
130
- }
131
- if (password.length > MAX_PASSWORD_LENGTH) {
132
- throw badRequest(`password must not exceed ${MAX_PASSWORD_LENGTH} characters`);
133
- }
134
- if (roles !== undefined && !Array.isArray(roles)) {
135
- throw badRequest('roles must be an array of strings when provided');
136
- }
137
- if (Array.isArray(roles) && !roles.every((role) => typeof role === 'string')) {
138
- throw badRequest('each role must be a string');
139
- }
140
- const rolesInput = Array.isArray(roles) ? roles : undefined;
141
- const input = rolesInput !== undefined
142
- ? { identifier: identifier.trim(), password, roles: rolesInput }
143
- : { identifier: identifier.trim(), password };
144
- const result = await registerFn(input);
145
- if (!result.success) {
146
- fail(response, result.error);
147
- return;
148
- }
149
- ok(response, 201, 'User registered successfully', { user: result.user });
150
- }
151
- catch (error) {
152
- next(error);
153
- }
154
- });
155
- router.post('/login', async (request, response, next) => {
156
- try {
157
- const body = parseBody(request.body);
158
- const { identifier, password } = body;
159
- if (typeof identifier !== 'string' || identifier.trim().length === 0) {
160
- throw badRequest('identifier is required and must be a non-empty string');
161
- }
162
- if (identifier.length > MAX_IDENTIFIER_LENGTH) {
163
- throw badRequest(`identifier must not exceed ${MAX_IDENTIFIER_LENGTH} characters`);
164
- }
165
- if (typeof password !== 'string' || password.length === 0) {
166
- throw badRequest('password is required');
167
- }
168
- if (password.length > MAX_PASSWORD_LENGTH) {
169
- throw badRequest(`password must not exceed ${MAX_PASSWORD_LENGTH} characters`);
170
- }
171
- const result = await loginFn({ identifier: identifier.trim(), password });
172
- if (!result.success) {
173
- fail(response, result.error);
174
- return;
175
- }
176
- setCookie(response, result.refreshToken, config);
177
- ok(response, 200, 'Login successful', { accessToken: result.accessToken, user: result.user });
178
- }
179
- catch (error) {
180
- next(error);
181
- }
182
- });
183
- router.post('/refresh', async (request, response, next) => {
184
- try {
185
- const fromCookie = readCookie(request.headers['cookie'], getCookieName(config));
186
- if (!fromCookie) {
187
- throw new SentriError('UNAUTHORIZED', 'Refresh token cookie is missing');
188
- }
189
- const result = await refreshFn(fromCookie);
190
- if (!result.success) {
191
- clearCookie(response, config);
192
- fail(response, result.error);
193
- return;
194
- }
195
- setCookie(response, result.refreshToken, config);
196
- ok(response, 200, 'Token refreshed', { accessToken: result.accessToken });
197
- }
198
- catch (error) {
199
- next(error);
200
- }
201
- });
202
- router.post('/logout', async (request, response, next) => {
203
- try {
204
- const fromCookie = readCookie(request.headers['cookie'], getCookieName(config));
205
- await logoutFn(fromCookie);
206
- clearCookie(response, config);
207
- ok(response, 200, 'Logged out', null);
208
- }
209
- catch (error) {
210
- next(error);
211
- }
212
- });
213
- router.post('/logout-all', protect(config), async (request, response, next) => {
214
- try {
215
- await logoutAllFn(request.user.id);
216
- clearCookie(response, config);
217
- ok(response, 200, 'All sessions revoked', null);
218
- }
219
- catch (error) {
220
- next(error);
221
- }
222
- });
223
- router.get('/me', protect(config), (request, response) => {
224
- ok(response, 200, 'OK', request.user);
225
- });
226
- router.post('/users/:userId/roles', protect(config), authorize('admin'), async (request, response, next) => {
227
- try {
228
- const body = parseBody(request.body);
229
- const { roles } = body;
230
- const rawUserId = request.params['userId'];
231
- const userId = typeof rawUserId === 'string' ? rawUserId : undefined;
232
- if (!userId) {
233
- throw badRequest('userId is required');
234
- }
235
- if (!Array.isArray(roles) || roles.length === 0) {
236
- throw badRequest('roles must be a non-empty array of strings');
237
- }
238
- if (!roles.every((role) => typeof role === 'string')) {
239
- throw badRequest('each role must be a string');
240
- }
241
- const result = await assignRolesFn(userId, roles);
242
- if (!result.success) {
243
- fail(response, result.error);
244
- return;
245
- }
246
- ok(response, 200, 'Roles assigned successfully', { user: result.user });
247
- }
248
- catch (error) {
249
- next(error);
250
- }
251
- });
252
- // Centralized error handler — converts SentriError (and unexpected errors) to the
253
- // standard envelope so every endpoint produces a consistent shape on failure.
254
- router.use((error, _request, response, _next) => {
255
- if (error instanceof SentriError) {
256
- fail(response, error);
257
- }
258
- else {
259
- response.status(500).json({ error: true, statusCode: 500, message: 'Internal server error', data: null });
260
- }
261
- });
262
- return router;
263
- }
264
- //# sourceMappingURL=router.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/middleware/router.ts"],"names":[],"mappings":"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"}
@@ -1,85 +0,0 @@
1
- import type { AssignRolesResult, AuthConfig, AuthResult, LoginInput, RefreshResult, RegisterInput, RegisterResult } from '../types/auth.js';
2
- /**
3
- * Register a new user.
4
- *
5
- * Validates that every requested role is in `validRoles`, rejects duplicate
6
- * identifiers, hashes the password with bcrypt, creates the user record via
7
- * the adapter, and returns the created user.
8
- *
9
- * No tokens are issued — the caller should invoke `login` after registration
10
- * if immediate authentication is desired.
11
- *
12
- * @param input - Registration data: identifier, plain-text password, and optional roles.
13
- * @param config - Auth configuration containing the adapter and role definitions.
14
- * @returns `{ success: true, user }` on success, or `{ success: false, error }` with
15
- * code `INVALID_ROLE` or `USER_ALREADY_EXISTS` on failure.
16
- */
17
- export declare function register(input: RegisterInput, config: AuthConfig): Promise<RegisterResult>;
18
- /**
19
- * Authenticate an existing user by identifier and plain-text password.
20
- *
21
- * Looks up the user, verifies the password with bcrypt, creates a new session,
22
- * and issues a JWT access token + refresh token pair. The access token embeds
23
- * the session ID so that `protect()` can reject it immediately after logout
24
- * without waiting for the token to expire.
25
- *
26
- * The failure response always uses code `INVALID_CREDENTIALS` regardless of
27
- * whether the identifier or the password was wrong, preventing user enumeration.
28
- *
29
- * @param input - Login data: identifier and plain-text password.
30
- * @param config - Auth configuration containing the adapter and JWT settings.
31
- * @returns `{ success: true, accessToken, refreshToken, user }` on success, or
32
- * `{ success: false, error }` with code `INVALID_CREDENTIALS` on failure.
33
- */
34
- export declare function login(input: LoginInput, config: AuthConfig): Promise<AuthResult>;
35
- /**
36
- * Exchange a valid refresh token for a new access + refresh token pair.
37
- *
38
- * Implements **session rotation**: the old session is deleted and a fresh
39
- * session is created, so each refresh token is single-use. An attacker
40
- * replaying a stolen refresh token after it has already been rotated will
41
- * find the session gone.
42
- *
43
- * @param refreshToken - The JWT refresh token (typically from an httpOnly cookie).
44
- * @param config - Auth configuration containing the adapter and JWT settings.
45
- * @returns `{ success: true, accessToken, refreshToken, user }` on success, or
46
- * `{ success: false, error }` with code `UNAUTHORIZED`, `TOKEN_EXPIRED`, or
47
- * `TOKEN_INVALID` on failure.
48
- */
49
- export declare function refresh(refreshToken: string, config: AuthConfig): Promise<RefreshResult>;
50
- /**
51
- * Invalidate a single session identified by a refresh token.
52
- *
53
- * Safe to call even when the token is already expired or invalid — the JWT
54
- * parse failure is silently swallowed and the function resolves normally.
55
- * This makes logout idempotent from the client's perspective.
56
- *
57
- * @param refreshToken - The JWT refresh token bound to the session to revoke.
58
- * @param config - Auth configuration containing the adapter and JWT settings.
59
- */
60
- export declare function logout(refreshToken: string, config: AuthConfig): Promise<void>;
61
- /**
62
- * Delete all sessions for a user, effectively logging them out of every device.
63
- *
64
- * Delegates to `adapter.session.deleteAllForUser`. No token is required — the
65
- * router route that calls this function is already guarded by `protect()`.
66
- *
67
- * @param userId - The user's primary key as stored in the database.
68
- * @param config - Auth configuration containing the adapter.
69
- */
70
- export declare function logoutAll(userId: string, config: AuthConfig): Promise<void>;
71
- /**
72
- * Add roles to a user account, merging them with any existing roles.
73
- *
74
- * Validates that every role in `rolesToAdd` is listed in `config.validRoles`.
75
- * The resulting role set is deduplicated before being persisted via
76
- * `adapter.user.updateRoles`.
77
- *
78
- * @param userId - The primary key of the user to update.
79
- * @param rolesToAdd - Role names to assign. Must all be present in `validRoles`.
80
- * @param config - Auth configuration containing the adapter and role definitions.
81
- * @returns `{ success: true, user }` on success, or `{ success: false, error }` with
82
- * code `INVALID_ROLE` or `USER_NOT_FOUND` on failure.
83
- */
84
- export declare function assignRoles(userId: string, rolesToAdd: string[], config: AuthConfig): Promise<AssignRolesResult>;
85
- //# sourceMappingURL=auth.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/services/auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE5I;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,aAAa,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,cAAc,CAAC,CAoBzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,KAAK,CACzB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,UAAU,CAAC,CAqBrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,OAAO,CAC3B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,aAAa,CAAC,CA+BxB;AAED;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAC1B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAAE,EACpB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,iBAAiB,CAAC,CAiB5B"}
@@ -1,173 +0,0 @@
1
- import { SentriError } from '../errors/AuthError.js';
2
- import { hashPassword, verifyPassword } from '../libs/hash.js';
3
- import { signAccessToken, signRefreshToken, verifyRefreshToken } from '../libs/token.js';
4
- import { resolveConfig, parseExpiry } from '../libs/config.js';
5
- /**
6
- * Register a new user.
7
- *
8
- * Validates that every requested role is in `validRoles`, rejects duplicate
9
- * identifiers, hashes the password with bcrypt, creates the user record via
10
- * the adapter, and returns the created user.
11
- *
12
- * No tokens are issued — the caller should invoke `login` after registration
13
- * if immediate authentication is desired.
14
- *
15
- * @param input - Registration data: identifier, plain-text password, and optional roles.
16
- * @param config - Auth configuration containing the adapter and role definitions.
17
- * @returns `{ success: true, user }` on success, or `{ success: false, error }` with
18
- * code `INVALID_ROLE` or `USER_ALREADY_EXISTS` on failure.
19
- */
20
- export async function register(input, config) {
21
- const resolved = resolveConfig(config);
22
- const requestedRoles = input.roles ?? [];
23
- const invalidRoles = requestedRoles.filter((r) => !resolved.validRoles.includes(r));
24
- if (invalidRoles.length > 0) {
25
- return { success: false, error: new SentriError('INVALID_ROLE', `Invalid roles: ${invalidRoles.join(', ')}`) };
26
- }
27
- const identifier = input.identifier.trim();
28
- const existing = await resolved.adapter.user.findByIdentifier(identifier);
29
- if (existing) {
30
- return { success: false, error: new SentriError('USER_ALREADY_EXISTS', 'User already exists') };
31
- }
32
- const passwordHash = await hashPassword(input.password, resolved.saltRounds);
33
- const created = await resolved.adapter.user.create({ identifier, passwordHash, roles: requestedRoles });
34
- const user = { id: created.id, identifier, roles: requestedRoles };
35
- return { success: true, user };
36
- }
37
- /**
38
- * Authenticate an existing user by identifier and plain-text password.
39
- *
40
- * Looks up the user, verifies the password with bcrypt, creates a new session,
41
- * and issues a JWT access token + refresh token pair. The access token embeds
42
- * the session ID so that `protect()` can reject it immediately after logout
43
- * without waiting for the token to expire.
44
- *
45
- * The failure response always uses code `INVALID_CREDENTIALS` regardless of
46
- * whether the identifier or the password was wrong, preventing user enumeration.
47
- *
48
- * @param input - Login data: identifier and plain-text password.
49
- * @param config - Auth configuration containing the adapter and JWT settings.
50
- * @returns `{ success: true, accessToken, refreshToken, user }` on success, or
51
- * `{ success: false, error }` with code `INVALID_CREDENTIALS` on failure.
52
- */
53
- export async function login(input, config) {
54
- const resolved = resolveConfig(config);
55
- const found = await resolved.adapter.user.findByIdentifier(input.identifier.trim());
56
- if (!found) {
57
- return { success: false, error: new SentriError('INVALID_CREDENTIALS', 'Invalid credentials') };
58
- }
59
- const valid = await verifyPassword(input.password, found.passwordHash);
60
- if (!valid) {
61
- return { success: false, error: new SentriError('INVALID_CREDENTIALS', 'Invalid credentials') };
62
- }
63
- const expiresAt = new Date(Date.now() + parseExpiry(resolved.refreshExpiresIn));
64
- const session = await resolved.adapter.session.create({ userId: found.id, expiresAt });
65
- const user = { id: found.id, identifier: found.identifier, roles: found.roles };
66
- // Embed sessionId in the access token so protect() can invalidate it on logout.
67
- const accessToken = signAccessToken({ ...user, sessionId: session.id }, config);
68
- const refreshToken = signRefreshToken(session.id, config);
69
- return { success: true, accessToken, refreshToken, user };
70
- }
71
- /**
72
- * Exchange a valid refresh token for a new access + refresh token pair.
73
- *
74
- * Implements **session rotation**: the old session is deleted and a fresh
75
- * session is created, so each refresh token is single-use. An attacker
76
- * replaying a stolen refresh token after it has already been rotated will
77
- * find the session gone.
78
- *
79
- * @param refreshToken - The JWT refresh token (typically from an httpOnly cookie).
80
- * @param config - Auth configuration containing the adapter and JWT settings.
81
- * @returns `{ success: true, accessToken, refreshToken, user }` on success, or
82
- * `{ success: false, error }` with code `UNAUTHORIZED`, `TOKEN_EXPIRED`, or
83
- * `TOKEN_INVALID` on failure.
84
- */
85
- export async function refresh(refreshToken, config) {
86
- const resolved = resolveConfig(config);
87
- let sessionId;
88
- try {
89
- ({ sessionId } = verifyRefreshToken(refreshToken, config));
90
- }
91
- catch (err) {
92
- if (err instanceof SentriError)
93
- return { success: false, error: err };
94
- return { success: false, error: new SentriError('TOKEN_INVALID', 'Invalid refresh token') };
95
- }
96
- const session = await resolved.adapter.session.findById(sessionId);
97
- if (!session) {
98
- return { success: false, error: new SentriError('UNAUTHORIZED', 'Session not found or revoked') };
99
- }
100
- if (session.expiresAt < new Date()) {
101
- await resolved.adapter.session.delete(sessionId);
102
- return { success: false, error: new SentriError('TOKEN_EXPIRED', 'Session has expired') };
103
- }
104
- // rotate: delete old session, create new one
105
- await resolved.adapter.session.delete(sessionId);
106
- const expiresAt = new Date(Date.now() + parseExpiry(resolved.refreshExpiresIn));
107
- const newSession = await resolved.adapter.session.create({ userId: session.userId, expiresAt });
108
- const user = { id: session.user.id, identifier: session.user.identifier, roles: session.user.roles };
109
- // Embed new sessionId in the rotated access token.
110
- const newAccessToken = signAccessToken({ ...user, sessionId: newSession.id }, config);
111
- const newRefreshToken = signRefreshToken(newSession.id, config);
112
- return { success: true, accessToken: newAccessToken, refreshToken: newRefreshToken, user };
113
- }
114
- /**
115
- * Invalidate a single session identified by a refresh token.
116
- *
117
- * Safe to call even when the token is already expired or invalid — the JWT
118
- * parse failure is silently swallowed and the function resolves normally.
119
- * This makes logout idempotent from the client's perspective.
120
- *
121
- * @param refreshToken - The JWT refresh token bound to the session to revoke.
122
- * @param config - Auth configuration containing the adapter and JWT settings.
123
- */
124
- export async function logout(refreshToken, config) {
125
- let sessionId;
126
- try {
127
- ({ sessionId } = verifyRefreshToken(refreshToken, config));
128
- }
129
- catch {
130
- return; // already invalid, nothing to revoke
131
- }
132
- await resolveConfig(config).adapter.session.delete(sessionId);
133
- }
134
- /**
135
- * Delete all sessions for a user, effectively logging them out of every device.
136
- *
137
- * Delegates to `adapter.session.deleteAllForUser`. No token is required — the
138
- * router route that calls this function is already guarded by `protect()`.
139
- *
140
- * @param userId - The user's primary key as stored in the database.
141
- * @param config - Auth configuration containing the adapter.
142
- */
143
- export async function logoutAll(userId, config) {
144
- await resolveConfig(config).adapter.session.deleteAllForUser(userId);
145
- }
146
- /**
147
- * Add roles to a user account, merging them with any existing roles.
148
- *
149
- * Validates that every role in `rolesToAdd` is listed in `config.validRoles`.
150
- * The resulting role set is deduplicated before being persisted via
151
- * `adapter.user.updateRoles`.
152
- *
153
- * @param userId - The primary key of the user to update.
154
- * @param rolesToAdd - Role names to assign. Must all be present in `validRoles`.
155
- * @param config - Auth configuration containing the adapter and role definitions.
156
- * @returns `{ success: true, user }` on success, or `{ success: false, error }` with
157
- * code `INVALID_ROLE` or `USER_NOT_FOUND` on failure.
158
- */
159
- export async function assignRoles(userId, rolesToAdd, config) {
160
- const resolved = resolveConfig(config);
161
- const invalidRoles = rolesToAdd.filter((role) => !resolved.validRoles.includes(role));
162
- if (invalidRoles.length > 0) {
163
- return { success: false, error: new SentriError('INVALID_ROLE', `Invalid roles: ${invalidRoles.join(', ')}`) };
164
- }
165
- const found = await resolved.adapter.user.findById(userId);
166
- if (!found) {
167
- return { success: false, error: new SentriError('USER_NOT_FOUND', 'User not found') };
168
- }
169
- const mergedRoles = Array.from(new Set([...found.roles, ...rolesToAdd]));
170
- await resolved.adapter.user.updateRoles(userId, mergedRoles);
171
- return { success: true, user: { id: found.id, identifier: found.identifier, roles: mergedRoles } };
172
- }
173
- //# sourceMappingURL=auth.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/services/auth.ts"],"names":[],"mappings":"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"}