@arcote.tech/arc-auth 0.4.10 → 0.5.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/package.json +2 -2
- package/src/aggregates/account.ts +204 -213
- package/src/aggregates/oauth-identity.ts +56 -52
- package/src/auth-builder.ts +10 -7
- package/src/routes/oauth-routes.ts +5 -1
- package/src/tokens/token.ts +1 -1
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
|
+
"version": "0.5.0",
|
|
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.
|
|
13
|
+
"@arcote.tech/arc": "^0.5.0",
|
|
14
14
|
"react": "^18.0.0 || ^19.0.0",
|
|
15
15
|
"typescript": "^5.0.0"
|
|
16
16
|
},
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
aggregate,
|
|
4
4
|
boolean,
|
|
5
5
|
date,
|
|
6
|
+
mergeUnsafe,
|
|
6
7
|
string,
|
|
7
|
-
type $type,
|
|
8
8
|
type ArcRawShape,
|
|
9
9
|
} from "@arcote.tech/arc";
|
|
10
10
|
import type { AccountId } from "../ids/account";
|
|
@@ -41,9 +41,17 @@ export const createAccountAggregate = <
|
|
|
41
41
|
const { accountId, token, customFields } = data;
|
|
42
42
|
const tokenFields = data.tokenFields ?? [];
|
|
43
43
|
|
|
44
|
+
const name = data.name as Data["name"];
|
|
45
|
+
|
|
44
46
|
/** Build JWT params from account — includes accountId + tokenFields */
|
|
45
|
-
const buildTokenParams = (account:
|
|
46
|
-
|
|
47
|
+
const buildTokenParams = (account: {
|
|
48
|
+
_id: unknown;
|
|
49
|
+
[key: string]: unknown;
|
|
50
|
+
}) => {
|
|
51
|
+
const params = { accountId: account._id as string } as Record<
|
|
52
|
+
string,
|
|
53
|
+
unknown
|
|
54
|
+
> & { accountId: string };
|
|
47
55
|
for (const field of tokenFields) {
|
|
48
56
|
params[field] = account[field];
|
|
49
57
|
}
|
|
@@ -51,70 +59,65 @@ export const createAccountAggregate = <
|
|
|
51
59
|
};
|
|
52
60
|
|
|
53
61
|
return (
|
|
54
|
-
aggregate(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
aggregate(
|
|
63
|
+
`${name}Accounts`,
|
|
64
|
+
accountId,
|
|
65
|
+
mergeUnsafe(
|
|
66
|
+
{
|
|
67
|
+
email: string().email(),
|
|
68
|
+
isEmailVerified: boolean(),
|
|
69
|
+
passwordHash: string().optional(),
|
|
70
|
+
authMethod: string(),
|
|
71
|
+
registeredAt: date(),
|
|
72
|
+
lastSignedInAt: date().optional(),
|
|
73
|
+
},
|
|
74
|
+
customFields,
|
|
75
|
+
),
|
|
76
|
+
)
|
|
63
77
|
// --- Public Events ---
|
|
64
78
|
|
|
65
79
|
.publicEvent(
|
|
66
80
|
"accountRegistered",
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
mergeUnsafe(
|
|
82
|
+
{
|
|
83
|
+
accountId,
|
|
84
|
+
email: string().email(),
|
|
85
|
+
passwordHash: string(),
|
|
86
|
+
},
|
|
87
|
+
customFields,
|
|
88
|
+
),
|
|
73
89
|
async (ctx, event) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
email,
|
|
77
|
-
passwordHash,
|
|
78
|
-
...customData
|
|
79
|
-
} = event.payload as any;
|
|
80
|
-
await ctx.set(id, {
|
|
81
|
-
email,
|
|
90
|
+
event.payload;
|
|
91
|
+
await ctx.set(event.payload.accountId, {
|
|
82
92
|
isEmailVerified: false,
|
|
83
|
-
passwordHash,
|
|
84
93
|
authMethod: "email",
|
|
85
94
|
registeredAt: event.createdAt,
|
|
86
|
-
lastSignedInAt:
|
|
87
|
-
...
|
|
88
|
-
}
|
|
95
|
+
lastSignedInAt: undefined,
|
|
96
|
+
...event.payload,
|
|
97
|
+
});
|
|
89
98
|
},
|
|
90
99
|
)
|
|
91
100
|
|
|
92
101
|
.publicEvent(
|
|
93
102
|
"accountRegisteredViaOAuth",
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
mergeUnsafe(
|
|
104
|
+
{
|
|
105
|
+
accountId,
|
|
106
|
+
email: string().email(),
|
|
107
|
+
provider: string(),
|
|
108
|
+
providerUserId: string(),
|
|
109
|
+
},
|
|
110
|
+
customFields,
|
|
111
|
+
),
|
|
101
112
|
async (ctx, event) => {
|
|
102
|
-
|
|
103
|
-
accountId: id,
|
|
104
|
-
email,
|
|
105
|
-
provider: _provider,
|
|
106
|
-
providerUserId: _providerUserId,
|
|
107
|
-
...customData
|
|
108
|
-
} = event.payload as any;
|
|
109
|
-
await ctx.set(id, {
|
|
110
|
-
email,
|
|
113
|
+
await ctx.set(event.payload.accountId, {
|
|
111
114
|
isEmailVerified: true,
|
|
112
|
-
passwordHash:
|
|
115
|
+
passwordHash: undefined,
|
|
113
116
|
authMethod: "oauth",
|
|
114
117
|
registeredAt: event.createdAt,
|
|
115
118
|
lastSignedInAt: event.createdAt,
|
|
116
|
-
...
|
|
117
|
-
}
|
|
119
|
+
...event.payload,
|
|
120
|
+
});
|
|
118
121
|
},
|
|
119
122
|
)
|
|
120
123
|
|
|
@@ -122,192 +125,180 @@ export const createAccountAggregate = <
|
|
|
122
125
|
"signedIn",
|
|
123
126
|
{ accountId, email: string().email() },
|
|
124
127
|
async (ctx, event) => {
|
|
125
|
-
await ctx.modify(
|
|
126
|
-
event.
|
|
127
|
-
|
|
128
|
-
lastSignedInAt: event.createdAt,
|
|
129
|
-
} as any,
|
|
130
|
-
);
|
|
128
|
+
await ctx.modify(event.payload.accountId, {
|
|
129
|
+
lastSignedInAt: event.createdAt,
|
|
130
|
+
});
|
|
131
131
|
},
|
|
132
132
|
)
|
|
133
133
|
|
|
134
134
|
.publicEvent("emailVerified", { accountId }, async (ctx, event) => {
|
|
135
|
-
await ctx.modify(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
isEmailVerified: true,
|
|
139
|
-
} as any,
|
|
140
|
-
);
|
|
135
|
+
await ctx.modify(event.payload.accountId, {
|
|
136
|
+
isEmailVerified: true,
|
|
137
|
+
});
|
|
141
138
|
})
|
|
142
139
|
|
|
143
140
|
// --- Mutate Methods ---
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
142
|
+
.mutateMethod("register", (fn) =>
|
|
143
|
+
fn
|
|
144
|
+
.withParams(
|
|
145
|
+
mergeUnsafe(
|
|
146
|
+
{
|
|
147
|
+
email: string().email(),
|
|
148
|
+
password: string().minLength(6).maxLength(32),
|
|
149
|
+
},
|
|
150
|
+
customFields,
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
.handle(
|
|
154
|
+
ONLY_SERVER &&
|
|
155
|
+
(async (ctx, params) => {
|
|
156
|
+
const existing = await ctx.$query.findOne({
|
|
157
|
+
email: params.email,
|
|
158
|
+
});
|
|
159
|
+
if (existing) {
|
|
160
|
+
return { error: "EMAIL_ALREADY_TAKEN" as const };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const id = accountId.generate();
|
|
164
|
+
const pwHash = await hashPassword(params.password);
|
|
165
|
+
|
|
166
|
+
await ctx.accountRegistered.emit({
|
|
167
|
+
...params,
|
|
168
|
+
accountId: id,
|
|
169
|
+
passwordHash: pwHash,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return { accountId: id };
|
|
173
|
+
}),
|
|
174
|
+
),
|
|
177
175
|
)
|
|
178
176
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
.mutateMethod(
|
|
183
|
-
"signIn",
|
|
184
|
-
{
|
|
185
|
-
params: {
|
|
177
|
+
.mutateMethod("signIn", (fn) =>
|
|
178
|
+
fn
|
|
179
|
+
.withParams({
|
|
186
180
|
email: string().email(),
|
|
187
181
|
password: string().minLength(6).maxLength(32),
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
182
|
+
})
|
|
183
|
+
.handle(
|
|
184
|
+
ONLY_SERVER &&
|
|
185
|
+
(async (ctx, params) => {
|
|
186
|
+
const account = await ctx.$query.findOne({
|
|
187
|
+
email: params.email,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (!account) {
|
|
191
|
+
return { error: "INVALID_EMAIL_OR_PASSWORD" as const };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const isValid = await verifyPassword(
|
|
195
|
+
params.password,
|
|
196
|
+
account.passwordHash!,
|
|
197
|
+
);
|
|
198
|
+
if (!isValid) {
|
|
199
|
+
return { error: "INVALID_EMAIL_OR_PASSWORD" as const };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!account.isEmailVerified) {
|
|
203
|
+
return {
|
|
204
|
+
error: "EMAIL_NOT_VERIFIED" as const,
|
|
205
|
+
email: params.email,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const jwtToken = token.generateJWT(buildTokenParams(account));
|
|
210
|
+
|
|
211
|
+
await ctx.signedIn.emit({
|
|
212
|
+
accountId: account._id,
|
|
213
|
+
email: params.email,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return { token: jwtToken };
|
|
217
|
+
}),
|
|
218
|
+
),
|
|
222
219
|
)
|
|
223
220
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
provider: string(),
|
|
233
|
-
providerUserId: string(),
|
|
234
|
-
...customFields,
|
|
235
|
-
},
|
|
236
|
-
result: {} as
|
|
237
|
-
| { accountId: $type<typeof accountId>; token: string }
|
|
238
|
-
| {
|
|
239
|
-
error: "EMAIL_ALREADY_TAKEN";
|
|
240
|
-
accountId: $type<typeof accountId>;
|
|
241
|
-
token: string;
|
|
221
|
+
.mutateMethod("registerViaOAuth", (fn) =>
|
|
222
|
+
fn
|
|
223
|
+
.withParams(
|
|
224
|
+
mergeUnsafe(
|
|
225
|
+
{
|
|
226
|
+
email: string().email(),
|
|
227
|
+
provider: string(),
|
|
228
|
+
providerUserId: string(),
|
|
242
229
|
},
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
230
|
+
customFields,
|
|
231
|
+
),
|
|
232
|
+
)
|
|
233
|
+
.handle(
|
|
234
|
+
ONLY_SERVER &&
|
|
235
|
+
(async (ctx, params) => {
|
|
236
|
+
const existing = await ctx.$query.findOne({
|
|
237
|
+
email: params.email,
|
|
238
|
+
});
|
|
239
|
+
if (existing) {
|
|
240
|
+
return {
|
|
241
|
+
error: "EMAIL_ALREADY_TAKEN" as const,
|
|
242
|
+
accountId: existing._id,
|
|
243
|
+
token: token.generateJWT(buildTokenParams(existing)),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const id = accountId.generate();
|
|
248
|
+
|
|
249
|
+
await ctx.accountRegisteredViaOAuth.emit({
|
|
250
|
+
...params,
|
|
251
|
+
accountId: id,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const newAccount = await ctx.$query.findOne({ _id: id });
|
|
255
|
+
return {
|
|
256
|
+
accountId: id,
|
|
257
|
+
token: token.generateJWT(
|
|
258
|
+
buildTokenParams(newAccount ?? { _id: id }),
|
|
259
|
+
),
|
|
260
|
+
};
|
|
261
|
+
}),
|
|
262
|
+
),
|
|
273
263
|
)
|
|
274
264
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
*/
|
|
279
|
-
.mutateMethod(
|
|
280
|
-
"signInViaOAuth",
|
|
281
|
-
{
|
|
282
|
-
params: {
|
|
265
|
+
.mutateMethod("signInViaOAuth", (fn) =>
|
|
266
|
+
fn
|
|
267
|
+
.withParams({
|
|
283
268
|
email: string().email(),
|
|
284
269
|
provider: string(),
|
|
285
270
|
providerUserId: string(),
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
271
|
+
})
|
|
272
|
+
.handle(
|
|
273
|
+
ONLY_SERVER &&
|
|
274
|
+
(async (ctx, params) => {
|
|
275
|
+
const account = await ctx.$query.findOne({
|
|
276
|
+
email: params.email,
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
if (!account) {
|
|
280
|
+
return { error: "ACCOUNT_NOT_FOUND" as const };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const jwtToken = token.generateJWT(buildTokenParams(account));
|
|
284
|
+
|
|
285
|
+
await ctx.signedIn.emit({
|
|
286
|
+
accountId: account._id,
|
|
287
|
+
email: params.email,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return { token: jwtToken };
|
|
291
|
+
}),
|
|
292
|
+
),
|
|
306
293
|
)
|
|
307
294
|
|
|
308
|
-
.protectBy(token, (params
|
|
309
|
-
.clientQuery("getAll",
|
|
310
|
-
|
|
295
|
+
.protectBy(token, (params) => ({ _id: params.accountId }))
|
|
296
|
+
.clientQuery("getAll", (fn) =>
|
|
297
|
+
fn.handle(async (ctx) => ctx.$query.find({})),
|
|
298
|
+
)
|
|
299
|
+
.clientQuery("getMe", (fn) =>
|
|
300
|
+
fn.handle(async (ctx) => ctx.$query.findOne({})),
|
|
301
|
+
)
|
|
311
302
|
);
|
|
312
303
|
};
|
|
313
304
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { aggregate, date, string } from "@arcote.tech/arc";
|
|
2
|
-
import type { OAuthIdentityId } from "../ids/oauth-identity";
|
|
3
2
|
import type { AccountId } from "../ids/account";
|
|
3
|
+
import type { OAuthIdentityId } from "../ids/oauth-identity";
|
|
4
4
|
|
|
5
5
|
export type OAuthIdentityAggregateData = {
|
|
6
6
|
name: string;
|
|
@@ -14,8 +14,9 @@ export const createOAuthIdentityAggregate = <
|
|
|
14
14
|
data: Data,
|
|
15
15
|
) => {
|
|
16
16
|
const { oauthIdentityId, accountId } = data;
|
|
17
|
+
const name = data.name as Data["name"];
|
|
17
18
|
|
|
18
|
-
return aggregate(`${
|
|
19
|
+
return aggregate(`${name}OAuthIdentities`, oauthIdentityId, {
|
|
19
20
|
accountId,
|
|
20
21
|
provider: string(),
|
|
21
22
|
providerUserId: string(),
|
|
@@ -39,15 +40,15 @@ export const createOAuthIdentityAggregate = <
|
|
|
39
40
|
provider,
|
|
40
41
|
providerUserId,
|
|
41
42
|
providerEmail,
|
|
42
|
-
} = event.payload
|
|
43
|
+
} = event.payload;
|
|
43
44
|
await ctx.set(id, {
|
|
44
45
|
accountId: accId,
|
|
45
46
|
provider,
|
|
46
47
|
providerUserId,
|
|
47
48
|
providerEmail,
|
|
48
49
|
linkedAt: event.createdAt,
|
|
49
|
-
lastUsedAt:
|
|
50
|
-
}
|
|
50
|
+
lastUsedAt: undefined,
|
|
51
|
+
});
|
|
51
52
|
},
|
|
52
53
|
)
|
|
53
54
|
|
|
@@ -55,9 +56,9 @@ export const createOAuthIdentityAggregate = <
|
|
|
55
56
|
"oauthIdentityUsed",
|
|
56
57
|
{ oauthIdentityId },
|
|
57
58
|
async (ctx, event) => {
|
|
58
|
-
await ctx.modify(event.payload.oauthIdentityId
|
|
59
|
+
await ctx.modify(event.payload.oauthIdentityId, {
|
|
59
60
|
lastUsedAt: event.createdAt,
|
|
60
|
-
}
|
|
61
|
+
});
|
|
61
62
|
},
|
|
62
63
|
)
|
|
63
64
|
|
|
@@ -65,65 +66,68 @@ export const createOAuthIdentityAggregate = <
|
|
|
65
66
|
"oauthIdentityUnlinked",
|
|
66
67
|
{ oauthIdentityId },
|
|
67
68
|
async (ctx, event) => {
|
|
68
|
-
await ctx.remove(event.payload.oauthIdentityId
|
|
69
|
+
await ctx.remove(event.payload.oauthIdentityId);
|
|
69
70
|
},
|
|
70
71
|
)
|
|
71
72
|
|
|
72
|
-
.mutateMethod(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
params: {
|
|
73
|
+
.mutateMethod("linkIdentity", (fn) =>
|
|
74
|
+
fn
|
|
75
|
+
.withParams({
|
|
76
76
|
accountId,
|
|
77
77
|
provider: string(),
|
|
78
78
|
providerUserId: string(),
|
|
79
79
|
providerEmail: string().email(),
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
80
|
+
})
|
|
81
|
+
.handle(
|
|
82
|
+
ONLY_SERVER &&
|
|
83
|
+
(async (ctx, params) => {
|
|
84
|
+
const existing = await ctx.$query.findOne({
|
|
85
|
+
provider: params.provider,
|
|
86
|
+
providerUserId: params.providerUserId,
|
|
87
|
+
});
|
|
88
|
+
if (existing) {
|
|
89
|
+
return { error: "OAUTH_IDENTITY_ALREADY_LINKED" as const };
|
|
90
|
+
}
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
const id = oauthIdentityId.generate();
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
94
|
+
await ctx.oauthIdentityLinked.emit({
|
|
95
|
+
oauthIdentityId: id,
|
|
96
|
+
accountId: params.accountId,
|
|
97
|
+
provider: params.provider,
|
|
98
|
+
providerUserId: params.providerUserId,
|
|
99
|
+
providerEmail: params.providerEmail,
|
|
100
|
+
});
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
return { oauthIdentityId: id };
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
105
|
)
|
|
106
106
|
|
|
107
|
-
.mutateMethod(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
})
|
|
115
|
-
|
|
107
|
+
.mutateMethod("markUsed", (fn) =>
|
|
108
|
+
fn.withParams({ oauthIdentityId }).handle(
|
|
109
|
+
ONLY_SERVER &&
|
|
110
|
+
(async (ctx, params) => {
|
|
111
|
+
await ctx["oauthIdentityUsed"].emit({
|
|
112
|
+
oauthIdentityId: params.oauthIdentityId,
|
|
113
|
+
});
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
116
|
)
|
|
117
|
-
.clientQuery("getAll",
|
|
118
|
-
|
|
119
|
-
"findByProvider",
|
|
120
|
-
async (ctx, params: { provider: string; providerUserId: string }) =>
|
|
121
|
-
ctx.$query.findOne({
|
|
122
|
-
provider: params.provider,
|
|
123
|
-
providerUserId: params.providerUserId,
|
|
124
|
-
}),
|
|
117
|
+
.clientQuery("getAll", (fn) =>
|
|
118
|
+
fn.handle(async (ctx) => ctx.$query.find({})),
|
|
125
119
|
)
|
|
126
|
-
|
|
120
|
+
.clientQuery("findByProvider", (fn) =>
|
|
121
|
+
fn
|
|
122
|
+
.withParams({ provider: string(), providerUserId: string() })
|
|
123
|
+
.handle(
|
|
124
|
+
async (ctx, params: { provider: string; providerUserId: string }) =>
|
|
125
|
+
ctx.$query.findOne({
|
|
126
|
+
provider: params.provider,
|
|
127
|
+
providerUserId: params.providerUserId,
|
|
128
|
+
}),
|
|
129
|
+
),
|
|
130
|
+
);
|
|
127
131
|
};
|
|
128
132
|
|
|
129
133
|
export type OAuthIdentityAggregate<
|
package/src/auth-builder.ts
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
context,
|
|
3
3
|
route,
|
|
4
|
-
type ArcAggregateElement,
|
|
5
4
|
type ArcContextElement,
|
|
6
5
|
type ArcRawShape,
|
|
7
6
|
} from "@arcote.tech/arc";
|
|
7
|
+
import type { AccountAggregate } from "./aggregates/account";
|
|
8
|
+
import type { OAuthIdentityAggregate } from "./aggregates/oauth-identity";
|
|
8
9
|
import { createAccountAggregate } from "./aggregates/account";
|
|
9
10
|
import { createOAuthIdentityAggregate } from "./aggregates/oauth-identity";
|
|
10
11
|
import { createAccountId } from "./ids/account";
|
|
11
12
|
import { createOAuthIdentityId } from "./ids/oauth-identity";
|
|
12
13
|
import { createToken } from "./tokens/token";
|
|
13
14
|
import { createOAuthRoutes } from "./routes/oauth-routes";
|
|
15
|
+
import type { AccountId } from "./ids/account";
|
|
14
16
|
import type { OAuthProvidersConfig } from "./providers/types";
|
|
17
|
+
import type { Token } from "./tokens/token";
|
|
15
18
|
|
|
16
19
|
export class AuthBuilder<
|
|
17
|
-
AccId,
|
|
18
|
-
Tok,
|
|
19
|
-
Account extends
|
|
20
|
-
OAuthIdentity extends
|
|
20
|
+
AccId extends AccountId,
|
|
21
|
+
Tok extends Token,
|
|
22
|
+
Account extends AccountAggregate,
|
|
23
|
+
OAuthIdentity extends OAuthIdentityAggregate | undefined,
|
|
21
24
|
EnabledProviders extends string[],
|
|
22
25
|
Elements extends ArcContextElement<any>[],
|
|
23
26
|
> {
|
|
@@ -39,7 +42,7 @@ export class AuthBuilder<
|
|
|
39
42
|
const OAuthIdentity = createOAuthIdentityAggregate({
|
|
40
43
|
name: this._name,
|
|
41
44
|
oauthIdentityId,
|
|
42
|
-
accountId: this.accountId
|
|
45
|
+
accountId: this.accountId,
|
|
43
46
|
});
|
|
44
47
|
|
|
45
48
|
const hasProviders = config.providers && config.baseUrl;
|
|
@@ -47,7 +50,7 @@ export class AuthBuilder<
|
|
|
47
50
|
? createOAuthRoutes({
|
|
48
51
|
providers: config.providers!,
|
|
49
52
|
baseUrl: config.baseUrl!,
|
|
50
|
-
token: this.token
|
|
53
|
+
token: this.token,
|
|
51
54
|
accountElement: this.Account,
|
|
52
55
|
oauthIdentityElement: OAuthIdentity,
|
|
53
56
|
})
|
|
@@ -248,9 +248,13 @@ export function createOAuthRoutes(data: OAuthRoutesData) {
|
|
|
248
248
|
const targetUrl = new URL(decodedRedirect, baseUrl);
|
|
249
249
|
targetUrl.searchParams.set("token", jwtToken);
|
|
250
250
|
|
|
251
|
-
//
|
|
251
|
+
// Set auth cookie + clear state cookie
|
|
252
252
|
const response = Response.redirect(targetUrl.toString(), 302);
|
|
253
253
|
const headers = new Headers(response.headers);
|
|
254
|
+
headers.append(
|
|
255
|
+
"Set-Cookie",
|
|
256
|
+
`arc_token=${encodeURIComponent(jwtToken)}; HttpOnly; SameSite=Lax; Path=/; Max-Age=604800`,
|
|
257
|
+
);
|
|
254
258
|
headers.append(
|
|
255
259
|
"Set-Cookie",
|
|
256
260
|
`oauth_state=; HttpOnly; SameSite=Lax; Path=/; Max-Age=0`,
|
package/src/tokens/token.ts
CHANGED
|
@@ -10,7 +10,7 @@ export const createToken = <const Data extends TokenData>(data: Data) =>
|
|
|
10
10
|
token(`${data.name}Account`, {
|
|
11
11
|
accountId: string(),
|
|
12
12
|
...(data.extraParams ?? {}),
|
|
13
|
-
}
|
|
13
|
+
}).secret(data.secret);
|
|
14
14
|
|
|
15
15
|
export type Token<Data extends TokenData = TokenData> = ReturnType<
|
|
16
16
|
typeof createToken<Data>
|