@arcote.tech/arc-auth 0.7.26 → 0.7.28

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-auth",
3
3
  "type": "module",
4
- "version": "0.7.26",
4
+ "version": "0.7.28",
5
5
  "private": false,
6
6
  "description": "Reusable authentication module for Arc framework — aggregate-based auth with factory pattern",
7
7
  "main": "./src/index.ts",
@@ -10,8 +10,8 @@
10
10
  "type-check": "tsc --noEmit"
11
11
  },
12
12
  "peerDependencies": {
13
- "@arcote.tech/arc": "^0.7.26",
14
- "@arcote.tech/platform": "^0.7.26",
13
+ "@arcote.tech/arc": "^0.7.28",
14
+ "@arcote.tech/platform": "^0.7.28",
15
15
  "react": "^18.0.0 || ^19.0.0",
16
16
  "typescript": "^5.0.0"
17
17
  },
@@ -36,24 +36,34 @@ async function hashPassword(password: string): Promise<string> {
36
36
  return await Bun.password.hash(password);
37
37
  }
38
38
 
39
- export type AccountAggregateData<CustomFields extends ArcRawShape> = {
40
- name: string;
39
+ export type AccountAggregateData<
40
+ Name extends string,
41
+ CustomFields extends ArcRawShape,
42
+ TokenFields extends ArcRawShape = {},
43
+ > = {
44
+ name: Name;
41
45
  accountId: AccountId;
42
- token: Token;
46
+ token: Token<TokenFields>;
43
47
  customFields: CustomFields;
44
- tokenFields?: string[];
48
+ tokenFields: TokenFields;
45
49
  };
46
50
 
47
51
  export const createAccountAggregate = <
52
+ const Name extends string,
48
53
  const CustomFields extends ArcRawShape,
49
- const Data extends AccountAggregateData<CustomFields>,
54
+ const TokenFields extends ArcRawShape = {},
50
55
  >(
51
- data: Data,
56
+ data: AccountAggregateData<Name, CustomFields, TokenFields>,
52
57
  ) => {
53
- const { accountId, token, customFields } = data;
54
- const tokenFields = data.tokenFields ?? [];
58
+ const { accountId, token, customFields, tokenFields } = data;
55
59
 
56
- const name = data.name as Data["name"];
60
+ const name = data.name;
61
+
62
+ // Pola tokena to też pola konta — schemat agregatu (i eventy/komendy, które
63
+ // dziś mergują `customFields`) używają sumy `customFields ∪ tokenFields`,
64
+ // żeby np. `role` było składowane/queryowalne. Token niesie tylko tokenFields.
65
+ const accountFields = mergeUnsafe(customFields, tokenFields);
66
+ const tokenFieldNames = Object.keys(tokenFields);
57
67
 
58
68
  /** Build JWT params from account — includes accountId + tokenFields */
59
69
  const buildTokenParams = (account: {
@@ -64,7 +74,7 @@ export const createAccountAggregate = <
64
74
  string,
65
75
  unknown
66
76
  > & { accountId: string };
67
- for (const field of tokenFields) {
77
+ for (const field of tokenFieldNames) {
68
78
  params[field] = account[field];
69
79
  }
70
80
  return params;
@@ -86,7 +96,7 @@ export const createAccountAggregate = <
86
96
  failedLoginCount: number(),
87
97
  lockedUntil: date().optional(),
88
98
  },
89
- customFields,
99
+ accountFields,
90
100
  ),
91
101
  )
92
102
  // --- Public Events ---
@@ -99,7 +109,7 @@ export const createAccountAggregate = <
99
109
  email: string().email(),
100
110
  passwordHash: string(),
101
111
  },
102
- customFields,
112
+ accountFields,
103
113
  ),
104
114
  async (ctx, event) => {
105
115
  // Auto-verify email on registration. Framework no longer assumes
@@ -126,7 +136,7 @@ export const createAccountAggregate = <
126
136
  provider: string(),
127
137
  providerUserId: string(),
128
138
  },
129
- customFields,
139
+ accountFields,
130
140
  ),
131
141
  async (ctx, event) => {
132
142
  await ctx.set(event.payload.accountId, {
@@ -188,7 +198,7 @@ export const createAccountAggregate = <
188
198
  // prawdy z checklistą klienta).
189
199
  password: string().minLength(1).maxLength(72),
190
200
  },
191
- customFields,
201
+ accountFields,
192
202
  ),
193
203
  )
194
204
  .withResult(
@@ -214,7 +224,7 @@ export const createAccountAggregate = <
214
224
  return {
215
225
  error: "EMAIL_ALREADY_TAKEN" as const,
216
226
  accountId: existing._id,
217
- token: token.generateJWT(buildTokenParams(existing)),
227
+ token: await token.generateJWT(buildTokenParams(existing)),
218
228
  };
219
229
  }
220
230
 
@@ -229,7 +239,7 @@ export const createAccountAggregate = <
229
239
 
230
240
  return {
231
241
  accountId: id,
232
- token: token.generateJWT(
242
+ token: await token.generateJWT(
233
243
  buildTokenParams({ _id: id, ...params }),
234
244
  ),
235
245
  };
@@ -300,7 +310,7 @@ export const createAccountAggregate = <
300
310
  return { error: "INVALID_EMAIL_OR_PASSWORD" as const };
301
311
  }
302
312
 
303
- const jwtToken = token.generateJWT(buildTokenParams(account));
313
+ const jwtToken = await token.generateJWT(buildTokenParams(account));
304
314
 
305
315
  await ctx.signedIn.emit({
306
316
  accountId: account._id,
@@ -334,7 +344,7 @@ export const createAccountAggregate = <
334
344
  return {
335
345
  error: "EMAIL_ALREADY_TAKEN" as const,
336
346
  accountId: existing._id,
337
- token: token.generateJWT(buildTokenParams(existing)),
347
+ token: await token.generateJWT(buildTokenParams(existing)),
338
348
  };
339
349
  }
340
350
 
@@ -347,7 +357,7 @@ export const createAccountAggregate = <
347
357
 
348
358
  return {
349
359
  accountId: id,
350
- token: token.generateJWT(
360
+ token: await token.generateJWT(
351
361
  buildTokenParams({ _id: id, ...params }),
352
362
  ),
353
363
  };
@@ -373,7 +383,7 @@ export const createAccountAggregate = <
373
383
  return { error: "ACCOUNT_NOT_FOUND" as const };
374
384
  }
375
385
 
376
- const jwtToken = token.generateJWT(buildTokenParams(account));
386
+ const jwtToken = await token.generateJWT(buildTokenParams(account));
377
387
 
378
388
  await ctx.signedIn.emit({
379
389
  accountId: account._id,
@@ -395,6 +405,7 @@ export const createAccountAggregate = <
395
405
 
396
406
  export type AccountAggregate<
397
407
  CustomFields extends ArcRawShape = ArcRawShape,
398
- Data extends AccountAggregateData<CustomFields> =
399
- AccountAggregateData<CustomFields>,
400
- > = ReturnType<typeof createAccountAggregate<CustomFields, Data>>;
408
+ TokenFields extends ArcRawShape = ArcRawShape,
409
+ > = ReturnType<
410
+ typeof createAccountAggregate<string, CustomFields, TokenFields>
411
+ >;
@@ -3,23 +3,23 @@ import {
3
3
  route,
4
4
  type ArcContextElement,
5
5
  type ArcRawShape,
6
+ type ArcTokenAny,
6
7
  } from "@arcote.tech/arc";
7
8
  import type { AccountAggregate } from "./aggregates/account";
8
- import type { OAuthIdentityAggregate } from "./aggregates/oauth-identity";
9
9
  import { createAccountAggregate } from "./aggregates/account";
10
+ import type { OAuthIdentityAggregate } from "./aggregates/oauth-identity";
10
11
  import { createOAuthIdentityAggregate } from "./aggregates/oauth-identity";
12
+ import type { AccountId } from "./ids/account";
11
13
  import { createAccountId } from "./ids/account";
12
14
  import { createOAuthIdentityId } from "./ids/oauth-identity";
13
- import { createToken } from "./tokens/token";
14
- import { createOAuthRoutes } from "./routes/oauth-routes";
15
- import { createLogoutRoute } from "./routes/logout-route";
16
- import type { AccountId } from "./ids/account";
17
15
  import type { OAuthProvidersConfig } from "./providers/types";
18
- import type { Token } from "./tokens/token";
16
+ import { createLogoutRoute } from "./routes/logout-route";
17
+ import { createOAuthRoutes } from "./routes/oauth-routes";
18
+ import { createToken } from "./tokens/token";
19
19
 
20
20
  export class AuthBuilder<
21
21
  AccId extends AccountId,
22
- Tok extends Token,
22
+ Tok extends ArcTokenAny,
23
23
  Account extends AccountAggregate,
24
24
  OAuthIdentity extends OAuthIdentityAggregate | undefined,
25
25
  EnabledProviders extends string[],
@@ -35,10 +35,7 @@ export class AuthBuilder<
35
35
  readonly elements: Elements,
36
36
  ) {}
37
37
 
38
- useOAuth(config: {
39
- providers?: OAuthProvidersConfig;
40
- baseUrl?: string;
41
- }) {
38
+ useOAuth(config: { providers?: OAuthProvidersConfig; baseUrl?: string }) {
42
39
  const oauthIdentityId = createOAuthIdentityId({ name: this._name });
43
40
  const OAuthIdentity = createOAuthIdentityAggregate({
44
41
  name: this._name,
@@ -77,12 +74,7 @@ export class AuthBuilder<
77
74
  this.Account,
78
75
  OAuthIdentity,
79
76
  routes?.enabledProviders ?? ([] as string[]),
80
- [
81
- this.Account,
82
- OAuthIdentity,
83
- oauthStart,
84
- oauthCallback,
85
- ] as const,
77
+ [this.Account, OAuthIdentity, oauthStart, oauthCallback] as const,
86
78
  );
87
79
  }
88
80
 
@@ -104,40 +96,36 @@ export function auth<
104
96
  const Name extends string,
105
97
  const CustomFields extends ArcRawShape,
106
98
  const Secret extends string | undefined,
107
- const TokenFields extends readonly (keyof CustomFields & string)[] = [],
99
+ const TokenFields extends ArcRawShape = {},
108
100
  >(config: {
109
101
  name: Name;
110
102
  customFields: CustomFields;
111
103
  secret: Secret;
104
+ /** Schemat pól wnoszonych do payloadu JWT (np. `{ role: stringEnum(...) }`).
105
+ * Mergowany do schematu agregatu (są to też pola konta) i wprost do tokena. */
112
106
  tokenFields?: TokenFields;
113
107
  }) {
114
108
  const accountId = createAccountId({ name: config.name });
109
+ const tokenFields = (config.tokenFields ?? {}) as TokenFields;
115
110
 
116
- // Extract token field schemas from customFields
117
- const extraParams: Record<string, any> = {};
118
- const tokenFieldsList = (config.tokenFields ?? []) as string[];
119
- for (const field of tokenFieldsList) {
120
- extraParams[field] = config.customFields[field];
121
- }
122
-
123
- const token = createToken({
111
+ const t = createToken({
124
112
  name: config.name,
125
113
  secret: config.secret,
126
- extraParams: Object.keys(extraParams).length > 0 ? extraParams : undefined,
114
+ tokenFields,
127
115
  });
128
116
 
129
117
  const Account = createAccountAggregate({
130
118
  name: config.name,
131
119
  accountId,
132
- token,
120
+ token: t,
133
121
  customFields: config.customFields,
134
- tokenFields: tokenFieldsList,
122
+ tokenFields,
135
123
  });
136
124
 
137
125
  return new AuthBuilder(
138
126
  config.name,
139
127
  accountId,
140
- token,
128
+ t,
141
129
  Account,
142
130
  undefined,
143
131
  [] as string[],
@@ -1,4 +1,8 @@
1
- import { route, type ArcAggregateElement } from "@arcote.tech/arc";
1
+ import {
2
+ route,
3
+ type ArcAggregateElement,
4
+ type ArcTokenAny,
5
+ } from "@arcote.tech/arc";
2
6
  import type { AccountAggregate } from "../aggregates/account";
3
7
  import type { OAuthIdentityAggregate } from "../aggregates/oauth-identity";
4
8
  import {
@@ -15,7 +19,9 @@ import type { Token } from "../tokens/token";
15
19
  export type OAuthRoutesData = {
16
20
  providers: OAuthProvidersConfig;
17
21
  baseUrl: string;
18
- token: Token;
22
+ // Konkretny token (z polami) — `ArcTokenAny` przepuszcza dowolny kształt;
23
+ // tu używamy tylko `generateJWT` w runtime, nie typu params.
24
+ token: ArcTokenAny;
19
25
  accountElement: AccountAggregate;
20
26
  oauthIdentityElement: OAuthIdentityAggregate;
21
27
  };
@@ -1,17 +1,26 @@
1
- import { string, token, type ArcRawShape } from "@arcote.tech/arc";
1
+ import { mergeUnsafe, string, token, type ArcRawShape } from "@arcote.tech/arc";
2
2
 
3
- export type TokenData = {
4
- name: string;
3
+ /**
4
+ * Buduje token konta. `tokenFields` to KONKRETNY schemat (`ArcRawShape`) pól,
5
+ * które mają trafić do payloadu JWT (np. `{ role: stringEnum(...) }`) — mergowany
6
+ * wprost z `accountId`. Dzięki temu typ params tokena pozostaje konkretnym
7
+ * kształtem (`{accountId} & TokenFields`), więc `protectBy(p => p.role)` widzi
8
+ * pola. KLUCZOWE: nigdy nie wpuszczać tu gołego `ArcRawShape`/`any` (index-
9
+ * signature degraduje typ agregatu → `ctx.query` = `unknown`).
10
+ */
11
+ export const createToken = <
12
+ const Name extends string,
13
+ const TokenFields extends ArcRawShape = {},
14
+ >(data: {
15
+ name: Name;
5
16
  secret: string | undefined;
6
- extraParams?: ArcRawShape;
7
- };
17
+ tokenFields: TokenFields;
18
+ }) =>
19
+ token(
20
+ `${data.name}Account`,
21
+ mergeUnsafe({ accountId: string() }, data.tokenFields),
22
+ ).secret(data.secret);
8
23
 
9
- export const createToken = <const Data extends TokenData>(data: Data) =>
10
- token(`${data.name}Account`, {
11
- accountId: string(),
12
- ...(data.extraParams ?? {}),
13
- }).secret(data.secret);
14
-
15
- export type Token<Data extends TokenData = TokenData> = ReturnType<
16
- typeof createToken<Data>
24
+ export type Token<TokenFields extends ArcRawShape = {}> = ReturnType<
25
+ typeof createToken<string, TokenFields>
17
26
  >;