@owlmeans/auth-email 0.0.0-stage → 0.1.19

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.
@@ -0,0 +1,95 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import Ajv from 'ajv'
3
+ import formatsPlugin from 'ajv-formats'
4
+ import {
5
+ EmailEmptyRequestSchema, EmailPasswordPinRequestSchema, EmailPasswordRemoveRequestSchema,
6
+ EmailPasswordSetRequestSchema, EmailPinRequestSchema, EmailSignInRequestSchema, EmailTechPinRequestSchema,
7
+ EmailPasswordPurpose,
8
+ } from '@owlmeans/auth-email'
9
+
10
+ const ajv = new Ajv()
11
+ formatsPlugin(ajv)
12
+
13
+ const pinRequest = ajv.compile(EmailPinRequestSchema)
14
+ const signIn = ajv.compile(EmailSignInRequestSchema)
15
+ const passwordPin = ajv.compile(EmailPasswordPinRequestSchema)
16
+ const passwordSet = ajv.compile(EmailPasswordSetRequestSchema)
17
+ const passwordRemove = ajv.compile(EmailPasswordRemoveRequestSchema)
18
+ const techPin = ajv.compile(EmailTechPinRequestSchema)
19
+ const empty = ajv.compile(EmailEmptyRequestSchema)
20
+
21
+ /** 22 base64url characters — 16 random bytes, the way the browser makes a device id. */
22
+ const DEVICE = 'AbCdEfGhIjKlMnOpQrSt_-'
23
+
24
+ describe('@owlmeans/auth-email — EmailPinRequestSchema', () => {
25
+ test('accepts an address with an optional locale and captcha token', () => {
26
+ expect(pinRequest({ email: 'person@example.com' })).toBe(true)
27
+ expect(pinRequest({ email: 'person@example.com', locale: 'pl', captcha: 'token' })).toBe(true)
28
+ })
29
+
30
+ test('refuses a malformed or overlong address', () => {
31
+ expect(pinRequest({ email: 'not-an-address' })).toBe(false)
32
+ expect(pinRequest({ email: `${'a'.repeat(250)}@example.com` })).toBe(false)
33
+ expect(pinRequest({})).toBe(false)
34
+ })
35
+
36
+ test('refuses fields it does not declare, an organization above all', () => {
37
+ expect(pinRequest({ email: 'person@example.com', entitySlug: 'acme' })).toBe(false)
38
+ expect(pinRequest({ email: 'person@example.com', source: 'web' })).toBe(false)
39
+ })
40
+ })
41
+
42
+ describe('@owlmeans/auth-email — EmailSignInRequestSchema', () => {
43
+ test('accepts the PIN mode and the password mode in one schema', () => {
44
+ expect(signIn({ email: 'person@example.com', device: DEVICE, challenge: 'signed', pin: '012345' })).toBe(true)
45
+ expect(signIn({ email: 'person@example.com', device: DEVICE, password: 'secret', captcha: 't', locale: 'de' })).toBe(true)
46
+ })
47
+
48
+ test('refuses a PIN that is not six digits', () => {
49
+ for (const pin of ['12345', '1234567', 'abcdef', '12 345']) {
50
+ expect(signIn({ email: 'person@example.com', device: DEVICE, challenge: 'signed', pin })).toBe(false)
51
+ }
52
+ })
53
+
54
+ test('requires a well-formed device id', () => {
55
+ expect(signIn({ email: 'person@example.com', challenge: 'signed', pin: '012345' })).toBe(false)
56
+ expect(signIn({ email: 'person@example.com', device: 'short', password: 'secret' })).toBe(false)
57
+ expect(signIn({ email: 'person@example.com', device: `${DEVICE}!`, password: 'secret' })).toBe(false)
58
+ })
59
+
60
+ test('refuses extra fields and an overlong password', () => {
61
+ expect(signIn({ email: 'person@example.com', device: DEVICE, password: 'secret', remember: true })).toBe(false)
62
+ expect(signIn({ email: 'person@example.com', device: DEVICE, password: 'x'.repeat(1025) })).toBe(false)
63
+ })
64
+ })
65
+
66
+ describe('@owlmeans/auth-email — password route schemas', () => {
67
+ test('a confirmation PIN names one of the three purposes', () => {
68
+ for (const purpose of Object.values(EmailPasswordPurpose)) {
69
+ expect(passwordPin({ purpose, locale: 'uk' })).toBe(true)
70
+ }
71
+ expect(passwordPin({ purpose: 'reset' })).toBe(false)
72
+ expect(passwordPin({})).toBe(false)
73
+ })
74
+
75
+ test('set takes an offer or a challenge + PIN, and an optional device to keep remembered', () => {
76
+ expect(passwordSet({ password: 'long-enough', offer: 'ticket' })).toBe(true)
77
+ expect(passwordSet({ password: 'long-enough', challenge: 'signed', pin: '123456', device: DEVICE })).toBe(true)
78
+ expect(passwordSet({ password: 'long-enough', offer: 'ticket', device: 'bad device' })).toBe(false)
79
+ expect(passwordSet({ offer: 'ticket' })).toBe(false)
80
+ })
81
+
82
+ test('remove needs a challenge + PIN and takes an optional device', () => {
83
+ expect(passwordRemove({ challenge: 'signed', pin: '123456' })).toBe(true)
84
+ expect(passwordRemove({ challenge: 'signed', pin: '123456', device: DEVICE })).toBe(true)
85
+ expect(passwordRemove({ challenge: 'signed', pin: '123456', device: 'x' })).toBe(false)
86
+ expect(passwordRemove({ challenge: 'signed' })).toBe(false)
87
+ })
88
+
89
+ test('decline and the waiver take an empty body; tech/pin a challenge + PIN', () => {
90
+ expect(empty({})).toBe(true)
91
+ expect(empty({ entitySlug: 'acme' })).toBe(false)
92
+ expect(techPin({ challenge: 'signed', pin: '123456' })).toBe(true)
93
+ expect(techPin({ challenge: 'signed', pin: '12345' })).toBe(false)
94
+ })
95
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": [
3
+ "@owlmeans/dep-config/tsconfig.base.json"
4
+ ],
5
+ "compilerOptions": {
6
+ "rootDir": "./src/",
7
+ "outDir": "./build/"
8
+ },
9
+ "exclude": [
10
+ "./dist/**/*",
11
+ "./build/**/*",
12
+ "./tests/**/*",
13
+ "./*.ts"
14
+ ]
15
+ }