@arcote.tech/arc-auth 0.7.25 → 0.7.27
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 +3 -3
- package/src/aggregates/account.ts +34 -23
- package/src/auth-builder.ts +18 -30
- package/src/routes/oauth-routes.ts +8 -2
- package/src/tokens/token.ts +22 -13
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.
|
|
4
|
+
"version": "0.7.27",
|
|
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.
|
|
14
|
-
"@arcote.tech/platform": "^0.7.
|
|
13
|
+
"@arcote.tech/arc": "^0.7.27",
|
|
14
|
+
"@arcote.tech/platform": "^0.7.27",
|
|
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<
|
|
40
|
-
|
|
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
|
|
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
|
|
54
|
+
const TokenFields extends ArcRawShape = {},
|
|
50
55
|
>(
|
|
51
|
-
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
408
|
+
TokenFields extends ArcRawShape = ArcRawShape,
|
|
409
|
+
> = ReturnType<
|
|
410
|
+
typeof createAccountAggregate<string, CustomFields, TokenFields>
|
|
411
|
+
>;
|
package/src/auth-builder.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
122
|
+
tokenFields,
|
|
135
123
|
});
|
|
136
124
|
|
|
137
125
|
return new AuthBuilder(
|
|
138
126
|
config.name,
|
|
139
127
|
accountId,
|
|
140
|
-
|
|
128
|
+
t,
|
|
141
129
|
Account,
|
|
142
130
|
undefined,
|
|
143
131
|
[] as string[],
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
};
|
package/src/tokens/token.ts
CHANGED
|
@@ -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
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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
|
|
10
|
-
|
|
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
|
>;
|