@wtfalch/auth 0.14.0 → 0.16.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/dist/auth.d.ts CHANGED
@@ -131,15 +131,21 @@ export interface Completed {
131
131
  location: string;
132
132
  cookies: SetCookie[];
133
133
  }
134
- /** A person signed in from a link rather than from a flow the browser started. */
135
- export type SignedIn = {
134
+ /**
135
+ * A person signed in without the browser driving the flow -- from a link, a
136
+ * password reset, or headless credentials. `E` is whichever entry point's
137
+ * own error vocabulary applies: `ResetError` for a link or reset code (the
138
+ * default, so `followLink` and `resetPassword` are unchanged), `SignInError`
139
+ * for a password.
140
+ */
141
+ export type SignedIn<E = ResetError> = {
136
142
  ok: true;
137
143
  location: string;
138
144
  cookies: SetCookie[];
139
145
  hosted?: true;
140
146
  } | {
141
147
  ok: false;
142
- error: ResetError;
148
+ error: E;
143
149
  message: string;
144
150
  };
145
151
  export interface QrSignInStart {
@@ -202,6 +208,21 @@ export interface Auth {
202
208
  code: string;
203
209
  next?: string;
204
210
  }, requestHeaders?: Headers): Promise<SignedIn>;
211
+ /**
212
+ * `signIn` as data, for a caller with no browser to carry through the OIDC
213
+ * redirect -- a CLI, a desktop app, or a server signing in on a person's
214
+ * behalf. There is no `authRequestId` to receive, because this runs its
215
+ * own transaction and finishes it itself: `ok: true` already carries the
216
+ * session, with no `complete()` call left to make. The same two checks as
217
+ * `followLink` apply -- an `ok: true` with an empty `cookies` means the
218
+ * issuer handed off to hosted login, and the cookie name is fixed, so the
219
+ * caller decides where the session goes.
220
+ */
221
+ signInWithPassword(input: {
222
+ email: string;
223
+ password: string;
224
+ next?: string;
225
+ }, requestHeaders?: Headers): Promise<SignedIn<SignInError>>;
205
226
  /** Emails a fresh verification link. Says nothing about whether the address exists. */
206
227
  resendVerification(email: string, next?: string): Promise<void>;
207
228
  /** Emails a password-reset link to {basePath}/reset?userId=&code=. Says nothing about whether the address exists. */
package/dist/auth.js CHANGED
@@ -541,6 +541,25 @@ export function createAuth(input) {
541
541
  return { ok: false, error: signInError(error) };
542
542
  }
543
543
  };
