@jwilleke/ngdpbase-types 0.0.0 → 5.18.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/LICENSE +201 -0
- package/README.md +16 -2
- package/lib/packages/ngdpbase-types/index.d.ts +32 -0
- package/lib/packages/ngdpbase-types/testing.d.ts +43 -0
- package/lib/packages/ngdpbase-types/testing.js +53 -0
- package/lib/src/cache/CacheDependencies.d.ts +50 -0
- package/lib/src/cache/CacheDependencies.js +50 -0
- package/lib/src/cache/ICacheAdapter.d.ts +86 -0
- package/lib/src/cache/RegionCache.d.ts +171 -0
- package/lib/src/context/ActorContext.d.ts +48 -0
- package/lib/src/context/ActorContext.js +33 -0
- package/lib/src/context/ApiContext.d.ts +194 -0
- package/lib/src/context/ApiContext.js +273 -0
- package/lib/src/context/BaseContext.d.ts +110 -0
- package/lib/src/context/BaseContext.js +143 -0
- package/lib/src/context/JobContext.d.ts +153 -0
- package/lib/src/context/JobContext.js +144 -0
- package/lib/src/context/WikiContext.d.ts +404 -0
- package/lib/src/context/bootActions.d.ts +64 -0
- package/lib/src/context/bootActions.js +111 -0
- package/lib/src/converters/HtmlConverter.d.ts +70 -0
- package/lib/src/converters/IContentConverter.d.ts +94 -0
- package/lib/src/converters/JSPWikiConverter.d.ts +117 -0
- package/lib/src/converters/conversionWarning.d.ts +15 -0
- package/lib/src/converters/ncm/bridge.d.ts +22 -0
- package/lib/src/converters/ncm/existing.d.ts +23 -0
- package/lib/src/converters/ncm/fix/blocks.d.ts +89 -0
- package/lib/src/converters/ncm/fix/bulletMarkers.d.ts +21 -0
- package/lib/src/converters/ncm/fix/index.d.ts +36 -0
- package/lib/src/converters/ncm/fix/jspwikiBullets.d.ts +20 -0
- package/lib/src/converters/ncm/fix/jspwikiCodeMarkers.d.ts +38 -0
- package/lib/src/converters/ncm/fix/jspwikiHeadings.d.ts +25 -0
- package/lib/src/converters/ncm/fix/jspwikiIndent.d.ts +24 -0
- package/lib/src/converters/ncm/fix/jspwikiInsert.d.ts +21 -0
- package/lib/src/converters/ncm/fix/jspwikiItalic.d.ts +29 -0
- package/lib/src/converters/ncm/fix/jspwikiLinkLeftovers.d.ts +27 -0
- package/lib/src/converters/ncm/fix/moreInformationFooter.d.ts +25 -0
- package/lib/src/converters/ncm/fix/styleClosers.d.ts +25 -0
- package/lib/src/converters/ncm/fix/tightenLists.d.ts +30 -0
- package/lib/src/converters/ncm/fix/types.d.ts +33 -0
- package/lib/src/converters/ncm/footnotes.d.ts +39 -0
- package/lib/src/converters/ncm/images.d.ts +49 -0
- package/lib/src/converters/ncm/index.d.ts +20 -0
- package/lib/src/converters/ncm/links.d.ts +21 -0
- package/lib/src/converters/ncm/normalize.d.ts +39 -0
- package/lib/src/converters/ncm/placeholder.d.ts +25 -0
- package/lib/src/converters/ncm/tables.d.ts +36 -0
- package/lib/src/converters/ncm/types.d.ts +42 -0
- package/lib/src/http/egressPolicy.d.ts +52 -0
- package/lib/src/http/guardedFetch.d.ts +89 -0
- package/lib/src/http/guardedFetch.js +113 -0
- package/lib/src/http/guardedLookup.d.ts +48 -0
- package/lib/src/http/guardedLookup.js +73 -0
- package/lib/src/http/ssrf.d.ts +83 -0
- package/lib/src/http/ssrf.js +311 -0
- package/lib/src/mail/MailProvider.d.ts +36 -0
- package/lib/src/mail/NodemailerMailProvider.d.ts +26 -0
- package/lib/src/managers/AddonsManager.d.ts +691 -0
- package/lib/src/managers/AgentTokenManager.d.ts +269 -0
- package/lib/src/managers/AssetManager.d.ts +116 -0
- package/lib/src/managers/AttachmentManager.d.ts +1032 -0
- package/lib/src/managers/AuthManager.d.ts +1257 -0
- package/lib/src/managers/BackgroundJobManager.d.ts +434 -0
- package/lib/src/managers/BaseManager.d.ts +391 -0
- package/lib/src/managers/BaseManager.js +415 -0
- package/lib/src/managers/CacheManager.d.ts +159 -0
- package/lib/src/managers/CatalogManager.d.ts +235 -0
- package/lib/src/managers/CommentManager.d.ts +75 -0
- package/lib/src/managers/ConfigurationManager.d.ts +747 -0
- package/lib/src/managers/DatabaseManager.d.ts +89 -0
- package/lib/src/managers/DatabaseManager.js +236 -0
- package/lib/src/managers/EmailManager.d.ts +47 -0
- package/lib/src/managers/ExportManager.d.ts +129 -0
- package/lib/src/managers/ExportManager.js +420 -0
- package/lib/src/managers/FootnoteManager.d.ts +118 -0
- package/lib/src/managers/MediaManager.d.ts +279 -0
- package/lib/src/managers/MetricsManager.d.ts +89 -0
- package/lib/src/managers/NotificationManager.d.ts +165 -0
- package/lib/src/managers/OidcManager.d.ts +210 -0
- package/lib/src/managers/OrganizationManager.d.ts +107 -0
- package/lib/src/managers/PageManager.d.ts +1645 -0
- package/lib/src/managers/PersonManager.d.ts +48 -0
- package/lib/src/managers/PluginManager.d.ts +179 -0
- package/lib/src/managers/PluginManager.js +393 -0
- package/lib/src/managers/PolicyEvaluator.d.ts +213 -0
- package/lib/src/managers/RenderingManager.d.ts +358 -0
- package/lib/src/managers/RoleManager.d.ts +134 -0
- package/lib/src/managers/SearchManager.d.ts +402 -0
- package/lib/src/managers/ShareManager.d.ts +251 -0
- package/lib/src/managers/TemplateManager.d.ts +176 -0
- package/lib/src/managers/ThemeManager.d.ts +56 -0
- package/lib/src/managers/UserManager.d.ts +480 -0
- package/lib/src/managers/ValidationManager.d.ts +426 -0
- package/lib/src/managers/VariableManager.d.ts +145 -0
- package/lib/src/middleware/csrf.d.ts +3 -0
- package/lib/src/parsers/LinkParser.d.ts +457 -0
- package/lib/src/parsers/MarkupParser.d.ts +769 -0
- package/lib/src/parsers/context/ParseContext.d.ts +314 -0
- package/lib/src/parsers/data/emoji-map.d.ts +18 -0
- package/lib/src/parsers/dom/DOMBuilder.d.ts +210 -0
- package/lib/src/parsers/dom/DOMParser.d.ts +252 -0
- package/lib/src/parsers/dom/Tokenizer.d.ts +358 -0
- package/lib/src/parsers/dom/WikiDocument.d.ts +272 -0
- package/lib/src/parsers/dom/handlers/DOMLinkHandler.d.ts +260 -0
- package/lib/src/parsers/dom/handlers/DOMPluginHandler.d.ts +212 -0
- package/lib/src/parsers/dom/handlers/DOMVariableHandler.d.ts +171 -0
- package/lib/src/parsers/filters/BaseFilter.d.ts +269 -0
- package/lib/src/parsers/filters/FilterChain.d.ts +320 -0
- package/lib/src/parsers/handlers/BaseSyntaxHandler.d.ts +331 -0
- package/lib/src/parsers/handlers/HandlerRegistry.d.ts +225 -0
- package/lib/src/parsers/handlers/JSPWikiPreprocessor.d.ts +89 -0
- package/lib/src/parsers/handlers/LinkParserHandler.d.ts +80 -0
- package/lib/src/parsers/handlers/PluginSyntaxHandler.d.ts +78 -0
- package/lib/src/parsers/handlers/WikiFormHandler.d.ts +149 -0
- package/lib/src/parsers/handlers/WikiTagHandler.d.ts +125 -0
- package/lib/src/parsers/jspwikiTableRow.d.ts +39 -0
- package/lib/src/parsers/styleBlockSyntax.d.ts +14 -0
- package/lib/src/plugins/types.d.ts +79 -0
- package/lib/src/providers/AgentTokenAuthProvider.d.ts +40 -0
- package/lib/src/providers/AuthentikBearerAuthProvider.d.ts +75 -0
- package/lib/src/providers/BaseAttachmentProvider.d.ts +197 -0
- package/lib/src/providers/BaseAuthProvider.d.ts +220 -0
- package/lib/src/providers/BaseCacheProvider.d.ts +140 -0
- package/lib/src/providers/BaseCredentialsProvider.d.ts +84 -0
- package/lib/src/providers/BaseDatabaseProvider.d.ts +22 -0
- package/lib/src/providers/BaseDatabaseProvider.js +12 -0
- package/lib/src/providers/BaseLoggingProvider.d.ts +68 -0
- package/lib/src/providers/BaseLoggingProvider.js +37 -0
- package/lib/src/providers/BaseMediaProvider.d.ts +308 -0
- package/lib/src/providers/BasePageProvider.d.ts +337 -0
- package/lib/src/providers/BaseProvider.d.ts +98 -0
- package/lib/src/providers/BaseProvider.js +74 -0
- package/lib/src/providers/BaseSearchProvider.d.ts +362 -0
- package/lib/src/providers/BasicAttachmentProvider.d.ts +599 -0
- package/lib/src/providers/CloudflareAccessAuthProvider.d.ts +72 -0
- package/lib/src/providers/FileCredentialsProvider.d.ts +33 -0
- package/lib/src/providers/FileJobStateProvider.d.ts +58 -0
- package/lib/src/providers/FileLoggingProvider.d.ts +43 -0
- package/lib/src/providers/FileLoggingProvider.js +120 -0
- package/lib/src/providers/FileOidcAdapter.d.ts +25 -0
- package/lib/src/providers/FileSystemMediaProvider.d.ts +403 -0
- package/lib/src/providers/LunrSearchProvider.d.ts +280 -0
- package/lib/src/providers/MagicLinkAuthProvider.d.ts +125 -0
- package/lib/src/providers/NullCacheProvider.d.ts +72 -0
- package/lib/src/providers/OidcAuthProvider.d.ts +180 -0
- package/lib/src/providers/OidcBearerAuthProvider.d.ts +30 -0
- package/lib/src/providers/PasskeyAuthProvider.d.ts +58 -0
- package/lib/src/providers/PasswordAuthProvider.d.ts +20 -0
- package/lib/src/providers/SqliteDatabaseProvider.d.ts +55 -0
- package/lib/src/providers/SqliteDatabaseProvider.js +90 -0
- package/lib/src/providers/SqliteJobStateProvider.d.ts +41 -0
- package/lib/src/providers/databaseMigrations.d.ts +8 -0
- package/lib/src/providers/databaseMigrations.js +47 -0
- package/lib/src/providers/jobState.d.ts +99 -0
- package/lib/src/providers/lunrQueryToMiniSearch.d.ts +32 -0
- package/lib/src/providers/mediaFilenameDate.d.ts +22 -0
- package/lib/src/providers/sqliteLocation.d.ts +17 -0
- package/lib/src/providers/sqliteLocation.js +55 -0
- package/lib/src/providers/sqliteMigrations.d.ts +48 -0
- package/lib/src/providers/sqliteMigrations.js +72 -0
- package/lib/src/rendering/githubAlerts.d.ts +22 -0
- package/lib/src/rendering/htmlPolicy.d.ts +76 -0
- package/lib/src/rendering/markdownConverter.d.ts +73 -0
- package/lib/src/security/PolicyDecisionPoint.d.ts +245 -0
- package/lib/src/security/PolicyInformationPoint.d.ts +448 -0
- package/lib/src/security/pageActions.d.ts +40 -0
- package/lib/src/security/permissions.generated.d.ts +78 -0
- package/lib/src/types/Asset.d.ts +414 -0
- package/lib/src/types/Catalog.d.ts +61 -0
- package/lib/src/types/Comment.d.ts +11 -0
- package/lib/src/types/Config.d.ts +388 -0
- package/lib/src/types/Organization.d.ts +78 -0
- package/lib/src/types/OrganizationProvider.d.ts +38 -0
- package/lib/src/types/Page.d.ts +220 -0
- package/lib/src/types/Person.d.ts +56 -0
- package/lib/src/types/PersonProvider.d.ts +25 -0
- package/lib/src/types/Policy.d.ts +139 -0
- package/lib/src/types/Policy.js +17 -0
- package/lib/src/types/Provider.d.ts +810 -0
- package/lib/src/types/Role.d.ts +84 -0
- package/lib/src/types/RoleProvider.d.ts +35 -0
- package/lib/src/types/Schema.d.ts +425 -0
- package/lib/src/types/Share.d.ts +172 -0
- package/lib/src/types/Share.js +81 -0
- package/lib/src/types/User.d.ts +304 -0
- package/lib/src/types/Version.d.ts +189 -0
- package/lib/src/types/WikiEngine.d.ts +128 -0
- package/lib/src/types/express.d.ts +120 -0
- package/lib/src/types/express.js +1 -0
- package/lib/src/types/guards.d.ts +78 -0
- package/lib/src/types/heic-decode.d.ts +16 -0
- package/lib/src/types/index.d.ts +19 -0
- package/lib/src/types/linkedom.d.ts +77 -0
- package/lib/src/types/markdown-it-plugins.d.ts +21 -0
- package/lib/src/utils/DeltaStorage.d.ts +118 -0
- package/lib/src/utils/LocaleUtils.d.ts +105 -0
- package/lib/src/utils/LocaleUtils.js +294 -0
- package/lib/src/utils/PageNameMatcher.d.ts +98 -0
- package/lib/src/utils/PathPreflight.d.ts +38 -0
- package/lib/src/utils/PathPreflight.js +50 -0
- package/lib/src/utils/SectionUtils.d.ts +40 -0
- package/lib/src/utils/addonConfigLayer.d.ts +144 -0
- package/lib/src/utils/addonConfigLayer.js +382 -0
- package/lib/src/utils/addonDatabaseRules.d.ts +25 -0
- package/lib/src/utils/addonDatabaseRules.js +57 -0
- package/lib/src/utils/addonHost.d.ts +51 -0
- package/lib/src/utils/addonHost.js +72 -0
- package/lib/src/utils/addonInstaller.d.ts +109 -0
- package/lib/src/utils/addonLock.d.ts +37 -0
- package/lib/src/utils/addonLock.js +48 -0
- package/lib/src/utils/addonPageSync.d.ts +45 -0
- package/lib/src/utils/addonProvenance.d.ts +48 -0
- package/lib/src/utils/addonTarball.d.ts +41 -0
- package/lib/src/utils/addonsPathResolver.d.ts +76 -0
- package/lib/src/utils/addonsPathResolver.js +129 -0
- package/lib/src/utils/assetMetadataTags.d.ts +66 -0
- package/lib/src/utils/atomicWrite.d.ts +28 -0
- package/lib/src/utils/auditConfigChange.d.ts +57 -0
- package/lib/src/utils/auditEventNames.d.ts +50 -0
- package/lib/src/utils/auditEventNames.generated.d.ts +163 -0
- package/lib/src/utils/auditEventNames.generated.js +168 -0
- package/lib/src/utils/auditEventNames.js +49 -0
- package/lib/src/utils/auditEvents.d.ts +265 -0
- package/lib/src/utils/auditEvents.js +309 -0
- package/lib/src/utils/auditRegistry.d.ts +102 -0
- package/lib/src/utils/auditRegistry.js +168 -0
- package/lib/src/utils/bip39English.d.ts +2 -0
- package/lib/src/utils/configEnvKeys.d.ts +68 -0
- package/lib/src/utils/configFiles.d.ts +74 -0
- package/lib/src/utils/configFiles.js +150 -0
- package/lib/src/utils/delegation.d.ts +48 -0
- package/lib/src/utils/deriveUsername.d.ts +21 -0
- package/lib/src/utils/explainMediaPath.d.ts +123 -0
- package/lib/src/utils/formerTitles.d.ts +69 -0
- package/lib/src/utils/frontmatterAccess.d.ts +54 -0
- package/lib/src/utils/headlessAdminPassword.d.ts +55 -0
- package/lib/src/utils/imageTransform.d.ts +52 -0
- package/lib/src/utils/instanceEnvSecret.d.ts +51 -0
- package/lib/src/utils/jobSlots.d.ts +43 -0
- package/lib/src/utils/keywordNormalizer.d.ts +127 -0
- package/lib/src/utils/logger.d.ts +70 -0
- package/lib/src/utils/logger.js +106 -0
- package/lib/src/utils/magicLinkDeviceState.d.ts +95 -0
- package/lib/src/utils/managerUtils.d.ts +40 -0
- package/lib/src/utils/migrateLegacyPrivateAttachments.d.ts +15 -0
- package/lib/src/utils/orgFilename.d.ts +22 -0
- package/lib/src/utils/pageFeature.d.ts +27 -0
- package/lib/src/utils/pageFrontmatter.d.ts +21 -0
- package/lib/src/utils/pageNotes.d.ts +150 -0
- package/lib/src/utils/pageTitleRule.d.ts +37 -0
- package/lib/src/utils/pageToArticle.d.ts +59 -0
- package/lib/src/utils/pageUrl.d.ts +18 -0
- package/lib/src/utils/passwordChange.d.ts +12 -0
- package/lib/src/utils/passwordHash.d.ts +60 -0
- package/lib/src/utils/permissionCatalog.d.ts +52 -0
- package/lib/src/utils/pluginFormatters.d.ts +452 -0
- package/lib/src/utils/pluginFormatters.js +714 -0
- package/lib/src/utils/privateLinkRewrite.d.ts +42 -0
- package/lib/src/utils/privateStoreAccess.d.ts +57 -0
- package/lib/src/utils/privateStoreAccess.js +65 -0
- package/lib/src/utils/privateStoreCatalogs.d.ts +40 -0
- package/lib/src/utils/privateStoreCrypto.d.ts +127 -0
- package/lib/src/utils/privateStoreDoor.d.ts +181 -0
- package/lib/src/utils/privateStoreExport.d.ts +72 -0
- package/lib/src/utils/privateStoreFiles.d.ts +120 -0
- package/lib/src/utils/privateStoreMeta.d.ts +64 -0
- package/lib/src/utils/privateStoreMigrations.d.ts +37 -0
- package/lib/src/utils/privateStorePath.d.ts +259 -0
- package/lib/src/utils/privateStorePath.js +436 -0
- package/lib/src/utils/privateStoreTakeout.d.ts +76 -0
- package/lib/src/utils/privateStoreUnlock.d.ts +175 -0
- package/lib/src/utils/proxyClaim.d.ts +59 -0
- package/lib/src/utils/proxyClaim.js +30 -0
- package/lib/src/utils/redactPrivateNames.d.ts +21 -0
- package/lib/src/utils/redactPrivateNames.js +89 -0
- package/lib/src/utils/redactSecrets.d.ts +58 -0
- package/lib/src/utils/redactSecrets.js +171 -0
- package/lib/src/utils/renameLinkRewrite.d.ts +112 -0
- package/lib/src/utils/roleGrants.d.ts +35 -0
- package/lib/src/utils/safeRedirect.d.ts +32 -0
- package/lib/src/utils/schedule.d.ts +54 -0
- package/lib/src/utils/secretFileMode.d.ts +17 -0
- package/lib/src/utils/seededShippedPages.d.ts +45 -0
- package/lib/src/utils/sensitiveValues.d.ts +59 -0
- package/lib/src/utils/sensitiveValues.js +106 -0
- package/lib/src/utils/sessionEnd.d.ts +12 -0
- package/lib/src/utils/sessionGeneration.d.ts +24 -0
- package/lib/src/utils/sessionIdle.d.ts +41 -0
- package/lib/src/utils/sessionLimits.d.ts +68 -0
- package/lib/src/utils/shareLockbox.d.ts +64 -0
- package/lib/src/utils/sigstoreTrustedRoot.d.ts +33 -0
- package/lib/src/utils/sniffMime.d.ts +21 -0
- package/lib/src/utils/storeSearchIndex.d.ts +75 -0
- package/lib/src/utils/subjectMayDo.d.ts +21 -0
- package/lib/src/utils/systemCategoryRules.d.ts +30 -0
- package/lib/src/utils/systemCategoryRules.js +41 -0
- package/lib/src/utils/userCreateError.d.ts +62 -0
- package/lib/src/utils/username.d.ts +53 -0
- package/lib/src/utils/vaultUrlRewrite.d.ts +27 -0
- package/lib/src/utils/version.d.ts +84 -0
- package/lib/src/utils/videoFrame.d.ts +21 -0
- package/package.json +54 -3
|
@@ -0,0 +1,1257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AuthManager — pluggable authentication provider chain.
|
|
3
|
+
*
|
|
4
|
+
* Registers one or more AuthProviders and delegates authenticate/initiate
|
|
5
|
+
* calls to the appropriate provider. Routes call only AuthManager — never
|
|
6
|
+
* individual providers directly.
|
|
7
|
+
*
|
|
8
|
+
* `ngdpbase.auth.factors` lists the methods offered (#1523): one entry per
|
|
9
|
+
* provider, with the `amr` / `aal` / `acr` it gives — lowered to what the
|
|
10
|
+
* provider's own code declares, never raised. What a sign-in must reach is set
|
|
11
|
+
* per role (`required-aal`), not here. Signing in still uses one factor; the
|
|
12
|
+
* multi-factor flow, the per-role check and the boot refusal follow under #1523.
|
|
13
|
+
*
|
|
14
|
+
* Built-in providers, each gated on its own config key and all registered
|
|
15
|
+
* through the public {@link AuthManager.registerProvider} (#1050):
|
|
16
|
+
* - PasswordAuthProvider (unless ngdpbase.auth.password.enabled is false)
|
|
17
|
+
* - MagicLinkAuthProvider (ngdpbase.auth.magic-link.enabled)
|
|
18
|
+
* - OidcAuthProvider, one per entry (ngdpbase.auth.oidc.providers; the old
|
|
19
|
+
* ngdpbase.auth.google-oidc.* keys are read as the `google` entry, #1743)
|
|
20
|
+
* - CloudflareAccessAuthProvider (ngdpbase.auth.cloudflare-access.enabled)
|
|
21
|
+
* - AuthentikBearerAuthProvider (ngdpbase.auth.authentik-bearer.enabled)
|
|
22
|
+
* - AgentTokenAuthProvider (ngdpbase.auth.agent-token.enabled)
|
|
23
|
+
*
|
|
24
|
+
* Addons register through the same method, so the contributed path is the one
|
|
25
|
+
* exercised on every boot rather than a second, less-travelled one.
|
|
26
|
+
*
|
|
27
|
+
* Future providers (see #421):
|
|
28
|
+
* - TotpAuthProvider
|
|
29
|
+
*
|
|
30
|
+
* @see {@link https://github.com/jwilleke/ngdpbase/issues/396}
|
|
31
|
+
*/
|
|
32
|
+
import BaseManager from './BaseManager.js';
|
|
33
|
+
import type { BackupData } from './BaseManager.js';
|
|
34
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
35
|
+
import type { PermissionSubject } from './UserManager.js';
|
|
36
|
+
import type { JobContext } from '../context/JobContext.js';
|
|
37
|
+
import type { Aal, AuthProvider, AuthInitiateContext, AuthVerifyCredentials, PhishingResistance, ViaToken } from '../providers/BaseAuthProvider.js';
|
|
38
|
+
import { type OutsideProviderIcon } from '../providers/OidcAuthProvider.js';
|
|
39
|
+
import { type PasskeyRelyingParty } from '../providers/PasskeyAuthProvider.js';
|
|
40
|
+
import { type CredentialKind, type CredentialRecord } from '../providers/BaseCredentialsProvider.js';
|
|
41
|
+
import { type AuditEventName } from '../utils/auditEventNames.js';
|
|
42
|
+
import { type ScopeDescription } from '../utils/permissionCatalog.js';
|
|
43
|
+
import type { Request } from 'express';
|
|
44
|
+
/** One entry of `ngdpbase.auth.factors`, as written in configuration (#1523). */
|
|
45
|
+
export interface FactorEntry {
|
|
46
|
+
authproviderid: string;
|
|
47
|
+
primary?: boolean;
|
|
48
|
+
amr?: string[];
|
|
49
|
+
aal?: Aal;
|
|
50
|
+
acr?: PhishingResistance;
|
|
51
|
+
enabled?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface Factor {
|
|
54
|
+
provider: string;
|
|
55
|
+
primary: boolean;
|
|
56
|
+
amr: string[];
|
|
57
|
+
aal: Aal;
|
|
58
|
+
acr?: PhishingResistance;
|
|
59
|
+
}
|
|
60
|
+
/** A factor a sign-in satisfied, and when — what step-up asks about (#1525). */
|
|
61
|
+
export interface SatisfiedFactor extends Omit<Factor, 'primary'> {
|
|
62
|
+
at: string;
|
|
63
|
+
}
|
|
64
|
+
/** NIST SP 800-63 factor type. */
|
|
65
|
+
export type FactorType = 'know' | 'have' | 'are';
|
|
66
|
+
/** What a set of satisfied factors amounts to (#1523). */
|
|
67
|
+
export interface Assurance {
|
|
68
|
+
/** The combined RFC 8176 `amr` array. */
|
|
69
|
+
amr: string[];
|
|
70
|
+
aal: Aal;
|
|
71
|
+
/** What we claim, strongest first: `phrh`, `phr`, then the AAL. Never overstated. */
|
|
72
|
+
acr: PhishingResistance | 'aal1' | 'aal2' | 'aal3';
|
|
73
|
+
/** Two or more distinct factor types — what MFA means here, not two providers. */
|
|
74
|
+
mfa: boolean;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* An outside provider's report, as kept (#1633): only well-formed values,
|
|
78
|
+
* bounded, and whether the admin trusts this provider's own second factor.
|
|
79
|
+
* Recorded in the sign-in's audit record whether or not it counts.
|
|
80
|
+
*/
|
|
81
|
+
export interface ReportedValues {
|
|
82
|
+
amr: string[];
|
|
83
|
+
acr?: string;
|
|
84
|
+
trusted: boolean;
|
|
85
|
+
}
|
|
86
|
+
/** A credential as shown to a person: never its secret (#1524). */
|
|
87
|
+
export type CredentialView = Omit<CredentialRecord, 'secret'>;
|
|
88
|
+
/** What enrolling a credential supplies; id and times are the store's. */
|
|
89
|
+
export interface CredentialInput {
|
|
90
|
+
kind: CredentialKind;
|
|
91
|
+
subject: string;
|
|
92
|
+
secret: string;
|
|
93
|
+
label: string;
|
|
94
|
+
}
|
|
95
|
+
/** The longest name a credential may carry. */
|
|
96
|
+
export declare const CREDENTIAL_LABEL_MAX = 60;
|
|
97
|
+
/**
|
|
98
|
+
* A credential's name, tidied: whitespace collapsed, at most 60 characters.
|
|
99
|
+
* Empty is refused — two passkeys both called "Passkey" cannot be told apart
|
|
100
|
+
* when one has to be removed (operator, 2026-10-04).
|
|
101
|
+
*/
|
|
102
|
+
export declare function credentialLabel(label: unknown): string;
|
|
103
|
+
/**
|
|
104
|
+
* A sign-in waiting for its second factor (#1523): the first factor passed,
|
|
105
|
+
* no session exists yet. Held by AuthManager only — never a provider —
|
|
106
|
+
* bound to the requesting browser, single-use, and short-lived.
|
|
107
|
+
*/
|
|
108
|
+
export interface PendingSignIn {
|
|
109
|
+
handle: string;
|
|
110
|
+
username: string;
|
|
111
|
+
first: AuthenticateResult;
|
|
112
|
+
/** The requesting browser's binding value (an HTTP-only cookie); every step must present it. */
|
|
113
|
+
binding: string;
|
|
114
|
+
requestedAt: number;
|
|
115
|
+
expiresAt: number;
|
|
116
|
+
requestIp?: string;
|
|
117
|
+
requestUserAgent?: string;
|
|
118
|
+
/** Private-store keys unlocked by the password, held until the sign-in completes or is dropped. */
|
|
119
|
+
privateStoreHandle?: string;
|
|
120
|
+
/** The second factor, once satisfied. */
|
|
121
|
+
approved?: SatisfiedFactor;
|
|
122
|
+
/** Refused by the person ("this wasn't me"). */
|
|
123
|
+
denied?: boolean;
|
|
124
|
+
/** When the last approval message went out, for the resend limit. */
|
|
125
|
+
lastSentAt?: number;
|
|
126
|
+
}
|
|
127
|
+
/** A second factor a person has enrolled and can use now. */
|
|
128
|
+
export interface AvailableSecondFactor {
|
|
129
|
+
id: 'email-link';
|
|
130
|
+
label: string;
|
|
131
|
+
/** Where it goes, masked for display (j***@example.com). */
|
|
132
|
+
target: string;
|
|
133
|
+
}
|
|
134
|
+
/** j***@example.com — enough to recognise, not enough to harvest. */
|
|
135
|
+
export declare function maskEmail(address: string): string;
|
|
136
|
+
/**
|
|
137
|
+
* Resolve an entry's `icon` (#1781). Accepted: an `https://` address, a site
|
|
138
|
+
* path (starts with one `/`, no `\`), or a Font Awesome name, normalised to
|
|
139
|
+
* its full class (`fa-users` → `fas fa-users`). Anything else — `http:`,
|
|
140
|
+
* `javascript:`, `data:`, a relative path — is refused with a warning naming
|
|
141
|
+
* the entry, and the default shown. Unset: the preset's icon, else generic.
|
|
142
|
+
*/
|
|
143
|
+
export declare function resolveOutsideProviderIcon(name: string, icon: unknown, preset: string | undefined, warn: (message: string) => void): OutsideProviderIcon;
|
|
144
|
+
/** An outside sign-in provider as offered: on the login page, and to connect on the profile (#1743). */
|
|
145
|
+
export interface OutsideProvider {
|
|
146
|
+
/** `oidc:<name>` */
|
|
147
|
+
id: string;
|
|
148
|
+
/** The `<name>` in its URLs. */
|
|
149
|
+
name: string;
|
|
150
|
+
displayName: string;
|
|
151
|
+
icon: OutsideProviderIcon;
|
|
152
|
+
}
|
|
153
|
+
/** An outside identity connected to an account, as shown to its owner (#1743). */
|
|
154
|
+
export interface ConnectedIdentity {
|
|
155
|
+
/** The credential row's id — what Disconnect removes. */
|
|
156
|
+
id: string;
|
|
157
|
+
/** What the provider was called when it was connected. */
|
|
158
|
+
label: string;
|
|
159
|
+
/** The provider's verified email, '' when it gave none. */
|
|
160
|
+
email: string;
|
|
161
|
+
createdAt: string;
|
|
162
|
+
lastUsedAt?: string;
|
|
163
|
+
/** The provider it came from, while it is configured and enabled; null when it is not, and it cannot sign in. */
|
|
164
|
+
provider: OutsideProvider | null;
|
|
165
|
+
}
|
|
166
|
+
/** How connecting an outside identity ended (#1743). */
|
|
167
|
+
export type ConnectOutcome = 'connected' | 'refused' | 'already-yours' | 'connected-elsewhere';
|
|
168
|
+
/**
|
|
169
|
+
* A verified outside identity no account has connected, held in the session
|
|
170
|
+
* until the person signs in another way and confirms it (#1743 slice 3). One
|
|
171
|
+
* at a time; a new one replaces the old. `email` is the provider's verified
|
|
172
|
+
* email, or ''.
|
|
173
|
+
*/
|
|
174
|
+
export interface PendingConnection {
|
|
175
|
+
providerId: string;
|
|
176
|
+
/** `${iss}#${sub}` */
|
|
177
|
+
subject: string;
|
|
178
|
+
email: string;
|
|
179
|
+
/** The provider's name, for the notice. */
|
|
180
|
+
displayName: string;
|
|
181
|
+
/** Epoch ms. */
|
|
182
|
+
expiresAt: number;
|
|
183
|
+
/** Where the sign-in that follows was going; a declined connection goes there. */
|
|
184
|
+
returnTo?: string;
|
|
185
|
+
}
|
|
186
|
+
/** How an outside sign-in's callback ended (#1743), for the route to answer. */
|
|
187
|
+
export type OutsideSignIn =
|
|
188
|
+
/** Signed in (`created`: an account was made for it first); `next` is where to go. */
|
|
189
|
+
{
|
|
190
|
+
outcome: 'signed-in' | 'created';
|
|
191
|
+
next: string;
|
|
192
|
+
}
|
|
193
|
+
/** Held as a pending connection: the person signs in another way to connect it. `error` is what to say, if anything. */
|
|
194
|
+
| {
|
|
195
|
+
outcome: 'connect-first';
|
|
196
|
+
error?: string;
|
|
197
|
+
}
|
|
198
|
+
/** Refused; nothing says whether an account exists. */
|
|
199
|
+
| {
|
|
200
|
+
outcome: 'refused';
|
|
201
|
+
}
|
|
202
|
+
/** The state was already used: no second session. */
|
|
203
|
+
| {
|
|
204
|
+
outcome: 'replayed';
|
|
205
|
+
};
|
|
206
|
+
/** What the one completion step leaves for the route (#1743): where to go instead, when a pending identity waits to be confirmed. */
|
|
207
|
+
export interface SignInCompleted {
|
|
208
|
+
next?: string;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* How a delegated credential is renewed (#1764): `fixed` ends on its date and
|
|
212
|
+
* someone makes a new one; `refreshable` is renewed automatically, with a new
|
|
213
|
+
* key each time. `extendable` (renewed by hand, same key) is #1734, not built.
|
|
214
|
+
*/
|
|
215
|
+
export type Renewal = 'fixed' | 'refreshable';
|
|
216
|
+
/**
|
|
217
|
+
* One row of a person's "My tokens and devices" list (#1764): an agent token
|
|
218
|
+
* they made (`token`), or an app or device they approved through this site's
|
|
219
|
+
* own sign-in server (`app`). Never a secret.
|
|
220
|
+
*/
|
|
221
|
+
export interface Delegation {
|
|
222
|
+
kind: 'token' | 'app';
|
|
223
|
+
id: string;
|
|
224
|
+
name: string;
|
|
225
|
+
/** An app approved on a device by entering a code. */
|
|
226
|
+
device: boolean;
|
|
227
|
+
renewal: Renewal;
|
|
228
|
+
/** What it may do: permission names (and, for an app, its sign-in scopes). */
|
|
229
|
+
scopes: string[];
|
|
230
|
+
/** `scopes` in plain words, for a person to read (#1764): `describeScopes`. */
|
|
231
|
+
can: ScopeDescription[];
|
|
232
|
+
/** ISO 8601: made or approved. */
|
|
233
|
+
createdAt: string | null;
|
|
234
|
+
/** ISO 8601: when it stops working; null when it does not end by date. */
|
|
235
|
+
expiresAt: string | null;
|
|
236
|
+
/** ISO 8601: when a refreshable one was last renewed; null if not yet. */
|
|
237
|
+
renewedAt: string | null;
|
|
238
|
+
/** ISO 8601: an agent token's last use; null for an app, or never used. */
|
|
239
|
+
lastUsedAt: string | null;
|
|
240
|
+
/**
|
|
241
|
+
* #1762 slice 6: who approved it while acting for this person under a proxy
|
|
242
|
+
* grant; null when the person approved it themselves.
|
|
243
|
+
*/
|
|
244
|
+
approvedBy: string | null;
|
|
245
|
+
/** The proxy grant it was approved under; ending that grant revokes it. Null when the person approved it. */
|
|
246
|
+
proxyGrantId: string | null;
|
|
247
|
+
}
|
|
248
|
+
export type DelegationKind = Delegation['kind'];
|
|
249
|
+
/** The setting that turns proxy access on (#1762); false by default. */
|
|
250
|
+
export declare const PROXY_ACCESS_ENABLED_KEY = "ngdpbase.auth.proxy-access.enabled";
|
|
251
|
+
/** The permission to make a proxy grant on your own account (#1762); step-up. */
|
|
252
|
+
export declare const PROXY_GRANT_PERMISSION = "proxy-grant";
|
|
253
|
+
/**
|
|
254
|
+
* A proxy grant (#1762 slice 1): `owner` lets `proxy` act for them within
|
|
255
|
+
* `permissions`. Kept as a signed `proxy` row of the credentials store. Slice 1
|
|
256
|
+
* records it only: no request is affected by it yet.
|
|
257
|
+
*/
|
|
258
|
+
export interface ProxyGrant {
|
|
259
|
+
id: string;
|
|
260
|
+
/** The person represented. */
|
|
261
|
+
owner: string;
|
|
262
|
+
/** The person who may act for them. */
|
|
263
|
+
proxy: string;
|
|
264
|
+
/** Permission names; each one the owner held when the grant was made. */
|
|
265
|
+
permissions: string[];
|
|
266
|
+
/** Who made it: the owner, or an administrator acting for them. */
|
|
267
|
+
createdBy: string;
|
|
268
|
+
/** ISO 8601. */
|
|
269
|
+
createdAt: string;
|
|
270
|
+
/** ISO 8601: when it stops; null lasts until revoked. */
|
|
271
|
+
expiresAt: string | null;
|
|
272
|
+
/**
|
|
273
|
+
* Vault access (operator, 2026-10-12). `copy`: the owner made it with their
|
|
274
|
+
* store unlocked and `vault-proxy` in it, so it carries a copy of their
|
|
275
|
+
* vault key sealed to the proxy's public key (#1762 slice 5) — the copy
|
|
276
|
+
* itself is in the row, never on the grant. `none`: the owner made it with
|
|
277
|
+
* no copy (no `vault-proxy`, or no encrypted store to copy). `never`: an
|
|
278
|
+
* administrator made it, so it never carries one.
|
|
279
|
+
*/
|
|
280
|
+
vault: 'none' | 'never' | 'copy';
|
|
281
|
+
}
|
|
282
|
+
/** A grant as listed: its terms, the permissions in plain words, and whether it lets the proxy give others access. */
|
|
283
|
+
export interface ProxyGrantView extends ProxyGrant {
|
|
284
|
+
can: ScopeDescription[];
|
|
285
|
+
/** It includes `proxy-grant`: the proxy may make grants for the owner (the UI warns, #1762 slice 7). */
|
|
286
|
+
mayRedelegate: boolean;
|
|
287
|
+
}
|
|
288
|
+
/** What `grantProxy` takes. */
|
|
289
|
+
export interface ProxyGrantInput {
|
|
290
|
+
owner: string;
|
|
291
|
+
proxy: string;
|
|
292
|
+
permissions: string[];
|
|
293
|
+
/** ISO 8601, in the future; omitted or null for none. */
|
|
294
|
+
expiresAt?: string | null;
|
|
295
|
+
}
|
|
296
|
+
/** Which side of the grants to list: the ones a person made (`asOwner`) or the ones naming them as proxy (`asProxy`). */
|
|
297
|
+
export type ProxyGrantSide = {
|
|
298
|
+
asOwner: string;
|
|
299
|
+
asProxy?: undefined;
|
|
300
|
+
} | {
|
|
301
|
+
asProxy: string;
|
|
302
|
+
asOwner?: undefined;
|
|
303
|
+
};
|
|
304
|
+
/** Where a signed-in person chooses whom to act for, and switches back (#1762 slice 4). */
|
|
305
|
+
export declare const ACTING_FOR_PATH = "/account/acting-for";
|
|
306
|
+
/**
|
|
307
|
+
* Why a session started or stopped acting for someone (#1762 slice 4), as the
|
|
308
|
+
* `proxy-switch` record's `reason` says it: the person chose a grant, chose
|
|
309
|
+
* themselves again, or the session was dropped back to them because the grant
|
|
310
|
+
* no longer allows it (ended, expired, either account inactive) or proxy
|
|
311
|
+
* access was turned off. `vault-opened` is not a switch: the grant's key copy
|
|
312
|
+
* was opened, so the represented person's encrypted vaults are open to the
|
|
313
|
+
* session that acts for them (#1762 slice 5).
|
|
314
|
+
*/
|
|
315
|
+
export type ActingForChange = 'chosen' | 'switched-back' | 'grant-ended' | 'proxy-access-off' | 'vault-opened';
|
|
316
|
+
/** The choice a signed-in person has (#1762 slice 4): themselves, or someone a live grant lets them act for. */
|
|
317
|
+
export interface ActingForChoices {
|
|
318
|
+
/** The person signed in on this session. */
|
|
319
|
+
self: string;
|
|
320
|
+
/** The grant this session acts under now, or null when the person acts as themselves. */
|
|
321
|
+
actingFor: string | null;
|
|
322
|
+
/** The live grants naming the person as proxy. */
|
|
323
|
+
grants: ProxyGrantView[];
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* How a session signed in (#1523): kept on the session so step-up (#1525) can
|
|
327
|
+
* ask "was a factor satisfied within N minutes", the audit record can say how,
|
|
328
|
+
* and UserInfo (#1529) can report `amr` / `acr` without overstating them.
|
|
329
|
+
*/
|
|
330
|
+
export interface SignInRecord extends Assurance {
|
|
331
|
+
provider: string;
|
|
332
|
+
factors: SatisfiedFactor[];
|
|
333
|
+
/** When the sign-in completed, RFC 3339. */
|
|
334
|
+
at: string;
|
|
335
|
+
}
|
|
336
|
+
/** How a refused request wants its answer: a page to redirect, JSON, or plain text (#1745). */
|
|
337
|
+
export type ReauthMode = 'json' | 'page' | 'text';
|
|
338
|
+
/** The step-up answer (#1525, #1635), for the caller to send in its own form. */
|
|
339
|
+
export interface FreshSignInAnswer {
|
|
340
|
+
/** The refused permission. */
|
|
341
|
+
permission: string;
|
|
342
|
+
/** An agent token or share link: it can never give a fresh sign-in, so there is no `reauth`. */
|
|
343
|
+
delegated: boolean;
|
|
344
|
+
/** What to tell the person. */
|
|
345
|
+
error: string;
|
|
346
|
+
/** Where to re-authenticate, returning to the page they were on. Absent for a delegated credential. */
|
|
347
|
+
reauth?: string;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Where to return after re-authenticating: this page for a page GET, and the
|
|
351
|
+
* page the person was on for anything else — a POST (the form is filled in
|
|
352
|
+
* again; a POST body is not replayed) or a script's request for data (#1738).
|
|
353
|
+
* Always a path on this site.
|
|
354
|
+
*/
|
|
355
|
+
export declare function reauthReturnTo(req: Request, mode?: ReauthMode): string;
|
|
356
|
+
/**
|
|
357
|
+
* Where an authentication attempt came from, and why (#1758). AuthManager is
|
|
358
|
+
* the one door for every check of a secret, and writes the audit record of a
|
|
359
|
+
* sign-in method's check itself; routes write none.
|
|
360
|
+
* - `sign-in` (default): a person signing in; a success counts on the account.
|
|
361
|
+
* - `re-check`: proving it again before a change (a password re-entered on the
|
|
362
|
+
* profile, a store door); audited, never counted as a sign-in.
|
|
363
|
+
*/
|
|
364
|
+
export interface AuthAttempt {
|
|
365
|
+
ipAddress?: string;
|
|
366
|
+
userAgent?: string;
|
|
367
|
+
purpose?: 'sign-in' | 're-check';
|
|
368
|
+
}
|
|
369
|
+
export interface AuthenticateResult {
|
|
370
|
+
success: boolean;
|
|
371
|
+
username?: string;
|
|
372
|
+
/** The provider that verified this sign-in (#1523). */
|
|
373
|
+
provider?: string;
|
|
374
|
+
/** The factors satisfied, each with its time; empty for a delegated credential (#1523). */
|
|
375
|
+
factors?: SatisfiedFactor[];
|
|
376
|
+
/**
|
|
377
|
+
* #946 — set by token-based providers. Carries the delegating token's
|
|
378
|
+
* identity and scopes so the caller can enforce the scope ceiling and stamp
|
|
379
|
+
* page provenance.
|
|
380
|
+
*
|
|
381
|
+
* #1048: references the same `ViaToken` the provider contract declares,
|
|
382
|
+
* rather than repeating the shape. The two ends of one value used to be
|
|
383
|
+
* written out twice, which is how they drifted far enough apart to need a
|
|
384
|
+
* cast between them.
|
|
385
|
+
*/
|
|
386
|
+
viaToken?: ViaToken;
|
|
387
|
+
/** #1576: the sign-in level a bearer credential carries; roles above it step down. */
|
|
388
|
+
aal?: Aal;
|
|
389
|
+
/** #1633: what an outside provider reported about its own sign-in, for the audit record. */
|
|
390
|
+
reported?: ReportedValues;
|
|
391
|
+
}
|
|
392
|
+
declare class AuthManager extends BaseManager {
|
|
393
|
+
private providers;
|
|
394
|
+
private factorEntries;
|
|
395
|
+
/** Built on first use and dropped on registration, since add-ons register late. */
|
|
396
|
+
private factorCache;
|
|
397
|
+
/** The credentials store (#1524); null when it could not be opened. */
|
|
398
|
+
private credentials;
|
|
399
|
+
constructor(engine: WikiEngine);
|
|
400
|
+
initialize(config?: Record<string, unknown>): Promise<void>;
|
|
401
|
+
/**
|
|
402
|
+
* `ngdpbase.auth.factors`, or the retired flat `required-factors` list
|
|
403
|
+
* converted to entries when a site still sets only that (#1523).
|
|
404
|
+
*/
|
|
405
|
+
private readFactorEntries;
|
|
406
|
+
/**
|
|
407
|
+
* The outside OpenID Connect providers (#1743): one `OidcAuthProvider` per
|
|
408
|
+
* enabled entry of `ngdpbase.auth.oidc.providers`, plus the old
|
|
409
|
+
* `ngdpbase.auth.google-oidc.*` keys as the `google` entry when the map has
|
|
410
|
+
* none. Connected identities live in the credentials store, so without it
|
|
411
|
+
* no outside sign-in can work and none is registered. An entry that cannot
|
|
412
|
+
* work (no issuer, no client id, a secret that is not an environment
|
|
413
|
+
* reference, nowhere to come back to) is refused with the reason.
|
|
414
|
+
*/
|
|
415
|
+
private registerOidcProviders;
|
|
416
|
+
/** A secret from its environment reference (`$NAME`); never the secret written into configuration. */
|
|
417
|
+
private secretFromEnvironment;
|
|
418
|
+
/**
|
|
419
|
+
* Apple's client secret is signed by the site (#1743 slice 4b): the team id,
|
|
420
|
+
* the key's id, and the EC P-256 private key Apple issued (the `.p8` file's
|
|
421
|
+
* PEM, from an environment reference; `\n` escapes are accepted for a
|
|
422
|
+
* one-line variable). Null, with the reason refused, when any is missing or
|
|
423
|
+
* the key is not one Apple signs with.
|
|
424
|
+
*/
|
|
425
|
+
private signingKeyFrom;
|
|
426
|
+
private outsideProvider;
|
|
427
|
+
/**
|
|
428
|
+
* Whether this enabled outside provider POSTs its callback (`form_post`,
|
|
429
|
+
* Apple) — the only callbacks the relay in front of the session and CSRF
|
|
430
|
+
* accepts as a POST (#1743 slice 4b).
|
|
431
|
+
*/
|
|
432
|
+
callbackIsPosted(providerId: string): boolean;
|
|
433
|
+
/**
|
|
434
|
+
* Can a visitor with no account get one by an outside sign-in (#1743)?
|
|
435
|
+
* True when at least one enabled provider may create accounts, by the one
|
|
436
|
+
* rule the callback applies (`outsideMayCreateAccounts`). The header's
|
|
437
|
+
* Register button counts it as open sign-up. Read when asked.
|
|
438
|
+
*/
|
|
439
|
+
outsideSignUpOpen(): boolean;
|
|
440
|
+
/** The outside sign-in providers configured and enabled now, in registration order (#1743). */
|
|
441
|
+
outsideProviders(): OutsideProvider[];
|
|
442
|
+
/** The enabled provider an identity row came from, by its issuer; null when none is. */
|
|
443
|
+
private providerOfIdentity;
|
|
444
|
+
/** Whether a credential row can start a sign-in now: an outside identity only while its provider is enabled. */
|
|
445
|
+
private isWayIn;
|
|
446
|
+
/**
|
|
447
|
+
* `username`'s connected outside identities (#1743), for its owner: each
|
|
448
|
+
* with the provider's email and whether that provider can sign in now.
|
|
449
|
+
*/
|
|
450
|
+
connectedIdentities(ctx: PermissionSubject, username: string): Promise<ConnectedIdentity[]>;
|
|
451
|
+
/**
|
|
452
|
+
* Begin connecting an outside identity to the signed-in person's own
|
|
453
|
+
* account (#1743): an `account-security` change, so step-up applies.
|
|
454
|
+
* Returns the provider's URL to send the browser to.
|
|
455
|
+
*/
|
|
456
|
+
startConnect(ctx: PermissionSubject, username: string, providerId: string, flow: {
|
|
457
|
+
redirect?: string;
|
|
458
|
+
deviceState: string;
|
|
459
|
+
}): Promise<string>;
|
|
460
|
+
/** The account a flow is connecting an identity to, or null for a sign-in (or an unknown flow) (#1743). */
|
|
461
|
+
flowConnectsFor(providerId: string, state: string): string | null;
|
|
462
|
+
/**
|
|
463
|
+
* Finish connecting an outside identity to `username` (#1743). The flow
|
|
464
|
+
* must be the one this person started to connect; the identity is the
|
|
465
|
+
* issuer plus subject, and belongs to exactly one account, so one already
|
|
466
|
+
* connected elsewhere is refused. Audited as a credential added.
|
|
467
|
+
*/
|
|
468
|
+
connectIdentity(ctx: PermissionSubject, username: string, providerId: string, credentials: AuthVerifyCredentials, attempt?: AuthAttempt): Promise<ConnectOutcome>;
|
|
469
|
+
/**
|
|
470
|
+
* Add an outside identity to `username` — the one place a connection is
|
|
471
|
+
* made (#1743): from the profile, at the next sign-in after the email step,
|
|
472
|
+
* and when an account is created for it. The identity belongs to exactly
|
|
473
|
+
* one account, so one already connected elsewhere is refused. Audited as a
|
|
474
|
+
* credential added, saying how. The caller has decided the person may.
|
|
475
|
+
*/
|
|
476
|
+
private attachIdentity;
|
|
477
|
+
/**
|
|
478
|
+
* Finish an outside sign-in's callback (#1743): the one decision about what
|
|
479
|
+
* a verified outside identity does here. Connected to an account: that
|
|
480
|
+
* account signs in. Connected to none: an account is created for it when
|
|
481
|
+
* the instance, the provider and the registration policy all allow it and
|
|
482
|
+
* the provider verified the email; otherwise (or when that email already
|
|
483
|
+
* belongs to an account) it is held in the session as a pending connection
|
|
484
|
+
* until the person signs in another way and confirms it. Email never signs
|
|
485
|
+
* anyone in.
|
|
486
|
+
*/
|
|
487
|
+
finishOutsideSignIn(req: Request, providerId: string, credentials: AuthVerifyCredentials, attempt?: AuthAttempt): Promise<OutsideSignIn>;
|
|
488
|
+
/**
|
|
489
|
+
* May an outside sign-in create an account (#1743)? All three must allow
|
|
490
|
+
* it: the instance ceiling `ngdpbase.auth.oidc.auto-provision`, the
|
|
491
|
+
* provider's own `auto-provision` in `ngdpbase.auth.oidc.providers`, and
|
|
492
|
+
* `ngdpbase.application.registration`, the policy for whether any path may
|
|
493
|
+
* create an account. Read when asked, so a change applies at once.
|
|
494
|
+
* {@link outsideSignUpOpen} asks the same rule for every enabled provider.
|
|
495
|
+
*/
|
|
496
|
+
private outsideMayCreateAccounts;
|
|
497
|
+
/**
|
|
498
|
+
* Does the admin trust this outside provider's own second factor (#1633)?
|
|
499
|
+
* Its entry's `trust-mfa` in `ngdpbase.auth.oidc.providers`, off unless set
|
|
500
|
+
* to true; read when asked, so a change applies at the next sign-in. The old
|
|
501
|
+
* `ngdpbase.auth.google-oidc.*` keys have no such switch: off.
|
|
502
|
+
*/
|
|
503
|
+
private trustsOutsideMfa;
|
|
504
|
+
/** The provider's entry in `ngdpbase.auth.oidc.providers` as configured now, or null. */
|
|
505
|
+
private oidcEntry;
|
|
506
|
+
/**
|
|
507
|
+
* Create the account for an outside identity's verified email (#1743), by
|
|
508
|
+
* the path magic-link auto-provisioning takes: a derived username,
|
|
509
|
+
* `UserManager.createUser` with registration's default roles (the provider
|
|
510
|
+
* gives none), no password (`isExternal`), the system principal acting with
|
|
511
|
+
* the provider named as the reason. `createUser` records the creation. An
|
|
512
|
+
* email that already belongs to an account creates nothing: the safe
|
|
513
|
+
* registration message is returned instead.
|
|
514
|
+
*/
|
|
515
|
+
private createAccountFor;
|
|
516
|
+
/** Delete an account created for an outside sign-in that could not be finished (#1743), through UserManager, which closes its access. */
|
|
517
|
+
private removeCreatedAccount;
|
|
518
|
+
/**
|
|
519
|
+
* The person's pending connection, for the sign-in page's notice and the
|
|
520
|
+
* confirmation page; an expired one is dropped (#1743).
|
|
521
|
+
*/
|
|
522
|
+
pendingConnection(req: Request): {
|
|
523
|
+
displayName: string;
|
|
524
|
+
email: string;
|
|
525
|
+
} | null;
|
|
526
|
+
/** The person cancelled the pending connection (#1743). */
|
|
527
|
+
dropPendingConnection(req: Request): void;
|
|
528
|
+
/**
|
|
529
|
+
* Connect the pending identity to the signed-in person's own account, on
|
|
530
|
+
* their confirmation (#1743). Never automatic: on a shared browser the
|
|
531
|
+
* pending identity may be someone else's, and connecting it would let that
|
|
532
|
+
* person sign in here as this account. Checked again now: signed in, not
|
|
533
|
+
* expired, the provider enabled and allowed for the account, and the
|
|
534
|
+
* identity not connected elsewhere. Returns where to send the person, with
|
|
535
|
+
* what happened.
|
|
536
|
+
*/
|
|
537
|
+
confirmPendingConnection(req: Request, ctx: PermissionSubject): Promise<string>;
|
|
538
|
+
/** The person declined the pending connection: drop it, and return where their sign-in was going (#1743). */
|
|
539
|
+
declinePendingConnection(req: Request): string;
|
|
540
|
+
/**
|
|
541
|
+
* Open the credentials store (#1524). Without the key nothing can be
|
|
542
|
+
* verified, so the store stays closed and the manager says so; boot itself
|
|
543
|
+
* never runs without the key, because bootstrap-env generates it.
|
|
544
|
+
*/
|
|
545
|
+
private openCredentialsStore;
|
|
546
|
+
/**
|
|
547
|
+
* Passkeys (#448), registered only when they can work: the credentials store
|
|
548
|
+
* is open, `ngdpbase.auth.passkey.enabled` is not false, and base-url is set
|
|
549
|
+
* explicitly and is a secure context. The relying party is that host — never
|
|
550
|
+
* the request — so a passkey made here works only on that hostname.
|
|
551
|
+
*/
|
|
552
|
+
private registerPasskeys;
|
|
553
|
+
private passkeyProvider;
|
|
554
|
+
/** The host passkeys are tied to, or null when passkeys are off (#448). */
|
|
555
|
+
passkeyRelyingParty(): PasskeyRelyingParty | null;
|
|
556
|
+
/** Options for `username` to enrol a passkey (#448); the caller keeps `challenge` for verify. */
|
|
557
|
+
/**
|
|
558
|
+
* May this account sign in with `providerId`? Its `allowedAuthMethods`, when
|
|
559
|
+
* set, list the providers it may use; absent or empty means any. The one
|
|
560
|
+
* answer for signing in and for enrolling (#1664): a method the account
|
|
561
|
+
* cannot sign in with is never offered or enrolled.
|
|
562
|
+
*/
|
|
563
|
+
userMayUseProvider(username: string, providerId: string): Promise<boolean>;
|
|
564
|
+
passkeyRegistrationOptions(ctx: PermissionSubject, username: string, displayName: string): Promise<unknown>;
|
|
565
|
+
/**
|
|
566
|
+
* Verify an enrolment against the challenge the caller kept, and store the
|
|
567
|
+
* passkey (#448). Returns the credential id, or null when it does not verify.
|
|
568
|
+
*/
|
|
569
|
+
passkeyRegister(ctx: PermissionSubject, username: string, response: unknown, expectedChallenge: string, label: string): Promise<string | null>;
|
|
570
|
+
/** Options for signing in with a passkey (#448); anyone may ask. */
|
|
571
|
+
passkeyAuthenticationOptions(): Promise<unknown>;
|
|
572
|
+
/**
|
|
573
|
+
* A row that does not verify was planted or damaged (#1524). It has already
|
|
574
|
+
* been dropped; this makes it impossible to miss: a critical security event,
|
|
575
|
+
* an escalating admin notification, and a degraded AuthManager until the
|
|
576
|
+
* store is put right.
|
|
577
|
+
*/
|
|
578
|
+
private raiseRejectedCredentials;
|
|
579
|
+
/**
|
|
580
|
+
* Whether `ctx` may act on `username`'s credentials: their own under
|
|
581
|
+
* `profile-manage`, anyone else's under `user-edit` (#1524). No PDP, no answer,
|
|
582
|
+
* no access.
|
|
583
|
+
*/
|
|
584
|
+
private mayManageCredentials;
|
|
585
|
+
/**
|
|
586
|
+
* Trust again the credential rows that failed verification (#1633): the
|
|
587
|
+
* operator's deliberate repair after the credentials key was lost or
|
|
588
|
+
* changed, so people keep their passkeys and verified addresses. Run from
|
|
589
|
+
* `scripts/retrust-credentials.ts`, never from a request — a web button
|
|
590
|
+
* that re-signs whatever is in the file would let anyone who can write the
|
|
591
|
+
* file plant a credential and have it blessed. Recorded as a security event
|
|
592
|
+
* naming every row and the reason.
|
|
593
|
+
*/
|
|
594
|
+
retrustRejectedCredentials(reason: string, ctx: JobContext): Promise<{
|
|
595
|
+
trusted: number;
|
|
596
|
+
remaining: number;
|
|
597
|
+
}>;
|
|
598
|
+
private requireCredentialsStore;
|
|
599
|
+
/**
|
|
600
|
+
* `username`'s credentials, without their secrets (#1524). Reading the list
|
|
601
|
+
* is not a change, so it asks whether the person HOLDS the permission and
|
|
602
|
+
* leaves out the fresh-sign-in rule (#1738): a stale sign-in that was
|
|
603
|
+
* refused here showed its owner no passkeys at all, and they believed them
|
|
604
|
+
* lost. Adding, renaming and removing still ask for a fresh sign-in.
|
|
605
|
+
*/
|
|
606
|
+
listCredentials(ctx: PermissionSubject, username: string): Promise<CredentialView[]>;
|
|
607
|
+
/** Enrol a credential for `username`; returns its id. Audited (#1524). */
|
|
608
|
+
addCredential(ctx: PermissionSubject, username: string, input: CredentialInput): Promise<string>;
|
|
609
|
+
/** Store and audit a credential the caller has already decided may be added; `how` says how it happened. */
|
|
610
|
+
private storeCredential;
|
|
611
|
+
/**
|
|
612
|
+
* Remove one of `username`'s credentials. Refused when it is their last way
|
|
613
|
+
* in: no password and no other credential that can start a sign-in (#1524).
|
|
614
|
+
*/
|
|
615
|
+
removeCredential(ctx: PermissionSubject, username: string, id: string): Promise<boolean>;
|
|
616
|
+
/**
|
|
617
|
+
* The one deactivation step (#1765): "this account can no longer sign in".
|
|
618
|
+
* Disable and delete both come through here, from UserManager, so nothing
|
|
619
|
+
* an account holds is left reachable by one path and not the other.
|
|
620
|
+
*
|
|
621
|
+
* - Both: every session of the account ends.
|
|
622
|
+
* - Disable: its agent tokens, app grants and shares are suspended by the
|
|
623
|
+
* live owner-is-active checks that refuse them, and its credentials stay,
|
|
624
|
+
* so turning the account back on restores them.
|
|
625
|
+
* - Delete: its agent tokens, app grants and shares are revoked, and its
|
|
626
|
+
* credentials-store rows (passkeys, verified addresses, devices) are
|
|
627
|
+
* removed. A row names its account by username, so a row left behind
|
|
628
|
+
* would sign in to a new account later created with the same name.
|
|
629
|
+
*
|
|
630
|
+
* The account change itself is audited by UserManager (user-delete, or the
|
|
631
|
+
* isActive change); each revocation is audited by its owner, and each removed
|
|
632
|
+
* credential here (`credential-remove`). Delete also ends every proxy grant
|
|
633
|
+
* the account is on, either side — the grants it made and the grants naming
|
|
634
|
+
* it as proxy (#1762), each recorded as `proxy-revoke` — whether or not
|
|
635
|
+
* proxy access is on. Disable leaves them, as it leaves tokens: acting under
|
|
636
|
+
* a grant must refuse it live while either account is inactive (#1762
|
|
637
|
+
* slice 2). The encrypted store is left to survivorship (#1545).
|
|
638
|
+
*/
|
|
639
|
+
closeAccess(username: string, mode: 'disable' | 'delete', ctx: PermissionSubject): Promise<void>;
|
|
640
|
+
/**
|
|
641
|
+
* `username`'s agent tokens and the apps and devices they approved, as one
|
|
642
|
+
* list (#1764). Reading it is not a change, so, like `listCredentials`, it
|
|
643
|
+
* asks whether the caller holds the permission (their own:
|
|
644
|
+
* `account-security`; anyone else's: `user-edit`) without a fresh sign-in.
|
|
645
|
+
*/
|
|
646
|
+
listDelegations(ctx: PermissionSubject, username: string): Promise<Delegation[]>;
|
|
647
|
+
/**
|
|
648
|
+
* Revoke one of `username`'s agent tokens or approved apps (#1764): the one
|
|
649
|
+
* door every Revoke goes through — the profile list, `DELETE /api/tokens/:id`,
|
|
650
|
+
* the admin user page — and `closeAccess`. Their own needs `account-security`
|
|
651
|
+
* (so a fresh sign-in); anyone else's `user-edit`. False when no such live
|
|
652
|
+
* item is theirs, so another person's is never revoked. Each is audited by
|
|
653
|
+
* its owner, by who revoked it: `token-revoke` / `oidcgrant-revoke`.
|
|
654
|
+
*/
|
|
655
|
+
revokeDelegation(ctx: PermissionSubject, username: string, kind: DelegationKind, id: string): Promise<boolean>;
|
|
656
|
+
/**
|
|
657
|
+
* Scope and permission names in plain words (#1764): the permission
|
|
658
|
+
* catalog's `label` for each (`ngdpbase.permissions.definitions`, add-on
|
|
659
|
+
* permissions merged in), the sign-in scopes' own lines, and anything
|
|
660
|
+
* declared nowhere by its name. The one lookup: the "My tokens and devices"
|
|
661
|
+
* list and the app consent page both read it.
|
|
662
|
+
*/
|
|
663
|
+
describeScopes(names: readonly string[]): ScopeDescription[];
|
|
664
|
+
/** One kind of `username`'s live delegated credentials, as list rows. */
|
|
665
|
+
private delegationsOf;
|
|
666
|
+
/**
|
|
667
|
+
* End one delegated credential the caller has already decided may be ended;
|
|
668
|
+
* only `username`'s own. Recorded by its owner as done by `ctx`: under a
|
|
669
|
+
* proxy grant, naming both people (#1762 slice 6).
|
|
670
|
+
*/
|
|
671
|
+
private endDelegation;
|
|
672
|
+
/**
|
|
673
|
+
* End each of `username`'s live delegated credentials of one kind that
|
|
674
|
+
* `which` picks: the one loop for an account closed (all of them) and a
|
|
675
|
+
* proxy grant ended (those approved under it, #1762 slice 6).
|
|
676
|
+
*/
|
|
677
|
+
private endDelegationsWhere;
|
|
678
|
+
/** Whether proxy access is on (`ngdpbase.auth.proxy-access.enabled`), read on every call. */
|
|
679
|
+
proxyAccessEnabled(): boolean;
|
|
680
|
+
private requireProxyAccess;
|
|
681
|
+
/**
|
|
682
|
+
* Whether `ctx` may make or end a grant on `owner`'s account (#1762): their
|
|
683
|
+
* own under `proxy-grant`, which asks for a fresh sign-in; anyone else's also
|
|
684
|
+
* under `user-edit`, so an administrator acting for someone is fresh too.
|
|
685
|
+
* `viewOnly` (a listing) asks whether the permission is held, without the
|
|
686
|
+
* fresh sign-in, as `mayManageCredentials` does.
|
|
687
|
+
*/
|
|
688
|
+
private mayManageProxyGrants;
|
|
689
|
+
/**
|
|
690
|
+
* Let `proxy` act for `owner` within `permissions` (#1762 slice 1: recorded
|
|
691
|
+
* only; no request is affected yet). Refused while proxy access is off.
|
|
692
|
+
*
|
|
693
|
+
* - The owner makes it under `proxy-grant`; an administrator makes it for
|
|
694
|
+
* them under `user-edit` too (see `mayManageProxyGrants`).
|
|
695
|
+
* - Delegate, not grant: every permission must be one the owner holds now,
|
|
696
|
+
* the check agent tokens make (`lackingScopes`). Any permission they hold
|
|
697
|
+
* may be included (operator, 2026-10-12), `proxy-grant` among them, which
|
|
698
|
+
* lets the proxy give others access too (`mayRedelegate`).
|
|
699
|
+
* - An administrator's grant never carries vault access: no key copy
|
|
700
|
+
* (`vault: 'never'`), and never `vault-proxy` (#1762 slice 7).
|
|
701
|
+
* - `expiresAt` is optional; without it the grant lasts until revoked.
|
|
702
|
+
*
|
|
703
|
+
* Recorded first, as `proxy-grant` (refuse on failure): `user` is the owner,
|
|
704
|
+
* `actor` who made it.
|
|
705
|
+
*/
|
|
706
|
+
grantProxy(ctx: PermissionSubject, input: ProxyGrantInput): Promise<ProxyGrantView>;
|
|
707
|
+
/** Where private stores live; undefined when this site has no page directory. */
|
|
708
|
+
private pagesDirectory;
|
|
709
|
+
/**
|
|
710
|
+
* Why `ctx` cannot give access to their own vaults now, in plain words, or
|
|
711
|
+
* null when they can (#1762 slice 5). The give form shows it beside the
|
|
712
|
+
* "vaults" choice; grantProxy refuses with the same words.
|
|
713
|
+
*
|
|
714
|
+
* - Only the person themselves, signed in as themselves: not an
|
|
715
|
+
* administrator, not a proxy re-delegating.
|
|
716
|
+
* - A person with an encrypted store must have it unlocked, so the grant
|
|
717
|
+
* can seal a copy of its key. A person with none needs nothing unlocked:
|
|
718
|
+
* the grant opens only their unencrypted vaults.
|
|
719
|
+
*/
|
|
720
|
+
vaultGrantBlocked(ctx: PermissionSubject, owner: string): Promise<string | null>;
|
|
721
|
+
/**
|
|
722
|
+
* The copy of the owner's vault key a grant to `proxy` carries (#1762 slice
|
|
723
|
+
* 5), sealed to the proxy's public key while the owner's store is unlocked;
|
|
724
|
+
* null when the owner has no encrypted store (nothing to copy). Refused, in
|
|
725
|
+
* plain words, when the owner's store is locked or the proxy has no key
|
|
726
|
+
* pair yet — a grant never promises vault access it cannot give.
|
|
727
|
+
*/
|
|
728
|
+
private sealVaultCopy;
|
|
729
|
+
/** The decision points and the represented person's live subject (`ctx` itself for one's own), as grantProxy and its form ask them. */
|
|
730
|
+
private proxyOwnerSubject;
|
|
731
|
+
/**
|
|
732
|
+
* What a grant on `owner`'s account may carry, in plain words (#1762 slice
|
|
733
|
+
* 7): every grantable catalog permission the owner holds now — the same
|
|
734
|
+
* `holds` question grantProxy asks — so the give form offers nothing
|
|
735
|
+
* grantProxy would refuse. An administrator's form for someone else, or a
|
|
736
|
+
* proxy's acting for them, never offers `vault-proxy`; the owner's own form
|
|
737
|
+
* offers it and says why it cannot be chosen now (`vaultGrantBlocked`).
|
|
738
|
+
* Asked like a listing (held, not fresh); refused
|
|
739
|
+
* while proxy access is off.
|
|
740
|
+
*/
|
|
741
|
+
proxyGrantOptions(ctx: PermissionSubject, owner: string): Promise<ScopeDescription[]>;
|
|
742
|
+
/**
|
|
743
|
+
* End a proxy grant (#1762). The owner under `proxy-grant`; anyone else
|
|
744
|
+
* under `user-edit` too, so another person's grant is never ended by someone
|
|
745
|
+
* without both. False when there is no such grant. Recorded as
|
|
746
|
+
* `proxy-revoke`, first. Refused while proxy access is off.
|
|
747
|
+
*/
|
|
748
|
+
revokeProxy(ctx: PermissionSubject, grantId: string): Promise<boolean>;
|
|
749
|
+
/**
|
|
750
|
+
* The live proxy grants on one side (#1762): those `asOwner` made, or those
|
|
751
|
+
* naming `asProxy`. One's own needs `proxy-grant` held; anyone else's
|
|
752
|
+
* `user-edit`, as `listDelegations`. A grant past its end date is left out.
|
|
753
|
+
* Refused while proxy access is off.
|
|
754
|
+
*/
|
|
755
|
+
listProxyGrants(ctx: PermissionSubject, side: ProxyGrantSide): Promise<ProxyGrantView[]>;
|
|
756
|
+
/** The live grants on one side, unchecked: each caller has already decided who may see them. */
|
|
757
|
+
private liveGrantsOn;
|
|
758
|
+
/**
|
|
759
|
+
* The grant with this id if it is in force right now, else null (#1762
|
|
760
|
+
* slice 2): the PDP's live re-read on every decision under `viaProxy`.
|
|
761
|
+
* Null when proxy access is off, when there is no such row (revoked, or the
|
|
762
|
+
* account closed), when its terms do not read, or when its end date has
|
|
763
|
+
* passed — each fails closed. Not a door: it answers the decision point,
|
|
764
|
+
* never a person, so it asks no permission.
|
|
765
|
+
*/
|
|
766
|
+
liveProxyGrant(grantId: string, now?: number): ProxyGrant | null;
|
|
767
|
+
/** The grant row with this id as it stands — live or not, whether proxy access is on or off — or null. */
|
|
768
|
+
private proxyGrantRecord;
|
|
769
|
+
/**
|
|
770
|
+
* The person signed in on this interactive session, or null. Never a
|
|
771
|
+
* bearer request: an agent token or an app can never act for anyone, so it
|
|
772
|
+
* can never choose to either. `session.username` stays the person who
|
|
773
|
+
* signed in while they act for someone; the subject changes, not the
|
|
774
|
+
* session.
|
|
775
|
+
*/
|
|
776
|
+
private sessionPerson;
|
|
777
|
+
/**
|
|
778
|
+
* The request's subject when the session acts for someone (#1762 slice 4):
|
|
779
|
+
* the one place it is built, called from the session middleware in app.ts
|
|
780
|
+
* with the session's own subject (`self`). Returns `self` unchanged when the
|
|
781
|
+
* session acts for nobody.
|
|
782
|
+
*
|
|
783
|
+
* When `session.actingFor` names a grant, the grant is re-read live on this
|
|
784
|
+
* request: in force, proxy access on, naming the session's person as proxy,
|
|
785
|
+
* and the represented account active. The subject is then the represented
|
|
786
|
+
* person — their username and live roles — with `viaProxy` naming the
|
|
787
|
+
* actor, the grant and its permissions; the PDP bounds every decision by
|
|
788
|
+
* the grant and by what the actor holds. The sign-in is the actor's
|
|
789
|
+
* (`signIn`, `aalCap`): the represented person's roles are held at the
|
|
790
|
+
* level the actor signed in at, and step-up asks the actor's own fresh
|
|
791
|
+
* sign-in. It carries no private-store handle: the session's key bag is the
|
|
792
|
+
* actor's own.
|
|
793
|
+
*
|
|
794
|
+
* Anything else — a grant revoked or expired mid-session, an inactive
|
|
795
|
+
* account, proxy access turned off, a subject that came with a token or a
|
|
796
|
+
* share — drops the session back to the person themselves: `actingFor` is
|
|
797
|
+
* cleared and the drop recorded as `proxy-switch`. A request never acts on
|
|
798
|
+
* a dead grant.
|
|
799
|
+
*/
|
|
800
|
+
subjectActingFor(req: Request, self: PermissionSubject): Promise<PermissionSubject>;
|
|
801
|
+
/**
|
|
802
|
+
* Open the grant's key copy into the actor's own bag (#1762 slice 5), once,
|
|
803
|
+
* when the grant carries `vault-proxy` and a copy and the actor's own store
|
|
804
|
+
* is unlocked; recorded as `proxy-switch` `vault-opened`, naming both
|
|
805
|
+
* people. Returns the handle the subject's claim carries to reach it, or
|
|
806
|
+
* undefined: no copy is held then, and a copy held for another grant is
|
|
807
|
+
* wiped. The copy is tried again when the actor unlocks their store later.
|
|
808
|
+
*/
|
|
809
|
+
private openVaultCopy;
|
|
810
|
+
/**
|
|
811
|
+
* Whom the signed-in person may act for (#1762 slice 4): themselves, and
|
|
812
|
+
* each person a live grant names them proxy for. Null when proxy access is
|
|
813
|
+
* off or this is not an interactive signed-in session — the choice page
|
|
814
|
+
* does not exist then.
|
|
815
|
+
*/
|
|
816
|
+
actingForChoices(req: Request): ActingForChoices | null;
|
|
817
|
+
/**
|
|
818
|
+
* Switch whom this session acts for (#1762 slice 4): a grant id, or null to
|
|
819
|
+
* act as yourself. The authority is the grant itself — live, naming the
|
|
820
|
+
* session's person as proxy, the represented account active — and the
|
|
821
|
+
* session's own sign-in; an agent token or an app never switches. Switching
|
|
822
|
+
* back is always allowed. Each change is recorded as `proxy-switch`. False
|
|
823
|
+
* when the switch is refused; the session is unchanged then.
|
|
824
|
+
*/
|
|
825
|
+
switchActingFor(req: Request, grantId: string | null): Promise<boolean>;
|
|
826
|
+
/**
|
|
827
|
+
* A `proxy-switch` record (#1762 slice 4): `user` the person represented,
|
|
828
|
+
* `actor` the person signed in — the attribution every proxy row carries.
|
|
829
|
+
* When the grant's record is already gone (revoked), the represented person
|
|
830
|
+
* is not known and the row names the signed-in person alone.
|
|
831
|
+
*/
|
|
832
|
+
private auditActingFor;
|
|
833
|
+
private proxyGrantView;
|
|
834
|
+
/**
|
|
835
|
+
* End a grant the caller has already decided may be ended: recorded first,
|
|
836
|
+
* then the tokens, apps and devices approved under it are revoked (#1762
|
|
837
|
+
* slice 6) — while the grant still stands, since a token under a grant no
|
|
838
|
+
* longer in force is no longer listed as live — and then it is removed.
|
|
839
|
+
*/
|
|
840
|
+
private endProxyGrant;
|
|
841
|
+
/** A proxy-grant record (#1762): `user` the person represented, `actor` who acted. */
|
|
842
|
+
private auditProxy;
|
|
843
|
+
/** Rename one of `username`'s credentials. The name is required, as at enrolment. */
|
|
844
|
+
renameCredential(ctx: PermissionSubject, username: string, id: string, label: unknown): Promise<boolean>;
|
|
845
|
+
/** Whether the account signs in with a password — UserManager is the door that sees the hash (#1524). */
|
|
846
|
+
private hasPassword;
|
|
847
|
+
private auditCredentialChange;
|
|
848
|
+
/**
|
|
849
|
+
* The factors offered, in configuration order (#1523).
|
|
850
|
+
*
|
|
851
|
+
* An entry counts only when it is enabled and its provider is registered and
|
|
852
|
+
* declares itself a factor; listed but unavailable is never offered. Each
|
|
853
|
+
* value is the lower of the entry and the provider's code: an entry that
|
|
854
|
+
* overstates is lowered to the truth, with a warning naming the key.
|
|
855
|
+
*/
|
|
856
|
+
getFactors(): Factor[];
|
|
857
|
+
private lowered;
|
|
858
|
+
/**
|
|
859
|
+
* What a set of satisfied factors amounts to (#1523), by NIST SP 800-63B.
|
|
860
|
+
*
|
|
861
|
+
* - `aal` is the strongest single factor's, lifted to AAL2 when the factors
|
|
862
|
+
* cover two or more distinct types — email excepted: it never lifts a
|
|
863
|
+
* sign-in above AAL1 (§5.1.3.1), though it still counts towards `mfa`.
|
|
864
|
+
* - `acr` is `phrh`, then `phr`, then the AAL — what we claim, never more.
|
|
865
|
+
* - `mfa` is two or more distinct types, which is what we require.
|
|
866
|
+
*/
|
|
867
|
+
assess(factors: ReadonlyArray<Pick<Factor, 'amr' | 'aal' | 'acr'>>): Assurance;
|
|
868
|
+
/**
|
|
869
|
+
* Authenticate using the specified provider — the one door (#1758).
|
|
870
|
+
*
|
|
871
|
+
* A sign-in method (a provider listed in `ngdpbase.auth.factors`) has its
|
|
872
|
+
* failures and its re-checks audited here, with where they came from; a
|
|
873
|
+
* successful sign-in is recorded, and counted on the account, when it
|
|
874
|
+
* completes (`completeSignIn`), once with every factor it took. A request
|
|
875
|
+
* credential (agent token, app token, gateway header) is checked on every
|
|
876
|
+
* API request and is not audited per request.
|
|
877
|
+
*/
|
|
878
|
+
authenticate(providerId: string, credentials: AuthVerifyCredentials, attempt?: AuthAttempt): Promise<AuthenticateResult>;
|
|
879
|
+
/**
|
|
880
|
+
* #1523: say how — the provider, and the factor it satisfied with its time.
|
|
881
|
+
* A delegated credential is never a factor, so it records none. An outside
|
|
882
|
+
* provider the admin trusts counts for what it reported (#1633,
|
|
883
|
+
* `outsideLevel`); step-up reads the same factor, so it uses the same level.
|
|
884
|
+
*/
|
|
885
|
+
private satisfiedFactors;
|
|
886
|
+
/**
|
|
887
|
+
* What an outside provider's own sign-in counts for here (#1633) — the one
|
|
888
|
+
* mapping, for every outside provider, by NIST SP 800-63B and RFC 8176.
|
|
889
|
+
* Untrusted: AAL1 with no `amr`, whatever was reported. Trusted (operator,
|
|
890
|
+
* 2026-10-12): the report is taken as given, to whatever level it shows.
|
|
891
|
+
* The reported RFC 8176 `amr` values count:
|
|
892
|
+
*
|
|
893
|
+
* - AAL3, phishing-resistant on hardware (`phrh`): a hardware-bound key
|
|
894
|
+
* (`hwk`) with a second factor or user verification — `mfa`, `user`, or a
|
|
895
|
+
* second factor type by the same rule `assess` applies to this site's own
|
|
896
|
+
* factors (`hwk` + `pin`, `hwk` + `fpt`, `hwk` + `pwd`; not `hwk` + `otp`).
|
|
897
|
+
* This site's passkey is phishing-resistant the same way, through its
|
|
898
|
+
* factor's `acr`, which `assess` carries to the sign-in.
|
|
899
|
+
* - AAL2: multi-factor — `mfa`, or two factor types by `assess`.
|
|
900
|
+
* - The `acr`, when it is an SP 800-63C identifier, raises the level to the
|
|
901
|
+
* one it states (`…/aal/2`, `…/aal/3`); it is never phishing resistance.
|
|
902
|
+
* - AAL1 otherwise.
|
|
903
|
+
*/
|
|
904
|
+
outsideLevel(reported: ReportedValues | undefined): Pick<Factor, 'amr' | 'aal' | 'acr'>;
|
|
905
|
+
/** The audit record of one sign-in method's check (#1758): written here, never by a route. */
|
|
906
|
+
private auditCheck;
|
|
907
|
+
/** Each role's `required-aal`, and whether the operator set it, read through the catalogue's owner (#1523, #448). */
|
|
908
|
+
private requiredLevels;
|
|
909
|
+
/** The highest level the available factors reach together; 0 without a primary factor (#1523). */
|
|
910
|
+
reachableAal(): Aal;
|
|
911
|
+
/**
|
|
912
|
+
* A role's level as enforced (#448): its `required-aal`, except that a
|
|
913
|
+
* SHIPPED default no available factor reaches counts as what is reachable —
|
|
914
|
+
* an upgrade never locks an instance out. An operator's own level is never
|
|
915
|
+
* lowered; the boot refuses it instead (`checkRequiredAal`).
|
|
916
|
+
*/
|
|
917
|
+
private effectiveLevels;
|
|
918
|
+
/**
|
|
919
|
+
* The highest level an account's own allowed sign-in methods reach, never
|
|
920
|
+
* below 1 (#1690). Operator rule (2026-10-08): whenever an account has no
|
|
921
|
+
* way to reach AAL2, AAL1 is what its roles require. The site-wide factors,
|
|
922
|
+
* narrowed to the account's `allowedAuthMethods` (none listed = all).
|
|
923
|
+
*/
|
|
924
|
+
aalCapFor(username: string): Promise<Aal>;
|
|
925
|
+
/**
|
|
926
|
+
* What an account can reach now, and what it could reach once it enrols
|
|
927
|
+
* the factors it is allowed but has not set up (#1690). A factor that has
|
|
928
|
+
* to be enrolled — a passkey — counts toward `now` only when the account
|
|
929
|
+
* has one (operator, 2026-10-08: allowed but not enrolled is no way to
|
|
930
|
+
* AAL2 yet). Both are at least 1. The gap between them is what the account
|
|
931
|
+
* is told, on every page, to close.
|
|
932
|
+
*/
|
|
933
|
+
aalReachFor(username: string): Promise<{
|
|
934
|
+
now: Aal;
|
|
935
|
+
ifEnrolled: Aal;
|
|
936
|
+
}>;
|
|
937
|
+
/**
|
|
938
|
+
* The roles an account holds at a lower level than they require only
|
|
939
|
+
* because it has not enrolled a factor it is allowed (#1690): it acts with
|
|
940
|
+
* them, and is told on every page to add the factor.
|
|
941
|
+
*/
|
|
942
|
+
rolesAwaitingEnrolment(roles: readonly string[], reach: {
|
|
943
|
+
now: number;
|
|
944
|
+
ifEnrolled: number;
|
|
945
|
+
}): string[];
|
|
946
|
+
/** A role level, lowered to what the account can reach when its cap is known (#1690). */
|
|
947
|
+
private capped;
|
|
948
|
+
/**
|
|
949
|
+
* The level a sign-in must reach for someone holding `roles`: the highest
|
|
950
|
+
* effective level among them (#1523), never above the account's own cap
|
|
951
|
+
* when it is known (#1690).
|
|
952
|
+
*/
|
|
953
|
+
requiredAalFor(roles: readonly string[], cap?: number): Aal;
|
|
954
|
+
/**
|
|
955
|
+
* Which of `roles` a session signed in at `signInAal` may act with (#448,
|
|
956
|
+
* decided 2026-10-03): a role whose effective level is above it steps down
|
|
957
|
+
* for this session — the person acts without it until they sign in strongly
|
|
958
|
+
* enough. Roles without a level are always kept, so a signed-in person keeps
|
|
959
|
+
* `profile-manage` and can reach their profile to enrol.
|
|
960
|
+
*/
|
|
961
|
+
rolesAtSignIn(roles: readonly string[], signInAal: number, cap?: number): {
|
|
962
|
+
kept: string[];
|
|
963
|
+
steppedDown: string[];
|
|
964
|
+
};
|
|
965
|
+
/** Every role whose configured level the available factors cannot reach, described (#1523). */
|
|
966
|
+
unreachableRequiredAal(): string[];
|
|
967
|
+
/**
|
|
968
|
+
* The start-up check, once add-ons have registered their providers (#1523,
|
|
969
|
+
* #448). Returns the problems that must refuse the boot: a level the operator
|
|
970
|
+
* set that no factor reaches. A shipped default that cannot be reached instead
|
|
971
|
+
* marks AuthManager degraded, naming what to do; those roles act at what is
|
|
972
|
+
* reachable meanwhile.
|
|
973
|
+
*/
|
|
974
|
+
checkRequiredAal(): string[];
|
|
975
|
+
/** Whether `username` has enrolled a credential of `kind` — for the step-down banner (#448). */
|
|
976
|
+
hasCredential(username: string, kind: CredentialKind): boolean;
|
|
977
|
+
/**
|
|
978
|
+
* Step-up is AuthManager's (#1758, decided 2026-10-11): the freshness rule
|
|
979
|
+
* (`stepUpNeeded`), the answer to a refused request, re-authentication and
|
|
980
|
+
* its audit. The PDP stays the decider and asks `stepUpNeeded`.
|
|
981
|
+
*
|
|
982
|
+
* The answer for a person who holds `permission` but whose sign-in is not
|
|
983
|
+
* fresh enough, with the `reauth-prompt` recorded. A delegated credential is
|
|
984
|
+
* refused outright: it can never give a fresh factor.
|
|
985
|
+
*/
|
|
986
|
+
askForFreshSignIn(req: Request, userContext: unknown, permission: string, mode: ReauthMode): Promise<FreshSignInAnswer>;
|
|
987
|
+
/** Record a step-up event: a prompt, a success or a failure (#1525). */
|
|
988
|
+
recordReauth(req: Request, eventType: AuditEventName, result: 'success' | 'failure', permission: string, detail: string): Promise<void>;
|
|
989
|
+
/**
|
|
990
|
+
* A re-authentication completes (#1525, #1758): the session's sign-in record
|
|
991
|
+
* gains the fresh factor, and the success is recorded. The route saves the
|
|
992
|
+
* session and answers.
|
|
993
|
+
*/
|
|
994
|
+
completeReauth(req: Request, result: AuthenticateResult, method: string): Promise<SignInRecord | null>;
|
|
995
|
+
/**
|
|
996
|
+
* The one sign-in completion step (#1758): every way a person finishes
|
|
997
|
+
* signing in — a password, a passkey, a second factor, an email link, an
|
|
998
|
+
* outside provider — ends here, so none can forget a part.
|
|
999
|
+
*
|
|
1000
|
+
* - A new session (#1043; a session already signed in on it ends, #1626),
|
|
1001
|
+
* with its sign-in time and a cookie as long as `ngdpbase.session.max-age`,
|
|
1002
|
+
* the cap from that time (#1767).
|
|
1003
|
+
* - The person, `isAuthenticated`, how they signed in (`session.signIn`,
|
|
1004
|
+
* #1523) and the account's password-change generation (#1482).
|
|
1005
|
+
* - Their private stores: a handle already unlocked (a two-step sign-in
|
|
1006
|
+
* unlocked at the password), or the password handed to the vault code
|
|
1007
|
+
* through the registered unlocker (#1594 adds a passkey's PRF output).
|
|
1008
|
+
* - One audit record of the completed sign-in, naming how, and the count on
|
|
1009
|
+
* the account. `authenticate()` records failures and re-checks; a
|
|
1010
|
+
* successful sign-in is recorded here, once, when it is complete.
|
|
1011
|
+
* - A pending outside identity (#1743): carried across the new session,
|
|
1012
|
+
* never connected here. The person is sent to confirm it (`next`,
|
|
1013
|
+
* `/login/connect/confirm`); ignored, it expires. An expired one is
|
|
1014
|
+
* dropped.
|
|
1015
|
+
* - Whom to act for (#1762 slice 4): a person a live proxy grant names is
|
|
1016
|
+
* sent to choose (`next`, `ACTING_FOR_PATH`); they act as themselves
|
|
1017
|
+
* until they choose someone. The new session never carries a choice.
|
|
1018
|
+
*
|
|
1019
|
+
* The route saves the session and answers, going to `next` when it is set.
|
|
1020
|
+
*/
|
|
1021
|
+
completeSignIn(req: Request, result: AuthenticateResult, how: {
|
|
1022
|
+
method: string;
|
|
1023
|
+
unlockWith?: {
|
|
1024
|
+
password: string;
|
|
1025
|
+
};
|
|
1026
|
+
privateStoreHandle?: string;
|
|
1027
|
+
returnTo?: string;
|
|
1028
|
+
}): Promise<SignInCompleted>;
|
|
1029
|
+
/**
|
|
1030
|
+
* Start the signed-in session's lifetime (#1767), on the new session after
|
|
1031
|
+
* regeneration: `signedInAt`, from which `ngdpbase.session.max-age` caps it
|
|
1032
|
+
* (checked per request in app.ts, `checkSessionLimits`), and a cookie that
|
|
1033
|
+
* lasts that long, so the cookie never ends the session before the cap.
|
|
1034
|
+
* Only a sign-in writes it; a re-authentication (`completeReauth`) does not.
|
|
1035
|
+
*/
|
|
1036
|
+
private startSessionLifetime;
|
|
1037
|
+
/**
|
|
1038
|
+
* Stamp the session with the account's password-change generation (#1482):
|
|
1039
|
+
* at every sign-in, and on the session that changed the password, so that
|
|
1040
|
+
* one stays signed in while every other session of the account ends.
|
|
1041
|
+
*/
|
|
1042
|
+
stampSessionGeneration(req: Request, username: string): Promise<void>;
|
|
1043
|
+
/**
|
|
1044
|
+
* The vault code's unlocker (#1758, #1594): given who signed in and their
|
|
1045
|
+
* password, unlock their private stores and return the session's handle.
|
|
1046
|
+
* Registered by whoever owns the vault code (today the routes, #1382);
|
|
1047
|
+
* sign-in completion and a two-step sign-in hand it the material, so no
|
|
1048
|
+
* route unlocks stores itself.
|
|
1049
|
+
*/
|
|
1050
|
+
setVaultUnlocker(unlock: (username: string, password: string) => Promise<string>): void;
|
|
1051
|
+
/** Unlock `username`'s stores with their password through the registered unlocker, or none. */
|
|
1052
|
+
unlockVaults(username: string, password: string): Promise<string | undefined>;
|
|
1053
|
+
private vaultUnlocker;
|
|
1054
|
+
private warnedNoUnlocker;
|
|
1055
|
+
/** A fresh session for a sign-in (#1043); the one already on it ends first (#1626, #1670). */
|
|
1056
|
+
private regenerate;
|
|
1057
|
+
/**
|
|
1058
|
+
* What a successful sign-in amounts to, for the session (#1523): its provider,
|
|
1059
|
+
* the factors it satisfied with their times, and their assessment. Null for a
|
|
1060
|
+
* failed result or a delegated credential, which never starts a session.
|
|
1061
|
+
*/
|
|
1062
|
+
signInRecord(result: AuthenticateResult): SignInRecord | null;
|
|
1063
|
+
/**
|
|
1064
|
+
* Step-up (#1525): which permissions ask for a fresh factor, and how fresh.
|
|
1065
|
+
* Which: every permission entry marked `step-up: true` in
|
|
1066
|
+
* `ngdpbase.permissions.definitions` — the permission entry is the one
|
|
1067
|
+
* declaration of its action (#1638). How fresh:
|
|
1068
|
+
* `ngdpbase.auth.step-up.max-age-minutes`; missing, malformed or 0 means no
|
|
1069
|
+
* step-up. A `permissions` list still set under `ngdpbase.auth.step-up` (the
|
|
1070
|
+
* shape before #1638) is honoured as well, and said once.
|
|
1071
|
+
*/
|
|
1072
|
+
stepUpPolicy(): {
|
|
1073
|
+
maxAgeMs: number;
|
|
1074
|
+
permissions: ReadonlySet<string>;
|
|
1075
|
+
};
|
|
1076
|
+
private warnedLegacyStepUp;
|
|
1077
|
+
/**
|
|
1078
|
+
* Whether `permission` needs a fresher factor than `signIn` holds (#1525).
|
|
1079
|
+
* Fresh: a factor satisfied within the step-up window that reaches the
|
|
1080
|
+
* level the person's roles require (at least 1 — a known device, aal 0,
|
|
1081
|
+
* never counts). A delegated credential has no sign-in of its own, so it
|
|
1082
|
+
* never satisfies step-up.
|
|
1083
|
+
*/
|
|
1084
|
+
stepUpNeeded(permission: string, signIn: SignInRecord | undefined, roles: readonly string[], delegated: boolean, now?: number, cap?: number): boolean;
|
|
1085
|
+
/**
|
|
1086
|
+
* A session's sign-in after a re-authentication (#1525): the new factors
|
|
1087
|
+
* join the old, the assessment is redone over all of them, and the sign-in
|
|
1088
|
+
* time moves to now — this is a fresh authentication. The provider stays
|
|
1089
|
+
* the one that started the session. Null when the result is no sign-in.
|
|
1090
|
+
*/
|
|
1091
|
+
reauthenticated(signIn: SignInRecord | undefined, result: AuthenticateResult): SignInRecord | null;
|
|
1092
|
+
private pendingSignIns;
|
|
1093
|
+
private approvalTokens;
|
|
1094
|
+
private enrolTokens;
|
|
1095
|
+
/** Mail can carry an email-link factor: a mail transport and an explicit base-url for the link. */
|
|
1096
|
+
private mailForLinks;
|
|
1097
|
+
private linkBase;
|
|
1098
|
+
/** Whether an email link can be a second factor on this site now (#1523): mail and an explicit base-url. */
|
|
1099
|
+
emailLinksAvailable(): boolean;
|
|
1100
|
+
/**
|
|
1101
|
+
* The second factors `username` has enrolled and can use now (#1523). Any
|
|
1102
|
+
* enrolled factor works; one that is not truly available (no mail transport)
|
|
1103
|
+
* is not offered. Empty means the sign-in is one step, as before.
|
|
1104
|
+
*/
|
|
1105
|
+
secondFactorsFor(username: string): AvailableSecondFactor[];
|
|
1106
|
+
/** Hold a sign-in whose first factor passed, until a second factor completes it. Returns its handle. */
|
|
1107
|
+
beginTwoStep(first: AuthenticateResult, ctx: {
|
|
1108
|
+
binding: string;
|
|
1109
|
+
ip?: string;
|
|
1110
|
+
userAgent?: string;
|
|
1111
|
+
privateStoreHandle?: string;
|
|
1112
|
+
}): string;
|
|
1113
|
+
/** The pending sign-in, for the browser that started it; null when gone, expired, or another browser. */
|
|
1114
|
+
pendingSignIn(handle: string | undefined, binding: string | undefined): PendingSignIn | null;
|
|
1115
|
+
/**
|
|
1116
|
+
* Email a one-time approval link for the pending sign-in (#1523, #1532).
|
|
1117
|
+
* The link only opens the approval page; the button there approves, so a
|
|
1118
|
+
* mail scanner opening links approves nothing. Limited to one per minute.
|
|
1119
|
+
*/
|
|
1120
|
+
sendEmailApproval(handle: string, binding: string): Promise<'sent' | 'too-soon' | 'unavailable'>;
|
|
1121
|
+
/** What the approval page shows about the waiting sign-in; null for an unknown or used link. */
|
|
1122
|
+
approvalDetails(token: string): {
|
|
1123
|
+
username: string;
|
|
1124
|
+
requestedAt: number;
|
|
1125
|
+
ip?: string;
|
|
1126
|
+
userAgent?: string;
|
|
1127
|
+
} | null;
|
|
1128
|
+
/** Approve the waiting sign-in from its link (the button press). Single use; returns the username, or null. */
|
|
1129
|
+
approveByToken(token: string): string | null;
|
|
1130
|
+
/** Refuse the waiting sign-in from its link ("this wasn't me"). Returns the username, or null. */
|
|
1131
|
+
denyByToken(token: string): string | null;
|
|
1132
|
+
/**
|
|
1133
|
+
* The finished sign-in, once its second factor is satisfied — both factors,
|
|
1134
|
+
* assessed together. Single use: the pending entry is gone after this.
|
|
1135
|
+
*/
|
|
1136
|
+
completeTwoStep(handle: string, binding: string): {
|
|
1137
|
+
result: AuthenticateResult;
|
|
1138
|
+
privateStoreHandle?: string;
|
|
1139
|
+
} | null;
|
|
1140
|
+
/** Drop a pending sign-in and lock any keys it held. */
|
|
1141
|
+
dropPending(handle: string): void;
|
|
1142
|
+
private sweepPending;
|
|
1143
|
+
/**
|
|
1144
|
+
* Start enrolling the account's email address as a second factor (#1523).
|
|
1145
|
+
* The address must prove it receives mail first: a factor that cannot be
|
|
1146
|
+
* satisfied would lock its owner out.
|
|
1147
|
+
*/
|
|
1148
|
+
startEmailFactorEnrolment(ctx: PermissionSubject, username: string): Promise<'sent' | 'no-email' | 'unavailable' | 'already'>;
|
|
1149
|
+
/** Finish enrolling from the emailed link, by the same signed-in person. Single use. */
|
|
1150
|
+
confirmEmailFactorEnrolment(ctx: PermissionSubject, token: string): Promise<boolean>;
|
|
1151
|
+
/**
|
|
1152
|
+
* Initiate a challenge-based auth flow (magic link email, OAuth redirect).
|
|
1153
|
+
*/
|
|
1154
|
+
initiate(providerId: string, context: AuthInitiateContext): Promise<void>;
|
|
1155
|
+
/**
|
|
1156
|
+
* Consume a single-use token, returning whether this caller consumed it.
|
|
1157
|
+
*
|
|
1158
|
+
* #1021: the return value is the single-use gate — a caller must not create
|
|
1159
|
+
* a session when it is false, because that means another request consumed
|
|
1160
|
+
* the token first. A provider with no `consumeToken` has no single-use
|
|
1161
|
+
* semantics to enforce, so it reports true rather than blocking sign-in.
|
|
1162
|
+
*/
|
|
1163
|
+
consumeToken(providerId: string, token: string): boolean;
|
|
1164
|
+
/**
|
|
1165
|
+
* Register an authentication provider (#1050).
|
|
1166
|
+
*
|
|
1167
|
+
* The built-ins call this during `initialize()`, so the path an addon uses is
|
|
1168
|
+
* the path exercised on every boot rather than a second, less-travelled one.
|
|
1169
|
+
* Config gating stays with the caller: this method decides nothing about
|
|
1170
|
+
* whether a provider *should* be active, only that it is.
|
|
1171
|
+
*
|
|
1172
|
+
* ## Duplicate ids: first registration wins
|
|
1173
|
+
*
|
|
1174
|
+
* Deliberate, and the reason is security rather than tidiness. Last-wins
|
|
1175
|
+
* would let an addon replace the built-in `password` provider with its own
|
|
1176
|
+
* `verify()` — an auth bypass contributed by a config change. Throwing would
|
|
1177
|
+
* be safe but lets one bad addon take the whole instance down at boot, and a
|
|
1178
|
+
* refused provider is a smaller failure than a site that will not start. So:
|
|
1179
|
+
* keep the incumbent, warn loudly, carry on.
|
|
1180
|
+
*
|
|
1181
|
+
* ## Late registration is allowed
|
|
1182
|
+
*
|
|
1183
|
+
* AuthManager initializes before AddonsManager (WikiEngine), so an addon
|
|
1184
|
+
* registering during its own startup is registering late by definition. Such
|
|
1185
|
+
* a provider is absent from `getFactors()` until it registers and cannot serve a request
|
|
1186
|
+
* that arrives first. Both are acceptable; neither is silent.
|
|
1187
|
+
*
|
|
1188
|
+
* ## There is no unregisterProvider()
|
|
1189
|
+
*
|
|
1190
|
+
* Not an oversight. Withdrawing a provider would not invalidate the sessions
|
|
1191
|
+
* already established through it, so "disabled" would mean "no new logins"
|
|
1192
|
+
* while existing ones continued — which reads as revocation without being it.
|
|
1193
|
+
* Honest session revocation is a larger feature than this seam, and pretending
|
|
1194
|
+
* otherwise here would be worse than the gap.
|
|
1195
|
+
*
|
|
1196
|
+
* @param provider the provider to register
|
|
1197
|
+
* @param source attribution for logs — an addon name, or 'built-in'
|
|
1198
|
+
* @returns true if registered; false if rejected as malformed or duplicate
|
|
1199
|
+
*/
|
|
1200
|
+
registerProvider(provider: AuthProvider, source?: string): boolean;
|
|
1201
|
+
/**
|
|
1202
|
+
* Begin a redirect-based flow; returns the URL to send the browser to (#1049).
|
|
1203
|
+
*
|
|
1204
|
+
* Throws when the provider is absent or cannot start a flow, deliberately
|
|
1205
|
+
* and unlike {@link getFlowRedirect} below. There is no sensible fallback for
|
|
1206
|
+
* "where should the browser go" — returning `'/'` would bounce the user back
|
|
1207
|
+
* to the page they just left with no error to explain it, which reads as the
|
|
1208
|
+
* button being broken. Callers guard with `isEnabled()` first.
|
|
1209
|
+
*/
|
|
1210
|
+
startFlow(providerId: string, context?: AuthInitiateContext): Promise<string>;
|
|
1211
|
+
/**
|
|
1212
|
+
* Where the user was headed before the flow began, keyed by the flow's own
|
|
1213
|
+
* handle — a magic-link token, an OAuth state nonce (#1049).
|
|
1214
|
+
*
|
|
1215
|
+
* Must be called BEFORE `consumeToken()`, which deletes the entry.
|
|
1216
|
+
*
|
|
1217
|
+
* Falls back to `'/'` rather than throwing, deliberately and unlike
|
|
1218
|
+
* {@link startFlow} above: losing the destination costs the user a redirect
|
|
1219
|
+
* to the front page, while failing the sign-in over it would cost them the
|
|
1220
|
+
* login itself.
|
|
1221
|
+
*/
|
|
1222
|
+
getFlowRedirect(providerId: string, handle: string): string;
|
|
1223
|
+
/**
|
|
1224
|
+
* The device-state recorded when this flow was initiated (#1022), or null.
|
|
1225
|
+
*
|
|
1226
|
+
* Must be called BEFORE `consumeToken()`, which deletes the entry.
|
|
1227
|
+
*
|
|
1228
|
+
* Null means "nothing to compare against" — a provider that does not bind,
|
|
1229
|
+
* or a handle issued before binding existed. It never means "did not match":
|
|
1230
|
+
* that distinction is made in `evaluateDeviceBinding`, and collapsing the two
|
|
1231
|
+
* here would make an unbindable flow look like an attack.
|
|
1232
|
+
*/
|
|
1233
|
+
getDeviceState(providerId: string, handle: string): string | null;
|
|
1234
|
+
/**
|
|
1235
|
+
* Create the account behind a first-time credential (#1026, #1049).
|
|
1236
|
+
*
|
|
1237
|
+
* Must be called before `authenticate(providerId, …)` on the completing
|
|
1238
|
+
* request, because the account has to exist before a session names it.
|
|
1239
|
+
*
|
|
1240
|
+
* Three outcomes, and the caller must tell them apart:
|
|
1241
|
+
* - `true` — account created, or already existed
|
|
1242
|
+
* - `false` — the provider tried and could not; treat as a failed sign-in
|
|
1243
|
+
* - `undefined` — nothing to provision here, which is not a failure
|
|
1244
|
+
*
|
|
1245
|
+
* The previous `provisionMagicLinkUser` returned `false` for a missing
|
|
1246
|
+
* provider, conflating "no such capability" with "tried and failed". The end
|
|
1247
|
+
* state was the same only because `authenticate()` then failed anyway.
|
|
1248
|
+
*/
|
|
1249
|
+
provisionIfNew(providerId: string, handle: string): Promise<boolean | undefined>;
|
|
1250
|
+
/** Returns true if the provider is registered. */
|
|
1251
|
+
isEnabled(providerId: string): boolean;
|
|
1252
|
+
/** Returns all registered providers (for admin UI). */
|
|
1253
|
+
getProviders(): AuthProvider[];
|
|
1254
|
+
backup(): Promise<BackupData>;
|
|
1255
|
+
restore(_backupData: BackupData): Promise<void>;
|
|
1256
|
+
}
|
|
1257
|
+
export default AuthManager;
|