kroxt 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 ADDED
@@ -0,0 +1,82 @@
1
+ # kroxt
2
+
3
+ A framework-agnostic, modular authentication engine for modern TypeScript applications. Built for security, extensibility, and ease of use.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Secure Hashing**: Powered by `argon2` for industry-standard password security.
8
+ - 🎟️ **Stateless Sessions**: Managed via `jose` with high-performance JWT signing and verification.
9
+ - 🌍 **OAuth Ready**: Built-in support for GitHub and Google OAuth via `arctic`.
10
+ - 🧩 **Database Agnostic**: Use Mongoose, Prisma, Drizzle, or even in-memory stores via the `AuthAdapter` pattern.
11
+ - ✅ **Zod Schema Support**: Perfectly preserves and types your extended user metadata.
12
+ - 🚀 **ESM First**: Native support for NodeNext module resolution.
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install kroxt
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ### 1. Initialize the Auth Engine
23
+
24
+ ```typescript
25
+ import { createAuth } from "kroxt";
26
+ import { myDatabaseAdapter } from "./myAdapter.js";
27
+
28
+ const auth = createAuth({
29
+ adapter: myDatabaseAdapter,
30
+ secret: process.env.AUTH_SECRET,
31
+ session: {
32
+ expires: "7d" // jose compatible duration
33
+ }
34
+ });
35
+ ```
36
+
37
+ ### 2. Sign Up a User
38
+
39
+ ```typescript
40
+ const { user, token } = await auth.signup({
41
+ email: "user@example.com",
42
+ firstName: "Tobi",
43
+ role: "tenant",
44
+ // ...any other extended fields supported by your adapter
45
+ }, "strong-password-123");
46
+ ```
47
+
48
+ ### 3. Log In
49
+
50
+ ```typescript
51
+ const { user, token } = await auth.loginWithPassword("user@example.com", "password");
52
+ ```
53
+
54
+ ### 4. Verify a Session
55
+
56
+ ```typescript
57
+ const payload = await auth.verifyToken(token);
58
+ // auth.verifyToken returns the signed payload { sub: string, role: string, ... }
59
+ ```
60
+
61
+ ## The Adapter Pattern
62
+
63
+ Gatekeeper doesn't care which DB you use. You just need to implement the `AuthAdapter` interface:
64
+
65
+ ```typescript
66
+ import type { AuthAdapter, User } from "kroxt/adapter";
67
+
68
+ export const myAdapter: AuthAdapter = {
69
+ createUser: async (data) => { /* logic */ },
70
+ findUserByEmail: async (email) => { /* logic */ },
71
+ findUserById: async (id) => { /* logic */ },
72
+ linkOAuthAccount: async (user, provider, provId) => { /* logic */ }
73
+ };
74
+ ```
75
+
76
+ ## Reference Project
77
+
78
+ Check out the `test-project` folder for a complete **Express + MongoDB** implementation using this library.
79
+
80
+ ## License
81
+
82
+ ISC
@@ -0,0 +1,13 @@
1
+ export interface BaseUser {
2
+ id: string;
3
+ email: string;
4
+ passwordHash?: string;
5
+ role?: string;
6
+ }
7
+ export type User<TExtended = Record<string, any>> = BaseUser & TExtended;
8
+ export interface AuthAdapter<TUser = User> {
9
+ createUser: (data: any) => Promise<TUser>;
10
+ findUserByEmail: (email: string) => Promise<TUser | null>;
11
+ findUserById: (id: string) => Promise<TUser | null>;
12
+ linkOAuthAccount: (userId: string, provider: string, providerId: string) => Promise<void>;
13
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { AuthAdapter, User } from "./adapter.js";
2
+ import type { Provider } from "./providers.js";
3
+ export interface CreateAuthOptions {
4
+ adapter: AuthAdapter<any>;
5
+ secret: string;
6
+ session?: {
7
+ expires?: string | number;
8
+ };
9
+ providers?: Provider[];
10
+ }
11
+ export declare function createAuth(options: CreateAuthOptions): {
12
+ signup: (userData: Omit<User<any>, "id">, password?: string) => Promise<{
13
+ user: any;
14
+ token: string;
15
+ }>;
16
+ loginWithPassword: (email: string, password: string) => Promise<{
17
+ user: any;
18
+ token: string;
19
+ }>;
20
+ verifyToken: (token: string) => Promise<import("jose").JWTPayload>;
21
+ generateToken: (user: User<any>) => Promise<string>;
22
+ _providers: Provider[];
23
+ };
@@ -0,0 +1,67 @@
1
+ import * as argon2 from "argon2";
2
+ import { SignJWT, jwtVerify } from "jose";
3
+ export function createAuth(options) {
4
+ const { adapter, secret, session, providers } = options;
5
+ const encodedSecret = new TextEncoder().encode(secret);
6
+ const expiration = session?.expires || "7d";
7
+ /**
8
+ * Generates a stateless JWT for a user session
9
+ */
10
+ async function generateToken(user) {
11
+ return new SignJWT({ sub: user.id, role: user.role })
12
+ .setProtectedHeader({ alg: "HS256" })
13
+ .setIssuedAt()
14
+ .setExpirationTime(expiration)
15
+ .sign(encodedSecret);
16
+ }
17
+ /**
18
+ * Verifies a JWT and returns the payload
19
+ */
20
+ async function verifyToken(token) {
21
+ try {
22
+ const { payload } = await jwtVerify(token, encodedSecret);
23
+ return payload;
24
+ }
25
+ catch (e) {
26
+ return null;
27
+ }
28
+ }
29
+ /**
30
+ * Signup with a new user payload (handles extended schemas out-of-the-box).
31
+ * Generates a password hash if password is provided.
32
+ */
33
+ async function signup(userData, password) {
34
+ let dataToSave = { ...userData };
35
+ if (password) {
36
+ dataToSave.passwordHash = await argon2.hash(password);
37
+ }
38
+ const newUser = await adapter.createUser(dataToSave);
39
+ const token = await generateToken(newUser);
40
+ return { user: newUser, token };
41
+ }
42
+ /**
43
+ * Standard Email/Password Login
44
+ */
45
+ async function loginWithPassword(email, password) {
46
+ const user = await adapter.findUserByEmail(email);
47
+ if (!user) {
48
+ throw new Error("Invalid credentials");
49
+ }
50
+ if (!user.passwordHash) {
51
+ throw new Error("User does not have a password setup. Did they use OAuth?");
52
+ }
53
+ const isValid = await argon2.verify(user.passwordHash, password);
54
+ if (!isValid) {
55
+ throw new Error("Invalid credentials");
56
+ }
57
+ const token = await generateToken(user);
58
+ return { user, token };
59
+ }
60
+ return {
61
+ signup,
62
+ loginWithPassword,
63
+ verifyToken,
64
+ generateToken,
65
+ _providers: providers // Exposing to internal router mechanisms if needed
66
+ };
67
+ }
@@ -0,0 +1,6 @@
1
+ export type { AuthAdapter, User, BaseUser } from "./adapter.js";
2
+ export { GitHub, Google } from "./providers.js";
3
+ export type { Provider, ProviderConfig } from "./providers.js";
4
+ export { createAuth } from "./core.js";
5
+ export type { CreateAuthOptions } from "./core.js";
6
+ export { createMemoryAdapter } from "./memoryAdapter.js";
@@ -0,0 +1,3 @@
1
+ export { GitHub, Google } from "./providers.js";
2
+ export { createAuth } from "./core.js";
3
+ export { createMemoryAdapter } from "./memoryAdapter.js";
@@ -0,0 +1,7 @@
1
+ import type { AuthAdapter, User } from "./adapter.js";
2
+ /**
3
+ * Creates an in-memory database adapter for the auth engine.
4
+ * This is useful for testing, prototyping, or when you don't need persistent storage.
5
+ * All data is kept in memory and is lost when the server restarts.
6
+ */
7
+ export declare function createMemoryAdapter<TUser extends User = User>(): AuthAdapter<TUser>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Creates an in-memory database adapter for the auth engine.
3
+ * This is useful for testing, prototyping, or when you don't need persistent storage.
4
+ * All data is kept in memory and is lost when the server restarts.
5
+ */
6
+ export function createMemoryAdapter() {
7
+ const users = new Map();
8
+ const accounts = new Map();
9
+ return {
10
+ createUser: async (data) => {
11
+ // Auto-generate ID if not provided
12
+ const id = data.id || Date.now().toString();
13
+ const newUser = { ...data, id };
14
+ // Store using email as the primary lookup key
15
+ users.set(newUser.email, newUser);
16
+ return newUser;
17
+ },
18
+ findUserByEmail: async (email) => {
19
+ return users.get(email) || null;
20
+ },
21
+ findUserById: async (id) => {
22
+ for (const user of users.values()) {
23
+ if (user.id === id) {
24
+ return user;
25
+ }
26
+ }
27
+ return null;
28
+ },
29
+ linkOAuthAccount: async (userId, provider, providerId) => {
30
+ const accountId = `${provider}_${providerId}`;
31
+ accounts.set(accountId, { userId, provider, providerId });
32
+ }
33
+ };
34
+ }
@@ -0,0 +1,11 @@
1
+ export interface ProviderConfig {
2
+ clientId: string;
3
+ clientSecret: string;
4
+ redirectURI?: string;
5
+ }
6
+ export interface Provider {
7
+ id: string;
8
+ handler: any;
9
+ }
10
+ export declare function GitHub(config: ProviderConfig): Provider;
11
+ export declare function Google(config: ProviderConfig): Provider;
@@ -0,0 +1,16 @@
1
+ import { GitHub as ArcticGitHub, Google as ArcticGoogle } from "arctic";
2
+ export function GitHub(config) {
3
+ return {
4
+ id: "github",
5
+ handler: new ArcticGitHub(config.clientId, config.clientSecret, null),
6
+ };
7
+ }
8
+ export function Google(config) {
9
+ if (!config.redirectURI) {
10
+ throw new Error("redirectURI is required for Google OAuth provider");
11
+ }
12
+ return {
13
+ id: "google",
14
+ handler: new ArcticGoogle(config.clientId, config.clientSecret, config.redirectURI),
15
+ };
16
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "kroxt",
3
+ "version": "1.0.0",
4
+ "description": "A framework-agnostic modular auth engine",
5
+ "type": "module",
6
+ "main": "./dist-lib/index.js",
7
+ "module": "./dist-lib/index.js",
8
+ "types": "./dist-lib/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist-lib/index.js",
12
+ "types": "./dist-lib/index.d.ts"
13
+ },
14
+ "./adapter": {
15
+ "import": "./dist-lib/adapter.js",
16
+ "types": "./dist-lib/adapter.d.ts"
17
+ },
18
+ "./core": {
19
+ "import": "./dist-lib/core.js",
20
+ "types": "./dist-lib/core.d.ts"
21
+ },
22
+ "./providers": {
23
+ "import": "./dist-lib/providers.js",
24
+ "types": "./dist-lib/providers.d.ts"
25
+ },
26
+ "./memoryAdapter": {
27
+ "import": "./dist-lib/memoryAdapter.js",
28
+ "types": "./dist-lib/memoryAdapter.d.ts"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist-lib",
33
+ "README.md"
34
+ ],
35
+ "scripts": {
36
+ "build": "npx tsc src/auth/index.ts --outDir dist-lib --module nodenext --declaration"
37
+ },
38
+ "dependencies": {
39
+ "arctic": "^3.7.0",
40
+ "argon2": "^0.44.0",
41
+ "jose": "^6.2.1",
42
+ "zod": "^3.23.8"
43
+ }
44
+ }