@ultimabase/password 0.0.0-stage → 0.1.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/CHANGELOG.md +13 -0
- package/README.md +4 -2
- package/dist/core.d.ts +57 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +124 -0
- package/dist/email-functions.d.ts +20 -0
- package/dist/email-functions.d.ts.map +1 -0
- package/dist/email-functions.js +107 -0
- package/dist/functions.d.ts +10 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +75 -0
- package/dist/hash.d.ts +16 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +103 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/input.d.ts +104 -0
- package/dist/input.d.ts.map +1 -0
- package/dist/input.js +231 -0
- package/dist/models.d.ts +57 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +122 -0
- package/dist/plugin.d.ts +75 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +172 -0
- package/dist/tokens.d.ts +34 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +52 -0
- package/package.json +49 -3
- package/src/core.ts +206 -0
- package/src/email-functions.ts +137 -0
- package/src/functions.ts +102 -0
- package/src/hash.ts +144 -0
- package/src/index.ts +13 -0
- package/src/input.ts +360 -0
- package/src/models.ts +190 -0
- package/src/plugin.ts +310 -0
- package/src/tokens.ts +98 -0
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { SchemaField } from 'ultimabase';
|
|
2
|
+
type Node = {
|
|
3
|
+
readonly [key: string]: unknown;
|
|
4
|
+
};
|
|
5
|
+
type Read = {
|
|
6
|
+
readonly value: unknown;
|
|
7
|
+
} | {
|
|
8
|
+
readonly message: string;
|
|
9
|
+
};
|
|
10
|
+
type Issue = {
|
|
11
|
+
readonly path: readonly string[];
|
|
12
|
+
readonly message: string;
|
|
13
|
+
};
|
|
14
|
+
type Result<O> = {
|
|
15
|
+
readonly value: O;
|
|
16
|
+
} | {
|
|
17
|
+
readonly issues: readonly Issue[];
|
|
18
|
+
};
|
|
19
|
+
/** Standard Schema with its JSON Schema export (standardschema.dev), copied as its specification asks. */
|
|
20
|
+
interface StandardSchema<I, O> {
|
|
21
|
+
readonly '~standard': {
|
|
22
|
+
readonly version: 1;
|
|
23
|
+
readonly vendor: string;
|
|
24
|
+
readonly validate: (value: unknown) => Result<O>;
|
|
25
|
+
readonly jsonSchema: {
|
|
26
|
+
readonly input: () => Node;
|
|
27
|
+
readonly output: () => Node;
|
|
28
|
+
};
|
|
29
|
+
readonly types?: {
|
|
30
|
+
readonly input: I;
|
|
31
|
+
readonly output: O;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** One property of an input: how it reads a value, and its JSON Schema. */
|
|
36
|
+
export interface Property {
|
|
37
|
+
readonly read: (value: unknown) => Read;
|
|
38
|
+
readonly json: Node;
|
|
39
|
+
readonly required: boolean;
|
|
40
|
+
/** The shared nodes its JSON Schema refers to, lifted to the root's. */
|
|
41
|
+
readonly defs?: Node;
|
|
42
|
+
}
|
|
43
|
+
/** The email trimmed, lowercased and in NFC, the account's subject (D-113, D-115). */
|
|
44
|
+
export declare function emailOf(value: unknown): Read;
|
|
45
|
+
/** How a password is typed does not change it: NFKC (D-115, NIST SP 800-63B). */
|
|
46
|
+
export declare const normalized: (password: string) => string;
|
|
47
|
+
/** A password to store, normalized: 8 to 256 characters, no other rule (D-113). */
|
|
48
|
+
export declare function passwordOf(value: unknown): Read;
|
|
49
|
+
/** `read`'s value, or a `VALIDATION_FAILED` naming `path`. */
|
|
50
|
+
export declare function valid<T>(read: (value: unknown) => Read, value: T, path: string): T;
|
|
51
|
+
/** A property of `read`, whose JSON Schema is `json`. */
|
|
52
|
+
export declare const property: (read: Property['read'], json: Node, required?: boolean) => Property;
|
|
53
|
+
/**
|
|
54
|
+
* A user field a client may send at sign-up, a json one with its `validate`;
|
|
55
|
+
* a relation stops startup (D-113, D-125).
|
|
56
|
+
*/
|
|
57
|
+
export declare function fieldProperty(field: SchemaField, required: boolean, where: string, schema?: object): Property;
|
|
58
|
+
/** What a named input's `run` reads, under `input` (D-109). */
|
|
59
|
+
export type Named<O> = {
|
|
60
|
+
readonly input: O;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* One `input` argument of the object `id`, typed `SignUpInput` over GraphQL
|
|
64
|
+
* for the id `SignUp`; the email is normalized, the dates read (D-113).
|
|
65
|
+
*/
|
|
66
|
+
export declare function namedInput<O extends object>(id: string, properties: ReadonlyMap<string, Property>): StandardSchema<Named<O>, Named<O>>;
|
|
67
|
+
/** What `signIn` and `User.signIn` take. */
|
|
68
|
+
export type Credentials = {
|
|
69
|
+
readonly email: string;
|
|
70
|
+
readonly password: string;
|
|
71
|
+
};
|
|
72
|
+
type ChangePassword = {
|
|
73
|
+
readonly currentPassword: string;
|
|
74
|
+
readonly newPassword: string;
|
|
75
|
+
};
|
|
76
|
+
type SignUp = Credentials & {
|
|
77
|
+
readonly [field: string]: unknown;
|
|
78
|
+
};
|
|
79
|
+
export declare const signInInput: StandardSchema<Named<Credentials>, Named<Credentials>>;
|
|
80
|
+
export declare const changePasswordInput: StandardSchema<Named<ChangePassword>, Named<ChangePassword>>;
|
|
81
|
+
type ResetPassword = {
|
|
82
|
+
readonly token: string;
|
|
83
|
+
readonly password: string;
|
|
84
|
+
};
|
|
85
|
+
/** What `requestPasswordReset` takes (D-117). */
|
|
86
|
+
export declare const requestResetInput: StandardSchema<Named<{
|
|
87
|
+
readonly email: string;
|
|
88
|
+
}>, Named<{
|
|
89
|
+
readonly email: string;
|
|
90
|
+
}>>;
|
|
91
|
+
/** What `resetPassword` takes: the email's token and the new password (D-117). */
|
|
92
|
+
export declare const resetPasswordInput: StandardSchema<Named<ResetPassword>, Named<ResetPassword>>;
|
|
93
|
+
/** What `verifyEmail` takes: the email's token (D-117). */
|
|
94
|
+
export declare const verifyEmailInput: StandardSchema<Named<{
|
|
95
|
+
readonly token: string;
|
|
96
|
+
}>, Named<{
|
|
97
|
+
readonly token: string;
|
|
98
|
+
}>>;
|
|
99
|
+
/** The email, the password and the listed user fields, by their names. */
|
|
100
|
+
export declare const signUpInput: (fields: ReadonlyMap<string, Property>) => StandardSchema<Named<SignUp>, Named<SignUp>>;
|
|
101
|
+
/** An output's schema: typed and exported, never validated (D-45). */
|
|
102
|
+
export declare function outputSchema<T>(node: Node): StandardSchema<T, T>;
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../src/input.ts"],"names":[],"mappings":"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"}
|
package/dist/input.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { PublicError, Refused } from 'ultimabase';
|
|
2
|
+
import { decimalPattern, issueOf } from 'ultimabase/internal';
|
|
3
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
4
|
+
const text = (value) => typeof value === 'string' ? { value } : { message: 'Expected a string.' };
|
|
5
|
+
/** The email trimmed, lowercased and in NFC, the account's subject (D-113, D-115). */
|
|
6
|
+
export function emailOf(value) {
|
|
7
|
+
if (typeof value !== 'string')
|
|
8
|
+
return { message: 'Expected a string.' };
|
|
9
|
+
const email = value.trim().toLowerCase().normalize('NFC');
|
|
10
|
+
if (email.length > 254 || !/^[^\s@]+@[^\s@]+$/.test(email)) {
|
|
11
|
+
return { message: 'Expected an email address.' };
|
|
12
|
+
}
|
|
13
|
+
return { value: email };
|
|
14
|
+
}
|
|
15
|
+
/** How a password is typed does not change it: NFKC (D-115, NIST SP 800-63B). */
|
|
16
|
+
export const normalized = (password) => password.normalize('NFKC');
|
|
17
|
+
/** A password to store, normalized: 8 to 256 characters, no other rule (D-113). */
|
|
18
|
+
export function passwordOf(value) {
|
|
19
|
+
if (typeof value !== 'string')
|
|
20
|
+
return { message: 'Expected a string.' };
|
|
21
|
+
const password = normalized(value);
|
|
22
|
+
const length = Array.from(password).length;
|
|
23
|
+
if (length < 8 || length > 256) {
|
|
24
|
+
return { message: 'A password has 8 to 256 characters.' };
|
|
25
|
+
}
|
|
26
|
+
return { value: password };
|
|
27
|
+
}
|
|
28
|
+
/** `read`'s value, or a `VALIDATION_FAILED` naming `path`. */
|
|
29
|
+
export function valid(read, value, path) {
|
|
30
|
+
const result = read(value);
|
|
31
|
+
if ('message' in result) {
|
|
32
|
+
const issues = [{ path: [path], message: result.message }];
|
|
33
|
+
throw new PublicError('VALIDATION_FAILED', `${path}: ${result.message}`, {
|
|
34
|
+
details: { issues },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- emailOf and passwordOf return the string they were given, normalized.
|
|
38
|
+
return result.value;
|
|
39
|
+
}
|
|
40
|
+
/** A property of `read`, whose JSON Schema is `json`. */
|
|
41
|
+
export const property = (read, json, required = true) => ({ read, json, required });
|
|
42
|
+
const [smallest, largest] = [-2_147_483_648, 2_147_483_647];
|
|
43
|
+
/**
|
|
44
|
+
* A field's check, the kernel's (D-127, D-132, D-139): a date may come as an
|
|
45
|
+
* ISO string, and an optional field takes `null`.
|
|
46
|
+
*/
|
|
47
|
+
function readOf(field) {
|
|
48
|
+
return (given) => {
|
|
49
|
+
if (given === null && field.optional && !field.list)
|
|
50
|
+
return { value: given };
|
|
51
|
+
const value = field.type === 'dateTime' && typeof given === 'string'
|
|
52
|
+
? new Date(given)
|
|
53
|
+
: given;
|
|
54
|
+
const message = issueOf(value, field);
|
|
55
|
+
return message === undefined ? { value } : { message };
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function jsonOf(field) {
|
|
59
|
+
const { type, values = [], optional } = field;
|
|
60
|
+
if (field.list) {
|
|
61
|
+
const items = jsonOf({ ...field, list: false, optional: false });
|
|
62
|
+
return {
|
|
63
|
+
type: 'array',
|
|
64
|
+
items: type === 'string' ? { ...items, minLength: 1 } : items,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const integer = field.integer === true;
|
|
68
|
+
const base = type === 'dateTime' ||
|
|
69
|
+
type === 'date' ||
|
|
70
|
+
type === 'enum' ||
|
|
71
|
+
type === 'decimal'
|
|
72
|
+
? 'string'
|
|
73
|
+
: integer
|
|
74
|
+
? 'integer'
|
|
75
|
+
: type;
|
|
76
|
+
return {
|
|
77
|
+
type: optional ? [base, 'null'] : base,
|
|
78
|
+
...(integer ? { minimum: smallest, maximum: largest } : {}),
|
|
79
|
+
...(type === 'decimal' ? { pattern: decimalPattern } : {}),
|
|
80
|
+
...(type === 'dateTime' ? { format: 'date-time' } : {}),
|
|
81
|
+
...(type === 'date' ? { format: 'date' } : {}),
|
|
82
|
+
...(type === 'enum' ? { enum: optional ? [...values, null] : values } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** A json field's input JSON Schema, `{}` without an export (D-125). */
|
|
86
|
+
function exported(schema) {
|
|
87
|
+
const standard = schema && Reflect.get(schema, '~standard');
|
|
88
|
+
const exporter = isObject(standard) ? standard['jsonSchema'] : undefined;
|
|
89
|
+
const input = isObject(exporter) ? exporter['input'] : undefined;
|
|
90
|
+
if (typeof input !== 'function')
|
|
91
|
+
return {};
|
|
92
|
+
try {
|
|
93
|
+
const node = Reflect.apply(input, exporter, [
|
|
94
|
+
{ target: 'draft-2020-12' },
|
|
95
|
+
]);
|
|
96
|
+
return isObject(node) ? node : {};
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// A schema it cannot export takes any JSON value, as a field without one.
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** A json field: any value read here, its schema run by the kernel (D-125). */
|
|
104
|
+
function jsonProperty(field, required, schema) {
|
|
105
|
+
const { $schema: _, $defs: defs, ...json } = exported(schema);
|
|
106
|
+
const typed = Object.keys(json).length > 0;
|
|
107
|
+
return {
|
|
108
|
+
read: (value) => ({ value }),
|
|
109
|
+
json: typed && field.optional ? { anyOf: [json, { type: 'null' }] } : json,
|
|
110
|
+
required,
|
|
111
|
+
...(isObject(defs) ? { defs } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A user field a client may send at sign-up, a json one with its `validate`;
|
|
116
|
+
* a relation stops startup (D-113, D-125).
|
|
117
|
+
*/
|
|
118
|
+
export function fieldProperty(field, required, where, schema) {
|
|
119
|
+
if (field.kind !== 'scalar') {
|
|
120
|
+
throw new Refused(`${where}: ${field.name} is a relation, which sign-up does not take.`);
|
|
121
|
+
}
|
|
122
|
+
if (field.type === 'json')
|
|
123
|
+
return jsonProperty(field, required, schema);
|
|
124
|
+
return property(readOf(field), jsonOf(field), required);
|
|
125
|
+
}
|
|
126
|
+
/** An object of `properties`, which refuses any other key. */
|
|
127
|
+
function objectRead(properties, value, path, issues) {
|
|
128
|
+
if (!isObject(value)) {
|
|
129
|
+
issues.push({ path, message: 'Expected an object.' });
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
const output = {};
|
|
133
|
+
for (const key of Object.keys(value)) {
|
|
134
|
+
if (!properties.has(key)) {
|
|
135
|
+
issues.push({ path: [...path, key], message: 'Unknown field.' });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const [key, { read, required }] of properties) {
|
|
139
|
+
const given = value[key];
|
|
140
|
+
if (given === undefined) {
|
|
141
|
+
if (required)
|
|
142
|
+
issues.push({ path: [...path, key], message: 'Required.' });
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const result = read(given);
|
|
146
|
+
if ('message' in result) {
|
|
147
|
+
issues.push({ path: [...path, key], message: result.message });
|
|
148
|
+
}
|
|
149
|
+
else
|
|
150
|
+
output[key] = result.value;
|
|
151
|
+
}
|
|
152
|
+
return output;
|
|
153
|
+
}
|
|
154
|
+
function objectJson(properties) {
|
|
155
|
+
const required = [...properties].filter(([, each]) => each.required);
|
|
156
|
+
return {
|
|
157
|
+
type: 'object',
|
|
158
|
+
properties: Object.fromEntries([...properties].map(([key, each]) => [key, each.json])),
|
|
159
|
+
required: required.map(([key]) => key),
|
|
160
|
+
additionalProperties: false,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* One `input` argument of the object `id`, typed `SignUpInput` over GraphQL
|
|
165
|
+
* for the id `SignUp`; the email is normalized, the dates read (D-113).
|
|
166
|
+
*/
|
|
167
|
+
export function namedInput(id, properties) {
|
|
168
|
+
const json = () => ({
|
|
169
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
170
|
+
type: 'object',
|
|
171
|
+
properties: { input: { $ref: `#/$defs/${id}` } },
|
|
172
|
+
required: ['input'],
|
|
173
|
+
additionalProperties: false,
|
|
174
|
+
$defs: Object.assign({}, ...[...properties.values()].map((each) => each.defs), { [id]: objectJson(properties) }),
|
|
175
|
+
});
|
|
176
|
+
const validate = (value) => {
|
|
177
|
+
const issues = [];
|
|
178
|
+
const root = isObject(value) ? value : {};
|
|
179
|
+
const input = objectRead(properties, root['input'], ['input'], issues);
|
|
180
|
+
if (!isObject(value))
|
|
181
|
+
issues.push({ path: [], message: 'Expected an object.' });
|
|
182
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- each property of O was read by its check above.
|
|
183
|
+
return issues.length > 0 ? { issues } : { value: { input: input } };
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
'~standard': Object.freeze({
|
|
187
|
+
version: 1,
|
|
188
|
+
vendor: 'ultimabase-password',
|
|
189
|
+
validate,
|
|
190
|
+
jsonSchema: { input: json, output: json },
|
|
191
|
+
}),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const string = { type: 'string' };
|
|
195
|
+
const email = property(emailOf, { type: 'string', format: 'email' });
|
|
196
|
+
export const signInInput = namedInput('SignIn', new Map([
|
|
197
|
+
['email', email],
|
|
198
|
+
['password', property(text, string)],
|
|
199
|
+
]));
|
|
200
|
+
export const changePasswordInput = namedInput('ChangePassword', new Map([
|
|
201
|
+
['currentPassword', property(text, string)],
|
|
202
|
+
['newPassword', property(passwordOf, string)],
|
|
203
|
+
]));
|
|
204
|
+
/** What `requestPasswordReset` takes (D-117). */
|
|
205
|
+
export const requestResetInput = namedInput('RequestPasswordReset', new Map([['email', email]]));
|
|
206
|
+
/** What `resetPassword` takes: the email's token and the new password (D-117). */
|
|
207
|
+
export const resetPasswordInput = namedInput('ResetPassword', new Map([
|
|
208
|
+
['token', property(text, string)],
|
|
209
|
+
['password', property(passwordOf, string)],
|
|
210
|
+
]));
|
|
211
|
+
/** What `verifyEmail` takes: the email's token (D-117). */
|
|
212
|
+
export const verifyEmailInput = namedInput('VerifyEmail', new Map([['token', property(text, string)]]));
|
|
213
|
+
/** The email, the password and the listed user fields, by their names. */
|
|
214
|
+
export const signUpInput = (fields) => namedInput('SignUp', new Map([
|
|
215
|
+
['email', email],
|
|
216
|
+
['password', property(passwordOf, string)],
|
|
217
|
+
...fields,
|
|
218
|
+
]));
|
|
219
|
+
/** An output's schema: typed and exported, never validated (D-45). */
|
|
220
|
+
export function outputSchema(node) {
|
|
221
|
+
const json = () => node;
|
|
222
|
+
return {
|
|
223
|
+
'~standard': Object.freeze({
|
|
224
|
+
version: 1,
|
|
225
|
+
vendor: 'ultimabase-password',
|
|
226
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- an output is typed by its function, never validated (D-45).
|
|
227
|
+
validate: (value) => ({ value: value }),
|
|
228
|
+
jsonSchema: { input: json, output: json },
|
|
229
|
+
}),
|
|
230
|
+
};
|
|
231
|
+
}
|
package/dist/models.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Field, ModelOf, ScalarOptions } from 'ultimabase';
|
|
2
|
+
import { PublicError } from 'ultimabase';
|
|
3
|
+
export declare const hour: number;
|
|
4
|
+
type HashFields = {
|
|
5
|
+
readonly userId: Field<string, {
|
|
6
|
+
readonly unique: true;
|
|
7
|
+
}>;
|
|
8
|
+
readonly hash: Field<string, ScalarOptions<string>>;
|
|
9
|
+
/** New each time the password is set, kept when it is rehashed. */
|
|
10
|
+
readonly stamp: Field<string, {
|
|
11
|
+
readonly unique: true;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
export declare const PasswordHash: ModelOf<'PasswordHash', HashFields, []>;
|
|
15
|
+
type AttemptFields = {
|
|
16
|
+
readonly subject: Field<string, ScalarOptions<string>>;
|
|
17
|
+
readonly address: Field<string, ScalarOptions<string>>;
|
|
18
|
+
readonly at: Field<Date, ScalarOptions<Date>>;
|
|
19
|
+
};
|
|
20
|
+
export declare const PasswordAttempt: ModelOf<'PasswordAttempt', AttemptFields, []>;
|
|
21
|
+
type RequestFields = {
|
|
22
|
+
readonly kind: Field<string, ScalarOptions<string>>;
|
|
23
|
+
/** An address's network, or a user for the emails it receives. */
|
|
24
|
+
readonly subject: Field<string, ScalarOptions<string>>;
|
|
25
|
+
readonly at: Field<Date, ScalarOptions<Date>>;
|
|
26
|
+
};
|
|
27
|
+
export declare const PasswordRequest: ModelOf<'PasswordRequest', RequestFields, []>;
|
|
28
|
+
/**
|
|
29
|
+
* What the limits count for `address`: an IPv6 address's /64 network, the
|
|
30
|
+
* IPv4 address an IPv4-mapped one holds, in any notation, any other as it is
|
|
31
|
+
* (D-114).
|
|
32
|
+
*/
|
|
33
|
+
export declare function networkOf(address: string): string;
|
|
34
|
+
/** Where an attempt comes from; `undefined` for server code, not counted. */
|
|
35
|
+
export type Attempt = {
|
|
36
|
+
readonly subject: string;
|
|
37
|
+
readonly address: string;
|
|
38
|
+
} | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Counts an attempt as failed until `run` succeeds, so concurrent attempts
|
|
41
|
+
* cannot pass the limit (D-113, D-114). Within 15 minutes, the 11th from one
|
|
42
|
+
* address on a subject is refused, as the 101st on it or from the address;
|
|
43
|
+
* a success clears its address's count, a busy refusal is not counted.
|
|
44
|
+
*/
|
|
45
|
+
export declare function limited<T>(attempt: Attempt, run: () => Promise<T>): Promise<T>;
|
|
46
|
+
/** What the hourly limits count (D-114, D-117). */
|
|
47
|
+
export type RequestKind = 'signUp' | 'reset' | 'verify' | 'email';
|
|
48
|
+
/**
|
|
49
|
+
* Counts a request of `kind` from `subject`; past `most` within one hour,
|
|
50
|
+
* the refusal instead, and the request is not counted. It is counted before
|
|
51
|
+
* the others are, so concurrent requests cannot pass the limit (D-114).
|
|
52
|
+
*/
|
|
53
|
+
export declare function hourly(kind: RequestKind, subject: string, most: number): Promise<PublicError | undefined>;
|
|
54
|
+
/** Refuses the eleventh request of `kind` from `address` within one hour (D-114, D-117). */
|
|
55
|
+
export declare function requestLimited(kind: RequestKind, address: string): Promise<void>;
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,EAAuB,WAAW,EAAE,MAAM,YAAY,CAAA;AAO7D,eAAO,MAAM,IAAI,EAAE,MAAuB,CAAA;AAE1C,KAAK,UAAU,GAAG;IACjB,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAA;KAAE,CAAC,CAAA;IACzD,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IACnD,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAA;KAAE,CAAC,CAAA;CACxD,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,cAAc,EAAE,UAAU,EAAE,EAAE,CAQ9D,CAAA;AAEH,KAAK,aAAa,GAAG;IACpB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IACtD,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IACtD,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;CAC7C,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,iBAAiB,EAAE,aAAa,EAAE,EAAE,CASvE,CAAA;AAEH,KAAK,aAAa,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IACnD,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;IACtD,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;CAC7C,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,iBAAiB,EAAE,aAAa,EAAE,EAAE,CASvE,CAAA;AAEH;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMjD;AAYD,6EAA6E;AAC7E,MAAM,MAAM,OAAO,GAClB;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAA;AAenE;;;;;GAKG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC9B,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAiCZ;AAED,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEjE;;;;GAIG;AACH,wBAAsB,MAAM,CAC3B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAmBlC;AAED,4FAA4F;AAC5F,wBAAsB,cAAc,CACnC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
package/dist/models.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// password's own models (D-110, D-113, D-114, D-117): each user's hash, and
|
|
2
|
+
// what the sign-in limit and the hourly limits count.
|
|
3
|
+
import ipaddr from 'ipaddr.js';
|
|
4
|
+
import { defineModel, master, PublicError } from 'ultimabase';
|
|
5
|
+
const M = { as: master };
|
|
6
|
+
const limit = 10;
|
|
7
|
+
const ceiling = 100;
|
|
8
|
+
const window = 15 * 60 * 1000;
|
|
9
|
+
const requests = 10;
|
|
10
|
+
export const hour = 60 * 60 * 1000;
|
|
11
|
+
export const PasswordHash = defineModel({
|
|
12
|
+
name: 'PasswordHash',
|
|
13
|
+
fields: (f) => ({
|
|
14
|
+
userId: f.string({ unique: true }),
|
|
15
|
+
hash: f.string(),
|
|
16
|
+
stamp: f.string({ unique: true }),
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
export const PasswordAttempt = defineModel({
|
|
20
|
+
name: 'PasswordAttempt',
|
|
21
|
+
fields: (f) => ({
|
|
22
|
+
subject: f.string(),
|
|
23
|
+
address: f.string(),
|
|
24
|
+
at: f.dateTime(),
|
|
25
|
+
}),
|
|
26
|
+
indexes: [{ fields: ['subject', 'address'] }, { fields: ['at'] }],
|
|
27
|
+
});
|
|
28
|
+
export const PasswordRequest = defineModel({
|
|
29
|
+
name: 'PasswordRequest',
|
|
30
|
+
fields: (f) => ({
|
|
31
|
+
kind: f.string(),
|
|
32
|
+
subject: f.string(),
|
|
33
|
+
at: f.dateTime(),
|
|
34
|
+
}),
|
|
35
|
+
indexes: [{ fields: ['kind', 'subject'] }, { fields: ['at'] }],
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* What the limits count for `address`: an IPv6 address's /64 network, the
|
|
39
|
+
* IPv4 address an IPv4-mapped one holds, in any notation, any other as it is
|
|
40
|
+
* (D-114).
|
|
41
|
+
*/
|
|
42
|
+
export function networkOf(address) {
|
|
43
|
+
if (!ipaddr.IPv6.isValid(address))
|
|
44
|
+
return address;
|
|
45
|
+
const ip = ipaddr.IPv6.parse(address);
|
|
46
|
+
if (ip.isIPv4MappedAddress())
|
|
47
|
+
return ip.toIPv4Address().toString();
|
|
48
|
+
const prefix = ip.parts.slice(0, 4).map((group) => group.toString(16));
|
|
49
|
+
return `${prefix.join(':')}::/64`;
|
|
50
|
+
}
|
|
51
|
+
const tooMany = (oldest, span, now) => new PublicError('TOO_MANY_ATTEMPTS', 'Too many attempts: try again later.', {
|
|
52
|
+
details: {
|
|
53
|
+
retryAfter: Math.max(Math.ceil((oldest.getTime() + span - now) / 1000), 1),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
/** The `n`th most recent failure but `id` matching `where`, if there is one. */
|
|
57
|
+
async function nth(where, id, n) {
|
|
58
|
+
const failures = await PasswordAttempt.findMany({ where: { ...where, id: { not: id } }, orderBy: { at: 'desc' }, take: n }, M);
|
|
59
|
+
return failures[n - 1]?.at;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Counts an attempt as failed until `run` succeeds, so concurrent attempts
|
|
63
|
+
* cannot pass the limit (D-113, D-114). Within 15 minutes, the 11th from one
|
|
64
|
+
* address on a subject is refused, as the 101st on it or from the address;
|
|
65
|
+
* a success clears its address's count, a busy refusal is not counted.
|
|
66
|
+
*/
|
|
67
|
+
export async function limited(attempt, run) {
|
|
68
|
+
if (attempt === undefined)
|
|
69
|
+
return run();
|
|
70
|
+
const { subject } = attempt;
|
|
71
|
+
const address = networkOf(attempt.address);
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
const since = new Date(now - window);
|
|
74
|
+
await PasswordAttempt.deleteMany({ where: { at: { lte: since } } }, M);
|
|
75
|
+
const { id } = await PasswordAttempt.create({ data: { subject, address, at: new Date(now) } }, M);
|
|
76
|
+
// Several may be reached: the latest says when to retry.
|
|
77
|
+
const [oldest] = [
|
|
78
|
+
await nth({ subject, address }, id, limit),
|
|
79
|
+
await nth({ subject }, id, ceiling),
|
|
80
|
+
await nth({ address }, id, ceiling),
|
|
81
|
+
]
|
|
82
|
+
.filter((at) => at !== undefined)
|
|
83
|
+
.toSorted((a, b) => b.getTime() - a.getTime());
|
|
84
|
+
if (oldest !== undefined) {
|
|
85
|
+
await PasswordAttempt.deleteMany({ where: { id } }, M);
|
|
86
|
+
throw tooMany(oldest, window, now);
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const result = await run();
|
|
90
|
+
await PasswordAttempt.deleteMany({ where: { subject, address } }, M);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (error instanceof PublicError && error.code === 'TOO_MANY_ATTEMPTS') {
|
|
95
|
+
await PasswordAttempt.deleteMany({ where: { id } }, M);
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Counts a request of `kind` from `subject`; past `most` within one hour,
|
|
102
|
+
* the refusal instead, and the request is not counted. It is counted before
|
|
103
|
+
* the others are, so concurrent requests cannot pass the limit (D-114).
|
|
104
|
+
*/
|
|
105
|
+
export async function hourly(kind, subject, most) {
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
await PasswordRequest.deleteMany({ where: { at: { lte: new Date(now - hour) } } }, M);
|
|
108
|
+
const { id } = await PasswordRequest.create({ data: { kind, subject, at: new Date(now) } }, M);
|
|
109
|
+
const where = { kind, subject, id: { not: id } };
|
|
110
|
+
const recent = await PasswordRequest.findMany({ where, orderBy: { at: 'desc' }, take: most }, M);
|
|
111
|
+
const oldest = recent[most - 1];
|
|
112
|
+
if (oldest === undefined)
|
|
113
|
+
return undefined;
|
|
114
|
+
await PasswordRequest.deleteMany({ where: { id } }, M);
|
|
115
|
+
return tooMany(oldest.at, hour, now);
|
|
116
|
+
}
|
|
117
|
+
/** Refuses the eleventh request of `kind` from `address` within one hour (D-114, D-117). */
|
|
118
|
+
export async function requestLimited(kind, address) {
|
|
119
|
+
const refused = await hourly(kind, networkOf(address), requests);
|
|
120
|
+
if (refused !== undefined)
|
|
121
|
+
throw refused;
|
|
122
|
+
}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Auth } from '@ultimabase/auth';
|
|
2
|
+
import type { Client, Field, Identity, Payload, PluginOf, PluginTypes, Queries, Register, UseOf, UserOf } from 'ultimabase';
|
|
3
|
+
import type { SignedIn } from './core.ts';
|
|
4
|
+
import type { SendEmail } from './email-functions.ts';
|
|
5
|
+
import type { Credentials } from './input.ts';
|
|
6
|
+
/** The user fields a sign-up may take; any name before the user is registered. */
|
|
7
|
+
export type UserField = Register extends {
|
|
8
|
+
readonly user: unknown;
|
|
9
|
+
} ? Extract<Exclude<keyof UserOf<Register>, 'id' | 'createdAt' | 'updatedAt'>, string> : string;
|
|
10
|
+
export interface PasswordOptions {
|
|
11
|
+
/** The user fields `signUp` takes besides the email and the password; `false` closes sign-up. */
|
|
12
|
+
readonly signUp?: readonly UserField[] | false;
|
|
13
|
+
/**
|
|
14
|
+
* Sends an email that carries a token: a reset or a verification link
|
|
15
|
+
* the application writes. Without it, no function sends one (D-117).
|
|
16
|
+
*/
|
|
17
|
+
readonly sendEmail?: SendEmail;
|
|
18
|
+
}
|
|
19
|
+
type Options = {
|
|
20
|
+
readonly as?: Identity;
|
|
21
|
+
};
|
|
22
|
+
/** A transport's client, which counts the attempt and gets the cookie (D-115). */
|
|
23
|
+
type SignInOptions = Options & {
|
|
24
|
+
readonly client?: Client;
|
|
25
|
+
};
|
|
26
|
+
type CreateData<F> = Parameters<Queries<F>['create']>[0]['data'];
|
|
27
|
+
type UserRow<F> = Payload<F, {}>;
|
|
28
|
+
/** password's commands on the user model (D-113). */
|
|
29
|
+
export interface PasswordCommands<F> {
|
|
30
|
+
/** Creates the user, its account and its hash; opens no session. */
|
|
31
|
+
signUp(args: Credentials & {
|
|
32
|
+
readonly data: CreateData<F>;
|
|
33
|
+
}, options?: Options): Promise<UserRow<F>>;
|
|
34
|
+
/**
|
|
35
|
+
* Opens a session; with a `client`, counted and with its cookie as the
|
|
36
|
+
* signIn function, otherwise never limited.
|
|
37
|
+
*/
|
|
38
|
+
signIn(args: Credentials, options?: SignInOptions): Promise<SignedIn<UserRow<F>>>;
|
|
39
|
+
/**
|
|
40
|
+
* Replaces the password and ends every session, as Master or as the user.
|
|
41
|
+
* It takes no current password: check the user's intent first, as
|
|
42
|
+
* changePassword does.
|
|
43
|
+
*/
|
|
44
|
+
setPassword(args: {
|
|
45
|
+
readonly id: string;
|
|
46
|
+
readonly password: string;
|
|
47
|
+
}, options?: Options): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
/** password's types (D-57). */
|
|
50
|
+
export interface Password extends PluginTypes {
|
|
51
|
+
readonly name: 'password';
|
|
52
|
+
readonly requires: Auth;
|
|
53
|
+
readonly appOptions: PasswordOptions;
|
|
54
|
+
readonly addedFields: {
|
|
55
|
+
readonly emailVerified: Field<boolean, {
|
|
56
|
+
readonly default: false;
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
readonly commands: PasswordCommands<this['fields']>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The password sign-in method; it requires `authPlugin()`.
|
|
63
|
+
*
|
|
64
|
+
* Known and kept (D-115): `signUp` refuses a taken email with
|
|
65
|
+
* `UNIQUE_VIOLATION`, which tells that the email has an account until email
|
|
66
|
+
* verification can hide it; a hundred failures on one account within 15
|
|
67
|
+
* minutes, from any addresses, lock it for everyone, and a hundred from one
|
|
68
|
+
* address lock that address out of every account (D-114); `SignedIn`
|
|
69
|
+
* holds the token even when the cookie is set.
|
|
70
|
+
*/
|
|
71
|
+
export declare const passwordPlugin: PluginOf<Password>;
|
|
72
|
+
/** The user model's entry: its commands, and its hash deleted with it. */
|
|
73
|
+
export declare const password: UseOf<Password>;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|