ideal-auth 0.6.1 → 0.7.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 CHANGED
@@ -552,18 +552,6 @@ cookie: {
552
552
 
553
553
  Zero framework imports. Works in Node, Bun, Deno, and edge runtimes.
554
554
 
555
- ## Claude Code
556
-
557
- If you use [Claude Code](https://claude.com/claude-code), install the ideal-auth plugin so your AI assistant knows the full API, cookie bridge patterns, security best practices, and implementation guides:
558
-
559
- ```bash
560
- claude plugin install github:ramonmalcolm10/ideal-auth
561
- ```
562
-
563
- After installing, Claude Code will automatically help with auth setup, login/registration flows, middleware, 2FA, password reset, rate limiting, and more — using the correct patterns for your framework.
564
-
565
- ---
566
-
567
555
  ## Support
568
556
 
569
557
  If this saved you time, consider supporting the project:
@@ -7,7 +7,7 @@ interface AuthInstanceDeps<TUser extends AnyUser> {
7
7
  rememberMaxAge: number;
8
8
  cookieOptions: ConfigurableCookieOptions;
9
9
  resolveUser?: (id: string) => Promise<TUser | null | undefined>;
10
- sessionFields?: readonly (keyof TUser & string)[];
10
+ sessionFields?: (keyof TUser & string)[];
11
11
  hash?: HashInstance;
12
12
  resolveUserByCredentials?: (credentials: Record<string, any>) => Promise<AnyUser | null | undefined>;
13
13
  credentialKey: string;
package/dist/auth.d.ts CHANGED
@@ -2,12 +2,11 @@ import type { AnyUser, AuthInstance, AuthConfig } from './types';
2
2
  /**
3
3
  * Create an auth factory.
4
4
  *
5
- * @example
6
- * // resolveUser user() returns SafeUser
7
- * createAuth<SafeUser>({ resolveUser: ... })
5
+ * `TUser` is the session user type — what `user()` returns.
6
+ * Do not include sensitive fields like password in this type.
8
7
  *
9
- * // sessionFields — user() returns Pick<DbUser, 'id' | 'email' | 'name'>
10
- * const sessionFields = ['email', 'name'] as const;
11
- * createAuth<DbUser, (typeof sessionFields)[number]>({ sessionFields, ... })
8
+ * @example
9
+ * type SessionUser = { id: string; email: string; name: string };
10
+ * createAuth<SessionUser>({ ... })
12
11
  */
13
- export declare function createAuth<TUser extends AnyUser, K extends keyof TUser & string = keyof TUser & string>(config: AuthConfig<TUser>): () => AuthInstance<TUser, Pick<TUser, 'id' | K>>;
12
+ export declare function createAuth<TUser extends AnyUser>(config: AuthConfig<TUser>): () => AuthInstance<TUser>;
package/dist/auth.js CHANGED
@@ -7,13 +7,12 @@ const SESSION_DEFAULTS = {
7
7
  /**
8
8
  * Create an auth factory.
9
9
  *
10
- * @example
11
- * // resolveUser user() returns SafeUser
12
- * createAuth<SafeUser>({ resolveUser: ... })
10
+ * `TUser` is the session user type — what `user()` returns.
11
+ * Do not include sensitive fields like password in this type.
13
12
  *
14
- * // sessionFields — user() returns Pick<DbUser, 'id' | 'email' | 'name'>
15
- * const sessionFields = ['email', 'name'] as const;
16
- * createAuth<DbUser, (typeof sessionFields)[number]>({ sessionFields, ... })
13
+ * @example
14
+ * type SessionUser = { id: string; email: string; name: string };
15
+ * createAuth<SessionUser>({ ... })
17
16
  */
18
17
  export function createAuth(config) {
19
18
  if (!config.secret || config.secret.length < 32) {
package/dist/types.d.ts CHANGED
@@ -72,19 +72,19 @@ export interface AuthConfigWithResolveUser<TUser extends AnyUser> extends AuthCo
72
72
  export interface AuthConfigWithSessionFields<TUser extends AnyUser, K extends keyof TUser & string = keyof TUser & string> extends AuthConfigBase<TUser> {
73
73
  /** Cannot use `resolveUser` together with `sessionFields`. */
74
74
  resolveUser?: never;
75
- sessionFields: readonly K[];
75
+ sessionFields: K[];
76
76
  }
77
77
  export type AuthConfig<TUser extends AnyUser = AnyUser> = AuthConfigWithResolveUser<TUser> | AuthConfigWithSessionFields<TUser>;
78
78
  export interface HashConfig {
79
79
  rounds?: number;
80
80
  }
81
- export interface AuthInstance<TUser extends AnyUser = AnyUser, TSessionUser = TUser> {
81
+ export interface AuthInstance<TUser extends AnyUser = AnyUser> {
82
82
  login(user: TUser, options?: LoginOptions): Promise<void>;
83
83
  loginById(id: string, options?: LoginOptions): Promise<void>;
84
84
  attempt(credentials: Record<string, any>, options?: LoginOptions): Promise<boolean>;
85
85
  logout(): Promise<void>;
86
86
  check(): Promise<boolean>;
87
- user(): Promise<TSessionUser | null>;
87
+ user(): Promise<TUser | null>;
88
88
  id(): Promise<string | null>;
89
89
  }
90
90
  export interface HashInstance {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ideal-auth",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Auth primitives for the JS ecosystem. Zero framework dependencies.",
5
5
  "scripts": {
6
6
  "build": "tsc",
@@ -71,33 +71,28 @@ Returns a factory function. Call `auth()` per request to get an `AuthInstance` s
71
71
  #### AuthConfig
72
72
 
73
73
  ```typescript
74
- // Session modeprovide exactly ONE of resolveUser or sessionFields
75
- // TypeScript will error if you provide both or neither
76
-
77
- // resolveUser mode: TUser is the safe type returned by resolveUser and user()
78
- type AuthConfigWithResolveUser<TUser> = {
79
- secret: string;
80
- cookie: CookieBridge;
81
- resolveUser: (id: string) => Promise<TUser | null | undefined>;
82
- sessionFields?: never;
83
- // resolveUserByCredentials can return any shape only needs id + passwordField
84
- resolveUserByCredentials?: (credentials: Record<string, any>) => Promise<AnyUser | null | undefined>;
85
- hash?: HashInstance;
86
- attemptUser?: (credentials: Record<string, any>) => Promise<TUser | null | undefined>;
87
- // ...session options
88
- };
89
-
90
- // sessionFields mode: user() returns Pick<TUser, 'id' | K>
91
- type AuthConfigWithSessionFields<TUser, K extends keyof TUser> = {
92
- secret: string;
93
- cookie: CookieBridge;
94
- resolveUser?: never;
95
- sessionFields: K[];
96
- resolveUserByCredentials?: (credentials: Record<string, any>) => Promise<AnyUser | null | undefined>;
97
- hash?: HashInstance;
98
- attemptUser?: (credentials: Record<string, any>) => Promise<TUser | null | undefined>;
99
- // ...session options
100
- };
74
+ // TUser = session user type what user() returns. Do not include password.
75
+ // Provide exactly ONE of resolveUser or sessionFields.
76
+ // TypeScript will error if you provide both or neither.
77
+
78
+ type AuthConfig<TUser> =
79
+ | {
80
+ resolveUser: (id: string) => Promise<TUser | null | undefined>;
81
+ sessionFields?: never;
82
+ // resolveUserByCredentials can return any shape — only needs id + passwordField
83
+ resolveUserByCredentials?: (credentials: Record<string, any>) => Promise<AnyUser | null | undefined>;
84
+ hash?: HashInstance;
85
+ attemptUser?: (credentials: Record<string, any>) => Promise<TUser | null | undefined>;
86
+ // ...session, secret, cookie
87
+ }
88
+ | {
89
+ resolveUser?: never;
90
+ sessionFields: (keyof TUser & string)[];
91
+ resolveUserByCredentials?: (credentials: Record<string, any>) => Promise<AnyUser | null | undefined>;
92
+ hash?: HashInstance;
93
+ attemptUser?: (credentials: Record<string, any>) => Promise<TUser | null | undefined>;
94
+ // ...session, secret, cookie
95
+ };
101
96
  ```
102
97
 
103
98
  #### Session Modes
@@ -124,29 +119,24 @@ const auth = createAuth<SafeUser>({
124
119
  const user = await auth().user(); // Type: SafeUser | null
125
120
  ```
126
121
 
127
- **Cookie-backed (`sessionFields`):** Cookie stores user ID + declared fields. `user()` returns `Pick<TUser, 'id' | K>` password excluded from the type.
128
-
129
- Define fields once as `const` and derive the type with `(typeof sessionFields)[number]`:
122
+ **Cookie-backed (`sessionFields`):** Cookie stores user ID + declared fields. `user()` returns `TUser | null`. Since `TUser` should not include password, the type is already safe.
130
123
 
131
124
  ```typescript
132
- type DbUser = { id: string; email: string; name: string; role: string; password: string };
133
-
134
- const sessionFields = ['email', 'name', 'role'] as const;
125
+ type SessionUser = { id: string; email: string; name: string; role: string }; // no password
135
126
 
136
- const auth = createAuth<DbUser, (typeof sessionFields)[number]>({
127
+ const auth = createAuth<SessionUser>({
137
128
  secret: process.env.IDEAL_AUTH_SECRET!,
138
129
  cookie: createCookieBridge(),
139
- sessionFields,
130
+ sessionFields: ['email', 'name', 'role'],
140
131
  resolveUserByCredentials: async (creds) => db.user.findFirst({ where: { email: creds.email } }),
141
132
  hash,
142
133
  });
143
- const user = await auth().user(); // Type: Pick<DbUser, 'id' | 'email' | 'name' | 'role'> | null
134
+ const user = await auth().user(); // Type: SessionUser | null
144
135
  ```
145
136
 
146
137
  Key rules:
138
+ - `TUser` is the session user type — what `user()` returns. Do not include password.
147
139
  - Provide exactly one of `resolveUser` or `sessionFields` — TypeScript errors if both or neither
148
- - `resolveUser` mode: `TUser` is the safe type. Only select non-sensitive fields in your query
149
- - `sessionFields` mode: `user()` type is `Pick<TUser, 'id' | ...fields>` — password excluded automatically
150
140
  - `resolveUserByCredentials` returns `AnyUser` — doesn't need to match `TUser`, only needs `id` + password field
151
141
  - Password is stripped from the cached user after `attempt()` — even same-request `user()` won't expose it
152
142
  - Cookie limit is ~4KB — store basic fields only