544
+ const signInWithPassword = async ({ email, password, next }, requestHeaders) => {
545
+ try {
546
+ const flow = await serverFlow();
547
+ const completed = await finishOrHandoff(() => broker().signIn({ authRequestId: flow.id, email, password }), flow, safeNextPath(next, options().afterLogin), requestHeaders);
548
+ emit({ type: 'signIn', email });
549
+ return { ok: true, ...completed };
550
+ }
551
+ catch (error) {
552
+ const code = signInError(error);
553
+ // `signInError` collapses everything the broker can say into one of
554
+ // four codes; only a real `BrokerError` carries a sentence worth
555
+ // showing; a transient failure gets the same generic message every
556
+ // other headless entry point uses for `unavailable`.
557
+ const message = code !== 'unavailable' && error instanceof BrokerError
558
+ ? (error.detail ?? error.error)
559
+ : 'the sign-in service did not answer';
560
+ return { ok: false, error: code, message };
561
+ }
562
+ };
544
563
  const signUp = async ({ authRequestId, ...user }) => {
545
564
  try {
546
565
  const redirectTo = await broker().signUp({ authRequestId, ...user });
@@ -814,6 +833,7 @@ export function createAuth(input) {
814
833
  endSession,
815
834
  authRequest,
816
835
  signIn,
836
+ signInWithPassword,
817
837
  signUp,
818
838
  startPasskeyRegistration,
819
839
  finishPasskeyRegistration,
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Whether a name somebody chose for themselves may be theirs here.
3
+ *
4
+ * The words are obscenity's English set plus names.list.ts, matched the way
5
+ * obscenity matches: look-alikes from other scripts become the Latin letter
6
+ * they imitate, numbers and symbols standing for letters become the letter
7
+ * (`n1gg3r`, `f4gg0t`, `$hit`), case is dropped and repeats collapse. Two
8
+ * matchers, because one cannot do both jobs: the STRICT one keeps the word
9
+ * boundaries obscenity's patterns rely on, so `cunt` is refused and
10
+ * `scunthorpe` is not; the LOOSE one skips everything that is not a letter and
11
+ * carries only the words that are slurs wherever they appear, so `n.i.g.g.e.r`
12
+ * and `xX_n1gg3r_Xx` are refused too. The strict one is shown the name in a
13
+ * few shapes -- as typed, split at case changes, with digits taken off the ends
14
+ * of its parts, and with its separators removed -- so `BigCunt`, `cunt69` and
15
+ * `c.u.n.t` are all one word to it.
16
+ *
17
+ * Reserved names are the ones that would let somebody pass as us or as a
18
+ * resource path, and the ones held for particular people (names.reserved.ts),
19
+ * compared on a flattened form so `adm1n` is as reserved as `admin`. A `1` is
20
+ * read as an `i` and as an `l`, so `wa1id` is `walid` too.
21
+ *
22
+ * The letter rules -- what characters, how long -- are the caller's: this is a
23
+ * login name in ZITADEL and one part of `name@tf.auth.wtfalch.dev`, so the
24
+ * default is ASCII letters, digits, `_`, `.` and `-`, three to thirty of them.
25
+ */
26
+ export type Refusal = 'too_short' | 'too_long' | 'characters' | 'reserved' | 'disallowed';
27
+ export type Verdict = {
28
+ ok: true;
29
+ } | {
30
+ ok: false;
31
+ reason: Refusal;
32
+ };
33
+ export interface NameRules {
34
+ minLength: number;
35
+ maxLength: number;
36
+ /** Tested against the name as typed. */
37
+ pattern: RegExp;
38
+ }
39
+ export declare const DEFAULT_RULES: NameRules;
40
+ /** The refusal as a sentence the person can act on. */
41
+ export declare function explain(reason: Refusal, rules?: NameRules): string;
42
+ export declare function checkName(name: string, rules?: NameRules): Verdict;
package/dist/names.js ADDED
@@ -0,0 +1,146 @@
1
+ import { DataSet, RegExpMatcher, collapseDuplicatesTransformer, englishDataset, englishRecommendedTransformers, parseRawPattern, resolveConfusablesTransformer, resolveLeetSpeakTransformer, skipNonAlphabeticTransformer, toAsciiLowerCaseTransformer, } from 'obscenity';
2
+ import reservedUsernames from 'reserved-usernames' with { type: 'json' };
3
+ import { NUMERIC, PHRASES, WHITELIST } from './names.list.js';
4
+ import { PEOPLE, SYSTEM } from './names.reserved.js';
5
+ export const DEFAULT_RULES = {
6
+ minLength: 3,
7
+ maxLength: 30,
8
+ pattern: /^[A-Za-z0-9][A-Za-z0-9._-]*$/,
9
+ };
10
+ const spellings = (entry) => [entry.word, ...(entry.also ?? [])];
11
+ /**
12
+ * Obscenity collapses repeated letters in the INPUT and matches the patterns
13
+ * as written, so a pattern has to be written the way its word looks after
14
+ * collapsing: `fitte` is met as `fite`. The English set allows two of a few
15
+ * letters (ni_gg_er, a_ss_); the loose matcher allows one of anything.
16
+ */
17
+ function collapsed(word, twice) {
18
+ return word.replace(/(.)\1+/g, (run, letter) => twice.includes(letter) ? run.slice(0, 2) : letter);
19
+ }
20
+ const ENGLISH_TWICE = 'beolsg';
21
+ /** Obscenity's English words and ours, with the boundaries each asks for. */
22
+ const strictWords = new DataSet().addAll(englishDataset);
23
+ for (const entry of PHRASES) {
24
+ strictWords.addPhrase((phrase) => {
25
+ phrase.setMetadata({ originalWord: entry.word });
26
+ for (const word of spellings(entry)) {
27
+ const written = collapsed(word, ENGLISH_TWICE);
28
+ phrase.addPattern(parseRawPattern(entry.anywhere ? written : `|${written}|`));
29
+ }
30
+ return phrase;
31
+ });
32
+ }
33
+ strictWords.addPhrase((phrase) => {
34
+ for (const term of WHITELIST)
35
+ phrase.addWhitelistedTerm(term);
36
+ return phrase;
37
+ });
38
+ const strict = new RegExpMatcher({ ...strictWords.build(), ...englishRecommendedTransformers });
39
+ /** Only the words that are slurs wherever they appear, read with everything between the letters skipped. */
40
+ const looseWords = new DataSet();
41
+ for (const entry of [
42
+ ...PHRASES.filter((entry) => entry.anywhere),
43
+ { word: 'nigger', also: ['nigga'] },
44
+ { word: 'kike' },
45
+ { word: 'retard' },
46
+ { word: 'tranny' },
47
+ ]) {
48
+ looseWords.addPhrase((phrase) => {
49
+ phrase.setMetadata({ originalWord: entry.word });
50
+ for (const word of spellings(entry))
51
+ phrase.addPattern(parseRawPattern(collapsed(word, '')));
52
+ return phrase;
53
+ });
54
+ }
55
+ const loose = new RegExpMatcher({
56
+ ...looseWords.build(),
57
+ blacklistMatcherTransformers: [
58
+ resolveConfusablesTransformer(),
59
+ resolveLeetSpeakTransformer(),
60
+ toAsciiLowerCaseTransformer(),
61
+ skipNonAlphabeticTransformer(),
62
+ collapseDuplicatesTransformer({ defaultThreshold: 1 }),
63
+ ],
64
+ });
65
+ /** What a digit or symbol stands for when it is standing for a letter. */
66
+ const LETTERS = {
67
+ '0': ['o'],
68
+ '1': ['i', 'l'],
69
+ '!': ['i', 'l'],
70
+ '|': ['i', 'l'],
71
+ '2': ['z'],
72
+ '3': ['e'],
73
+ '4': ['a'],
74
+ '@': ['a'],
75
+ '5': ['s'],
76
+ $: ['s'],
77
+ '6': ['g', 'b'],
78
+ '7': ['t'],
79
+ '8': ['b'],
80
+ '9': ['g'],
81
+ };
82
+ /**
83
+ * Lowercase letters with the digits read as letters and nothing else, runs
84
+ * collapsed. Every reading, since `1` may be an `i` or an `l`; the first is
85
+ * the one a list entry is stored under.
86
+ */
87
+ function flattenings(name) {
88
+ let forms = [''];
89
+ for (const char of name.toLowerCase()) {
90
+ const readings = /[a-z]/.test(char) ? [char] : (LETTERS[char] ?? []);
91
+ if (readings.length === 0)
92
+ continue;
93
+ // Bounded: a name of nothing but `1`s would otherwise double at every one.
94
+ const next = forms.length * readings.length > 64 ? readings.slice(0, 1) : readings;
95
+ forms = forms.flatMap((form) => next.map((letter) => form + letter));
96
+ }
97
+ return [...new Set(forms.map((form) => form.replace(/(.)\1+/g, '$1')))];
98
+ }
99
+ const reserved = new Set([...reservedUsernames, ...SYSTEM, ...PEOPLE].map((entry) => flattenings(entry)[0]));
100
+ /** The name in the shapes a word might hide in. */
101
+ function shapes(name) {
102
+ const parts = name.split(/[^A-Za-z0-9æøåÆØÅ]+/).filter(Boolean);
103
+ return [
104
+ name,
105
+ name.replace(/([a-zæøå])([A-ZÆØÅ])/g, '$1 $2'),
106
+ parts.map((part) => part.replace(/^\d+|\d+$/g, '')).join(' '),
107
+ parts.join(''),
108
+ ];
109
+ }
110
+ /** The refusal as a sentence the person can act on. */
111
+ export function explain(reason, rules = DEFAULT_RULES) {
112
+ switch (reason) {
113
+ case 'too_short':
114
+ return `A username needs at least ${rules.minLength} characters.`;
115
+ case 'too_long':
116
+ return `A username can have at most ${rules.maxLength} characters.`;
117
+ case 'characters':
118
+ return 'A username is letters, digits, dots, dashes and underscores, and starts with a letter or digit.';
119
+ case 'reserved':
120
+ return 'That username is reserved.';
121
+ case 'disallowed':
122
+ return 'That username is not allowed.';
123
+ }
124
+ }
125
+ export function checkName(name, rules = DEFAULT_RULES) {
126
+ const typed = name.trim();
127
+ if (typed.length < rules.minLength)
128
+ return { ok: false, reason: 'too_short' };
129
+ if (typed.length > rules.maxLength)
130
+ return { ok: false, reason: 'too_long' };
131
+ if (!rules.pattern.test(typed))
132
+ return { ok: false, reason: 'characters' };
133
+ const digitsKept = typed.toLowerCase().replace(/[^a-z0-9]/g, '');
134
+ if (NUMERIC.some((entry) => digitsKept.includes(entry)))
135
+ return { ok: false, reason: 'disallowed' };
136
+ if (loose.hasMatch(typed))
137
+ return { ok: false, reason: 'disallowed' };
138
+ if (shapes(typed).some((shape) => strict.hasMatch(shape))) {
139
+ return { ok: false, reason: 'disallowed' };
140
+ }
141
+ // Last, so that a number that means something (`14.88` reads as `lab`) is refused for what it is.
142
+ if (flattenings(typed).some((form) => reserved.has(form))) {
143
+ return { ok: false, reason: 'reserved' };
144
+ }
145
+ return { ok: true };
146
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * What obscenity's English set does not know, and what is ours. Edit freely.
3
+ *
4
+ * `anywhere` words are refused wherever they appear, however spelled -- so
5
+ * only words with no innocent containing word belong there; `coon` (raccoon)
6
+ * and `paki` (Pakistan) do not. The rest are refused as a whole name or as a
7
+ * part of one split at separators and case changes: `nazi` and `big_nazi`
8
+ * are out, `ashkenazi` is not. `also` lists other spellings of the same word.
9
+ */
10
+ export interface Entry {
11
+ word: string;
12
+ anywhere?: boolean;
13
+ also?: string[];
14
+ }
15
+ export declare const PHRASES: Entry[];
16
+ /** Innocent names obscenity's English patterns would otherwise catch. */
17
+ export declare const WHITELIST: string[];
18
+ /** Read on the name with its digits kept, for the things that are numbers. */
19
+ export declare const NUMERIC: string[];
@@ -0,0 +1,42 @@
1
+ export const PHRASES = [
2
+ // en, beyond obscenity's set
3
+ { word: 'faggot', anywhere: true },
4
+ { word: 'chink', anywhere: true },
5
+ { word: 'wetback', anywhere: true },
6
+ { word: 'raghead', anywhere: true },
7
+ { word: 'towelhead', anywhere: true },
8
+ { word: 'hitler', anywhere: true },
9
+ { word: 'gook' },
10
+ { word: 'spic' },
11
+ { word: 'coon' },
12
+ { word: 'paki' },
13
+ { word: 'nazi' },
14
+ { word: 'pedo', also: ['paedo', 'pedophile', 'paedophile'] },
15
+ { word: 'rapist' },
16
+ { word: 'homo' },
17
+ // no
18
+ { word: 'neger', anywhere: true },
19
+ { word: 'svarting', anywhere: true },
20
+ { word: 'sotrør', anywhere: true, also: ['sotror'] },
21
+ { word: 'morraknuller', anywhere: true },
22
+ { word: 'morrapuler', anywhere: true },
23
+ { word: 'pakkis' }, // collapsed to `pakis`, it would hide in pakistan; whole word only
24
+ { word: 'faen' },
25
+ { word: 'fitte' },
26
+ { word: 'kuk' },
27
+ { word: 'pikk' },
28
+ { word: 'hore' },
29
+ { word: 'ræva', also: ['raeva', 'reva'] },
30
+ { word: 'helvete' },
31
+ { word: 'jævla', also: ['jaevla', 'jevla', 'jævlig', 'jaevlig', 'jevlig'] },
32
+ { word: 'drittsekk' },
33
+ { word: 'soper' },
34
+ { word: 'homse' },
35
+ { word: 'mongo', also: ['mongis'] },
36
+ { word: 'tater' },
37
+ { word: 'sigøyner', also: ['sigoyner'] },
38
+ ];
39
+ /** Innocent names obscenity's English patterns would otherwise catch. */
40
+ export const WHITELIST = ['shiitake', 'fager'];
41
+ /** Read on the name with its digits kept, for the things that are numbers. */
42
+ export const NUMERIC = ['kkk', '1488'];
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Names nobody may choose. Edit freely; order and duplicates do not matter.
3
+ *
4
+ * Both lists are compared on a flattened form: lower case, digits and symbols
5
+ * read as the letters they stand for, repeated letters collapsed. So `adm1n`,
6
+ * `Admin`, `a.d.m.i.n` and `aadmin` are all `admin`, and `K4i` is `kai`.
7
+ *
8
+ * SYSTEM is every name that would let somebody pass as us, as the software,
9
+ * as a mailbox that means something, or as a path an app is likely to have.
10
+ * The `reserved-usernames` package is applied as well, so this list is the
11
+ * one we can read and vouch for rather than the only one.
12
+ *
13
+ * PEOPLE are names held for particular people. They are refused like the
14
+ * system ones, so the person they are held for gets them by being given
15
+ * them, not by racing for them.
16
+ */
17
+ export declare const SYSTEM: string[];
18
+ export declare const PEOPLE: string[];
@@ -0,0 +1,732 @@
1
+ /**
2
+ * Names nobody may choose. Edit freely; order and duplicates do not matter.
3
+ *
4
+ * Both lists are compared on a flattened form: lower case, digits and symbols
5
+ * read as the letters they stand for, repeated letters collapsed. So `adm1n`,
6
+ * `Admin`, `a.d.m.i.n` and `aadmin` are all `admin`, and `K4i` is `kai`.
7
+ *
8
+ * SYSTEM is every name that would let somebody pass as us, as the software,
9
+ * as a mailbox that means something, or as a path an app is likely to have.
10
+ * The `reserved-usernames` package is applied as well, so this list is the
11
+ * one we can read and vouch for rather than the only one.
12
+ *
13
+ * PEOPLE are names held for particular people. They are refused like the
14
+ * system ones, so the person they are held for gets them by being given
15
+ * them, not by racing for them.
16
+ */
17
+ export const SYSTEM = [
18
+ // Who runs the place
19
+ 'admin',
20
+ 'admins',
21
+ 'administrator',
22
+ 'administrators',
23
+ 'sysadmin',
24
+ 'sysop',
25
+ 'root',
26
+ 'superuser',
27
+ 'su',
28
+ 'sudo',
29
+ 'owner',
30
+ 'owners',
31
+ 'master',
32
+ 'manager',
33
+ 'managers',
34
+ 'management',
35
+ 'operator',
36
+ 'operators',
37
+ 'moderator',
38
+ 'moderators',
39
+ 'mod',
40
+ 'mods',
41
+ 'staff',
42
+ 'team',
43
+ 'teams',
44
+ 'crew',
45
+ 'official',
46
+ 'officials',
47
+ 'founder',
48
+ 'founders',
49
+ 'ceo',
50
+ 'cto',
51
+ 'cfo',
52
+ 'coo',
53
+ 'boss',
54
+ 'chief',
55
+ 'director',
56
+ 'employee',
57
+ 'employees',
58
+ 'agent',
59
+ 'agents',
60
+ 'representative',
61
+ 'rep',
62
+ 'verified',
63
+ 'trusted',
64
+ 'vip',
65
+ // The software and the machines
66
+ 'system',
67
+ 'sys',
68
+ 'daemon',
69
+ 'service',
70
+ 'services',
71
+ 'server',
72
+ 'servers',
73
+ 'host',
74
+ 'hosts',
75
+ 'hostname',
76
+ 'localhost',
77
+ 'local',
78
+ 'bot',
79
+ 'bots',
80
+ 'robot',
81
+ 'robots',
82
+ 'automation',
83
+ 'cron',
84
+ 'scheduler',
85
+ 'worker',
86
+ 'workers',
87
+ 'job',
88
+ 'jobs',
89
+ 'queue',
90
+ 'cache',
91
+ 'db',
92
+ 'database',
93
+ 'databases',
94
+ 'sql',
95
+ 'postgres',
96
+ 'postgresql',
97
+ 'redis',
98
+ 'backup',
99
+ 'backups',
100
+ 'storage',
101
+ 'bucket',
102
+ 'cdn',
103
+ 'static',
104
+ 'assets',
105
+ 'public',
106
+ 'private',
107
+ 'internal',
108
+ 'external',
109
+ 'default',
110
+ 'config',
111
+ 'configuration',
112
+ 'settings',
113
+ 'preferences',
114
+ 'options',
115
+ 'env',
116
+ 'environment',
117
+ 'dev',
118
+ 'development',
119
+ 'staging',
120
+ 'stage',
121
+ 'prod',
122
+ 'production',
123
+ 'test',
124
+ 'tests',
125
+ 'testing',
126
+ 'tester',
127
+ 'demo',
128
+ 'sandbox',
129
+ 'alpha',
130
+ 'beta',
131
+ 'preview',
132
+ 'canary',
133
+ 'nightly',
134
+ 'debug',
135
+ 'trace',
136
+ 'log',
137
+ 'logs',
138
+ 'metrics',
139
+ 'monitor',
140
+ 'monitoring',
141
+ 'health',
142
+ 'healthz',
143
+ 'healthcheck',
144
+ 'status',
145
+ 'ping',
146
+ 'uptime',
147
+ // The network and its protocols
148
+ 'www',
149
+ 'web',
150
+ 'http',
151
+ 'https',
152
+ 'ftp',
153
+ 'sftp',
154
+ 'ssh',
155
+ 'telnet',
156
+ 'dns',
157
+ 'ns',
158
+ 'mx',
159
+ 'vpn',
160
+ 'proxy',
161
+ 'gateway',
162
+ 'router',
163
+ 'firewall',
164
+ 'ip',
165
+ 'tcp',
166
+ 'udp',
167
+ 'smtp',
168
+ 'imap',
169
+ 'pop',
170
+ 'mail',
171
+ 'email',
172
+ 'emails',
173
+ 'e-mail',
174
+ 'mailer',
175
+ 'mailer-daemon',
176
+ 'mailbox',
177
+ 'inbox',
178
+ 'outbox',
179
+ 'postmaster',
180
+ 'hostmaster',
181
+ 'webmaster',
182
+ 'listmaster',
183
+ 'abuse',
184
+ 'noc',
185
+ 'security',
186
+ 'usenet',
187
+ 'news',
188
+ 'uucp',
189
+ 'noreply',
190
+ 'no-reply',
191
+ 'no_reply',
192
+ 'donotreply',
193
+ 'do-not-reply',
194
+ 'reply',
195
+ 'bounce',
196
+ 'bounces',
197
+ 'api',
198
+ 'apis',
199
+ 'graphql',
200
+ 'rest',
201
+ 'rpc',
202
+ 'grpc',
203
+ 'ws',
204
+ 'wss',
205
+ 'socket',
206
+ 'websocket',
207
+ 'webhook',
208
+ 'webhooks',
209
+ 'callback',
210
+ 'callbacks',
211
+ 'oauth',
212
+ 'oauth2',
213
+ 'oidc',
214
+ 'openid',
215
+ 'sso',
216
+ 'saml',
217
+ 'ldap',
218
+ 'auth',
219
+ 'authn',
220
+ 'authz',
221
+ 'authentication',
222
+ 'authorization',
223
+ 'authorize',
224
+ 'token',
225
+ 'tokens',
226
+ 'jwt',
227
+ 'key',
228
+ 'keys',
229
+ 'apikey',
230
+ 'secret',
231
+ 'secrets',
232
+ 'cert',
233
+ 'certs',
234
+ 'certificate',
235
+ 'ssl',
236
+ 'tls',
237
+ 'acme',
238
+ 'well-known',
239
+ 'robots.txt',
240
+ 'sitemap',
241
+ 'favicon',
242
+ 'manifest',
243
+ // Getting in and out
244
+ 'login',
245
+ 'logon',
246
+ 'logout',
247
+ 'logoff',
248
+ 'signin',
249
+ 'sign-in',
250
+ 'sign_in',
251
+ 'signout',
252
+ 'sign-out',
253
+ 'sign_out',
254
+ 'signup',
255
+ 'sign-up',
256
+ 'sign_up',
257
+ 'register',
258
+ 'registration',
259
+ 'unregister',
260
+ 'session',
261
+ 'sessions',
262
+ 'password',
263
+ 'passwords',
264
+ 'passwd',
265
+ 'pwd',
266
+ 'reset',
267
+ 'recover',
268
+ 'recovery',
269
+ 'forgot',
270
+ 'verify',
271
+ 'verification',
272
+ 'confirm',
273
+ 'confirmation',
274
+ 'activate',
275
+ 'activation',
276
+ 'deactivate',
277
+ 'invite',
278
+ 'invites',
279
+ 'invitation',
280
+ 'invitations',
281
+ 'join',
282
+ 'onboarding',
283
+ 'welcome',
284
+ 'account',
285
+ 'accounts',
286
+ 'profile',
287
+ 'profiles',
288
+ 'user',
289
+ 'users',
290
+ 'username',
291
+ 'usernames',
292
+ 'member',
293
+ 'members',
294
+ 'membership',
295
+ 'people',
296
+ 'person',
297
+ 'identity',
298
+ 'identities',
299
+ 'credential',
300
+ 'credentials',
301
+ // Pronouns and placeholders that read as somebody
302
+ 'me',
303
+ 'my',
304
+ 'mine',
305
+ 'myself',
306
+ 'self',
307
+ 'i',
308
+ 'you',
309
+ 'your',
310
+ 'yours',
311
+ 'yourself',
312
+ 'we',
313
+ 'us',
314
+ 'our',
315
+ 'ours',
316
+ 'they',
317
+ 'them',
318
+ 'their',
319
+ 'everyone',
320
+ 'everybody',
321
+ 'anyone',
322
+ 'anybody',
323
+ 'someone',
324
+ 'somebody',
325
+ 'nobody',
326
+ 'noone',
327
+ 'no-one',
328
+ 'all',
329
+ 'any',
330
+ 'none',
331
+ 'guest',
332
+ 'guests',
333
+ 'visitor',
334
+ 'anonymous',
335
+ 'anon',
336
+ 'unknown',
337
+ 'unnamed',
338
+ 'noname',
339
+ 'nobody',
340
+ 'null',
341
+ 'nil',
342
+ 'nan',
343
+ 'undefined',
344
+ 'void',
345
+ 'true',
346
+ 'false',
347
+ 'yes',
348
+ 'no',
349
+ 'ok',
350
+ 'example',
351
+ 'examples',
352
+ 'sample',
353
+ 'samples',
354
+ 'placeholder',
355
+ 'dummy',
356
+ 'foo',
357
+ 'bar',
358
+ 'baz',
359
+ 'tbd',
360
+ 'tba',
361
+ 'todo',
362
+ 'temp',
363
+ 'tmp',
364
+ 'deleted',
365
+ 'removed',
366
+ 'banned',
367
+ 'suspended',
368
+ 'disabled',
369
+ 'inactive',
370
+ 'blocked',
371
+ 'spam',
372
+ 'spammer',
373
+ 'junk',
374
+ 'trash',
375
+ 'fake',
376
+ 'scam',
377
+ // Mailboxes and departments people write to
378
+ 'info',
379
+ 'information',
380
+ 'contact',
381
+ 'contacts',
382
+ 'contactus',
383
+ 'contact-us',
384
+ 'hello',
385
+ 'hi',
386
+ 'hey',
387
+ 'help',
388
+ 'helpdesk',
389
+ 'support',
390
+ 'customerservice',
391
+ 'customer-service',
392
+ 'customercare',
393
+ 'service-desk',
394
+ 'servicedesk',
395
+ 'faq',
396
+ 'feedback',
397
+ 'complaints',
398
+ 'report',
399
+ 'reports',
400
+ 'sales',
401
+ 'marketing',
402
+ 'billing',
403
+ 'invoice',
404
+ 'invoices',
405
+ 'payment',
406
+ 'payments',
407
+ 'pay',
408
+ 'finance',
409
+ 'accounting',
410
+ 'legal',
411
+ 'privacy',
412
+ 'compliance',
413
+ 'hr',
414
+ 'careers',
415
+ 'recruiting',
416
+ 'press',
417
+ 'media',
418
+ 'pr',
419
+ 'partners',
420
+ 'partner',
421
+ 'partnership',
422
+ 'affiliate',
423
+ 'affiliates',
424
+ 'sponsor',
425
+ 'sponsors',
426
+ 'newsletter',
427
+ 'newsletters',
428
+ 'notification',
429
+ 'notifications',
430
+ 'alert',
431
+ 'alerts',
432
+ 'update',
433
+ 'updates',
434
+ 'announcement',
435
+ 'announcements',
436
+ 'digest',
437
+ 'subscribe',
438
+ 'unsubscribe',
439
+ 'subscription',
440
+ 'subscriptions',
441
+ // Pages an app is likely to have
442
+ 'home',
443
+ 'homepage',
444
+ 'index',
445
+ 'main',
446
+ 'app',
447
+ 'apps',
448
+ 'application',
449
+ 'applications',
450
+ 'dashboard',
451
+ 'console',
452
+ 'panel',
453
+ 'cpanel',
454
+ 'control',
455
+ 'about',
456
+ 'aboutus',
457
+ 'about-us',
458
+ 'terms',
459
+ 'tos',
460
+ 'terms-of-service',
461
+ 'policy',
462
+ 'policies',
463
+ 'cookie',
464
+ 'cookies',
465
+ 'imprint',
466
+ 'copyright',
467
+ 'license',
468
+ 'licenses',
469
+ 'licence',
470
+ 'dmca',
471
+ 'pricing',
472
+ 'price',
473
+ 'prices',
474
+ 'plan',
475
+ 'plans',
476
+ 'premium',
477
+ 'pro',
478
+ 'plus',
479
+ 'free',
480
+ 'upgrade',
481
+ 'downgrade',
482
+ 'checkout',
483
+ 'cart',
484
+ 'shop',
485
+ 'store',
486
+ 'order',
487
+ 'orders',
488
+ 'receipt',
489
+ 'refund',
490
+ 'refunds',
491
+ 'donate',
492
+ 'donation',
493
+ 'blog',
494
+ 'blogs',
495
+ 'article',
496
+ 'articles',
497
+ 'post',
498
+ 'posts',
499
+ 'page',
500
+ 'pages',
501
+ 'wiki',
502
+ 'doc',
503
+ 'docs',
504
+ 'documentation',
505
+ 'guide',
506
+ 'guides',
507
+ 'tutorial',
508
+ 'tutorials',
509
+ 'learn',
510
+ 'community',
511
+ 'agora',
512
+ 'forum',
513
+ 'forums',
514
+ 'chat',
515
+ 'chats',
516
+ 'message',
517
+ 'messages',
518
+ 'messaging',
519
+ 'dm',
520
+ 'search',
521
+ 'explore',
522
+ 'discover',
523
+ 'browse',
524
+ 'trending',
525
+ 'popular',
526
+ 'featured',
527
+ 'new',
528
+ 'latest',
529
+ 'top',
530
+ 'best',
531
+ 'recent',
532
+ 'random',
533
+ 'archive',
534
+ 'archives',
535
+ 'history',
536
+ 'activity',
537
+ 'event',
538
+ 'events',
539
+ 'calendar',
540
+ 'schedule',
541
+ 'task',
542
+ 'tasks',
543
+ 'project',
544
+ 'projects',
545
+ 'group',
546
+ 'groups',
547
+ 'organization',
548
+ 'organizations',
549
+ 'organisation',
550
+ 'organisations',
551
+ 'org',
552
+ 'orgs',
553
+ 'company',
554
+ 'companies',
555
+ 'enterprise',
556
+ 'business',
557
+ 'workspace',
558
+ 'workspaces',
559
+ 'site',
560
+ 'sites',
561
+ 'website',
562
+ 'portal',
563
+ 'hub',
564
+ 'center',
565
+ 'centre',
566
+ 'lab',
567
+ 'labs',
568
+ 'studio',
569
+ 'developer',
570
+ 'developers',
571
+ 'engineering',
572
+ 'design',
573
+ 'product',
574
+ 'products',
575
+ 'feature',
576
+ 'features',
577
+ 'roadmap',
578
+ 'changelog',
579
+ 'release',
580
+ 'releases',
581
+ 'version',
582
+ 'versions',
583
+ 'download',
584
+ 'downloads',
585
+ 'upload',
586
+ 'uploads',
587
+ 'file',
588
+ 'files',
589
+ 'image',
590
+ 'images',
591
+ 'img',
592
+ 'photo',
593
+ 'photos',
594
+ 'video',
595
+ 'videos',
596
+ 'audio',
597
+ 'music',
598
+ 'stream',
599
+ 'streams',
600
+ 'live',
601
+ 'play',
602
+ 'player',
603
+ 'game',
604
+ 'games',
605
+ 'install',
606
+ 'setup',
607
+ 'start',
608
+ 'getting-started',
609
+ 'connect',
610
+ 'share',
611
+ 'embed',
612
+ 'widget',
613
+ 'widgets',
614
+ 'plugin',
615
+ 'plugins',
616
+ 'extension',
617
+ 'extensions',
618
+ 'integration',
619
+ 'integrations',
620
+ 'marketplace',
621
+ 'tools',
622
+ 'resources',
623
+ 'library',
624
+ 'gallery',
625
+ 'stats',
626
+ 'statistics',
627
+ 'analytics',
628
+ 'insights',
629
+ 'export',
630
+ 'import',
631
+ 'sync',
632
+ 'print',
633
+ 'edit',
634
+ 'delete',
635
+ 'remove',
636
+ 'create',
637
+ 'add',
638
+ 'save',
639
+ 'cancel',
640
+ 'submit',
641
+ 'send',
642
+ 'error',
643
+ 'errors',
644
+ '404',
645
+ '500',
646
+ 'not-found',
647
+ 'notfound',
648
+ 'forbidden',
649
+ 'unauthorized',
650
+ // Us, and the software we run
651
+ 'tf',
652
+ 'offline',
653
+ 'the',
654
+ 'wtfalch',
655
+ 'tallis',
656
+ 'falch',
657
+ 'valet',
658
+ 'lokessmie',
659
+ 'proach',
660
+ 'zitadel',
661
+ 'coolify',
662
+ 'cloudflare',
663
+ 'resend',
664
+ // The same, in Norwegian
665
+ 'drift',
666
+ 'driftsansvarlig',
667
+ 'brukerstotte',
668
+ 'hjelp',
669
+ 'kundeservice',
670
+ 'kundestotte',
671
+ 'kontakt',
672
+ 'kontakt-oss',
673
+ 'kontaktoss',
674
+ 'post',
675
+ 'epost',
676
+ 'e-post',
677
+ 'nyheter',
678
+ 'nyhetsbrev',
679
+ 'personvern',
680
+ 'vilkar',
681
+ 'betaling',
682
+ 'faktura',
683
+ 'konto',
684
+ 'kontoer',
685
+ 'bruker',
686
+ 'brukere',
687
+ 'brukernavn',
688
+ 'innlogging',
689
+ 'logginn',
690
+ 'logg-inn',
691
+ 'loggut',
692
+ 'logg-ut',
693
+ 'registrer',
694
+ 'registrering',
695
+ 'passord',
696
+ 'sikkerhet',
697
+ 'ansatt',
698
+ 'ansatte',
699
+ 'ledelse',
700
+ 'leder',
701
+ 'eier',
702
+ 'sjef',
703
+ 'offisiell',
704
+ 'tjeneste',
705
+ 'tjenester',
706
+ 'hjem',
707
+ 'forside',
708
+ 'om',
709
+ 'om-oss',
710
+ 'omoss',
711
+ 'sok',
712
+ 'alle',
713
+ 'ingen',
714
+ 'gjest',
715
+ 'gjester',
716
+ 'anonym',
717
+ 'ukjent',
718
+ 'slettet',
719
+ 'sperret',
720
+ 'prove',
721
+ 'eksempel',
722
+ ];
723
+ export const PEOPLE = [
724
+ 'william',
725
+ 'kai',
726
+ 'alex',
727
+ 'walid',
728
+ 'odin',
729
+ 'audun',
730
+ 'peter',
731
+ 'anne',
732
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/auth",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Sign in against auth.wtfalch.dev: a server session for a Next.js app, and a browser client with silent single sign-on across subdomains.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,6 +30,10 @@
30
30
  "types": "./dist/namespaces.d.ts",
31
31
  "default": "./dist/namespaces.js"
32
32
  },
33
+ "./names": {
34
+ "types": "./dist/names.d.ts",
35
+ "default": "./dist/names.js"
36
+ },
33
37
  "./namespace-server": {
34
38
  "types": "./dist/namespace-server.d.ts",
35
39
  "default": "./dist/namespace-server.js"
@@ -55,7 +59,9 @@
55
59
  },
56
60
  "dependencies": {
57
61
  "jose": "^6.2.10",
58
- "openid-client": "6.8.7"
62
+ "obscenity": "^0.4.6",
63
+ "openid-client": "6.8.7",
64
+ "reserved-usernames": "^1.1.6"
59
65
  },
60
66
  "peerDependencies": {
61
67
  "next": ">=15.0.0"