@nxgt/janus 0.11.0 → 0.13.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/README.md +109 -15
- package/dist/auth/config/janus-config.d.ts +2 -0
- package/dist/auth/config/janus-config.d.ts.map +1 -1
- package/dist/auth/config/resolved-config.d.ts +1 -0
- package/dist/auth/config/resolved-config.d.ts.map +1 -1
- package/dist/auth/email-flows/reset-password.d.ts.map +1 -1
- package/dist/auth/index.d.ts +2 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/password-written.d.ts +21 -5
- package/dist/auth/password-written.d.ts.map +1 -1
- package/dist/auth/port/assert-stores.d.ts.map +1 -1
- package/dist/auth/port/memory/sessions.d.ts.map +1 -1
- package/dist/auth/port/types/sessions.d.ts +16 -1
- package/dist/auth/port/types/sessions.d.ts.map +1 -1
- package/dist/auth/port/types/tokens.d.ts +10 -4
- package/dist/auth/port/types/tokens.d.ts.map +1 -1
- package/dist/auth/second-factor/{regenerate-attempts.d.ts → app-code-attempts.d.ts} +13 -10
- package/dist/auth/second-factor/app-code-attempts.d.ts.map +1 -0
- package/dist/auth/sessions/authenticate.d.ts.map +1 -1
- package/dist/auth/sessions/fresh.d.ts +26 -0
- package/dist/auth/sessions/fresh.d.ts.map +1 -0
- package/dist/auth/sessions/standing.d.ts +20 -0
- package/dist/auth/sessions/standing.d.ts.map +1 -0
- package/dist/auth/step-up/codes.d.ts +29 -0
- package/dist/auth/step-up/codes.d.ts.map +1 -0
- package/dist/auth/step-up/flows.d.ts +19 -0
- package/dist/auth/step-up/flows.d.ts.map +1 -0
- package/dist/auth/step-up/open.d.ts +23 -0
- package/dist/auth/step-up/open.d.ts.map +1 -0
- package/dist/auth/step-up/request.d.ts +12 -0
- package/dist/auth/step-up/request.d.ts.map +1 -0
- package/dist/auth/types/email-flows.d.ts +6 -3
- package/dist/auth/types/email-flows.d.ts.map +1 -1
- package/dist/auth/types/index.d.ts +1 -0
- package/dist/auth/types/index.d.ts.map +1 -1
- package/dist/auth/types/janus.d.ts +2 -1
- package/dist/auth/types/janus.d.ts.map +1 -1
- package/dist/auth/types/step-up.d.ts +79 -0
- package/dist/auth/types/step-up.d.ts.map +1 -0
- package/dist/auth/users/any-type-api.d.ts +2 -2
- package/dist/auth/users/any-type-api.d.ts.map +1 -1
- package/dist/auth/users/type-api.d.ts +1 -1
- package/dist/auth/users/type-api.d.ts.map +1 -1
- package/dist/chunks/{index-5e2fw7he.js → index-j2gd8fry.js} +12 -3
- package/dist/chunks/{index-5e2fw7he.js.map → index-j2gd8fry.js.map} +4 -4
- package/dist/chunks/{index-5tm04t5p.js → index-jp2kka6d.js} +2 -2
- package/dist/chunks/{index-6f6wja93.js → index-km7ffctx.js} +2 -2
- package/dist/chunks/{index-4m49cye4.js → index-rd2rddvv.js} +15 -12
- package/dist/chunks/{index-4m49cye4.js.map → index-rd2rddvv.js.map} +3 -3
- package/dist/conformance/cases/outage.d.ts.map +1 -1
- package/dist/conformance/cases/sessions/index.d.ts.map +1 -1
- package/dist/conformance/cases/sessions/reauthentication.d.ts +4 -0
- package/dist/conformance/cases/sessions/reauthentication.d.ts.map +1 -0
- package/dist/conformance/cases/tokens/index.d.ts.map +1 -1
- package/dist/conformance/cases/tokens/kinds.d.ts +4 -0
- package/dist/conformance/cases/tokens/kinds.d.ts.map +1 -0
- package/dist/conformance/index.js +147 -60
- package/dist/conformance/index.js.map +8 -6
- package/dist/errors/janus-error/codes.d.ts +7 -0
- package/dist/errors/janus-error/codes.d.ts.map +1 -1
- package/dist/errors/janus-error/index.d.ts +1 -1
- package/dist/errors/janus-error/index.d.ts.map +1 -1
- package/dist/errors/janus-error/refusals.d.ts +8 -0
- package/dist/errors/janus-error/refusals.d.ts.map +1 -1
- package/dist/errors/janus-error/status.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +185 -23
- package/dist/index.js.map +20 -14
- package/dist/permissions/index.js +3 -3
- package/docs/README.md +2 -1
- package/docs/guide/adapters.md +41 -6
- package/docs/guide/email-flows.md +40 -1
- package/docs/guide/errors.md +1 -0
- package/docs/guide/events.md +1 -1
- package/docs/guide/passwords.md +22 -0
- package/docs/guide/second-factor.md +22 -16
- package/docs/guide/sessions.md +7 -0
- package/docs/guide/step-up.md +178 -0
- package/docs/guide/users.md +8 -3
- package/docs/guide/vocabulary.md +3 -2
- package/docs/roadmap.md +19 -24
- package/docs/troubleshooting.md +193 -8
- package/package.json +1 -1
- package/dist/auth/second-factor/regenerate-attempts.d.ts.map +0 -1
- /package/dist/chunks/{index-5tm04t5p.js.map → index-jp2kka6d.js.map} +0 -0
- /package/dist/chunks/{index-6f6wja93.js.map → index-km7ffctx.js.map} +0 -0
package/README.md
CHANGED
|
@@ -165,7 +165,7 @@ async function signIn(email: string, password: string): Promise<Response> {
|
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
`JanusError` is the base of everything thrown at call time. It extends `Error`,
|
|
168
|
-
so no consumer has to order their `catch` blocks. `code` is a union of
|
|
168
|
+
so no consumer has to order their `catch` blocks. `code` is a union of twenty
|
|
169
169
|
string literals, so a `switch` over it is exhaustive and adding a code breaks the
|
|
170
170
|
compilation of callers that exhaust it. `statusOf(code)` answers the status
|
|
171
171
|
below, as `JanusErrorStatus` — a union of the eight literals, which a
|
|
@@ -190,6 +190,7 @@ if (error instanceof JanusError) {
|
|
|
190
190
|
| `CODE_INVALID` | 401 — a one-time code that does not match: a second factor's, or one sent by e-mail; `attemptsLeft` from either `confirm` belongs in the body |
|
|
191
191
|
| `SECOND_FACTOR_NOT_ENROLLED`, `SECOND_FACTOR_ACTIVE` | 409 — the factor is not in the state the call needs |
|
|
192
192
|
| `USER_INACTIVE` | 403 |
|
|
193
|
+
| `STEP_UP_REQUIRED` | 403 — the session proved who it is too long ago for this action: ask for a step-up, then send the request again |
|
|
193
194
|
| `TOKEN_UNKNOWN`, `TOKEN_SPENT`, `TOKEN_EXPIRED`, `TOKEN_STALE` | 400 |
|
|
194
195
|
| `INVALID_CURSOR` | 400 |
|
|
195
196
|
| `UNSUPPORTED` | 501 — a wiring mistake, and the message names the store to change |
|
|
@@ -197,7 +198,7 @@ if (error instanceof JanusError) {
|
|
|
197
198
|
|
|
198
199
|
Each code has its class, all exported: `StoreFailure`, `StoreConflict` (`on:
|
|
199
200
|
'login' | 'version'`), `NotFoundError`, `UserInvalidError`, `CredentialError`,
|
|
200
|
-
`UserInactiveError`, `TokenError` (the `TOKEN_*` codes and `CODE_INVALID`),
|
|
201
|
+
`UserInactiveError`, `StepUpRequiredError`, `TokenError` (the `TOKEN_*` codes and `CODE_INVALID`),
|
|
201
202
|
`SecondFactorError`, `InvalidCursorError`, `UnsupportedError` and
|
|
202
203
|
`PermissionDepthError`. `StoreFailure` and `StoreConflict` are exported
|
|
203
204
|
**because an adapter throws them**. An adapter defines no error class of its own, so `instanceof` holds
|
|
@@ -323,6 +324,8 @@ await auth.resetPassword.request(email); // … | null
|
|
|
323
324
|
await auth.resetPassword.confirm(token, newPassword);
|
|
324
325
|
await auth.signInCode.request(email); // { code, challenge, email, expiresAt, user } | null
|
|
325
326
|
await auth.signInCode.confirm(challenge, code); // { status: 'signedIn', user, session, token }
|
|
327
|
+
await auth.stepUp.request(user); // { via: 'email', code, challenge, email, expiresAt, user }
|
|
328
|
+
await auth.stepUp.confirm(request, challenge, code); // the request's session, authenticatedAt: now
|
|
326
329
|
|
|
327
330
|
// Several user types
|
|
328
331
|
const clinic = janus({
|
|
@@ -465,12 +468,38 @@ await tokens.spendUserTokens(userId, 'signInCode', now); // 1: `kept`, now
|
|
|
465
468
|
await tokens.spendUserTokens(userId, 'signInCode', now); // 0: none left unspent
|
|
466
469
|
```
|
|
467
470
|
|
|
471
|
+
`reauthenticateSession(id, at)` moves a standing session's `authenticatedAt`
|
|
472
|
+
— what a step-up writes once a signed-in user proved again who they are —
|
|
473
|
+
and answers `null` for a revoked one, which it never brings back:
|
|
474
|
+
|
|
475
|
+
```ts
|
|
476
|
+
const { sessions } = createMemoryStores();
|
|
477
|
+
const session = {
|
|
478
|
+
id: mintId(),
|
|
479
|
+
tokenHash: 'f'.repeat(64),
|
|
480
|
+
userId: mintId(),
|
|
481
|
+
authenticatedAt: new Date('2026-01-01T00:00:00.000Z'),
|
|
482
|
+
expiresAt: new Date(Date.now() + 86_400_000),
|
|
483
|
+
revokedAt: null,
|
|
484
|
+
createdAt: new Date('2026-01-01T00:00:00.000Z'),
|
|
485
|
+
};
|
|
486
|
+
await sessions.insertSession(session);
|
|
487
|
+
await sessions.reauthenticateSession(session.id, new Date()); // { …, authenticatedAt: now }
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
A token of kind `stepUp` is that confirmation's challenge: kept apart from
|
|
491
|
+
`signInCode`, so neither is ever redeemed as the other.
|
|
492
|
+
|
|
468
493
|
An adapter written against `@nxgt/janus` 0.3 does not compile against this
|
|
469
494
|
port until it implements `countAttempt`, nor one written against 0.6 until it
|
|
470
|
-
implements `spendUserTokens`,
|
|
495
|
+
implements `spendUserTokens`, nor one written against 0.11 until it
|
|
496
|
+
implements `reauthenticateSession`, and `janus()` refuses each at wiring —
|
|
471
497
|
[Writing an adapter](docs/guide/adapters.md) has the contracts:
|
|
472
|
-
[`countAttempt`](docs/guide/adapters.md#tokenstorecountattempt)
|
|
473
|
-
[`spendUserTokens`](docs/guide/adapters.md#tokenstorespendusertokens)
|
|
498
|
+
[`countAttempt`](docs/guide/adapters.md#tokenstorecountattempt),
|
|
499
|
+
[`spendUserTokens`](docs/guide/adapters.md#tokenstorespendusertokens) and
|
|
500
|
+
[`reauthenticateSession`](docs/guide/adapters.md#sessionstorereauthenticatesession).
|
|
501
|
+
A store that lists the token kinds — a `CHECK`, a validator's enum — adds
|
|
502
|
+
`stepUp`.
|
|
474
503
|
|
|
475
504
|
### Second factor — `secondFactor`
|
|
476
505
|
|
|
@@ -591,6 +620,46 @@ A six-digit code sent to the user's e-mail signs them in, with no password.
|
|
|
591
620
|
[The sign-in code guide](docs/guide/sign-in-code.md) has the request that
|
|
592
621
|
tells nobody who exists, the challenge in a cookie, every error, and a test.
|
|
593
622
|
|
|
623
|
+
### Step-up — `stepUp` and `assertFresh`
|
|
624
|
+
|
|
625
|
+
```ts
|
|
626
|
+
import { assertFresh } from '@nxgt/janus';
|
|
627
|
+
|
|
628
|
+
// A sensitive route asks for a recent proof: signed in, or confirmed since.
|
|
629
|
+
const current = await auth.authenticate(request);
|
|
630
|
+
if (current === null) return new Response(null, { status: 401 });
|
|
631
|
+
assertFresh(current.session, '10m'); // StepUpRequiredError, STEP_UP_REQUIRED (403), past ten minutes
|
|
632
|
+
|
|
633
|
+
// The client, told STEP_UP_REQUIRED, asks for a step-up:
|
|
634
|
+
const issued = await auth.stepUp.request(current.user);
|
|
635
|
+
if (issued.via === 'email') await sendMail(issued.email, `Confirm it is you: ${issued.code}`);
|
|
636
|
+
// …keeps issued.challenge, and sends it back with the code:
|
|
637
|
+
const session = await auth.stepUp.confirm(request, challenge, code); // authenticatedAt: now
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
A signed-in user proves again who they are before something a stolen
|
|
641
|
+
session should not do alone.
|
|
642
|
+
|
|
643
|
+
- **On every user type with an e-mail**, as `signInCode`. A type with no
|
|
644
|
+
e-mail has no `stepUp`.
|
|
645
|
+
- **`request(user)` answers `via: 'email'`** — a six-digit `code` to send to
|
|
646
|
+
`email` — or, for a user whose second factor is active, **`via:
|
|
647
|
+
'secondFactor'`**: nothing to send, ask for their app's code. A step-up is
|
|
648
|
+
never weaker than the sign-in the account asks for. One step-up is live
|
|
649
|
+
per user; a challenge lives `'10m'` (`tokens.stepUp`).
|
|
650
|
+
- **`confirm(request, challenge, code)` stamps the session the request
|
|
651
|
+
presents**: its `authenticatedAt` moves to now, and it is answered. It
|
|
652
|
+
opens no session and hands out no token. Five attempts per challenge; an
|
|
653
|
+
app's codes are also counted per user, five per 15-minute window, with
|
|
654
|
+
`regenerateRecoveryCodes`. A request whose session is not a standing one
|
|
655
|
+
of the challenge's user is `TOKEN_UNKNOWN`.
|
|
656
|
+
- **`assertFresh(session, maxAge, clock?)`** reads `authenticatedAt` and
|
|
657
|
+
nothing else: `STEP_UP_REQUIRED` (403) when it is `maxAge` old or more. A
|
|
658
|
+
fresh sign-in is fresh too.
|
|
659
|
+
|
|
660
|
+
[The step-up guide](docs/guide/step-up.md) has the two requests, every
|
|
661
|
+
error, and the codes an app confirms.
|
|
662
|
+
|
|
594
663
|
### User events — `events`
|
|
595
664
|
|
|
596
665
|
```ts
|
|
@@ -784,7 +853,7 @@ describeJanusStores({
|
|
|
784
853
|
});
|
|
785
854
|
```
|
|
786
855
|
|
|
787
|
-
There are
|
|
856
|
+
There are 54 cases. They cover:
|
|
788
857
|
- round-trip, byte for byte — including every edge character the core lets
|
|
789
858
|
through (control characters, U+FFFF, a surrogate pair);
|
|
790
859
|
- uniqueness, as a constraint: of twenty concurrent inserts of one login,
|
|
@@ -793,10 +862,15 @@ There are 49 cases. They cover:
|
|
|
793
862
|
- versions: a refused update writes nothing;
|
|
794
863
|
- **omission**, named after the Kratos `PUT` trap;
|
|
795
864
|
- pagination;
|
|
796
|
-
- sessions
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
865
|
+
- sessions: `reauthenticateSession` moves `authenticatedAt` and nothing else,
|
|
866
|
+
and never brings back a revoked session, even one revoked at the same
|
|
867
|
+
moment;
|
|
868
|
+
- one-time tokens: a token of every kind the port names is stored, counted
|
|
869
|
+
and spent — a `CHECK` or an enum that forgot `stepUp` fails here — and a
|
|
870
|
+
step-up is never counted, spent nor answered as a sign-in code; of twenty
|
|
871
|
+
concurrent redemptions, exactly one succeeds; of twenty concurrent
|
|
872
|
+
`countAttempt` calls, each answers a distinct count, and none is counted
|
|
873
|
+
once a racing redemption spent the token;
|
|
800
874
|
- spending a user's tokens of one kind — `spendUserTokens` — spends only the
|
|
801
875
|
unspent ones of that user and kind, spares the one named by `except`, and
|
|
802
876
|
never spends the same token as a racing redemption;
|
|
@@ -805,7 +879,7 @@ There are 49 cases. They cover:
|
|
|
805
879
|
removed by one that names `null`;
|
|
806
880
|
- deletion: a user's logins are freed, and every session and token of theirs
|
|
807
881
|
goes, with a replay answering `false` or `0` rather than failing;
|
|
808
|
-
- **outages**, one case for each of the
|
|
882
|
+
- **outages**, one case for each of the fourteen methods whose honest answer can
|
|
809
883
|
be "nothing".
|
|
810
884
|
|
|
811
885
|
The suite imports no test framework and no assertion library. It runs under
|
|
@@ -852,6 +926,18 @@ the body of the code form — never in the e-mail, never in a URL, where logs,
|
|
|
852
926
|
proxies and the `Referer` header see it, and never in a log. The e-mail holds
|
|
853
927
|
the code and nothing else.
|
|
854
928
|
|
|
929
|
+
**Confirm a step-up on the request that asked for it.** `stepUp.confirm`
|
|
930
|
+
stamps the session the request presents, and only a standing session of
|
|
931
|
+
the challenge's user: a challenge carried to another browser, or confirmed
|
|
932
|
+
after a sign-out, is `TOKEN_UNKNOWN`. Its challenge is a secret like
|
|
933
|
+
`signInCode`'s — and check freshness on the server with `assertFresh`, never
|
|
934
|
+
from a flag the client keeps.
|
|
935
|
+
|
|
936
|
+
**Rate-limit `stepUp.request` per user.** An app's codes are counted per
|
|
937
|
+
user and window, but an e-mailed code gets five guesses per challenge and a
|
|
938
|
+
new challenge takes only a new `request`: without a limit, a stolen session
|
|
939
|
+
can keep asking — and fill the user's inbox while it does.
|
|
940
|
+
|
|
855
941
|
**Answer `signInCode.request` the same whether it issued a code or not** —
|
|
856
942
|
the same status, body and cookie: set a random challenge when it answered
|
|
857
943
|
`null`. The code route then still tells a decoy (`TOKEN_UNKNOWN`) from a
|
|
@@ -939,6 +1025,14 @@ waiting on its second factor is spent with them — as it is by `setPassword`
|
|
|
939
1025
|
and `changePassword`; what the visitor does next is
|
|
940
1026
|
your policy. A password refused for its length does not spend the token.
|
|
941
1027
|
|
|
1028
|
+
**Only the last reset link works, and only until the password changes.** A
|
|
1029
|
+
`resetPassword.request` spends the user's earlier links, and any password
|
|
1030
|
+
written — by a link, `changePassword` or `setPassword` — spends every link
|
|
1031
|
+
still live, so an older e-mail's link answers `TOKEN_SPENT`. Tell the visitor
|
|
1032
|
+
to use the latest e-mail, and rate-limit `resetPassword.request` per address:
|
|
1033
|
+
each request cancels the link before it. **Nothing limits password guesses**: rate-limit
|
|
1034
|
+
`signIn` per login and per client ([passwords](docs/guide/passwords.md#rate-limit-password-guessing)).
|
|
1035
|
+
|
|
942
1036
|
**A sign-in can move a user's `version`.** Rewriting a stale hash is a write. A
|
|
943
1037
|
user object read before that sign-in, and then passed as `ifVersion`, gets
|
|
944
1038
|
`VERSION_CONFLICT`. That is the conflict doing its job: read the user again.
|
|
@@ -1016,17 +1110,17 @@ that sends one, since it is awaited: queue the event and return.
|
|
|
1016
1110
|
|
|
1017
1111
|
## Type safety, counted
|
|
1018
1112
|
|
|
1019
|
-
**One hundred and twenty-
|
|
1113
|
+
**One hundred and twenty-nine plausible mistakes, one hundred and twenty-nine refused at compile time — and
|
|
1020
1114
|
two gaps, named.**
|
|
1021
1115
|
|
|
1022
1116
|
The lists are typechecked and never run, with one `@ts-expect-error` per
|
|
1023
1117
|
mistake beside the shapes that must keep compiling. One is a single file:
|
|
1024
1118
|
`test/types/refusals.ts` (fifteen, on the shared vocabulary). The other three
|
|
1025
|
-
are folders with one file per behaviour: `test/types/port/` (twenty-
|
|
1119
|
+
are folders with one file per behaviour: `test/types/port/` (twenty-four, on
|
|
1026
1120
|
the identity stores' port, from the point of view of the person implementing
|
|
1027
|
-
it), `test/types/auth/` (
|
|
1121
|
+
it), `test/types/auth/` (forty-two, on `janus()`, from the point of view of
|
|
1028
1122
|
the application — twelve of them on the second factor, three on sign-in codes,
|
|
1029
|
-
three on user events) and `test/types/permissions/` (forty-eight, on the
|
|
1123
|
+
three on user events, four on step-ups) and `test/types/permissions/` (forty-eight, on the
|
|
1030
1124
|
permission model and the questions asked of it). The rule comes from
|
|
1031
1125
|
`nxgt-data`, and so does the reason to distrust the claim without the files:
|
|
1032
1126
|
when it was last measured on `@nxgt/mongo`, *seven of twelve plausible
|
|
@@ -48,6 +48,8 @@ interface SharedConfig {
|
|
|
48
48
|
readonly resetPassword?: Duration;
|
|
49
49
|
/** How long an e-mailed sign-in code waits. `'10m'` when absent. */
|
|
50
50
|
readonly signInCode?: Duration;
|
|
51
|
+
/** How long a step-up's challenge waits, e-mailed or not. `'10m'` when absent. */
|
|
52
|
+
readonly stepUp?: Duration;
|
|
51
53
|
};
|
|
52
54
|
/**
|
|
53
55
|
* A TOTP second factor, for every user type with a password. Absent, no
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"janus-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/janus-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9D,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC5B,oFAAoF;IACpF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC9C,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,UAAU,YAAY;IACrB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;IACjC,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE;QACjB,2BAA2B;QAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC;QAChC,0BAA0B;QAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC;QAClC,oEAAoE;QACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"janus-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/janus-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolved-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolved-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC3B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE;QACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"resolved-config.d.ts","sourceRoot":"","sources":["../../../src/auth/config/resolved-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE;QAClB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC3B,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE;QACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,QAAQ,CAAC,YAAY,EAAE;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;KAChC,GAAG,IAAI,CAAC;IACT,QAAQ,CAAC,MAAM,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;QAC7C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KACzB,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reset-password.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/reset-password.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAOZ,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"reset-password.d.ts","sourceRoot":"","sources":["../../../src/auth/email-flows/reset-password.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAOZ,MAAM,YAAY,CAAC;AAKpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGjD,KAAK,aAAa,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,aAAa,CAqCf"}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { assertStores } from './port/assert-stores';
|
|
|
6
6
|
export { createMemoryStores } from './port/memory';
|
|
7
7
|
export type { JanusStores, Json, JsonObject, PasswordRecord, SecondFactorRecord, SessionId, SessionRecord, SessionStore, StoreCapabilities, TokenKind, TokenRecord, TokenStore, UserPageRequest, UserPatch, UserRecord, UserStore, } from './port/types';
|
|
8
8
|
export type { SealingKey } from './sealing';
|
|
9
|
+
export { assertFresh } from './sessions/fresh';
|
|
9
10
|
export type { StandardSchemaV1 } from './standard-schema';
|
|
10
|
-
export type { Authenticated, Checked, EmailOf, HeaderRecord, IssuedCode, IssuedToken, Janus, LoginOf, PasswordApi, RecoveredSignIn, RecoveryCodesIssued, RequestLike, RequiredStringKeys, ResetPasswordApi, SecondFactorApi, SecondFactorEnrolment, SecondFactorRequired, Session, SharedApi, SignedIn, SignInCodeApi, SignInResult, TypeApi, TypesOf, User, UserBase, UserOf, UserRef, UserTypeApi, VerifyEmailApi, WriteOptions, } from './types';
|
|
11
|
+
export type { Authenticated, Checked, EmailOf, HeaderRecord, IssuedCode, IssuedToken, Janus, LoginOf, PasswordApi, RecoveredSignIn, RecoveryCodesIssued, RequestLike, RequiredStringKeys, ResetPasswordApi, SecondFactorApi, SecondFactorEnrolment, SecondFactorRequired, Session, SharedApi, SignedIn, SignInCodeApi, SignInResult, StepUpApi, StepUpByApp, StepUpByEmail, TypeApi, TypesOf, User, UserBase, UserOf, UserRef, UserTypeApi, VerifyEmailApi, WriteOptions, } from './types';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EACX,SAAS,EACT,iBAAiB,EACjB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,WAAW,EACX,IAAI,EACJ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,OAAO,EACP,WAAW,EACX,cAAc,EACd,YAAY,GACZ,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,YAAY,EACZ,UAAU,EACV,WAAW,EACX,eAAe,EACf,SAAS,EACT,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,cAAc,GACd,MAAM,UAAU,CAAC;AAClB,YAAY,EACX,SAAS,EACT,iBAAiB,EACjB,aAAa,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EACX,WAAW,EACX,IAAI,EACJ,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,UAAU,EACV,eAAe,EACf,SAAS,EACT,UAAU,EACV,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,WAAW,EACX,KAAK,EACL,OAAO,EACP,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,OAAO,EACP,SAAS,EACT,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,SAAS,EACT,WAAW,EACX,aAAa,EACb,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,OAAO,EACP,WAAW,EACX,cAAc,EACd,YAAY,GACZ,MAAM,SAAS,CAAC"}
|
|
@@ -4,12 +4,28 @@ import { type AnyUser, type Context } from './context';
|
|
|
4
4
|
import type { UserRecord } from './port/types';
|
|
5
5
|
import type { SignInResult } from './types';
|
|
6
6
|
/**
|
|
7
|
-
* What writing a password ends besides
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* What writing a password ends besides. A reset, a change and a set each call
|
|
8
|
+
* it **after** the write, and a failure here throws — it is never ignored:
|
|
9
|
+
*
|
|
10
|
+
* - The user's reset links still unspent. A link answers a request to
|
|
11
|
+
* replace the password; once it is replaced, an older link would replace
|
|
12
|
+
* it again, without knowing the new one.
|
|
13
|
+
* - Every second-factor challenge still open, so a sign-in started with the
|
|
14
|
+
* old password cannot be finished — the other half of
|
|
15
|
+
* {@link heldByPassword}, which reads **after** it issued.
|
|
16
|
+
*
|
|
17
|
+
* Both spends are attempted even when one fails, so a store failing one kind
|
|
18
|
+
* still spends the other; the first failure is then thrown.
|
|
19
|
+
*
|
|
20
|
+
* After the write, not before: a link issued while the password is written
|
|
21
|
+
* is spent too, where one issued between an earlier spend and the write
|
|
22
|
+
* would survive. So an outage here leaves the password written and the
|
|
23
|
+
* older links live, and the caller is told, with `STORE_FAILED`; the next
|
|
24
|
+
* `resetPassword.request` spends them. Sign-in codes and step-ups are left
|
|
25
|
+
* alone: the password proves neither, and a step-up belongs to a session,
|
|
26
|
+
* which a reset revokes.
|
|
11
27
|
*/
|
|
12
|
-
export declare function
|
|
28
|
+
export declare function endWhatThePasswordOpened(context: Context, userId: Id): Promise<void>;
|
|
13
29
|
/**
|
|
14
30
|
* Whether the password a sign-in verified is still the user's, once the
|
|
15
31
|
* sign-in answered. A password written meanwhile ends the sign-in: its
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-written.d.ts","sourceRoot":"","sources":["../../src/auth/password-written.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAGZ,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C
|
|
1
|
+
{"version":3,"file":"password-written.d.ts","sourceRoot":"","sources":["../../src/auth/password-written.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EACN,KAAK,OAAO,EACZ,KAAK,OAAO,EAGZ,MAAM,WAAW,CAAC;AAEnB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,EAAE,GACR,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CACnC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,UAAU,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,EAC7B,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CAkBlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assert-stores.d.ts","sourceRoot":"","sources":["../../../src/auth/port/assert-stores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,iBAAiB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"assert-stores.d.ts","sourceRoot":"","sources":["../../../src/auth/port/assert-stores.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAoC9D;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC3B,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,MAAM,GACX,iBAAiB,CAmCnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/memory/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,YAAY,EAAE,MAAM,UAAU,CAAC;AASvE,mCAAmC;AACnC,wBAAgB,kBAAkB,IAAI,YAAY,
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/memory/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,YAAY,EAAE,MAAM,UAAU,CAAC;AASvE,mCAAmC;AACnC,wBAAgB,kBAAkB,IAAI,YAAY,CAkEjD"}
|
|
@@ -20,7 +20,10 @@ export interface SessionRecord {
|
|
|
20
20
|
/** `sha256` of the session token, hex. Unique across the store. */
|
|
21
21
|
readonly tokenHash: string;
|
|
22
22
|
readonly userId: Id;
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* When credentials were last presented — at the sign-in, or since by a
|
|
25
|
+
* step-up on this session — not when the session was last extended.
|
|
26
|
+
*/
|
|
24
27
|
readonly authenticatedAt: Date;
|
|
25
28
|
readonly expiresAt: Date;
|
|
26
29
|
/** `null` while the session stands. */
|
|
@@ -53,6 +56,18 @@ export interface SessionStore {
|
|
|
53
56
|
* *allowed* to be extended yet is the core's decision, made before the call.
|
|
54
57
|
*/
|
|
55
58
|
extendSession(id: SessionId, expiresAt: Date): Promise<SessionRecord | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Moves `authenticatedAt` to `at`, **only while the session is not
|
|
61
|
+
* revoked**, and answers the record as written: what a step-up calls once
|
|
62
|
+
* the user proved again, on this session, who they are. `expiresAt` and
|
|
63
|
+
* every other field stay as they were.
|
|
64
|
+
*
|
|
65
|
+
* `null` when there is no such session or it has been revoked — a
|
|
66
|
+
* confirmation racing a revocation must never bring the session back.
|
|
67
|
+
* Whether the session may be confirmed — standing, not lapsed — is the
|
|
68
|
+
* core's decision, made before the call.
|
|
69
|
+
*/
|
|
70
|
+
reauthenticateSession(id: SessionId, at: Date): Promise<SessionRecord | null>;
|
|
56
71
|
/**
|
|
57
72
|
* Revokes one session. `true` when the session exists — revoked by this call
|
|
58
73
|
* or already — and `false` when there is none.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/sessions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C,mEAAmE;AACnE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;IACpB
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/sessions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C,mEAAmE;AACnE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,IAAI,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC5B;;;OAGG;IACH,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpD,mEAAmE;IACnE,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAEzE;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAE7E;;;;;;;;;;OAUG;IACH,qBAAqB,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAE9E;;;;;OAKG;IACH,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEzD;;;;OAIG;IACH,kBAAkB,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE9E;;;;;OAKG;IACH,kBAAkB,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEhD;;;;;;;;OAQG;IACH,qBAAqB,CAAC,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACtD"}
|
|
@@ -16,8 +16,12 @@ import type { Id } from '../../../ids/id';
|
|
|
16
16
|
* attempts at `regenerateRecoveryCodes`, a token of this kind whose secret
|
|
17
17
|
* nobody is given, never redeemed, only counted.
|
|
18
18
|
* - `signInCode`: a code sent by e-mail to sign in without a password.
|
|
19
|
+
* - `stepUp`: a signed-in user's confirmation of a sensitive action — a code
|
|
20
|
+
* sent by e-mail, or, with an active second factor, a code from their app.
|
|
21
|
+
* A kind of its own, so a sign-in code never confirms an action nor an
|
|
22
|
+
* action's code signs anyone in.
|
|
19
23
|
*/
|
|
20
|
-
export type TokenKind = 'verifyEmail' | 'resetPassword' | 'secondFactor' | 'signInCode';
|
|
24
|
+
export type TokenKind = 'verifyEmail' | 'resetPassword' | 'secondFactor' | 'signInCode' | 'stepUp';
|
|
21
25
|
/** A one-time token, as a store holds it: its hash, never its secret. */
|
|
22
26
|
export interface TokenRecord {
|
|
23
27
|
/** `sha256` of the token's secret, hex. Unique across the store. */
|
|
@@ -30,7 +34,8 @@ export interface TokenRecord {
|
|
|
30
34
|
*/
|
|
31
35
|
readonly address: string;
|
|
32
36
|
/**
|
|
33
|
-
* For a token redeemed with
|
|
37
|
+
* For a token redeemed with an e-mailed code — `signInCode`, and a
|
|
38
|
+
* `stepUp` sent by e-mail — the code's hash, keyed
|
|
34
39
|
* by the token's secret, so the tokens alone do not reveal it. `null` for
|
|
35
40
|
* every other kind: the core writes it so, and a store keeps what it is
|
|
36
41
|
* given.
|
|
@@ -99,8 +104,9 @@ export interface TokenStore {
|
|
|
99
104
|
* Spends every **unspent** token of one user and one `kind` at `at` —
|
|
100
105
|
* but the one whose hash is `except`, when given — and answers how many
|
|
101
106
|
* it spent. What issuing a sign-in code calls, sparing the code it just
|
|
102
|
-
* issued, so only the last code sent works
|
|
103
|
-
* calls, so no
|
|
107
|
+
* issued, so only the last code sent works — and so for a reset link and
|
|
108
|
+
* a step-up; and what writing a password calls, so no reset link and no
|
|
109
|
+
* second-factor challenge opened with the old one survives.
|
|
104
110
|
*
|
|
105
111
|
* - A spent token keeps its `spentAt`: it never changes once set.
|
|
106
112
|
* - A token of another `kind`, or of another user, is not touched.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/auth/port/types/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,SAAS,GAClB,aAAa,GACb,eAAe,GACf,cAAc,GACd,YAAY,GACZ,QAAQ,CAAC;AAEZ,yEAAyE;AACzE,MAAM,WAAW,WAAW;IAC3B,oEAAoE;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;CACzB;AAED,kDAAkD;AAClD,MAAM,WAAW,UAAU;IAC1B;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,CACX,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,EACf,EAAE,EAAE,IAAI,GACN,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAE9E;;;;;;;;;;;;;;;;;OAiBG;IACH,eAAe,CACd,MAAM,EAAE,EAAE,EACV,IAAI,EAAE,SAAS,EACf,EAAE,EAAE,IAAI,EACR,MAAM,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnB;;;;OAIG;IACH,gBAAgB,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9C"}
|
|
@@ -2,9 +2,11 @@ import type { Context } from '../context';
|
|
|
2
2
|
import type { UserRecord } from '../port/types';
|
|
3
3
|
import type { Sealer } from '../sealing';
|
|
4
4
|
/**
|
|
5
|
-
* The attempts at
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* The attempts at an app's code outside a sign-in — `regenerateRecoveryCodes`
|
|
6
|
+
* and a step-up confirmed with the app — counted by the store, in one count
|
|
7
|
+
* both share: the same {@link CODE_ATTEMPTS} a challenge takes, per user and
|
|
8
|
+
* per window, so a stolen session cannot guess the app's code at leisure,
|
|
9
|
+
* nor buy more guesses by switching from one to the other.
|
|
8
10
|
*
|
|
9
11
|
* **No port of its own.** The count is a one-time token of kind
|
|
10
12
|
* `secondFactor`, counted by `countAttempt` like a challenge's: one
|
|
@@ -13,7 +15,7 @@ import type { Sealer } from '../sealing';
|
|
|
13
15
|
* without the sealing keys can name it — nor redeem it as a challenge.
|
|
14
16
|
*
|
|
15
17
|
* What it is for decides when it starts again: the **window**, a fixed
|
|
16
|
-
* {@link
|
|
18
|
+
* {@link APP_CODE_WINDOW_MS} slice of the clock, and the factor's
|
|
17
19
|
* `lastStep`, which **any code accepted** moves — a regenerate that
|
|
18
20
|
* succeeded starts the count again, and so does a sign-in finished with the
|
|
19
21
|
* app.
|
|
@@ -24,11 +26,12 @@ import type { Sealer } from '../sealing';
|
|
|
24
26
|
* from them — a password written between guesses buys none.
|
|
25
27
|
*/
|
|
26
28
|
/** How long the attempts of one window last: fifteen minutes. */
|
|
27
|
-
export declare const
|
|
29
|
+
export declare const APP_CODE_WINDOW_MS: number;
|
|
28
30
|
/**
|
|
29
|
-
* Counts one attempt at
|
|
30
|
-
*
|
|
31
|
-
*
|
|
31
|
+
* Counts one attempt at the app's code — to regenerate the user's recovery
|
|
32
|
+
* codes, or to confirm a step-up — before the code is compared, and
|
|
33
|
+
* answers how many are left. Past the last one, it refuses — the right
|
|
34
|
+
* code included — until the window ends.
|
|
32
35
|
*/
|
|
33
|
-
export declare function
|
|
34
|
-
//# sourceMappingURL=
|
|
36
|
+
export declare function countAppCodeAttempt(context: Context, sealer: Sealer, record: UserRecord, where: string): Promise<number>;
|
|
37
|
+
//# sourceMappingURL=app-code-attempts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-code-attempts.d.ts","sourceRoot":"","sources":["../../../src/auth/second-factor/app-code-attempts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,KAAK,EAAe,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGzC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,QAAc,CAAC;AA8C9C;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACxC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAgBjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticate.d.ts","sourceRoot":"","sources":["../../../src/auth/sessions/authenticate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"authenticate.d.ts","sourceRoot":"","sources":["../../../src/auth/sessions/authenticate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,YAAY,CAAC;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAI3D,oFAAoF;AACpF,wBAAsB,YAAY,CACjC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CA0CxC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `assertFresh`: whether a session proved who it is recently enough for a
|
|
3
|
+
* sensitive action — signed in, or confirmed by a step-up, less than
|
|
4
|
+
* `maxAge` ago.
|
|
5
|
+
*/
|
|
6
|
+
import { type Clock } from '../../time/clock';
|
|
7
|
+
import { type Duration } from '../../time/duration';
|
|
8
|
+
import type { Session } from '../types';
|
|
9
|
+
/**
|
|
10
|
+
* Refuses a session whose credentials were last presented `maxAge` ago or
|
|
11
|
+
* more — at its sign-in, or since by `stepUp.confirm` — with
|
|
12
|
+
* `STEP_UP_REQUIRED` (403): ask for a step-up, then run the action again. One
|
|
13
|
+
* field of the session `authenticate` already answered, so no store is read.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const current = await auth.authenticate(request);
|
|
17
|
+
* if (current === null) return new Response(null, { status: 401 });
|
|
18
|
+
* assertFresh(current.session, '10m'); // STEP_UP_REQUIRED past ten minutes
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Pass the `clock` given to `janus()` when it is not the system's. A
|
|
22
|
+
* `maxAge` that is not a duration is a `TypeError`: it is written in the
|
|
23
|
+
* code, never read from a request.
|
|
24
|
+
*/
|
|
25
|
+
export declare function assertFresh(session: Pick<Session, 'authenticatedAt' | 'userId'>, maxAge: Duration, clock?: Clock): void;
|
|
26
|
+
//# sourceMappingURL=fresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fresh.d.ts","sourceRoot":"","sources":["../../../src/auth/sessions/fresh.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,KAAK,KAAK,EAAe,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,KAAK,QAAQ,EAAiB,MAAM,qBAAqB,CAAC;AACnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,QAAQ,CAAC,EACpD,MAAM,EAAE,QAAQ,EAChB,KAAK,GAAE,KAAmB,GACxB,IAAI,CASN"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The standing session a request presents — one rule for `authenticate` and
|
|
3
|
+
* `stepUp.confirm`, so the two never disagree on what "signed in" means.
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '../context';
|
|
6
|
+
import type { SessionRecord } from '../port/types';
|
|
7
|
+
import type { RequestLike } from '../types';
|
|
8
|
+
/** A standing session, and the session token that presented it. */
|
|
9
|
+
export interface Presented {
|
|
10
|
+
readonly token: string;
|
|
11
|
+
readonly session: SessionRecord;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Answers the session `request` presents, with its token — or `null` when
|
|
15
|
+
* none stands: no token, or a session gone, revoked or lapsed. Expiry is
|
|
16
|
+
* decided here, against the context's clock: a store may still hold a
|
|
17
|
+
* session past its term.
|
|
18
|
+
*/
|
|
19
|
+
export declare function standingSession(context: Context, request: RequestLike): Promise<Presented | null>;
|
|
20
|
+
//# sourceMappingURL=standing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standing.d.ts","sourceRoot":"","sources":["../../../src/auth/sessions/standing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,WAAW,GAClB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAa3B"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two codes that confirm a step-up: the one e-mailed with the challenge,
|
|
3
|
+
* and, for a user whose second factor is active, the one their app shows.
|
|
4
|
+
* Each spends the challenge once it matched.
|
|
5
|
+
*/
|
|
6
|
+
import type { ResolvedType } from '../config';
|
|
7
|
+
import type { Context } from '../context';
|
|
8
|
+
import type { TokenRecord, UserRecord } from '../port/types';
|
|
9
|
+
/** A challenge counted: the token as counted, its secret, and what is left of its attempts. */
|
|
10
|
+
export interface CountedStepUp {
|
|
11
|
+
readonly token: TokenRecord;
|
|
12
|
+
readonly secret: string;
|
|
13
|
+
readonly attemptsLeft: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Checks the e-mailed code. Refused when the user's second factor became
|
|
17
|
+
* active since it was sent — their app is what confirms now — and when the
|
|
18
|
+
* e-mail it was sent to is not theirs any more.
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkEmailedCode(context: Context, type: ResolvedType, user: UserRecord, counted: CountedStepUp, code: string, where: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Checks a code from the user's app. Its attempts are counted twice before it
|
|
23
|
+
* is compared: against the challenge, and per user and window, as
|
|
24
|
+
* `regenerateRecoveryCodes`' are — a stolen session asking for challenge
|
|
25
|
+
* after challenge still gets five guesses per window, not five per
|
|
26
|
+
* challenge. The code accepted is written, so it confirms nothing else.
|
|
27
|
+
*/
|
|
28
|
+
export declare function checkAppCode(context: Context, type: ResolvedType, user: UserRecord, counted: CountedStepUp, code: string, where: string): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=codes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../../../src/auth/step-up/codes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAQ1C,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI7D,+FAA+F;AAC/F,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAKD;;;;GAIG;AACH,wBAAsB,gBAAgB,CACrC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CACjC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CA0Cf"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A step-up: a signed-in user proving again who they are before a sensitive
|
|
3
|
+
* action, with a code sent by e-mail — or from their app, when their second
|
|
4
|
+
* factor is active — and **the session they are on stamped as freshly
|
|
5
|
+
* confirmed**. It opens no session: its `authenticatedAt` moves, which is
|
|
6
|
+
* what a route checks with `assertFresh`.
|
|
7
|
+
*
|
|
8
|
+
* The same six digits, challenge and five attempts as a sign-in code, on a
|
|
9
|
+
* token kind of its own: a sign-in code never confirms an action, nor an
|
|
10
|
+
* action's code signs anyone in.
|
|
11
|
+
*/
|
|
12
|
+
import type { At } from '../at';
|
|
13
|
+
import type { ResolvedType } from '../config';
|
|
14
|
+
import type { AnyUser, Context } from '../context';
|
|
15
|
+
import type { StepUpApi } from '../types';
|
|
16
|
+
import { type IssuedStepUp } from './request';
|
|
17
|
+
/** The step-up of one user type. */
|
|
18
|
+
export declare function stepUpFlows(context: Context, type: ResolvedType, at: At): StepUpApi<AnyUser, IssuedStepUp>['stepUp'];
|
|
19
|
+
//# sourceMappingURL=flows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flows.d.ts","sourceRoot":"","sources":["../../../src/auth/step-up/flows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGnD,OAAO,KAAK,EAAwB,SAAS,EAAE,MAAM,UAAU,CAAC;AAGhE,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,WAAW,CAAC;AAE7D,oCAAoC;AACpC,wBAAgB,WAAW,CAC1B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,EAAE,GACJ,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,CAiB5C"}
|