@owlmeans/server-auth-otp 0.1.18-rc.2 → 0.1.18-rc.20
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/README.md +2 -2
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/server-auth-otp/SKILL.md +83 -18
- package/build/plugin.d.ts.map +1 -1
- package/build/plugin.js +7 -4
- package/build/plugin.js.map +1 -1
- package/package.json +13 -13
- package/src/plugin.ts +7 -4
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Email OTP `AuthPlugin` for OwlMeans servers — passwordless login via time-limi
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
bun add @owlmeans/server-auth-otp
|
|
15
|
+
bun add @owlmeans/server-auth-otp@^0.1.18-rc.17
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
@@ -47,7 +47,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
47
47
|
your project's skill store (`.agents/skills/`):
|
|
48
48
|
|
|
49
49
|
```sh
|
|
50
|
-
npx @owlmeans/agent-skills
|
|
50
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.14
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/server-auth-otp",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.20",
|
|
5
|
+
"generatedAt": "2026-09-10T23:12:07.510Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,32 +8,51 @@ metadata:
|
|
|
8
8
|
|
|
9
9
|
# Using `@owlmeans/server-auth-otp`
|
|
10
10
|
|
|
11
|
+
**Install:** `"@owlmeans/server-auth-otp": "^0.1.18-rc.20"` in `dependencies`
|
|
12
|
+
|
|
11
13
|
Email OTP authentication plugin for the OwlMeans auth-manager plugin system. Relies on `@owlmeans/auth-otp` for the OTP service interface, a Redis resource for code storage, and a `MailerService` to send codes.
|
|
12
14
|
|
|
15
|
+
## `@owlmeans/auth-otp` — the contracts
|
|
16
|
+
|
|
17
|
+
`@owlmeans/auth-otp` has no skill of its own because it has no behaviour: it is the contract half of
|
|
18
|
+
this pair, and everything it declares is implemented here. It carries the `OtpService` interface —
|
|
19
|
+
`issueChallenge(email)`, which generates a code, persists it with a TTL and mails it, and
|
|
20
|
+
`verifyChallenge(email, code)`, which verifies and consumes it — plus the names both halves agree
|
|
21
|
+
on: `OTP_SERVICE`, `OTP_AUTH_TYPE` (`'email-otp'`), `OTP_RESOURCE`, `OTP_TTL_SECONDS` (600) and
|
|
22
|
+
`OTP_CODE_LENGTH` (6). Nothing else; no Redis, no mailer, no plugin.
|
|
23
|
+
|
|
24
|
+
Depend on it from a shared package that must name the auth type or type the service, and depend on
|
|
25
|
+
`@owlmeans/server-auth-otp` only where the server wires itself up — the same producer/consumer split
|
|
26
|
+
every other contracts-and-driver pair in the framework uses. Add a constant or a method signature to
|
|
27
|
+
the contracts package, never to the implementation.
|
|
28
|
+
|
|
13
29
|
## Public API surface
|
|
14
30
|
|
|
15
31
|
| Symbol | Kind | Purpose |
|
|
16
32
|
|--------|------|---------|
|
|
17
33
|
| `makeOtpService(alias?)` | fn | Service factory — stores/verifies OTP codes |
|
|
18
34
|
| `appendOtpPlugin(context)` | fn | Registers the OTP `AuthPlugin` into the server-auth plugin registry |
|
|
35
|
+
| `OTP_SERVICE` | const | `'auth-otp-service'` — the service alias `makeOtpService` registers under |
|
|
19
36
|
| `OTP_AUTH_TYPE` | const | `'email-otp'` — the auth type string to pass in `init` requests |
|
|
20
37
|
| `OTP_RESOURCE` | const | Redis resource alias for code storage |
|
|
21
38
|
| `OTP_TTL_SECONDS` | const | Code TTL (600 s = 10 min) |
|
|
22
39
|
| `OTP_CODE_LENGTH` | const | 6 |
|
|
40
|
+
| `SERVER_AUTH_OTP` | const | `'server-auth-otp'` — this package's own alias |
|
|
41
|
+
| `OtpConfig`, `OtpContext` | type | The `cfg.otp` overrides below, as a server config/context |
|
|
23
42
|
|
|
24
43
|
## Registration requirements
|
|
25
44
|
|
|
26
45
|
```ts
|
|
27
46
|
import { makeOtpService, appendOtpPlugin, OTP_RESOURCE } from '@owlmeans/server-auth-otp'
|
|
28
47
|
import { makeRedisResource } from '@owlmeans/redis-resource'
|
|
29
|
-
import {
|
|
48
|
+
import { makeDefaultConsoleMailerService, MAILER_SERVICE } from '@owlmeans/mailer'
|
|
30
49
|
import { makeMailgunMailerService } from '@owlmeans/server-mailer-mailgun'
|
|
31
50
|
|
|
32
51
|
// 1. Register the Redis code-cache resource.
|
|
33
52
|
context.registerResource(makeRedisResource(OTP_RESOURCE))
|
|
34
53
|
|
|
35
|
-
// 2. Register a MailerService (console for dev/tests, Mailgun for prod).
|
|
36
|
-
context.registerService(
|
|
54
|
+
// 2. Register a MailerService under MAILER_SERVICE (console for dev/tests, Mailgun for prod).
|
|
55
|
+
context.registerService(makeDefaultConsoleMailerService())
|
|
37
56
|
// or:
|
|
38
57
|
context.registerService(makeMailgunMailerService(MAILER_SERVICE))
|
|
39
58
|
|
|
@@ -48,13 +67,22 @@ appendOtpPlugin(context)
|
|
|
48
67
|
|
|
49
68
|
**Init** — client sends `{ type: 'email-otp', userId: 'user@email.com' }`:
|
|
50
69
|
- OTP service generates a 6-digit code, stores it in Redis with 10 min TTL, emails it.
|
|
51
|
-
- Returns `{ challenge: email }` in a signed envelope
|
|
70
|
+
- Returns `{ challenge: '<email>::<nonce>' }` in a signed envelope — see Gotchas below for why the
|
|
71
|
+
nonce is required, not optional.
|
|
52
72
|
|
|
53
|
-
**Authenticate** — client sends `{ challenge: <signed-envelope>, userId: email, credential: '123456',
|
|
54
|
-
- Envelope is opened → email is extracted
|
|
73
|
+
**Authenticate** — client sends `{ challenge: <signed-envelope>, userId: email, credential: '123456', type: 'email-otp', role: AuthRole.User, scopes: [ALL_SCOPES] }`:
|
|
74
|
+
- Envelope is opened → `email::nonce` is extracted, split on `::` to recover the email (the nonce
|
|
75
|
+
itself is discarded — it only exists to make the challenge unique, see Gotchas).
|
|
55
76
|
- OTP service verifies the code (throws `AuthenFailed` if wrong or expired), then deletes it.
|
|
56
|
-
- `IdentityLinkingService` finds
|
|
57
|
-
|
|
77
|
+
- `IdentityLinkingService` finds the linked profile, or links this email to the person's platform
|
|
78
|
+
identity — registering an account, a profile and an organization entity only when the address is
|
|
79
|
+
new to the platform.
|
|
80
|
+
- Copies `userId`, `profileId`, `entitySlug`, `role` and `scopes` from the resolved payload onto the
|
|
81
|
+
credential, sets `credential.type = AuthenticationType.OneTimeToken`, and returns the signed auth
|
|
82
|
+
token.
|
|
83
|
+
- `type`, `role`, `scopes` are required by the shared `AuthCredentialsSchema` (spread from
|
|
84
|
+
`AuthPayloadSchema.required`) even though the OTP plugin overwrites `role`/`scopes`/`type` on
|
|
85
|
+
success — a caller that omits them never reaches the plugin at all (see Gotchas).
|
|
58
86
|
|
|
59
87
|
## Config overrides (optional)
|
|
60
88
|
|
|
@@ -72,14 +100,51 @@ cfg.otp = {
|
|
|
72
100
|
|
|
73
101
|
- Always register the Redis resource AND the mailer service BEFORE the OTP service.
|
|
74
102
|
- Call `appendOtpPlugin(context)` once per context — it adds to the shared plugin registry singleton.
|
|
75
|
-
-
|
|
103
|
+
- `credential.entitySlug` on the authenticate request **selects nothing**. The plugin copies it into
|
|
104
|
+
the linking details as `clientId` (defaulting to `'default'`) and `entityId`, and
|
|
105
|
+
`@owlmeans/server-auth-identity` reads neither: `getLinkedProfile` keys on the external login key
|
|
106
|
+
built from the auth type, the `'email'` service and the address, and `linkProfile` either reuses
|
|
107
|
+
the person's existing platform profile — matched on the account name — or mints a brand-new
|
|
108
|
+
organization entity. Whatever the caller sent is then overwritten with the linked profile's own
|
|
109
|
+
slug before the envelope is signed, so the address alone decides which identity and which
|
|
110
|
+
organization the token names.
|
|
76
111
|
- Errors from this plugin are `AuthenFailed` (from `@owlmeans/auth`) — callers catch that, not raw `Error`.
|
|
77
|
-
- For tests,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
-
|
|
85
|
-
|
|
112
|
+
- For tests, register `makeDefaultConsoleMailerService()` and read `svc.captured[n].text` to extract
|
|
113
|
+
the code. Bare `makeConsoleMailerService()` registers under `CONSOLE_MAILER` (`'console-mailer'`),
|
|
114
|
+
which is not the alias `makeOtpService` resolves — it looks up `cfg.otp?.mailerAlias ?? MAILER_SERVICE`.
|
|
115
|
+
|
|
116
|
+
## Gotchas
|
|
117
|
+
|
|
118
|
+
- **The challenge must never be just the plaintext email.** The auth manager's anti-replay guard
|
|
119
|
+
(`AUTH_CACHE` in `@owlmeans/server-auth`) burns the *decoded* challenge into a create-once record
|
|
120
|
+
before the plugin's own credential check runs. `AUTH_CACHE` defaults to a **static, in-memory**
|
|
121
|
+
resource — the manager's context appends it, and nothing here upgrades it to Redis; the OTP codes
|
|
122
|
+
are the only thing this package keeps in Redis. If `init()` returned the bare email, that decoded
|
|
123
|
+
value would be identical across every independent login attempt for the same address, so a second
|
|
124
|
+
legitimate login within the cache TTL (`AUTHEN_TIMEFRAME`, 15 min) — right code or wrong —
|
|
125
|
+
collides with the still-cached prior attempt and throws `AuthenFailed('challenge')` (the
|
|
126
|
+
resource's `RecordExists` underneath), not an OTP-specific error. Fix: `init()` appends a fresh
|
|
127
|
+
`createIdOfLength(16, IdStyle.Base58)` nonce (`'<email>::<nonce>'`); `authenticate()` splits it
|
|
128
|
+
back apart. Never revert to a bare-email challenge.
|
|
129
|
+
- **`AuthCredentialsSchema.credential` has a `minLength` floor** (from `@owlmeans/auth`) sized for
|
|
130
|
+
long tokens/signatures from other plugins (Ed25519 signature, OAuth code). A 6-digit OTP code is
|
|
131
|
+
legitimately shorter — the floor is `minLength: 1` and must stay low enough to admit it, or every
|
|
132
|
+
authenticate call 400s before the plugin ever runs.
|
|
133
|
+
- **`scopes`/`role`/`type` are schema-required on the authenticate body**, spread from
|
|
134
|
+
`AuthPayloadSchema.required` into `AuthCredentialsSchema` — even though this plugin overwrites
|
|
135
|
+
all three on success. A caller built without going through `@owlmeans/client-auth`'s
|
|
136
|
+
`AuthenticationControl` (which fills them automatically) must set them explicitly or the request
|
|
137
|
+
fails Fastify schema validation (`FST_ERR_VALIDATION`) before reaching this plugin at all.
|
|
138
|
+
- **The resulting auth token can exceed 1024 characters** — it wraps the full `AuthCredentials`
|
|
139
|
+
envelope, including the original allowance challenge, base64-encoded. A consumer route that
|
|
140
|
+
accepts this token (e.g. an OIDC `PROVIDER_INTERACTION` finalizer) must size its own `token`
|
|
141
|
+
field schema accordingly; the generic `AuthTokenSchema` (`maxLength: 1024`) is too small.
|
|
142
|
+
|
|
143
|
+
## Related
|
|
144
|
+
|
|
145
|
+
- `@owlmeans/auth-otp` — the `OtpService` interface and the shared constants
|
|
146
|
+
- `@owlmeans/mailer` — `MailerService`, the console transport
|
|
147
|
+
- `@owlmeans/server-mailer-mailgun` — the production Mailgun transport
|
|
148
|
+
- `server-auth` skill — the auth-manager plugin system this plugin registers into
|
|
149
|
+
- `server-auth-identity` skill — how the linked profile and its organization entity are stored
|
|
150
|
+
- `auth-protocol` skill — error hierarchy and identity read rules
|
package/build/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAsEvD,sGAAsG;AACtG,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,KAAG,CAG1F,CAAA"}
|
package/build/plugin.js
CHANGED
|
@@ -35,13 +35,16 @@ const otpPlugin = (context) => ({
|
|
|
35
35
|
await otp.verifyChallenge(email, code);
|
|
36
36
|
const identityAlias = context.cfg.otp?.identityAlias ?? AUTH_IDENTITY_LINKING;
|
|
37
37
|
const identity = context.service(identityAlias);
|
|
38
|
-
|
|
38
|
+
// What arrives on the credential is the organization SLUG the user is logging into — it is
|
|
39
|
+
// what a person can be asked to type. The linked profile below answers with the canonical
|
|
40
|
+
// slug, which is what the token ends up carrying.
|
|
41
|
+
const entitySlug = credential.entitySlug;
|
|
39
42
|
const details = {
|
|
40
43
|
type: OTP_AUTH_TYPE,
|
|
41
44
|
service: 'email',
|
|
42
|
-
clientId:
|
|
45
|
+
clientId: entitySlug ?? 'default',
|
|
43
46
|
userId: email,
|
|
44
|
-
entityId,
|
|
47
|
+
entityId: entitySlug,
|
|
45
48
|
};
|
|
46
49
|
let payload = await identity.getLinkedProfile(details);
|
|
47
50
|
if (payload == null) {
|
|
@@ -51,7 +54,7 @@ const otpPlugin = (context) => ({
|
|
|
51
54
|
credential.type = AuthenticationType.OneTimeToken;
|
|
52
55
|
credential.userId = payload.userId;
|
|
53
56
|
credential.profileId = payload.profileId;
|
|
54
|
-
credential.
|
|
57
|
+
credential.entitySlug = payload.entitySlug;
|
|
55
58
|
credential.role = payload.role ?? AuthRole.User;
|
|
56
59
|
credential.scopes = payload.scopes ?? [ALL_SCOPES];
|
|
57
60
|
return { token: '' };
|
package/build/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAEvF,OAAO,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAA;AAEtE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAE/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAG/D,0FAA0F;AAC1F,MAAM,mBAAmB,GAAG,IAAI,CAAA;AAEhC,gFAAgF;AAChF,MAAM,SAAS,GAAG,CAA+C,OAAU,EAAc,EAAE,CAAC,CAAC;IAC3F,IAAI,EAAE,aAAa;IAEnB,IAAI,EAAE,KAAK,EAAC,OAAO,EAAC,EAAE;QACpB,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5D,MAAM,IAAI,YAAY,CAAC,WAAW,CAAC,CAAA;QACrC,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QACjD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAa,WAAW,CAAC,CAAA;QACpD,MAAM,GAAG,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;QAC/B,0FAA0F;QAC1F,yFAAyF;QACzF,wFAAwF;QACxF,MAAM,KAAK,GAAG,gBAAgB,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QAClD,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,GAAG,mBAAmB,GAAG,KAAK,EAAE,EAAE,CAAA;IAChE,CAAC;IAED,YAAY,EAAE,KAAK,EAAC,UAAU,EAAC,EAAE;QAC/B,6FAA6F;QAC7F,MAAM,KAAK,GAAG,UAAU,CAAC,SAAS,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAA;QACjE,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAA;QAElC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,YAAY,CAAC,WAAW,CAAC,CAAA;QACrC,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,YAAY,CAAC,UAAU,CAAC,CAAA;QACpC,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAa,WAAW,CAAC,CAAA;QACpD,MAAM,GAAG,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAEtC,MAAM,aAAa,GAAI,OAAO,CAAC,GAAW,CAAC,GAAG,EAAE,aAAa,IAAI,qBAAqB,CAAA;QACtF,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAyB,aAAa,CAAC,CAAA;QAEvE,MAAM,
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/server-auth-otp",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.20",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -22,21 +22,21 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
26
|
-
"@owlmeans/auth-otp": "^0.1.18-rc.
|
|
27
|
-
"@owlmeans/basic-ids": "^0.1.18-rc.
|
|
28
|
-
"@owlmeans/context": "^0.1.18-rc.
|
|
29
|
-
"@owlmeans/mailer": "^0.1.18-rc.
|
|
30
|
-
"@owlmeans/oidc": "^0.1.18-rc.
|
|
31
|
-
"@owlmeans/redis-resource": "^0.1.18-rc.
|
|
32
|
-
"@owlmeans/resource": "^0.1.18-rc.
|
|
33
|
-
"@owlmeans/server-auth": "^0.1.18-rc.
|
|
34
|
-
"@owlmeans/server-auth-identity": "^0.1.18-rc.
|
|
35
|
-
"@owlmeans/server-context": "^0.1.18-rc.
|
|
25
|
+
"@owlmeans/auth": "^0.1.18-rc.11",
|
|
26
|
+
"@owlmeans/auth-otp": "^0.1.18-rc.10",
|
|
27
|
+
"@owlmeans/basic-ids": "^0.1.18-rc.11",
|
|
28
|
+
"@owlmeans/context": "^0.1.18-rc.10",
|
|
29
|
+
"@owlmeans/mailer": "^0.1.18-rc.10",
|
|
30
|
+
"@owlmeans/oidc": "^0.1.18-rc.15",
|
|
31
|
+
"@owlmeans/redis-resource": "^0.1.18-rc.14",
|
|
32
|
+
"@owlmeans/resource": "^0.1.18-rc.11",
|
|
33
|
+
"@owlmeans/server-auth": "^0.1.18-rc.20",
|
|
34
|
+
"@owlmeans/server-auth-identity": "^0.1.18-rc.15",
|
|
35
|
+
"@owlmeans/server-context": "^0.1.18-rc.14"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@owlmeans/dep-config": "workspace:*",
|
|
39
|
-
"@types/bun": "^1.
|
|
39
|
+
"@types/bun": "^1.4.0",
|
|
40
40
|
"nodemon": "^3.1.14",
|
|
41
41
|
"typescript": "^7.0.2"
|
|
42
42
|
},
|
package/src/plugin.ts
CHANGED
|
@@ -47,13 +47,16 @@ const otpPlugin = <C extends OtpConfig, T extends OtpContext<C>>(context: T): Au
|
|
|
47
47
|
const identityAlias = (context.cfg as any).otp?.identityAlias ?? AUTH_IDENTITY_LINKING
|
|
48
48
|
const identity = context.service<IdentityLinkingService>(identityAlias)
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
// What arrives on the credential is the organization SLUG the user is logging into — it is
|
|
51
|
+
// what a person can be asked to type. The linked profile below answers with the canonical
|
|
52
|
+
// slug, which is what the token ends up carrying.
|
|
53
|
+
const entitySlug = credential.entitySlug
|
|
51
54
|
const details = {
|
|
52
55
|
type: OTP_AUTH_TYPE,
|
|
53
56
|
service: 'email',
|
|
54
|
-
clientId:
|
|
57
|
+
clientId: entitySlug ?? 'default',
|
|
55
58
|
userId: email,
|
|
56
|
-
entityId,
|
|
59
|
+
entityId: entitySlug,
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
let payload = await identity.getLinkedProfile(details)
|
|
@@ -65,7 +68,7 @@ const otpPlugin = <C extends OtpConfig, T extends OtpContext<C>>(context: T): Au
|
|
|
65
68
|
credential.type = AuthenticationType.OneTimeToken
|
|
66
69
|
credential.userId = payload.userId
|
|
67
70
|
credential.profileId = payload.profileId
|
|
68
|
-
credential.
|
|
71
|
+
credential.entitySlug = payload.entitySlug
|
|
69
72
|
credential.role = payload.role ?? AuthRole.User
|
|
70
73
|
credential.scopes = payload.scopes ?? [ALL_SCOPES]
|
|
71
74
|
|