@arcote.tech/arc-auth 0.4.6 → 0.4.7

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.4.6",
4
+ "version": "0.4.7",
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,7 +10,7 @@
10
10
  "type-check": "tsc --noEmit"
11
11
  },
12
12
  "peerDependencies": {
13
- "@arcote.tech/arc": "^0.4.6",
13
+ "@arcote.tech/arc": "^0.4.7",
14
14
  "react": "^18.0.0 || ^19.0.0",
15
15
  "typescript": "^5.0.0"
16
16
  },
@@ -29,6 +29,7 @@ export type AccountAggregateData<CustomFields extends ArcRawShape> = {
29
29
  accountId: AccountId;
30
30
  token: Token;
31
31
  customFields: CustomFields;
32
+ tokenFields?: string[];
32
33
  };
33
34
 
34
35
  export const createAccountAggregate = <
@@ -38,6 +39,16 @@ export const createAccountAggregate = <
38
39
  data: Data,
39
40
  ) => {
40
41
  const { accountId, token, customFields } = data;
42
+ const tokenFields = data.tokenFields ?? [];
43
+
44
+ /** Build JWT params from account — includes accountId + tokenFields */
45
+ const buildTokenParams = (account: any) => {
46
+ const params: any = { accountId: account._id };
47
+ for (const field of tokenFields) {
48
+ params[field] = account[field];
49
+ }
50
+ return params;
51
+ };
41
52
 
42
53
  return (
43
54
  aggregate(`${data.name}Accounts`, accountId, {
@@ -199,7 +210,7 @@ export const createAccountAggregate = <
199
210
  };
200
211
  }
201
212
 
202
- const jwtToken = token.generateJWT({ accountId: account._id });
213
+ const jwtToken = token.generateJWT(buildTokenParams(account));
203
214
 
204
215
  await ctx.signedIn.emit({
205
216
  accountId: account._id,
@@ -223,10 +234,11 @@ export const createAccountAggregate = <
223
234
  ...customFields,
224
235
  },
225
236
  result: {} as
226
- | { accountId: $type<typeof accountId> }
237
+ | { accountId: $type<typeof accountId>; token: string }
227
238
  | {
228
239
  error: "EMAIL_ALREADY_TAKEN";
229
240
  accountId: $type<typeof accountId>;
241
+ token: string;
230
242
  },
231
243
  },
232
244
  ONLY_SERVER &&
@@ -236,6 +248,7 @@ export const createAccountAggregate = <
236
248
  return {
237
249
  error: "EMAIL_ALREADY_TAKEN" as const,
238
250
  accountId: existing._id,
251
+ token: token.generateJWT(buildTokenParams(existing)),
239
252
  };
240
253
  }
241
254
 
@@ -250,7 +263,12 @@ export const createAccountAggregate = <
250
263
  ...custom,
251
264
  });
252
265
 
253
- return { accountId: id };
266
+ // Fetch newly created account to get defaults (e.g. role)
267
+ const newAccount = await ctx.$query.findOne({ _id: id });
268
+ return {
269
+ accountId: id,
270
+ token: token.generateJWT(buildTokenParams(newAccount ?? { _id: id })),
271
+ };
254
272
  }),
255
273
  )
256
274
 
@@ -276,7 +294,7 @@ export const createAccountAggregate = <
276
294
  return { error: "ACCOUNT_NOT_FOUND" as const };
277
295
  }
278
296
 
279
- const jwtToken = token.generateJWT({ accountId: account._id });
297
+ const jwtToken = token.generateJWT(buildTokenParams(account));
280
298
 
281
299
  await ctx.signedIn.emit({
282
300
  accountId: account._id,
@@ -290,7 +308,6 @@ export const createAccountAggregate = <
290
308
  .protectBy(token, (params: any) => ({ _id: params.accountId }))
291
309
  .clientQuery("getAll", async (ctx) => ctx.$query.find({}))
292
310
  .clientQuery("getMe", async (ctx) => ctx.$query.findOne({}))
293
- .build()
294
311
  );
295
312
  };
296
313
 
@@ -123,7 +123,7 @@ export const createOAuthIdentityAggregate = <
123
123
  providerUserId: params.providerUserId,
124
124
  }),
125
125
  )
126
- .build();
126
+ ;
127
127
  };
128
128
 
129
129
  export type OAuthIdentityAggregate<
@@ -1,11 +1,9 @@
1
1
  import {
2
- aggregateContextElement,
3
2
  context,
4
3
  route,
5
4
  type ArcAggregateElement,
6
5
  type ArcContextElement,
7
6
  type ArcRawShape,
8
- type AggregateConstructorAny,
9
7
  } from "@arcote.tech/arc";
10
8
  import { createAccountAggregate } from "./aggregates/account";
11
9
  import { createOAuthIdentityAggregate } from "./aggregates/oauth-identity";
@@ -18,9 +16,8 @@ import type { OAuthProvidersConfig } from "./providers/types";
18
16
  export class AuthBuilder<
