@adonis-agora/authkit-server 0.90.2 → 1.0.0-rc.1

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 (99) hide show
  1. package/CHANGELOG.md +3654 -0
  2. package/build/commands/clients_create.d.ts +1 -0
  3. package/build/commands/clients_create.js +15 -2
  4. package/build/commands/commands.json +7 -0
  5. package/build/host/ui/admin.html +1 -1
  6. package/build/host/views/account/apps.edge +8 -3
  7. package/build/host/views/partials/styles.edge +1 -1
  8. package/build/index.d.ts +5 -3
  9. package/build/index.js +4 -5
  10. package/build/providers/authkit_server_provider.js +8 -0
  11. package/build/src/accounts/lucid_account_store.d.ts +0 -18
  12. package/build/src/accounts/lucid_account_store.js +7 -48
  13. package/build/src/accounts/lucid_store/mfa.js +1 -1
  14. package/build/src/accounts/lucid_store/organizations.d.ts +1 -1
  15. package/build/src/accounts/lucid_store/password_hygiene.d.ts +2 -3
  16. package/build/src/accounts/lucid_store/password_hygiene.js +2 -3
  17. package/build/src/accounts/lucid_store/shared.d.ts +0 -7
  18. package/build/src/accounts/lucid_store/shared.js +0 -14
  19. package/build/src/accounts/lucid_stores.d.ts +1 -1
  20. package/build/src/adapters/redis_adapter.js +12 -2
  21. package/build/src/console_authorize.d.ts +56 -0
  22. package/build/src/console_authorize.js +85 -0
  23. package/build/src/define_config.d.ts +26 -14
  24. package/build/src/define_config.js +5 -3
  25. package/build/src/doctor/checks.d.ts +10 -5
  26. package/build/src/doctor/checks.js +52 -8
  27. package/build/src/host/account_api/account_api_controller.d.ts +2 -1
  28. package/build/src/host/account_api/account_api_controller.js +5 -11
  29. package/build/src/host/admin_api/api_clients_controller.d.ts +9 -3
  30. package/build/src/host/admin_api/dto.d.ts +3 -1
  31. package/build/src/host/admin_api/dto.js +3 -1
  32. package/build/src/host/admin_clients_service.d.ts +13 -5
  33. package/build/src/host/admin_clients_service.js +28 -6
  34. package/build/src/host/admin_console/admin_shell_controller.d.ts +16 -1
  35. package/build/src/host/admin_console/admin_shell_controller.js +21 -7
  36. package/build/src/host/admin_console/console_clients_controller.d.ts +21 -2
  37. package/build/src/host/admin_console/console_clients_controller.js +9 -0
  38. package/build/src/host/admin_console/console_impersonation_controller.js +3 -8
  39. package/build/src/host/admin_console/console_overview_controller.d.ts +2 -2
  40. package/build/src/host/admin_console/console_overview_controller.js +4 -2
  41. package/build/src/host/admin_console_warning.d.ts +8 -0
  42. package/build/src/host/admin_console_warning.js +18 -0
  43. package/build/src/host/admin_validators.d.ts +15 -11
  44. package/build/src/host/admin_validators.js +16 -9
  45. package/build/src/host/auth_host_config.d.ts +5 -0
  46. package/build/src/host/client_names.d.ts +9 -2
  47. package/build/src/host/client_names.js +11 -3
  48. package/build/src/host/consent_params.d.ts +7 -0
  49. package/build/src/host/consent_params.js +27 -0
  50. package/build/src/host/console_denied_page.d.ts +6 -0
  51. package/build/src/host/console_denied_page.js +18 -0
  52. package/build/src/host/console_sso.d.ts +24 -0
  53. package/build/src/host/console_sso.js +70 -0
  54. package/build/src/host/controllers/account_apps_controller.js +4 -3
  55. package/build/src/host/controllers/account_confirm_controller.d.ts +0 -7
  56. package/build/src/host/controllers/account_confirm_controller.js +0 -6
  57. package/build/src/host/controllers/account_orgs_controller.d.ts +0 -50
  58. package/build/src/host/controllers/account_orgs_controller.js +7 -113
  59. package/build/src/host/controllers/account_security_controller.js +7 -11
  60. package/build/src/host/controllers/account_session_controller.js +10 -1
  61. package/build/src/host/controllers/agent_consent_controller.js +23 -5
  62. package/build/src/host/controllers/console_sso_controller.d.ts +12 -0
  63. package/build/src/host/controllers/console_sso_controller.js +61 -0
  64. package/build/src/host/controllers/interaction_controller.js +9 -3
  65. package/build/src/host/controllers/poppy_consent_controller.js +33 -7
  66. package/build/src/host/controllers/registration_controller.js +7 -2
  67. package/build/src/host/i18n.d.ts +0 -4
  68. package/build/src/host/i18n.js +0 -6
  69. package/build/src/host/impersonation.d.ts +7 -15
  70. package/build/src/host/impersonation.js +7 -16
  71. package/build/src/host/interaction_recovery.js +4 -1
  72. package/build/src/host/login_attempt.js +5 -1
  73. package/build/src/host/middleware/account_auth.js +6 -0
  74. package/build/src/host/rate_limit.js +5 -1
  75. package/build/src/host/register_auth_host.d.ts +8 -0
  76. package/build/src/host/register_auth_host.js +116 -11
  77. package/build/src/host/renderers/inertia_renderer.d.ts +9 -21
  78. package/build/src/host/renderers/inertia_renderer.js +5 -6
  79. package/build/src/host/screen_props.d.ts +309 -0
  80. package/build/src/host/screen_props.js +11 -0
  81. package/build/src/host/sudo_mode.d.ts +5 -11
  82. package/build/src/host/sudo_mode.js +5 -11
  83. package/build/src/host/ui-dist/assets/index-D0EIeyKK.js +67 -0
  84. package/build/src/host/ui-dist/index.html +1 -1
  85. package/build/src/password/password_manager.js +5 -0
  86. package/build/src/provider/build_provider.js +6 -34
  87. package/build/src/provider/oidc_service.d.ts +0 -2
  88. package/build/src/provider/oidc_service.js +1 -12
  89. package/build/src/provider/token_exchange.d.ts +1 -3
  90. package/build/src/provider/token_exchange.js +1 -1
  91. package/build/stubs/config/authkit_react.stub +3 -3
  92. package/package.json +12 -11
  93. package/skills/authkit-interactions/SKILL.md +32 -5
  94. package/stubs/config/authkit_react.stub +3 -3
  95. package/build/src/commands/import_clients.d.ts +0 -40
  96. package/build/src/commands/import_clients.js +0 -81
  97. package/build/src/host/ui-dist/assets/index-DUVBDAyh.js +0 -67
  98. package/build/src/mixins/with_mfa.d.ts +0 -35
  99. package/build/src/mixins/with_mfa.js +0 -21
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Props of every screen authkit-server renders through `cfg.render` — what an
3
+ * `inertiaRenderer` page (or a custom renderer) receives for each view.
4
+ *
5
+ * `AuthkitScreenProps['<view>']` is the shape a page gets, `messages` included
6
+ * (the renderer adds it). The controllers call `cfg.render` with
7
+ * {@link ScreenRenderProps} (the same shape without `messages`), and `render` is
8
+ * typed per view, so a controller that drifts from these types fails the
9
+ * package's `tsc`.
10
+ */
11
+ import type { OrgInvitation, OrgMember, OrgSummary } from '../accounts/account_store.js';
12
+ import type { PatRecord } from '../pat/pat_store.js';
13
+ import type { AccountConfirmProps, AccountEmailConfirmedProps, AccountLoginProps, AccountMfaProps, AccountSecurityProps } from './account_screen_props.js';
14
+ import type { BotProtectionWidget } from './bot_protection.js';
15
+ import type { ClientBrand } from './branding.js';
16
+ import type { CustomMfaDescriptor } from './custom_mfa.js';
17
+ import type { AuthMessages } from './i18n.js';
18
+ import type { LastLoginMethod } from './last_login_method.js';
19
+ import type { MagicChannelProp } from './login_channel.js';
20
+ import type { ResolvedAuthMethods, ResolvedOrganizationsPolicySetting } from './runtime_toggles.js';
21
+ /** What every screen receives from the renderer. */
22
+ interface WithMessages {
23
+ /** The i18n catalog for the request's locale (added by the renderer). */
24
+ messages: AuthMessages;
25
+ }
26
+ /** The account a login step is about (step 2+), or `null`. */
27
+ export interface LoginAccount {
28
+ fullName: string | null;
29
+ globalRoles: string[];
30
+ }
31
+ /**
32
+ * `login` — the OIDC interaction's login screen. `step` drives the sub-view:
33
+ * `identifier` (ask for the e-mail), `password`, or `password_expired`.
34
+ */
35
+ export interface LoginProps extends WithMessages {
36
+ uid: string;
37
+ csrfToken: string;
38
+ brand?: ClientBrand;
39
+ step?: 'identifier' | 'password' | 'password_expired';
40
+ email?: string;
41
+ account?: LoginAccount | null;
42
+ error?: string | null;
43
+ /** `true` shows the link to sign up. */
44
+ registrationEnabled?: boolean;
45
+ /** Login methods allowed now (global settings × the account's preference). */
46
+ authMethods?: ResolvedAuthMethods;
47
+ magicLinkAvailable?: boolean;
48
+ otpEnabled?: boolean;
49
+ /** The method this device signed in with last time, for the "last used" badge. */
50
+ lastUsedLoginMethod?: LastLoginMethod;
51
+ passkeyFirstAvailable?: boolean;
52
+ botProtection?: BotProtectionWidget;
53
+ rememberEnabled?: boolean;
54
+ rememberDays?: number;
55
+ /** `true` after a magic link / code was sent. */
56
+ magicLinkSent?: boolean;
57
+ /** Which part of the "sent" state to show; `none` hides both. */
58
+ magicChannel?: MagicChannelProp | 'none';
59
+ /** Error of the one-time code field. */
60
+ otpError?: string;
61
+ }
62
+ /** `signup` — the interaction's registration screen. */
63
+ export interface SignupProps extends WithMessages {
64
+ uid: string;
65
+ csrfToken: string;
66
+ brand?: ClientBrand;
67
+ error?: string;
68
+ registrationDisabled?: boolean;
69
+ registrationDisabledMessage?: string;
70
+ /** `true` when signup sends a magic link instead of asking for a password. */
71
+ passwordlessSignup?: boolean;
72
+ botProtection?: BotProtectionWidget;
73
+ /** `true` after the passwordless signup link was sent. */
74
+ magicLinkSent?: boolean;
75
+ otpEnabled?: boolean;
76
+ }
77
+ /** `forgot` — request a password reset e-mail. */
78
+ export interface ForgotProps extends WithMessages {
79
+ csrfToken: string;
80
+ error?: string;
81
+ botProtection?: BotProtectionWidget;
82
+ /** `true` after the e-mail was sent. */
83
+ sent?: boolean;
84
+ }
85
+ /** `reset` — choose a new password from the e-mailed link. */
86
+ export interface ResetProps extends WithMessages {
87
+ token: string;
88
+ csrfToken: string;
89
+ error?: string;
90
+ /** `true` once the password was changed. */
91
+ done?: boolean;
92
+ }
93
+ /** `verify-email` — result of the e-mail verification link. */
94
+ export interface VerifyEmailProps extends WithMessages {
95
+ verified: boolean;
96
+ }
97
+ /** A host-registered second factor, as the `mfa-challenge` screen lists it. */
98
+ export type MfaChallengeCustomMethod = CustomMfaDescriptor & {
99
+ beginUrl: string;
100
+ verifyUrl: string;
101
+ started: boolean;
102
+ };
103
+ /** `mfa-challenge` — the second factor of a login. */
104
+ export interface MfaChallengeProps extends WithMessages {
105
+ uid: string;
106
+ csrfToken: string;
107
+ brand?: ClientBrand;
108
+ error?: string;
109
+ totpAvailable?: boolean;
110
+ passkeyAvailable?: boolean;
111
+ /** MFA is required but the account has no factor enrolled. */
112
+ noEnrollment?: boolean;
113
+ trustedDevicesEnabled?: boolean;
114
+ trustedDeviceDays?: number;
115
+ /** Too many wrong codes: the code field is locked for now. */
116
+ otpLocked?: boolean;
117
+ customMfaMethods?: MfaChallengeCustomMethod[];
118
+ completedMfaMethods?: string[];
119
+ requiredMfaFactors?: number;
120
+ }
121
+ /** A scope on the consent screen. */
122
+ export interface ConsentScope {
123
+ id: string;
124
+ label: string;
125
+ }
126
+ /**
127
+ * The authorization request behind a consent, as the provider parsed it. Only
128
+ * `client_id` is always there: device-flow and CIBA requests carry no
129
+ * `redirect_uri` / `response_type`, and plain OAuth requests may omit `scope`.
130
+ * Every other request parameter (`audience`, `resource`, `claims`, …) keeps its
131
+ * raw value under its own name.
132
+ */
133
+ export type ConsentRequestParams = {
134
+ client_id: string;
135
+ redirect_uri?: string;
136
+ response_type?: string;
137
+ scope?: string;
138
+ state?: string;
139
+ prompt?: string;
140
+ nonce?: string;
141
+ code_challenge?: string;
142
+ code_challenge_method?: string;
143
+ } & Record<string, unknown>;
144
+ /** `consent` — the user grants the client the requested scopes. */
145
+ export interface ConsentProps extends WithMessages {
146
+ uid: string;
147
+ csrfToken: string;
148
+ brand?: ClientBrand;
149
+ /** The authorization request parameters. */
150
+ params: ConsentRequestParams;
151
+ /** The client's registered `client_name`, or its `client_id`. */
152
+ clientName: string;
153
+ /** `clientName`, HTML-escaped (Edge raw output). */
154
+ clientNameHtml: string;
155
+ scopes: ConsentScope[];
156
+ }
157
+ /** `maintenance` — maintenance mode is on. */
158
+ export interface MaintenanceProps extends WithMessages {
159
+ uid?: string;
160
+ csrfToken: string;
161
+ brand?: ClientBrand;
162
+ message: string;
163
+ /** Admins may still sign in. */
164
+ adminLoginAllowed?: boolean;
165
+ adminLoginNote?: string;
166
+ }
167
+ /** `otp-unlock` — result of the "unlock my code login" link. */
168
+ export interface OtpUnlockProps extends WithMessages {
169
+ ok: boolean;
170
+ expired?: boolean;
171
+ }
172
+ /** `session-expired` — the interaction expired or was lost. */
173
+ export interface SessionExpiredProps extends WithMessages {
174
+ /** Where the "sign in again" button goes (`interactionRecovery.redirectTo`). */
175
+ loginUrl: string;
176
+ brand?: ClientBrand;
177
+ }
178
+ /** `throttled` — the request hit a rate limit. */
179
+ export interface ThrottledProps extends WithMessages {
180
+ brand?: ClientBrand;
181
+ /** Seconds until a retry is allowed, or `null` when unknown. */
182
+ retryAfter: number | null;
183
+ retryUrl: string;
184
+ }
185
+ /** The agent asking for access, on `agents/consent`. */
186
+ export interface AgentConsentRequest {
187
+ /** Device-flow user code, when the agent uses it. */
188
+ userCode?: string;
189
+ agentName: string | null;
190
+ agentOrigin: string | null;
191
+ logoUri?: string | null;
192
+ scopes: Array<{
193
+ id: string;
194
+ description: string;
195
+ }>;
196
+ /** Extra hidden form fields to send back. */
197
+ hidden?: Array<{
198
+ name: string;
199
+ value: string;
200
+ }>;
201
+ }
202
+ /** `agents/consent` — the user approves a personal agent's request. */
203
+ export interface AgentsConsentProps extends WithMessages {
204
+ csrfToken: string;
205
+ /** The form's POST target. */
206
+ action: string;
207
+ /** `null` while the user still has to enter a user code. */
208
+ request: AgentConsentRequest | null;
209
+ account: {
210
+ email: string | null;
211
+ } | null;
212
+ /** i18n key of the error to show, or `null`. */
213
+ error: string | null;
214
+ }
215
+ /** `agents/done` — outcome of a personal agent consent. */
216
+ export interface AgentsDoneProps extends WithMessages {
217
+ status: 'approved' | 'denied' | 'expired' | 'invalid';
218
+ agentName: string | null;
219
+ scopes: Array<{
220
+ id: string;
221
+ description: string;
222
+ }>;
223
+ }
224
+ /** `account/apps` — apps and personal agents with access to the account. */
225
+ export interface AccountAppsProps extends WithMessages {
226
+ csrfToken: string;
227
+ supported: boolean;
228
+ /** Personal agents with a delegation, or `null` when the feature is off. */
229
+ agents: Array<{
230
+ id: string;
231
+ name: string;
232
+ scopes: Array<{
233
+ id: string;
234
+ description: string;
235
+ }>;
236
+ }> | null;
237
+ /** Flash with the revoked app, or `null`. */
238
+ revoked: string | null;
239
+ apps: Array<{
240
+ clientId: string;
241
+ name: string;
242
+ /** The client's registered `logo_uri`, or `null`. */
243
+ logoUrl: string | null;
244
+ accessTokens: number;
245
+ refreshTokens: number;
246
+ }>;
247
+ }
248
+ /** `account/tokens` — personal access tokens. */
249
+ export interface AccountTokensProps extends WithMessages {
250
+ csrfToken: string;
251
+ /** The token just created, shown once, or `null`. */
252
+ createdToken: string | null;
253
+ tokens: Array<Pick<PatRecord, 'id' | 'name' | 'scopes' | 'audience' | 'lastUsedAt' | 'createdAt'>>;
254
+ }
255
+ /** An organization on `account/orgs`. */
256
+ export type AccountOrg = OrgSummary & {
257
+ role: string;
258
+ members: OrgMember[];
259
+ canManage: boolean;
260
+ isActive: boolean;
261
+ };
262
+ /**
263
+ * `account/orgs` — the user's organizations. With `subview: 'accept-invitation'`
264
+ * it is the invitation acceptance screen (`invitation`, `token`).
265
+ */
266
+ export interface AccountOrgsProps extends WithMessages {
267
+ csrfToken: string;
268
+ supported?: boolean;
269
+ orgs?: AccountOrg[];
270
+ pendingInvitations?: Array<OrgInvitation & {
271
+ orgName: string;
272
+ }>;
273
+ allowSelfCreate?: ResolvedOrganizationsPolicySetting['allowSelfCreate'];
274
+ availableRoles?: ResolvedOrganizationsPolicySetting['roles'];
275
+ subview?: 'accept-invitation';
276
+ invitation?: OrgInvitation | null;
277
+ token?: string;
278
+ }
279
+ /** Props of every screen, by view name. */
280
+ export interface AuthkitScreenProps {
281
+ login: LoginProps;
282
+ signup: SignupProps;
283
+ consent: ConsentProps;
284
+ forgot: ForgotProps;
285
+ reset: ResetProps;
286
+ 'verify-email': VerifyEmailProps;
287
+ 'mfa-challenge': MfaChallengeProps;
288
+ 'otp-unlock': OtpUnlockProps;
289
+ maintenance: MaintenanceProps;
290
+ 'session-expired': SessionExpiredProps;
291
+ throttled: ThrottledProps;
292
+ 'agents/consent': AgentsConsentProps;
293
+ 'agents/done': AgentsDoneProps;
294
+ 'account/login': AccountLoginProps;
295
+ 'account/tokens': AccountTokensProps;
296
+ 'account/mfa': AccountMfaProps;
297
+ 'account/security': AccountSecurityProps;
298
+ 'account/apps': AccountAppsProps;
299
+ 'account/orgs': AccountOrgsProps;
300
+ 'account/confirm': AccountConfirmProps;
301
+ 'account/email-confirmed': AccountEmailConfirmedProps;
302
+ }
303
+ /** Every screen authkit-server renders. */
304
+ export type AuthkitScreen = keyof AuthkitScreenProps;
305
+ /** What a controller hands `cfg.render` for a view: the page props without `messages`. */
306
+ export type ScreenRenderProps<View extends AuthkitScreen> = Omit<AuthkitScreenProps[View], 'messages'>;
307
+ /** `cfg.render`, typed per view. */
308
+ export type AuthkitScreenRenderer = <View extends AuthkitScreen>(ctx: import('@adonisjs/core/http').HttpContext, view: View, props: ScreenRenderProps<View>) => unknown;
309
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Props of every screen authkit-server renders through `cfg.render` — what an
3
+ * `inertiaRenderer` page (or a custom renderer) receives for each view.
4
+ *
5
+ * `AuthkitScreenProps['<view>']` is the shape a page gets, `messages` included
6
+ * (the renderer adds it). The controllers call `cfg.render` with
7
+ * {@link ScreenRenderProps} (the same shape without `messages`), and `render` is
8
+ * typed per view, so a controller that drifts from these types fails the
9
+ * package's `tsc`.
10
+ */
11
+ export {};
@@ -68,8 +68,8 @@ export declare const SUDO_SESSION_KEY = "authkit_sudo_at";
68
68
  * número e isso é contratual — está pinado em
