@arcote.tech/arc-auth 0.4.5 → 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 +2 -2
- package/src/aggregates/account.ts +22 -5
- package/src/aggregates/oauth-identity.ts +1 -1
- package/src/auth-builder.ts +24 -16
- package/src/index.ts +1 -91
- package/src/routes/oauth-routes.ts +4 -6
- package/src/tokens/token.ts +4 -2
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.
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
|
package/src/auth-builder.ts
CHANGED
|
@@ -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
|
|
22
|
-
|
|
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.
|
|
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.
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
[
|
|
138
|
+
[Account] as const,
|
|
131
139
|
);
|
|
132
140
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,96 +1,6 @@
|
|
|
1
|
-
|
|
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:
|
|
20
|
-
oauthIdentityElement:
|
|
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
|
|
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 ---
|
package/src/tokens/token.ts
CHANGED
|
@@ -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
|
-
|
|
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>
|