simple-m-auth 1.0.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/README.md +186 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +72 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto.d.ts +8 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +33 -0
- package/dist/crypto.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/session.d.ts +24 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +109 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# simple-m-auth
|
|
2
|
+
|
|
3
|
+
Dead simple session-based authentication. Just create `auth.config.ts` and go.
|
|
4
|
+
|
|
5
|
+
Based on [Lucia Auth](https://lucia-auth.com) principles: separate session ID and secret, SHA-256 hashed secrets, constant-time comparison.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install simple-m-auth
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
Create `auth.config.ts` in your project root:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import type { AuthConfig } from 'simple-m-auth';
|
|
19
|
+
import { prisma } from './db';
|
|
20
|
+
|
|
21
|
+
export const authConfig: AuthConfig = {
|
|
22
|
+
db: {
|
|
23
|
+
insertSession: async (session) => {
|
|
24
|
+
await prisma.session.create({
|
|
25
|
+
data: {
|
|
26
|
+
id: session.id,
|
|
27
|
+
secretHash: Buffer.from(session.secretHash),
|
|
28
|
+
userId: session.userId,
|
|
29
|
+
createdAt: session.createdAt,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
getSessionById: async (sessionId) => {
|
|
35
|
+
const session = await prisma.session.findUnique({
|
|
36
|
+
where: { id: sessionId },
|
|
37
|
+
});
|
|
38
|
+
if (!session) return null;
|
|
39
|
+
return {
|
|
40
|
+
...session,
|
|
41
|
+
secretHash: new Uint8Array(session.secretHash),
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
deleteSession: async (sessionId) => {
|
|
46
|
+
await prisma.session.deleteMany({ where: { id: sessionId } });
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
getUserById: async (userId) => {
|
|
50
|
+
return prisma.user.findUnique({ where: { id: userId } });
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
cookie: {
|
|
55
|
+
// Example for Next.js App Router
|
|
56
|
+
set: async (name, value, options) => {
|
|
57
|
+
const { cookies } = await import('next/headers');
|
|
58
|
+
const cookieStore = await cookies();
|
|
59
|
+
cookieStore.set(name, value, options);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
get: async (name) => {
|
|
63
|
+
const { cookies } = await import('next/headers');
|
|
64
|
+
const cookieStore = await cookies();
|
|
65
|
+
return cookieStore.get(name)?.value ?? null;
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
delete: async (name) => {
|
|
69
|
+
const { cookies } = await import('next/headers');
|
|
70
|
+
const cookieStore = await cookies();
|
|
71
|
+
cookieStore.delete(name);
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
sessionExpiresIn: 60 * 60 * 24 * 7, // 1 week
|
|
76
|
+
cookieName: 'session_token',
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Database Schema
|
|
81
|
+
|
|
82
|
+
```sql
|
|
83
|
+
CREATE TABLE session (
|
|
84
|
+
id TEXT PRIMARY KEY,
|
|
85
|
+
secret_hash BLOB NOT NULL,
|
|
86
|
+
user_id TEXT NOT NULL REFERENCES user(id),
|
|
87
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
88
|
+
);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Prisma:
|
|
92
|
+
|
|
93
|
+
```prisma
|
|
94
|
+
model Session {
|
|
95
|
+
id String @id
|
|
96
|
+
secretHash Bytes @map("secret_hash")
|
|
97
|
+
userId String @map("user_id")
|
|
98
|
+
user User @relation(fields: [userId], references: [id])
|
|
99
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
100
|
+
|
|
101
|
+
@@map("session")
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Usage
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { createSession, validateSession, getUser, deleteSession } from 'simple-m-auth';
|
|
109
|
+
|
|
110
|
+
// Login - create session after verifying password
|
|
111
|
+
async function login(userId: string) {
|
|
112
|
+
const session = await createSession(userId);
|
|
113
|
+
// Cookie is automatically set
|
|
114
|
+
return session;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check if user is authenticated
|
|
118
|
+
async function auth() {
|
|
119
|
+
const session = await validateSession();
|
|
120
|
+
if (!session) {
|
|
121
|
+
redirect('/login');
|
|
122
|
+
}
|
|
123
|
+
return session;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Get current user
|
|
127
|
+
async function getCurrentUser() {
|
|
128
|
+
const user = await getUser();
|
|
129
|
+
return user; // null if not authenticated
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Logout
|
|
133
|
+
async function logout() {
|
|
134
|
+
await deleteSession();
|
|
135
|
+
// Cookie is automatically cleared
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Next.js Example
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
// app/api/auth/login/route.ts
|
|
143
|
+
import { createSession } from 'simple-m-auth';
|
|
144
|
+
import { verifyPassword } from '@/lib/password';
|
|
145
|
+
import { prisma } from '@/lib/db';
|
|
146
|
+
|
|
147
|
+
export async function POST(request: Request) {
|
|
148
|
+
const { email, password } = await request.json();
|
|
149
|
+
|
|
150
|
+
const user = await prisma.user.findUnique({ where: { email } });
|
|
151
|
+
if (!user || !await verifyPassword(password, user.passwordHash)) {
|
|
152
|
+
return Response.json({ error: 'Invalid credentials' }, { status: 401 });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await createSession(user.id);
|
|
156
|
+
return Response.json({ success: true });
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// app/api/auth/logout/route.ts
|
|
162
|
+
import { deleteSession } from 'simple-m-auth';
|
|
163
|
+
|
|
164
|
+
export async function POST() {
|
|
165
|
+
await deleteSession();
|
|
166
|
+
return Response.json({ success: true });
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// lib/auth.ts
|
|
172
|
+
import { getUser } from 'simple-m-auth';
|
|
173
|
+
import { redirect } from 'next/navigation';
|
|
174
|
+
|
|
175
|
+
export async function requireAuth() {
|
|
176
|
+
const user = await getUser();
|
|
177
|
+
if (!user) {
|
|
178
|
+
redirect('/login');
|
|
179
|
+
}
|
|
180
|
+
return user;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAa7C,wBAAsB,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,CA+BtD;AAiCD,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAGlD;AAED,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { pathToFileURL } from 'url';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
let cachedConfig = null;
|
|
5
|
+
const CONFIG_FILENAMES = [
|
|
6
|
+
'auth.config.ts',
|
|
7
|
+
'auth.config.js',
|
|
8
|
+
'auth.config.mjs',
|
|
9
|
+
];
|
|
10
|
+
export async function loadConfig() {
|
|
11
|
+
if (cachedConfig) {
|
|
12
|
+
return cachedConfig;
|
|
13
|
+
}
|
|
14
|
+
const cwd = process.cwd();
|
|
15
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
16
|
+
const configPath = join(cwd, filename);
|
|
17
|
+
if (existsSync(configPath)) {
|
|
18
|
+
try {
|
|
19
|
+
const configModule = await import(pathToFileURL(configPath).href);
|
|
20
|
+
const config = configModule.authConfig || configModule.default;
|
|
21
|
+
if (!config) {
|
|
22
|
+
throw new Error(`Config file ${filename} must export 'authConfig' or default export`);
|
|
23
|
+
}
|
|
24
|
+
validateConfig(config);
|
|
25
|
+
cachedConfig = config;
|
|
26
|
+
return config;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new Error(`Failed to load ${filename}: ${error}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`No auth config found. Create one of: ${CONFIG_FILENAMES.join(', ')} in your project root.`);
|
|
34
|
+
}
|
|
35
|
+
function validateConfig(config) {
|
|
36
|
+
if (!config.db) {
|
|
37
|
+
throw new Error('AuthConfig.db is required');
|
|
38
|
+
}
|
|
39
|
+
if (typeof config.db.insertSession !== 'function') {
|
|
40
|
+
throw new Error('AuthConfig.db.insertSession must be a function');
|
|
41
|
+
}
|
|
42
|
+
if (typeof config.db.getSessionById !== 'function') {
|
|
43
|
+
throw new Error('AuthConfig.db.getSessionById must be a function');
|
|
44
|
+
}
|
|
45
|
+
if (typeof config.db.deleteSession !== 'function') {
|
|
46
|
+
throw new Error('AuthConfig.db.deleteSession must be a function');
|
|
47
|
+
}
|
|
48
|
+
if (typeof config.db.getUserById !== 'function') {
|
|
49
|
+
throw new Error('AuthConfig.db.getUserById must be a function');
|
|
50
|
+
}
|
|
51
|
+
if (!config.cookie) {
|
|
52
|
+
throw new Error('AuthConfig.cookie is required');
|
|
53
|
+
}
|
|
54
|
+
if (typeof config.cookie.set !== 'function') {
|
|
55
|
+
throw new Error('AuthConfig.cookie.set must be a function');
|
|
56
|
+
}
|
|
57
|
+
if (typeof config.cookie.get !== 'function') {
|
|
58
|
+
throw new Error('AuthConfig.cookie.get must be a function');
|
|
59
|
+
}
|
|
60
|
+
if (typeof config.cookie.delete !== 'function') {
|
|
61
|
+
throw new Error('AuthConfig.cookie.delete must be a function');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Allow manual config setting (useful for testing or non-standard setups)
|
|
65
|
+
export function setConfig(config) {
|
|
66
|
+
validateConfig(config);
|
|
67
|
+
cachedConfig = config;
|
|
68
|
+
}
|
|
69
|
+
export function clearConfig() {
|
|
70
|
+
cachedConfig = null;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,IAAI,YAAY,GAAsB,IAAI,CAAC;AAE3C,MAAM,gBAAgB,GAAG;IACvB,gBAAgB;IAChB,gBAAgB;IAChB,iBAAiB;CAClB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE1B,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAEvC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC;gBAClE,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,IAAI,YAAY,CAAC,OAAO,CAAC;gBAE/D,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,6CAA6C,CAAC,CAAC;gBACxF,CAAC;gBAED,cAAc,CAAC,MAAM,CAAC,CAAC;gBACvB,YAAY,GAAG,MAAM,CAAC;gBACtB,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,KAAK,CAAC,kBAAkB,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb,wCAAwC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,CAC5F,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,MAAW;IACjC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,EAAE,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,EAAE,CAAC,cAAc,KAAK,UAAU,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,EAAE,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,EAAE,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,SAAS,CAAC,MAAkB;IAC1C,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,YAAY,GAAG,MAAM,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC"}
|
package/dist/crypto.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function generateSecureRandomString(): string;
|
|
2
|
+
export declare function hashSecret(secret: string): Promise<Uint8Array>;
|
|
3
|
+
export declare function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;
|
|
4
|
+
export declare function parseToken(token: string): {
|
|
5
|
+
id: string;
|
|
6
|
+
secret: string;
|
|
7
|
+
} | null;
|
|
8
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAEA,wBAAgB,0BAA0B,IAAI,MAAM,CASnD;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAIpE;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,OAAO,CASvE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAM/E"}
|
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const ALPHABET = 'abcdefghijkmnpqrstuvwxyz23456789'; // human readable, no confusing chars
|
|
2
|
+
export function generateSecureRandomString() {
|
|
3
|
+
const bytes = new Uint8Array(24); // 120 bits of entropy
|
|
4
|
+
crypto.getRandomValues(bytes);
|
|
5
|
+
let result = '';
|
|
6
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
7
|
+
result += ALPHABET[bytes[i] >> 3];
|
|
8
|
+
}
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
11
|
+
export async function hashSecret(secret) {
|
|
12
|
+
const secretBytes = new TextEncoder().encode(secret);
|
|
13
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', secretBytes);
|
|
14
|
+
return new Uint8Array(hashBuffer);
|
|
15
|
+
}
|
|
16
|
+
export function constantTimeEqual(a, b) {
|
|
17
|
+
if (a.byteLength !== b.byteLength) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
let c = 0;
|
|
21
|
+
for (let i = 0; i < a.byteLength; i++) {
|
|
22
|
+
c |= a[i] ^ b[i];
|
|
23
|
+
}
|
|
24
|
+
return c === 0;
|
|
25
|
+
}
|
|
26
|
+
export function parseToken(token) {
|
|
27
|
+
const parts = token.split('.');
|
|
28
|
+
if (parts.length !== 2) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return { id: parts[0], secret: parts[1] };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"AAAA,MAAM,QAAQ,GAAG,kCAAkC,CAAC,CAAC,qCAAqC;AAE1F,MAAM,UAAU,0BAA0B;IACxC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,sBAAsB;IACxD,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAE9B,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc;IAC7C,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACtE,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,CAAa,EAAE,CAAa;IAC5D,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { AuthConfig, Session, SessionWithToken, CookieOptions, } from './types.js';
|
|
2
|
+
export { createSession, validateSession, getUser, deleteSession, deleteSessionById, } from './session.js';
|
|
3
|
+
export { setConfig, clearConfig } from './config.js';
|
|
4
|
+
export { generateSecureRandomString, hashSecret, constantTimeEqual, } from './crypto.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,UAAU,EACV,OAAO,EACP,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,eAAe,EACf,OAAO,EACP,aAAa,EACb,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGrD,OAAO,EACL,0BAA0B,EAC1B,UAAU,EACV,iBAAiB,GAClB,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Session functions
|
|
2
|
+
export { createSession, validateSession, getUser, deleteSession, deleteSessionById, } from './session.js';
|
|
3
|
+
// Config utilities (for advanced use)
|
|
4
|
+
export { setConfig, clearConfig } from './config.js';
|
|
5
|
+
// Crypto utilities (if someone needs them)
|
|
6
|
+
export { generateSecureRandomString, hashSecret, constantTimeEqual, } from './crypto.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,oBAAoB;AACpB,OAAO,EACL,aAAa,EACb,eAAe,EACf,OAAO,EACP,aAAa,EACb,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAEtB,sCAAsC;AACtC,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAErD,2CAA2C;AAC3C,OAAO,EACL,0BAA0B,EAC1B,UAAU,EACV,iBAAiB,GAClB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Session, SessionWithToken } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a new session for a user and set the cookie
|
|
4
|
+
*/
|
|
5
|
+
export declare function createSession(userId: string): Promise<SessionWithToken>;
|
|
6
|
+
/**
|
|
7
|
+
* Validate the current session from cookie
|
|
8
|
+
* Returns the session if valid, null otherwise
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateSession(): Promise<Session | null>;
|
|
11
|
+
/**
|
|
12
|
+
* Get the current user from session
|
|
13
|
+
* Returns null if no valid session or user not found
|
|
14
|
+
*/
|
|
15
|
+
export declare function getUser<TUser = any>(): Promise<TUser | null>;
|
|
16
|
+
/**
|
|
17
|
+
* Delete the current session and clear cookie
|
|
18
|
+
*/
|
|
19
|
+
export declare function deleteSession(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Delete a specific session by ID (useful for "logout all devices")
|
|
22
|
+
*/
|
|
23
|
+
export declare function deleteSessionById(sessionId: string): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAY5D;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAoC7E;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAoC/D;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,KAAK,GAAG,GAAG,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CASlE;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAenD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGxE"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { loadConfig } from './config.js';
|
|
2
|
+
import { generateSecureRandomString, hashSecret, constantTimeEqual, parseToken, } from './crypto.js';
|
|
3
|
+
const DEFAULT_EXPIRES_IN = 60 * 60 * 24; // 24 hours
|
|
4
|
+
const DEFAULT_COOKIE_NAME = 'session_token';
|
|
5
|
+
/**
|
|
6
|
+
* Create a new session for a user and set the cookie
|
|
7
|
+
*/
|
|
8
|
+
export async function createSession(userId) {
|
|
9
|
+
const config = await loadConfig();
|
|
10
|
+
const id = generateSecureRandomString();
|
|
11
|
+
const secret = generateSecureRandomString();
|
|
12
|
+
const secretHash = await hashSecret(secret);
|
|
13
|
+
const createdAt = new Date();
|
|
14
|
+
const token = `${id}.${secret}`;
|
|
15
|
+
const session = {
|
|
16
|
+
id,
|
|
17
|
+
secretHash,
|
|
18
|
+
userId,
|
|
19
|
+
createdAt,
|
|
20
|
+
token,
|
|
21
|
+
};
|
|
22
|
+
await config.db.insertSession({
|
|
23
|
+
id,
|
|
24
|
+
secretHash,
|
|
25
|
+
userId,
|
|
26
|
+
createdAt,
|
|
27
|
+
});
|
|
28
|
+
const cookieName = config.cookieName ?? DEFAULT_COOKIE_NAME;
|
|
29
|
+
const maxAge = config.sessionExpiresIn ?? DEFAULT_EXPIRES_IN;
|
|
30
|
+
await config.cookie.set(cookieName, token, {
|
|
31
|
+
maxAge,
|
|
32
|
+
httpOnly: true,
|
|
33
|
+
secure: true,
|
|
34
|
+
path: '/',
|
|
35
|
+
sameSite: 'lax',
|
|
36
|
+
});
|
|
37
|
+
return session;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate the current session from cookie
|
|
41
|
+
* Returns the session if valid, null otherwise
|
|
42
|
+
*/
|
|
43
|
+
export async function validateSession() {
|
|
44
|
+
const config = await loadConfig();
|
|
45
|
+
const cookieName = config.cookieName ?? DEFAULT_COOKIE_NAME;
|
|
46
|
+
const expiresIn = config.sessionExpiresIn ?? DEFAULT_EXPIRES_IN;
|
|
47
|
+
const token = await config.cookie.get(cookieName);
|
|
48
|
+
if (!token) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const parsed = parseToken(token);
|
|
52
|
+
if (!parsed) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const session = await config.db.getSessionById(parsed.id);
|
|
56
|
+
if (!session) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// Check expiration
|
|
60
|
+
const now = Date.now();
|
|
61
|
+
const sessionAge = now - session.createdAt.getTime();
|
|
62
|
+
if (sessionAge >= expiresIn * 1000) {
|
|
63
|
+
await config.db.deleteSession(session.id);
|
|
64
|
+
await config.cookie.delete(cookieName);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
// Verify secret
|
|
68
|
+
const tokenSecretHash = await hashSecret(parsed.secret);
|
|
69
|
+
if (!constantTimeEqual(tokenSecretHash, session.secretHash)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return session;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the current user from session
|
|
76
|
+
* Returns null if no valid session or user not found
|
|
77
|
+
*/
|
|
78
|
+
export async function getUser() {
|
|
79
|
+
const config = await loadConfig();
|
|
80
|
+
const session = await validateSession();
|
|
81
|
+
if (!session) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
return config.db.getUserById(session.userId);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Delete the current session and clear cookie
|
|
88
|
+
*/
|
|
89
|
+
export async function deleteSession() {
|
|
90
|
+
const config = await loadConfig();
|
|
91
|
+
const cookieName = config.cookieName ?? DEFAULT_COOKIE_NAME;
|
|
92
|
+
const token = await config.cookie.get(cookieName);
|
|
93
|
+
if (!token) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const parsed = parseToken(token);
|
|
97
|
+
if (parsed) {
|
|
98
|
+
await config.db.deleteSession(parsed.id);
|
|
99
|
+
}
|
|
100
|
+
await config.cookie.delete(cookieName);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Delete a specific session by ID (useful for "logout all devices")
|
|
104
|
+
*/
|
|
105
|
+
export async function deleteSessionById(sessionId) {
|
|
106
|
+
const config = await loadConfig();
|
|
107
|
+
await config.db.deleteSession(sessionId);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface Session {
|
|
2
|
+
id: string;
|
|
3
|
+
secretHash: Uint8Array;
|
|
4
|
+
userId: string;
|
|
5
|
+
createdAt: Date;
|
|
6
|
+
}
|
|
7
|
+
export interface SessionWithToken extends Session {
|
|
8
|
+
token: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CookieOptions {
|
|
11
|
+
maxAge: number;
|
|
12
|
+
httpOnly: boolean;
|
|
13
|
+
secure: boolean;
|
|
14
|
+
path: string;
|
|
15
|
+
sameSite: 'lax' | 'strict' | 'none';
|
|
16
|
+
}
|
|
17
|
+
export interface AuthConfig<TUser = any> {
|
|
18
|
+
db: {
|
|
19
|
+
insertSession: (session: Session) => Promise<void>;
|
|
20
|
+
getSessionById: (sessionId: string) => Promise<Session | null>;
|
|
21
|
+
deleteSession: (sessionId: string) => Promise<void>;
|
|
22
|
+
getUserById: (userId: string) => Promise<TUser | null>;
|
|
23
|
+
};
|
|
24
|
+
cookie: {
|
|
25
|
+
set: (name: string, value: string, options: CookieOptions) => void | Promise<void>;
|
|
26
|
+
get: (name: string) => string | null | Promise<string | null>;
|
|
27
|
+
delete: (name: string) => void | Promise<void>;
|
|
28
|
+
};
|
|
29
|
+
sessionExpiresIn?: number;
|
|
30
|
+
cookieName?: string;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAiB,SAAQ,OAAO;IAC/C,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,UAAU,CAAC,KAAK,GAAG,GAAG;IACrC,EAAE,EAAE;QACF,aAAa,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QACnD,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;QAC/D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QACpD,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;KACxD,CAAC;IAEF,MAAM,EAAE;QACN,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACnF,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QAC9D,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAChD,CAAC;IAEF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "simple-m-auth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Dead simple session-based authentication. Just create auth.config.ts and go.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"auth",
|
|
23
|
+
"authentication",
|
|
24
|
+
"session",
|
|
25
|
+
"lucia",
|
|
26
|
+
"simple"
|
|
27
|
+
],
|
|
28
|
+
"author": "",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^25.0.3",
|
|
32
|
+
"typescript": "^5.0.0"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|