69
69
  * `tests/host/account_confirm_controller.spec.ts` com `assert.isNumber`. Então
70
70
  * a vinculação é ADITIVA, em vez de mudar a forma do valor pinado para
71
- * `{ at, accountId }`. Bônus: as assinaturas públicas de `markSudo` /
72
- * `isSudoActive` (exportadas em `index.ts`) ficam intactas.
71
+ * `{ at, accountId }`. Bônus: a assinatura pública de `isSudoActive`
72
+ * (exportada em `index.ts`) fica intacta.
73
73
  */
74
74
  export declare const SUDO_ACCOUNT_SESSION_KEY = "authkit_sudo_account";
75
75
  /**
@@ -81,15 +81,9 @@ export declare const SUDO_ACCOUNT_SESSION_KEY = "authkit_sudo_account";
81
81
  * herdando o dono antigo. O resultado é uma marca órfã, que `isSudoActive`
82
82
  * recusa (fail-closed).
83
83
  *
84
- * @deprecated Para conceder sudo, use `completeSudo(sudoContextFrom(ctx), id)`.
85
- * `markSudo` grava a marca e nada mais: não registra o audit `sudo.confirmed`,
86
- * não lembra o método usado e não redireciona para o `return_to`. Um host que a
87
- * chame direto — o caminho natural no callback de `oidcStepUp`, antes de
88
- * `completeSudo` ser público — concede privilégio sem deixar rastro nenhum.
89
- * Continua exportada porque o login primário do próprio pacote a usa (ver o
90
- * CHANGELOG desta versão) e porque removê-la seria breaking; o uso legítimo
91
- * restante é ler/limpar a marca em fluxos que não são confirmação de
92
- * identidade.
84
+ * Interno: o único caminho público para conceder sudo é
85
+ * `completeSudo(sudoContextFrom(ctx), id)`, que além da marca registra o audit
86
+ * `sudo.confirmed`, lembra o método usado e redireciona para o `return_to`.
93
87
  */