19
17
  AccId,
20
18
  Tok,
21
- Account extends AggregateConstructorAny,
22
- AccountEl extends ArcAggregateElement<Account>,
23
- OAuthIdentity extends AggregateConstructorAny | undefined,
19
+ Account extends ArcAggregateElement<any, any, any, any, any, any>,
20
+ OAuthIdentity extends ArcAggregateElement<any, any, any, any, any, any> | undefined,
24
21
  EnabledProviders extends string[],
25
22
  Elements extends ArcContextElement<any>[],
26
23
  > {
@@ -29,7 +26,6 @@ export class AuthBuilder<
29
26
  readonly accountId: AccId,
30
27
  readonly token: Tok,
31
28
  readonly Account: Account,
32
- readonly accountElement: AccountEl,
33
29
  readonly OAuthIdentity: OAuthIdentity,
34
30
  readonly enabledProviders: EnabledProviders,
35
31
  readonly elements: Elements,
@@ -45,7 +41,6 @@ export class AuthBuilder<
45
41
  oauthIdentityId,
46
42
  accountId: this.accountId as any,
47
43
  });
48
- const oauthIdentityElement = aggregateContextElement(OAuthIdentity);
49
44
 
50
45
  const hasProviders = config.providers && config.baseUrl;
51
46
  const routes = hasProviders
@@ -53,8 +48,8 @@ export class AuthBuilder<
53
48
  providers: config.providers!,
54
49
  baseUrl: config.baseUrl!,
55
50
  token: this.token as any,
56
- accountElement: this.accountElement,
57
- oauthIdentityElement,
51
+ accountElement: this.Account,
52
+ oauthIdentityElement: OAuthIdentity,
58
53
  })
59
54
  : null;
60
55
 
@@ -76,12 +71,11 @@ export class AuthBuilder<
76
71
  this.accountId,
77
72
  this.token,
78
73
  this.Account,
79
- this.accountElement,
80
74
  OAuthIdentity,
81
75
  routes?.enabledProviders ?? ([] as string[]),
82
76
  [
83
- this.accountElement,
84
- oauthIdentityElement,
77
+ this.Account,
78
+ OAuthIdentity,
85
79
  oauthStart,
86
80
  oauthCallback,
87
81
  ] as const,
@@ -104,29 +98,43 @@ export function auth<
104
98
  const Name extends string,
105
99
  const CustomFields extends ArcRawShape,
106
100
  const Secret extends string | undefined,
101
+ const TokenFields extends readonly (keyof CustomFields & string)[] = [],
107
102
  >(config: {
108
103
  name: Name;
109
104
  customFields: CustomFields;
110
105
  secret: Secret;
106
+ tokenFields?: TokenFields;
111
107
  }) {
112
108
  const accountId = createAccountId({ name: config.name });
113
- const token = createToken({ name: config.name, secret: config.secret });
109
+
110
+ // Extract token field schemas from customFields
111
+ const extraParams: Record<string, any> = {};
112
+ const tokenFieldsList = (config.tokenFields ?? []) as string[];
113
+ for (const field of tokenFieldsList) {
114
+ extraParams[field] = config.customFields[field];
115
+ }
116
+
117
+ const token = createToken({
118
+ name: config.name,
119
+ secret: config.secret,
120
+ extraParams: Object.keys(extraParams).length > 0 ? extraParams : undefined,
121
+ });
122
+
114
123
  const Account = createAccountAggregate({
115
124
  name: config.name,
116
125
  accountId,
117
126
  token,
118
127
  customFields: config.customFields,
128
+ tokenFields: tokenFieldsList,
119
129
  });
120
- const accountElement = aggregateContextElement(Account);
121
130
 
122
131
  return new AuthBuilder(
123
132
  config.name,
124
133
  accountId,
125
134
  token,
126
135
  Account,
127
- accountElement,
128
136
  undefined,
129
137
  [] as string[],
130
- [accountElement] as const,
138
+ [Account] as const,
131
139
  );
132
140
  }
package/src/index.ts CHANGED
@@ -1,96 +1,6 @@
1
- import {
2
- aggregateContextElement,
3
- context,
4
- type ArcRawShape,
5
- } from "@arcote.tech/arc";
6
- import { createAccountAggregate } from "./aggregates/account";
7
- import { createOAuthIdentityAggregate } from "./aggregates/oauth-identity";
8
- import { createAccountId } from "./ids/account";
9
- import { createOAuthIdentityId } from "./ids/oauth-identity";
10
- import { createToken } from "./tokens/token";
11
- import { createOAuthRoutes } from "./routes/oauth-routes";
12
- import type { OAuthProvidersConfig } from "./providers/types";
13
-
14
- // --- New typed builder API ---
1
+ // --- Auth builder API ---
15
2
  export { auth, AuthBuilder } from "./auth-builder";
16
3
 
