@gauts/auth 0.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.
- package/LICENSE +21 -0
- package/README.md +222 -0
- package/SECURITY.md +5 -0
- package/dist/adapters/hono/index.d.ts +30 -0
- package/dist/adapters/hono/index.d.ts.map +1 -0
- package/dist/adapters/hono/index.js +87 -0
- package/dist/adapters/hono/index.js.map +1 -0
- package/dist/adapters/redis/index.d.ts +7 -0
- package/dist/adapters/redis/index.d.ts.map +1 -0
- package/dist/adapters/redis/index.js +53 -0
- package/dist/adapters/redis/index.js.map +1 -0
- package/dist/auth.d.ts +17 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +34 -0
- package/dist/auth.js.map +1 -0
- package/dist/client/index.d.ts +19 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +36 -0
- package/dist/client/index.js.map +1 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +67 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +7 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/password/index.d.ts +8 -0
- package/dist/password/index.d.ts.map +1 -0
- package/dist/password/index.js +46 -0
- package/dist/password/index.js.map +1 -0
- package/dist/session/schema.d.ts +5 -0
- package/dist/session/schema.d.ts.map +1 -0
- package/dist/session/schema.js +47 -0
- package/dist/session/schema.js.map +1 -0
- package/dist/session/service.d.ts +11 -0
- package/dist/session/service.d.ts.map +1 -0
- package/dist/session/service.js +211 -0
- package/dist/session/service.js.map +1 -0
- package/dist/session/token.d.ts +4 -0
- package/dist/session/token.d.ts.map +1 -0
- package/dist/session/token.js +5 -0
- package/dist/session/token.js.map +1 -0
- package/dist/session/types.d.ts +78 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/package.json +98 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GVALFER
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# `@gauts/auth`
|
|
2
|
+
|
|
3
|
+
Reusable password and opaque server-side session authentication for Node.js applications.
|
|
4
|
+
|
|
5
|
+
`@gauts/auth` validates sessions through Redis and records session history through an application-owned database adapter. It does not create endpoints, database models, users, or application authorization rules.
|
|
6
|
+
|
|
7
|
+
The package is under active development and has not been published yet.
|
|
8
|
+
|
|
9
|
+
## Design
|
|
10
|
+
|
|
11
|
+
- Argon2id by default, with explicit bcrypt support.
|
|
12
|
+
- One stable 256-bit opaque browser token per session.
|
|
13
|
+
- SHA-256 token hashes in Redis and the audit database.
|
|
14
|
+
- Redis-authoritative authentication with no database fallback.
|
|
15
|
+
- Sliding inactivity expiration without an absolute lifetime.
|
|
16
|
+
- Exact canonical IP and complete User-Agent session binding.
|
|
17
|
+
- Framework integrations through explicit package exports.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @gauts/auth redis hono
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The package is not on npm yet. During local development, use the tarball produced by `npm pack`.
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { createAuth, type SessionAudit } from "@gauts/auth";
|
|
31
|
+
import { createHonoAdapter } from "@gauts/auth/hono";
|
|
32
|
+
import { createRedisStore } from "@gauts/auth/redis";
|
|
33
|
+
|
|
34
|
+
const audit: SessionAudit = {
|
|
35
|
+
create: (session) => db.sessions.create(session),
|
|
36
|
+
findActive: (subjectId, now) => db.sessions.findActive(subjectId, now),
|
|
37
|
+
findForRevoke: (subjectId, sessionId) => db.sessions.findForRevoke(subjectId, sessionId),
|
|
38
|
+
revoke: (sessionIds, revokedAt) => db.sessions.revoke(sessionIds, revokedAt),
|
|
39
|
+
touch: (sessionId, expiresAt, updatedAt) => db.sessions.touch(sessionId, expiresAt, updatedAt),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type AccountSession = {
|
|
43
|
+
email: string;
|
|
44
|
+
role: "owner" | "admin";
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const auth = createAuth<AccountSession>({
|
|
48
|
+
redis: createRedisStore(redis, { prefix: "my-app:auth" }),
|
|
49
|
+
audit,
|
|
50
|
+
session: {
|
|
51
|
+
ttl: 60 * 60 * 24 * 7,
|
|
52
|
+
touchAfter: 60 * 60 * 24,
|
|
53
|
+
max: 10,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const hono = createHonoAdapter({
|
|
58
|
+
auth,
|
|
59
|
+
getClient: (c) => ({
|
|
60
|
+
ip: getTrustedClientIp(c),
|
|
61
|
+
userAgent: c.req.header("user-agent") ?? null,
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The application must obtain the client IP according to its own trusted-proxy configuration. The package canonicalizes the supplied IPv4 or IPv6 value but never decides which forwarding headers are trusted.
|
|
67
|
+
|
|
68
|
+
## Passwords
|
|
69
|
+
|
|
70
|
+
Argon2id is the default:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const hash = await auth.password.hash(password);
|
|
74
|
+
const valid = await auth.password.verify(password, hash);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Applications whose database contains bcrypt hashes select bcrypt explicitly:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const auth = createAuth({
|
|
81
|
+
password: {
|
|
82
|
+
algorithm: "bcrypt",
|
|
83
|
+
},
|
|
84
|
+
redis: createRedisStore(redis),
|
|
85
|
+
audit,
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The configured algorithm is used for both hashing and verification. The package does not detect, convert, migrate, or fall back to another algorithm.
|
|
90
|
+
|
|
91
|
+
## Login
|
|
92
|
+
|
|
93
|
+
The application owns the endpoint and account lookup:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
app.post("/auth/login", async (c) => {
|
|
97
|
+
const { email, password } = await c.req.json();
|
|
98
|
+
const account = await findAccount(email);
|
|
99
|
+
|
|
100
|
+
if (!account || !(await auth.password.verify(password, account.passwordHash))) {
|
|
101
|
+
return c.json({ error: "Invalid credentials." }, 401);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const created = await auth.session.create({
|
|
105
|
+
subjectId: account.id,
|
|
106
|
+
data: {
|
|
107
|
+
email: account.email,
|
|
108
|
+
role: account.role,
|
|
109
|
+
},
|
|
110
|
+
client: await getClient(c),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
hono.setSession(c, created);
|
|
114
|
+
|
|
115
|
+
return c.json({ account: created.session.data });
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Applications must apply their own login rate limiting and account-enumeration protection.
|
|
120
|
+
|
|
121
|
+
## Protected routes
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
app.get("/account", hono.requireSession, async (c) => {
|
|
125
|
+
const session = c.get("authSession");
|
|
126
|
+
const account = await findAccountById(session.subjectId);
|
|
127
|
+
|
|
128
|
+
return c.json({ account });
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The middleware reads the cookie, resolves Redis, compares IP and User-Agent, renews expiry when due, and places the typed session in the Hono context. It does not load application data from the database.
|
|
133
|
+
|
|
134
|
+
## Logout
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
app.post("/auth/logout", async (c) => {
|
|
138
|
+
const token = hono.getToken(c);
|
|
139
|
+
|
|
140
|
+
if (token) {
|
|
141
|
+
await auth.session.revokeToken(token);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
hono.clearSession(c);
|
|
145
|
+
return c.body(null, 204);
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Active sessions
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const sessions = await auth.session.list(accountId);
|
|
153
|
+
|
|
154
|
+
await auth.session.revoke({
|
|
155
|
+
subjectId: accountId,
|
|
156
|
+
sessionId,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await auth.session.revokeSubject(accountId);
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The audit database supplies session history. Redis is checked in a batch before a session is reported as active. Token hashes are never returned by `list`.
|
|
163
|
+
|
|
164
|
+
## Synchronizing session data
|
|
165
|
+
|
|
166
|
+
When cached session claims change:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
await auth.session.sync(accountId, {
|
|
170
|
+
email: account.email,
|
|
171
|
+
role: account.role,
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Synchronization preserves the existing token and Redis TTL.
|
|
176
|
+
|
|
177
|
+
## Session audit contract
|
|
178
|
+
|
|
179
|
+
Each consuming application maps `SessionAudit` to its database. The durable record contains:
|
|
180
|
+
|
|
181
|
+
- Session ID.
|
|
182
|
+
- Subject ID and relation owned by the application.
|
|
183
|
+
- SHA-256 token hash.
|
|
184
|
+
- Canonical IP.
|
|
185
|
+
- Country and platform metadata.
|
|
186
|
+
- Complete User-Agent.
|
|
187
|
+
- Creation, current expiry, update, and revocation timestamps.
|
|
188
|
+
|
|
189
|
+
The database never stores the raw browser token.
|
|
190
|
+
|
|
191
|
+
## Expiration
|
|
192
|
+
|
|
193
|
+
Defaults:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
session: {
|
|
197
|
+
ttl: 7 * 24 * 60 * 60,
|
|
198
|
+
touchAfter: 24 * 60 * 60,
|
|
199
|
+
max: 10,
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- Inactive sessions expire after seven days.
|
|
204
|
+
- Active sessions renew at most once every 24 hours.
|
|
205
|
+
- Renewal keeps the same browser token.
|
|
206
|
+
- There is no absolute session lifetime.
|
|
207
|
+
|
|
208
|
+
## Client mismatch
|
|
209
|
+
|
|
210
|
+
If the canonical IP or complete User-Agent differs, the package deletes the Redis session first and marks its audit record as revoked. Clearing the response cookie is client cleanup; Redis deletion is what removes access.
|
|
211
|
+
|
|
212
|
+
Strict IP binding can log out legitimate users whose public IP changes. It is defense in depth and does not replace TLS, secure cookies, CSRF protection, XSS prevention, or re-authentication for sensitive actions.
|
|
213
|
+
|
|
214
|
+
## Package exports
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
@gauts/auth createAuth and public core types
|
|
218
|
+
@gauts/auth/redis createRedisStore
|
|
219
|
+
@gauts/auth/hono createHonoAdapter
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
See [`PLAN.md`](./PLAN.md) for the implementation phases and complete security decisions.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Context, Env, MiddlewareHandler } from "hono";
|
|
2
|
+
import type { Auth } from "../../auth.js";
|
|
3
|
+
import type { SessionClientInput } from "../../client/index.js";
|
|
4
|
+
import type { CreatedSession, Session } from "../../session/types.js";
|
|
5
|
+
export type HonoAuthVariables<TData extends object> = {
|
|
6
|
+
authSession: Session<TData>;
|
|
7
|
+
};
|
|
8
|
+
export type HonoAuthEnv<TData extends object> = Env & {
|
|
9
|
+
Variables: HonoAuthVariables<TData>;
|
|
10
|
+
};
|
|
11
|
+
export type HonoCookieConfig = {
|
|
12
|
+
domain?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
sameSite?: "Strict" | "Lax" | "None";
|
|
16
|
+
secure?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type HonoAdapterConfig<TData extends object> = {
|
|
19
|
+
auth: Auth<TData>;
|
|
20
|
+
cookie?: HonoCookieConfig;
|
|
21
|
+
getClient: (c: Context) => Promise<SessionClientInput> | SessionClientInput;
|
|
22
|
+
};
|
|
23
|
+
export type HonoAdapter<TData extends object> = {
|
|
24
|
+
clearSession(c: Context): void;
|
|
25
|
+
getToken(c: Context): string | null;
|
|
26
|
+
requireSession: MiddlewareHandler<HonoAuthEnv<TData>>;
|
|
27
|
+
setSession(c: Context, created: CreatedSession<TData>): void;
|
|
28
|
+
};
|
|
29
|
+
export declare const createHonoAdapter: <TData extends object>({ auth, cookie, getClient, }: HonoAdapterConfig<TData>) => HonoAdapter<TData>;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/hono/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAG5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,iBAAiB,CAAC,KAAK,SAAS,MAAM,IAAI;IACpD,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,IAAI,GAAG,GAAG;IACpD,SAAS,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,KAAK,SAAS,MAAM,IAAI;IACpD,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,kBAAkB,CAAC,GAAG,kBAAkB,CAAC;CAC7E,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,KAAK,SAAS,MAAM,IAAI;IAC9C,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,iBAAiB,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;IACtD,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;CAC9D,CAAC;AA6CF,eAAO,MAAM,iBAAiB,GAAI,KAAK,SAAS,MAAM,EAAE,8BAIrD,iBAAiB,CAAC,KAAK,CAAC,KAAG,WAAW,CAAC,KAAK,CA+D9C,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
|
|
2
|
+
import { createError, isAuthError } from "../../errors.js";
|
|
3
|
+
const COOKIE_NAME_PATTERN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
4
|
+
const resolveCookie = (config = {}) => {
|
|
5
|
+
const name = config.name ?? "__Host-session";
|
|
6
|
+
const path = config.path ?? "/";
|
|
7
|
+
const sameSite = config.sameSite ?? "Lax";
|
|
8
|
+
const secure = config.secure ?? true;
|
|
9
|
+
if (!COOKIE_NAME_PATTERN.test(name)) {
|
|
10
|
+
throw createError("AUTH_CONFIG_INVALID", "Session cookie name is invalid.");
|
|
11
|
+
}
|
|
12
|
+
if (!path.startsWith("/")) {
|
|
13
|
+
throw createError("AUTH_CONFIG_INVALID", "Session cookie path must start with /.");
|
|
14
|
+
}
|
|
15
|
+
if (name.startsWith("__Host-") && (!secure || path !== "/" || config.domain)) {
|
|
16
|
+
throw createError("AUTH_CONFIG_INVALID", "__Host- cookies require secure=true, path=/, and no domain.");
|
|
17
|
+
}
|
|
18
|
+
if (name.startsWith("__Secure-") && !secure) {
|
|
19
|
+
throw createError("AUTH_CONFIG_INVALID", "__Secure- cookies require secure=true.");
|
|
20
|
+
}
|
|
21
|
+
if (sameSite === "None" && !secure) {
|
|
22
|
+
throw createError("AUTH_CONFIG_INVALID", "SameSite=None cookies require secure=true.");
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
name,
|
|
26
|
+
options: {
|
|
27
|
+
...(config.domain ? { domain: config.domain } : {}),
|
|
28
|
+
httpOnly: true,
|
|
29
|
+
path,
|
|
30
|
+
sameSite,
|
|
31
|
+
secure,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export const createHonoAdapter = ({ auth, cookie, getClient, }) => {
|
|
36
|
+
const resolved = resolveCookie(cookie);
|
|
37
|
+
const getToken = (c) => {
|
|
38
|
+
const token = getCookie(c, resolved.name)?.trim();
|
|
39
|
+
return token?.length ? token : null;
|
|
40
|
+
};
|
|
41
|
+
const setToken = (c, token, expiresAt) => {
|
|
42
|
+
setCookie(c, resolved.name, token, {
|
|
43
|
+
...resolved.options,
|
|
44
|
+
expires: expiresAt,
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
const clearSession = (c) => {
|
|
48
|
+
deleteCookie(c, resolved.name, resolved.options);
|
|
49
|
+
};
|
|
50
|
+
const requireSession = async (c, next) => {
|
|
51
|
+
const token = getToken(c);
|
|
52
|
+
if (!token) {
|
|
53
|
+
throw createError("SESSION_INVALID", "Authentication required.");
|
|
54
|
+
}
|
|
55
|
+
let resolvedSession;
|
|
56
|
+
try {
|
|
57
|
+
resolvedSession = await auth.session.resolve({
|
|
58
|
+
client: await getClient(c),
|
|
59
|
+
token,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (isAuthError(error) && error.code === "SESSION_CLIENT_MISMATCH") {
|
|
64
|
+
clearSession(c);
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
if (!resolvedSession) {
|
|
69
|
+
clearSession(c);
|
|
70
|
+
throw createError("SESSION_INVALID", "Authentication required.");
|
|
71
|
+
}
|
|
72
|
+
if (resolvedSession.renewed) {
|
|
73
|
+
setToken(c, token, resolvedSession.session.expiresAt);
|
|
74
|
+
}
|
|
75
|
+
c.set("authSession", resolvedSession.session);
|
|
76
|
+
await next();
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
clearSession,
|
|
80
|
+
getToken,
|
|
81
|
+
requireSession,
|
|
82
|
+
setSession: (c, created) => {
|
|
83
|
+
setToken(c, created.token, created.session.expiresAt);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/hono/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIjE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAgC3D,MAAM,mBAAmB,GAAG,gCAAgC,CAAC;AAE7D,MAAM,aAAa,GAAG,CAAC,SAA2B,EAAE,EAAE,EAAE;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,gBAAgB,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC;IAChC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC;IAErC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,MAAM,WAAW,CAAC,qBAAqB,EAAE,iCAAiC,CAAC,CAAC;IAC9E,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,WAAW,CAAC,qBAAqB,EAAE,wCAAwC,CAAC,CAAC;IACrF,CAAC;IAED,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,IAAI,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7E,MAAM,WAAW,CACf,qBAAqB,EACrB,6DAA6D,CAC9D,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5C,MAAM,WAAW,CAAC,qBAAqB,EAAE,wCAAwC,CAAC,CAAC;IACrF,CAAC;IAED,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACnC,MAAM,WAAW,CAAC,qBAAqB,EAAE,4CAA4C,CAAC,CAAC;IACzF,CAAC;IAED,OAAO;QACL,IAAI;QACJ,OAAO,EAAE;YACP,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnD,QAAQ,EAAE,IAAa;YACvB,IAAI;YACJ,QAAQ;YACR,MAAM;SACP;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAuB,EACtD,IAAI,EACJ,MAAM,EACN,SAAS,GACgB,EAAsB,EAAE;IACjD,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAEvC,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAiB,EAAE;QAC7C,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;QAElD,OAAO,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACtC,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAE,KAAa,EAAE,SAAe,EAAQ,EAAE;QACpE,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE;YACjC,GAAG,QAAQ,CAAC,OAAO;YACnB,OAAO,EAAE,SAAS;SACnB,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,CAAU,EAAQ,EAAE;QACxC,YAAY,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC,CAAC;IAEF,MAAM,cAAc,GAA0C,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAE1B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,WAAW,CAAC,iBAAiB,EAAE,0BAA0B,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,eAAe,CAAC;QAEpB,IAAI,CAAC;YACH,eAAe,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;gBAC3C,MAAM,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC;gBAC1B,KAAK;aACN,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;gBACnE,YAAY,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,KAAK,CAAC;QACd,CAAC;QAED,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,YAAY,CAAC,CAAC,CAAC,CAAC;YAChB,MAAM,WAAW,CAAC,iBAAiB,EAAE,0BAA0B,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;YAC5B,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACxD,CAAC;QAED,CAAC,CAAC,GAAG,CAAC,aAAa,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;IAEF,OAAO;QACL,YAAY;QACZ,QAAQ;QACR,cAAc;QACd,UAAU,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE;YACzB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACxD,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RedisClientType } from "redis";
|
|
2
|
+
import type { RedisSessionStore } from "../../session/types.js";
|
|
3
|
+
export type RedisStoreConfig = {
|
|
4
|
+
prefix?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const createRedisStore: (client: RedisClientType, config?: RedisStoreConfig) => RedisSessionStore;
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/redis/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAaF,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,eAAe,EACvB,SAAQ,gBAAqB,KAC5B,iBAsDF,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createError } from "../../errors.js";
|
|
2
|
+
const getPrefix = (input) => {
|
|
3
|
+
const candidate = input?.trim();
|
|
4
|
+
const prefix = candidate?.length ? candidate : "gauts:auth";
|
|
5
|
+
if (prefix.length > 128 || !/^[A-Za-z0-9:_-]+$/.test(prefix)) {
|
|
6
|
+
throw createError("AUTH_CONFIG_INVALID", "Redis key prefix is invalid.");
|
|
7
|
+
}
|
|
8
|
+
return prefix.replace(/:+$/g, "");
|
|
9
|
+
};
|
|
10
|
+
export const createRedisStore = (client, config = {}) => {
|
|
11
|
+
const prefix = getPrefix(config.prefix);
|
|
12
|
+
const getKey = (tokenHash) => `${prefix}:session:${tokenHash}`;
|
|
13
|
+
return {
|
|
14
|
+
create: async (tokenHash, value, ttl) => {
|
|
15
|
+
const result = await client.set(getKey(tokenHash), value, { EX: ttl, NX: true });
|
|
16
|
+
if (result !== "OK") {
|
|
17
|
+
throw new Error("Redis session key already exists.");
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
get: async (tokenHash) => {
|
|
21
|
+
return client.get(getKey(tokenHash));
|
|
22
|
+
},
|
|
23
|
+
getMany: async (tokenHashes) => {
|
|
24
|
+
return tokenHashes.length === 0
|
|
25
|
+
? []
|
|
26
|
+
: client.mGet(tokenHashes.map((tokenHash) => getKey(tokenHash)));
|
|
27
|
+
},
|
|
28
|
+
update: async (tokenHash, value, ttl) => {
|
|
29
|
+
const result = await client.set(getKey(tokenHash), value, { EX: ttl, XX: true });
|
|
30
|
+
return result === "OK";
|
|
31
|
+
},
|
|
32
|
+
keep: async (tokenHash, value) => {
|
|
33
|
+
const result = await client.set(getKey(tokenHash), value, {
|
|
34
|
+
KEEPTTL: true,
|
|
35
|
+
XX: true,
|
|
36
|
+
});
|
|
37
|
+
return result === "OK";
|
|
38
|
+
},
|
|
39
|
+
delete: async (tokenHashes) => {
|
|
40
|
+
if (tokenHashes.length > 0) {
|
|
41
|
+
await client.del(tokenHashes.map((tokenHash) => getKey(tokenHash)));
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
exists: async (tokenHashes) => {
|
|
45
|
+
if (tokenHashes.length === 0) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
const values = await client.mGet(tokenHashes.map((tokenHash) => getKey(tokenHash)));
|
|
49
|
+
return values.map((value) => value !== null);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/redis/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAO9C,MAAM,SAAS,GAAG,CAAC,KAAc,EAAU,EAAE;IAC3C,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAChC,MAAM,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC;IAE5D,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,MAAM,WAAW,CAAC,qBAAqB,EAAE,8BAA8B,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,MAAuB,EACvB,SAA2B,EAAE,EACV,EAAE;IACrB,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,CAAC,SAAiB,EAAE,EAAE,CAAC,GAAG,MAAM,YAAY,SAAS,EAAE,CAAC;IAEvE,OAAO;QACL,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACtC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAEjF,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;QAED,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;YACvB,OAAO,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;QACvC,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE;YAC7B,OAAO,WAAW,CAAC,MAAM,KAAK,CAAC;gBAC7B,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YACtC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAEjF,OAAO,MAAM,KAAK,IAAI,CAAC;QACzB,CAAC;QAED,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE;YAC/B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE;gBACxD,OAAO,EAAE,IAAI;gBACb,EAAE,EAAE,IAAI;aACT,CAAC,CAAC;YAEH,OAAO,MAAM,KAAK,IAAI,CAAC;QACzB,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE;YAC5B,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC3B,MAAM,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE;YAC5B,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC7B,OAAO,EAAE,CAAC;YACZ,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAEpF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;QAC/C,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PasswordConfig, SessionConfig } from "./config.js";
|
|
2
|
+
import { type PasswordService } from "./password/index.js";
|
|
3
|
+
import type { RedisSessionStore, SessionAudit, SessionService } from "./session/types.js";
|
|
4
|
+
export type AuthConfig = {
|
|
5
|
+
password?: PasswordConfig;
|
|
6
|
+
session?: SessionConfig;
|
|
7
|
+
};
|
|
8
|
+
export type AuthDeps = AuthConfig & {
|
|
9
|
+
audit: SessionAudit;
|
|
10
|
+
redis: RedisSessionStore;
|
|
11
|
+
};
|
|
12
|
+
export type Auth<TData extends object> = {
|
|
13
|
+
password: PasswordService;
|
|
14
|
+
session: SessionService<TData>;
|
|
15
|
+
};
|
|
16
|
+
export declare const createAuth: <TData extends object = Record<string, unknown>>({ audit, password, redis, session, }: AuthDeps) => Auth<TData>;
|
|
17
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjE,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE1F,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG;IAClC,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,iBAAiB,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,IAAI,CAAC,KAAK,SAAS,MAAM,IAAI;IACvC,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;CAChC,CAAC;AAcF,eAAO,MAAM,UAAU,GAAI,KAAK,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,sCAKxE,QAAQ,KAAG,IAAI,CAAC,KAAK,CAoBvB,CAAC"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolvePasswordConfig, resolveSessionConfig } from "./config.js";
|
|
2
|
+
import { createError } from "./errors.js";
|
|
3
|
+
import { createPassword } from "./password/index.js";
|
|
4
|
+
import { createSessionService } from "./session/service.js";
|
|
5
|
+
const requireMethods = (name, value, methods) => {
|
|
6
|
+
if (typeof value !== "object" || value === null) {
|
|
7
|
+
throw createError("AUTH_CONFIG_INVALID", `${name} adapter is invalid.`);
|
|
8
|
+
}
|
|
9
|
+
const adapter = value;
|
|
10
|
+
if (methods.some((method) => typeof adapter[method] !== "function")) {
|
|
11
|
+
throw createError("AUTH_CONFIG_INVALID", `${name} adapter is invalid.`);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
export const createAuth = ({ audit, password, redis, session, }) => {
|
|
15
|
+
requireMethods("Redis", redis, [
|
|
16
|
+
"create",
|
|
17
|
+
"delete",
|
|
18
|
+
"exists",
|
|
19
|
+
"get",
|
|
20
|
+
"getMany",
|
|
21
|
+
"keep",
|
|
22
|
+
"update",
|
|
23
|
+
]);
|
|
24
|
+
requireMethods("Audit", audit, ["create", "findActive", "findForRevoke", "revoke", "touch"]);
|
|
25
|
+
return {
|
|
26
|
+
password: createPassword(resolvePasswordConfig(password)),
|
|
27
|
+
session: createSessionService({
|
|
28
|
+
audit,
|
|
29
|
+
config: resolveSessionConfig(session),
|
|
30
|
+
redis,
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAwB,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAkB5D,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,KAAc,EAAE,OAAiB,EAAQ,EAAE;IAC/E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,WAAW,CAAC,qBAAqB,EAAE,GAAG,IAAI,sBAAsB,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,OAAO,GAAG,KAAgC,CAAC;IAEjD,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,UAAU,CAAC,EAAE,CAAC;QACpE,MAAM,WAAW,CAAC,qBAAqB,EAAE,GAAG,IAAI,sBAAsB,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAiD,EACzE,KAAK,EACL,QAAQ,EACR,KAAK,EACL,OAAO,GACE,EAAe,EAAE;IAC1B,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE;QAC7B,QAAQ;QACR,QAAQ;QACR,QAAQ;QACR,KAAK;QACL,SAAS;QACT,MAAM;QACN,QAAQ;KACT,CAAC,CAAC;IACH,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IAE7F,OAAO;QACL,QAAQ,EAAE,cAAc,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACzD,OAAO,EAAE,oBAAoB,CAAC;YAC5B,KAAK;YACL,MAAM,EAAE,oBAAoB,CAAC,OAAO,CAAC;YACrC,KAAK;SACN,CAAC;KACH,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type SessionClientInput = {
|
|
2
|
+
country?: string | null;
|
|
3
|
+
ip?: string | null;
|
|
4
|
+
platform?: string | null;
|
|
5
|
+
userAgent?: string | null;
|
|
6
|
+
};
|
|
7
|
+
export type SessionIdentityInput = Pick<SessionClientInput, "ip" | "userAgent">;
|
|
8
|
+
export type SessionClient = {
|
|
9
|
+
country: string | null;
|
|
10
|
+
ip: string | null;
|
|
11
|
+
platform: string | null;
|
|
12
|
+
userAgent: string | null;
|
|
13
|
+
};
|
|
14
|
+
export type SessionIdentity = Pick<SessionClient, "ip" | "userAgent">;
|
|
15
|
+
export declare const normalizeIp: (input?: string | null) => string | null;
|
|
16
|
+
export declare const normalizeClient: (input: SessionClientInput) => SessionClient;
|
|
17
|
+
export declare const normalizeIdentity: (input: SessionIdentityInput) => SessionIdentity;
|
|
18
|
+
export declare const matchesClient: (stored: SessionClient, current: SessionIdentity) => boolean;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC;AAEhF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC;AAEtE,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,GAAG,IAAI,KAAG,MAAM,GAAG,IAkB5D,CAAC;AAgBF,eAAO,MAAM,eAAe,GAAI,OAAO,kBAAkB,KAAG,aAK1D,CAAC;AAEH,eAAO,MAAM,iBAAiB,GAAI,OAAO,oBAAoB,KAAG,eAG9D,CAAC;AAEH,eAAO,MAAM,aAAa,GAAI,QAAQ,aAAa,EAAE,SAAS,eAAe,KAAG,OACZ,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import ipaddr from "ipaddr.js";
|
|
2
|
+
export const normalizeIp = (input) => {
|
|
3
|
+
let value = input?.trim();
|
|
4
|
+
if (!value) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
8
|
+
value = value.slice(1, -1);
|
|
9
|
+
}
|
|
10
|
+
if (!ipaddr.isValid(value)) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const normalized = ipaddr.process(value).toString();
|
|
14
|
+
return normalized === "::1" ? "127.0.0.1" : normalized;
|
|
15
|
+
};
|
|
16
|
+
const normalizeCountry = (input) => {
|
|
17
|
+
const value = input?.trim().toUpperCase();
|
|
18
|
+
return value && /^[A-Z]{2}$/.test(value) ? value : null;
|
|
19
|
+
};
|
|
20
|
+
const normalizePlatform = (input) => {
|
|
21
|
+
const value = input?.replace(/^"+|"+$/g, "").trim();
|
|
22
|
+
return value ? value.slice(0, 255) : null;
|
|
23
|
+
};
|
|
24
|
+
const normalizeUserAgent = (input) => (input?.length ? input : null);
|
|
25
|
+
export const normalizeClient = (input) => ({
|
|
26
|
+
country: normalizeCountry(input.country),
|
|
27
|
+
ip: normalizeIp(input.ip),
|
|
28
|
+
platform: normalizePlatform(input.platform),
|
|
29
|
+
userAgent: normalizeUserAgent(input.userAgent),
|
|
30
|
+
});
|
|
31
|
+
export const normalizeIdentity = (input) => ({
|
|
32
|
+
ip: normalizeIp(input.ip),
|
|
33
|
+
userAgent: normalizeUserAgent(input.userAgent),
|
|
34
|
+
});
|
|
35
|
+
export const matchesClient = (stored, current) => stored.ip === current.ip && stored.userAgent === current.userAgent;
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,WAAW,CAAC;AAoB/B,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAqB,EAAiB,EAAE;IAClE,IAAI,KAAK,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IAE1B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACjD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;IAEpD,OAAO,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;AACzD,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,KAAqB,EAAiB,EAAE;IAChE,MAAM,KAAK,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE1C,OAAO,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CAAC,KAAqB,EAAiB,EAAE;IACjE,MAAM,KAAK,GAAG,KAAK,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAEpD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,KAAqB,EAAiB,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAEpG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAyB,EAAiB,EAAE,CAAC,CAAC;IAC5E,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC;IACxC,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;IACzB,QAAQ,EAAE,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC3C,SAAS,EAAE,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC;CAC/C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAA2B,EAAmB,EAAE,CAAC,CAAC;IAClF,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;IACzB,SAAS,EAAE,kBAAkB,CAAC,KAAK,CAAC,SAAS,CAAC;CAC/C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAqB,EAAE,OAAwB,EAAW,EAAE,CACxF,MAAM,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,IAAI,MAAM,CAAC,SAAS,KAAK,OAAO,CAAC,SAAS,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type Argon2idConfig = {
|
|
2
|
+
algorithm?: "argon2id";
|
|
3
|
+
hashLength?: number;
|
|
4
|
+
maxBytes?: number;
|
|
5
|
+
memoryCost?: number;
|
|
6
|
+
parallelism?: number;
|
|
7
|
+
timeCost?: number;
|
|
8
|
+
};
|
|
9
|
+
export type BcryptConfig = {
|
|
10
|
+
algorithm: "bcrypt";
|
|
11
|
+
maxBytes?: number;
|
|
12
|
+
rounds?: number;
|
|
13
|
+
};
|
|
14
|
+
export type PasswordConfig = Argon2idConfig | BcryptConfig;
|
|
15
|
+
export type SessionConfig = {
|
|
16
|
+
max?: number;
|
|
17
|
+
touchAfter?: number;
|
|
18
|
+
ttl?: number;
|
|
19
|
+
};
|
|
20
|
+
export type ResolvedPasswordConfig = {
|
|
21
|
+
algorithm: "argon2id";
|
|
22
|
+
hashLength: number;
|
|
23
|
+
maxBytes: number;
|
|
24
|
+
memoryCost: number;
|
|
25
|
+
parallelism: number;
|
|
26
|
+
timeCost: number;
|
|
27
|
+
} | {
|
|
28
|
+
algorithm: "bcrypt";
|
|
29
|
+
maxBytes: number;
|
|
30
|
+
rounds: number;
|
|
31
|
+
};
|
|
32
|
+
export type ResolvedSessionConfig = {
|
|
33
|
+
max: number;
|
|
34
|
+
touchAfter: number;
|
|
35
|
+
ttl: number;
|
|
36
|
+
};
|
|
37
|
+
export declare const resolvePasswordConfig: (config?: PasswordConfig) => ResolvedPasswordConfig;
|
|
38
|
+
export declare const resolveSessionConfig: (config?: SessionConfig) => ResolvedSessionConfig;
|
|
39
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,YAAY,CAAC;AAE3D,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAC9B;IACE,SAAS,EAAE,UAAU,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACD;IACE,SAAS,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEN,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAkCF,eAAO,MAAM,qBAAqB,GAAI,SAAQ,cAAmB,KAAG,sBAiCnE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,SAAQ,aAAkB,KAAG,qBAgBjE,CAAC"}
|