94
88
  export declare function markSudo(ctx: HttpContext): void;
95
89
  /**
@@ -87,8 +87,8 @@ export const SUDO_SESSION_KEY = 'authkit_sudo_at';
87
87
  * número e isso é contratual — está pinado em
88
88
  * `tests/host/account_confirm_controller.spec.ts` com `assert.isNumber`. Então
89
89
  * a vinculação é ADITIVA, em vez de mudar a forma do valor pinado para
90
- * `{ at, accountId }`. Bônus: as assinaturas públicas de `markSudo` /
91
- * `isSudoActive` (exportadas em `index.ts`) ficam intactas.
90
+ * `{ at, accountId }`. Bônus: a assinatura pública de `isSudoActive`
91
+ * (exportada em `index.ts`) fica intacta.
92
92
  */
93
93
  export const SUDO_ACCOUNT_SESSION_KEY = 'authkit_sudo_account';
94
94
  /**
@@ -100,15 +100,9 @@ export const SUDO_ACCOUNT_SESSION_KEY = 'authkit_sudo_account';
100
100
  * herdando o dono antigo. O resultado é uma marca órfã, que `isSudoActive`
101
101
  * recusa (fail-closed).
102
102
  *
103
- * @deprecated Para conceder sudo, use `completeSudo(sudoContextFrom(ctx), id)`.
104
- * `markSudo` grava a marca e nada mais: não registra o audit `sudo.confirmed`,
105
- * não lembra o método usado e não redireciona para o `return_to`. Um host que a
106
- * chame direto — o caminho natural no callback de `oidcStepUp`, antes de
107
- * `completeSudo` ser público — concede privilégio sem deixar rastro nenhum.
108
- * Continua exportada porque o login primário do próprio pacote a usa (ver o
109
- * CHANGELOG desta versão) e porque removê-la seria breaking; o uso legítimo
110
- * restante é ler/limpar a marca em fluxos que não são confirmação de
111
- * identidade.
103
+ * Interno: o único caminho público para conceder sudo é
104
+ * `completeSudo(sudoContextFrom(ctx), id)`, que além da marca registra o audit
105
+ * `sudo.confirmed`, lembra o método usado e redireciona para o `return_to`.
112
106
  */
113
107
  export function markSudo(ctx) {
114
108
  const accountId = ctx.session.get(ACCOUNT_SESSION_KEY);