17
- // --- Deprecated: use auth().useOAuth().build() instead ---
18
-
19
- export type AuthMode = "email" | "oauth" | "both";
20
-
21
- export type AuthContextData = {
22
- name: string;
23
- customFields: ArcRawShape;
24
- secret: string | undefined;
25
- mode?: AuthMode;
26
- providers?: OAuthProvidersConfig;
27
- baseUrl?: string;
28
- };
29
-
30
- /** @deprecated Use `auth({...}).useOAuth({...}).build()` instead */
31
- export const createAuthContext = <const Data extends AuthContextData>(
32
- data: Data,
33
- ) => {
34
- const mode = data.mode ?? "both";
35
- const accountId = createAccountId({ name: data.name });
36
- const token = createToken({ name: data.name, secret: data.secret });
37
-
38
- const Account = createAccountAggregate({
39
- name: data.name,
40
- accountId,
41
- token,
42
- customFields: data.customFields,
43
- });
44
-
45
- const accountElement = aggregateContextElement(Account);
46
-
47
- const elements: any[] = [accountElement];
48
-
49
- let OAuthIdentity: ReturnType<typeof createOAuthIdentityAggregate> | undefined;
50
- let enabledProviders: string[] = [];
51
-
52
- if (mode !== "email" && data.providers) {
53
- const oauthIdentityId = createOAuthIdentityId({ name: data.name });
54
-
55
- OAuthIdentity = createOAuthIdentityAggregate({
56
- name: data.name,
57
- oauthIdentityId,
58
- accountId,
59
- });
60
-
61
- const oauthIdentityElement = aggregateContextElement(OAuthIdentity);
62
- elements.push(oauthIdentityElement);
63
-
64
- if (data.baseUrl) {
65
- const oauthRoutes = createOAuthRoutes({
66
- providers: data.providers,
67
- baseUrl: data.baseUrl,
68
- token,
69
- accountElement,
70
- oauthIdentityElement,
71
- });
72
- elements.push(oauthRoutes.oauthStart);
73
- elements.push(oauthRoutes.oauthCallback);
74
- enabledProviders = oauthRoutes.enabledProviders;
75
- }
76
- }
77
-
78
- const authContext = context(elements);
79
-
80
- return {
81
- context: authContext,
82
- accountId,
83
- token,
84
- Account,
85
- OAuthIdentity,
86
- mode,
87
- enabledProviders,
88
- };
89
- };
90
-
91
- export type AuthContext<Data extends AuthContextData = AuthContextData> =
92
- ReturnType<typeof createAuthContext<Data>>;
93
-
94
4
  // Re-exports
95
5
  export { createAccountAggregate } from "./aggregates/account";
96
6
  export type { AccountAggregate } from "./aggregates/account";
@@ -16,8 +16,8 @@ export type OAuthRoutesData = {
16
16
  providers: OAuthProvidersConfig;
17
17
  baseUrl: string;
18
18
  token: Token;
19
- accountElement: ArcAggregateElement<AccountAggregate>;
20
- oauthIdentityElement: ArcAggregateElement<OAuthIdentityAggregate>;
19
+ accountElement: AccountAggregate;
20
+ oauthIdentityElement: OAuthIdentityAggregate;
21
21
  };
22
22
 
23
23
  const providerAdapters: Record<string, OAuthProviderAdapter> = {
@@ -230,8 +230,9 @@ export function createOAuthRoutes(data: OAuthRoutesData) {
230
230
  : {}),
231
231
  });
232
232
 
233
- // Both branches return accountId extract it directly
233
+ // Both branches return accountId + token (JWT with tokenFields)
234
234
  const accountId = registerResult.accountId;
235
+ jwtToken = registerResult.token;
235
236
 
236
237
  // Link the OAuth identity
237
238
  await oauthIdentities.linkIdentity({
@@ -240,9 +241,6 @@ export function createOAuthRoutes(data: OAuthRoutesData) {
240
241
  providerUserId: profile.providerUserId,
241
242
  providerEmail: profile.email,
242
243
  });
243
-
244
- // Generate JWT
245
- jwtToken = token.generateJWT({ accountId });
246
244
  }
247
245
 
248
246
  // --- Redirect with token ---
@@ -1,14 +1,16 @@
1
- import { string, token } from "@arcote.tech/arc";
1
+ import { string, token, type ArcRawShape } from "@arcote.tech/arc";
2
2
 
3
3
  export type TokenData = {
4
4
  name: string;
5
5
  secret: string | undefined;
6
+ extraParams?: ArcRawShape;
6
7
  };
7
8
 
8
9
  export const createToken = <const Data extends TokenData>(data: Data) =>
9
10
  token(`${data.name}Account`, {
10
11
  accountId: string(),
11
- }).secret(data.secret);
12
+ ...(data.extraParams ?? {}),
13
+ } as any).secret(data.secret);
12
14
 
13
15
  export type Token<Data extends TokenData = TokenData> = ReturnType<
14
16
  typeof createToken<Data>