@owlmeans/auth-otp 0.1.18-rc.34 → 0.1.18-rc.35

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.
Files changed (2) hide show
  1. package/README.md +100 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # @owlmeans/auth-otp
2
+
3
+ The shared contract of OwlMeans passwordless email login: the `OtpService` interface and the
4
+ constants both sides of an email-OTP flow agree on — the service alias, the auth type a client
5
+ selects, the code-store alias, the code length, its lifetime and the failed-attempt limit. A shared
6
+ (common) package depends on it when it must name the `'email-otp'` auth type or type the service. It
7
+ contains no runtime code beyond those constants: it generates, stores and mails nothing. A server
8
+ wires the actual flow with `@owlmeans/server-auth-otp`, which implements `OtpService`, the challenge
9
+ stores, throttles and the auth plugin.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ bun add @owlmeans/auth-otp@^0.1.18-rc.35
15
+ ```
16
+
17
+ ## Concepts
18
+
19
+ - **Contracts half** — this package is the producer/consumer contract; `@owlmeans/server-auth-otp`
20
+ is the implementation. A new constant or method signature is added here, never only in the
21
+ implementation.
22
+ - **Challenge** — one issuance of a code. `issueChallenge(email)` generates a code, persists it with
23
+ a TTL, mails it and returns an **opaque issuance id** that contains neither the email nor the code.
24
+ - **Verification** — `verifyChallenge(email, issuanceId, code)` checks and atomically consumes that
25
+ exact issuance; it resolves on success and throws otherwise.
26
+ - **Attempt limit** — a challenge is invalidated on its `OTP_MAX_FAILED_ATTEMPTS`-th (fifth) failed
27
+ guess.
28
+
29
+ ## Usage
30
+
31
+ ### Select the email-OTP auth type on a client
32
+
33
+ A client names the plugin by its auth type when it starts an authentication; the constant keeps the
34
+ string identical on both sides.
35
+
36
+ ```typescript
37
+ import { OTP_AUTH_TYPE, OTP_CODE_LENGTH } from '@owlmeans/auth-otp'
38
+
39
+ const init = { type: OTP_AUTH_TYPE, userId: email } // 'email-otp'
40
+
41
+ const isComplete = (code: string) => code.length === OTP_CODE_LENGTH && /^\d+$/.test(code)
42
+ ```
43
+
44
+ ### Type the service in shared code
45
+
46
+ ```typescript
47
+ import type { BasicContext, BasicConfig } from '@owlmeans/context'
48
+ import { OTP_SERVICE } from '@owlmeans/auth-otp'
49
+ import type { OtpService } from '@owlmeans/auth-otp'
50
+
51
+ export const otpService = (ctx: BasicContext<BasicConfig>) => ctx.service<OtpService>(OTP_SERVICE)
52
+
53
+ // server side, after @owlmeans/server-auth-otp registered the implementation
54
+ const issuanceId = await otpService(ctx).issueChallenge('user@example.com')
55
+ await otpService(ctx).verifyChallenge('user@example.com', issuanceId, '123456')
56
+ ```
57
+
58
+ ## API
59
+
60
+ ### Types
61
+
62
+ | Symbol | Kind | Purpose |
63
+ |---|---|---|
64
+ | `OtpService` | interface | extends `InitializedService` (`@owlmeans/context`) |
65
+ | `OtpService.issueChallenge(email)` | method | `→ Promise<string>` — generate, persist with TTL, email; returns the opaque issuance id |
66
+ | `OtpService.verifyChallenge(email, issuanceId, code)` | method | `→ Promise<void>` — verify and atomically consume one issuance |
67
+
68
+ ### Constants
69
+
70
+ | Symbol | Value | Purpose |
71
+ |---|---|---|
72
+ | `OTP_SERVICE` | `'auth-otp-service'` | service alias the implementation registers under |
73
+ | `OTP_AUTH_TYPE` | `'email-otp'` | auth plugin type string a client sends |
74
+ | `OTP_RESOURCE` | `'auth-otp-cache'` | resource alias for pending codes |
75
+ | `OTP_TTL_SECONDS` | `600` | code lifetime (10 minutes) |
76
+ | `OTP_CODE_LENGTH` | `6` | numeric digits per code |
77
+ | `OTP_MAX_FAILED_ATTEMPTS` | `5` | a challenge is invalidated on its fifth failed guess |
78
+
79
+ The barrel exports only these string and number constants plus the type; the package tests pin every
80
+ value, because changing one changes what server and clients agree on.
81
+
82
+ ## Common pitfalls
83
+
84
+ - Do not import this package expecting an implementation — `makeOtpService`, `appendOtpPlugin` and
85
+ the stores live in `@owlmeans/server-auth-otp`.
86
+ - Never encode the email or the code into the issuance id; it stays opaque and owned by the OTP
87
+ store, which counts attempts and consumes it.
88
+ - Do not hard-code `'email-otp'` or `6` in clients — import `OTP_AUTH_TYPE` and `OTP_CODE_LENGTH`.
89
+ - Depend on `@owlmeans/server-auth-otp` only where the server wires itself up; shared packages depend
90
+ on this one.
91
+
92
+ The `server-auth-otp` skill covers this package together with its implementation: registration
93
+ order, the auth flow, config overrides and throttling.
94
+
95
+ ## Related packages
96
+
97
+ - [`@owlmeans/server-auth-otp`](../server-auth-otp) — the `OtpService` implementation, challenge stores, throttles and the `AuthPlugin`
98
+ - [`@owlmeans/context`](../context) — `InitializedService`, the base of `OtpService`
99
+ - [`@owlmeans/auth`](../auth) — the auth payload and credential types the OTP flow produces
100
+ - [`@owlmeans/mailer`](../mailer) — the `MailerService` that delivers the codes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/auth-otp",
3
- "version": "0.1.18-rc.34",
3
+ "version": "0.1.18-rc.35",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {