@jwilleke/ngdpbase-types 0.0.0 → 5.19.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 +427 -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 +188 -0
- package/lib/src/managers/DatabaseManager.js +590 -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/sqliteBackup.d.ts +128 -0
- package/lib/src/providers/sqliteBackup.js +365 -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 +169 -0
- package/lib/src/utils/auditEventNames.generated.js +174 -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,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MagicLinkAuthProvider — passwordless authentication via one-time email links.
|
|
3
|
+
*
|
|
4
|
+
* Registered by AuthManager when `ngdpbase.auth.magic-link.enabled` is true.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. initiate() — look up user by email, generate token, send link via mail transport
|
|
8
|
+
* 2. verify() — validate token (exists + not expired), return AuthResult
|
|
9
|
+
* 3. consumeToken() — delete token; its return value gates the session (#1021)
|
|
10
|
+
*
|
|
11
|
+
* `verify()` being side-effect free is load-bearing, not incidental (#1019): the
|
|
12
|
+
* route layer calls it twice, once on the GET that renders the sign-in
|
|
13
|
+
* confirmation page and again on the POST that completes the login. Only the
|
|
14
|
+
* POST consumes. Moving consumption into verify() would reintroduce the bug
|
|
15
|
+
* where a mail scanner's link pre-fetch burns the token before the user clicks.
|
|
16
|
+
*
|
|
17
|
+
* Tokens are in-memory only — lost on restart, acceptable for the default 15-min TTL.
|
|
18
|
+
*
|
|
19
|
+
* @see AuthManager
|
|
20
|
+
* @see BaseAuthProvider
|
|
21
|
+
*/
|
|
22
|
+
import type { AuthProvider, AuthInitiateContext, AuthVerifyCredentials, AuthResult, FactorDescription } from './BaseAuthProvider.js';
|
|
23
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
24
|
+
import type { MailProvider } from '../mail/MailProvider.js';
|
|
25
|
+
/**
|
|
26
|
+
* What an email link gives, in either role — starting a sign-in or confirming
|
|
27
|
+
* one as a second factor (#1523: one email-link factor). Email is never
|
|
28
|
+
* phishing-resistant and never above AAL1 (SP 800-63B §5.1.3.1).
|
|
29
|
+
*/
|
|
30
|
+
export declare const EMAIL_FACTOR: {
|
|
31
|
+
amr: string[];
|
|
32
|
+
aal: 1;
|
|
33
|
+
};
|
|
34
|
+
export interface MagicLinkConfig {
|
|
35
|
+
ttlMs: number;
|
|
36
|
+
mailProvider: MailProvider;
|
|
37
|
+
}
|
|
38
|
+
export declare class MagicLinkAuthProvider implements AuthProvider {
|
|
39
|
+
private engine;
|
|
40
|
+
private config;
|
|
41
|
+
readonly id = "magic-link";
|
|
42
|
+
readonly displayName = "Magic Link";
|
|
43
|
+
/** Email is never phishing-resistant and never above AAL1: SP 800-63B §5.1.3.1 bars it as out-of-band (#1523). */
|
|
44
|
+
readonly factor: FactorDescription;
|
|
45
|
+
/** token → entry */
|
|
46
|
+
private tokens;
|
|
47
|
+
/** email → timestamp of last request (rate-limit) */
|
|
48
|
+
private rateLimitMap;
|
|
49
|
+
constructor(engine: WikiEngine, config: MagicLinkConfig);
|
|
50
|
+
/**
|
|
51
|
+
* Initiate the magic-link flow: look up the user, generate a token, send the email.
|
|
52
|
+
* Silently succeeds if the email is not registered — prevents user enumeration.
|
|
53
|
+
*/
|
|
54
|
+
initiate(context: AuthInitiateContext): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Verify a token. Returns AuthResult on success, null if invalid or expired.
|
|
57
|
+
* Does NOT consume the token — call consumeToken() after the session is created.
|
|
58
|
+
*/
|
|
59
|
+
verify(credentials: AuthVerifyCredentials): Promise<AuthResult | null>;
|
|
60
|
+
/**
|
|
61
|
+
* Delete the token and report whether THIS caller was the one that did.
|
|
62
|
+
*
|
|
63
|
+
* #1021: the return value is the single-use gate, not a courtesy. The route
|
|
64
|
+
* used to verify, then consume, with an `await` between the two — so two
|
|
65
|
+
* POSTs carrying the same token (a double-clicked confirmation button, a
|
|
66
|
+
* client retry, a prefetch racing the real submit) could both clear
|
|
67
|
+
* verification before either consumed, and both would then establish a
|
|
68
|
+
* session. #1019's CSRF-protected POST does not close that: both submits
|
|
69
|
+
* come from the same session with the same valid token.
|
|
70
|
+
*
|
|
71
|
+
* `Map.delete` returns true only for the call that found an entry, and it is
|
|
72
|
+
* synchronous so it cannot interleave. Exactly one caller can ever see true,
|
|
73
|
+
* which makes consumption — not verification — the thing a session is gated
|
|
74
|
+
* on.
|
|
75
|
+
*/
|
|
76
|
+
consumeToken(token: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* The requesting browser's state value for this token (#1022), or null.
|
|
79
|
+
*
|
|
80
|
+
* Must be read BEFORE consumeToken(), which deletes the entry — same
|
|
81
|
+
* constraint as getFlowRedirect below, and for the same reason.
|
|
82
|
+
*/
|
|
83
|
+
getDeviceState(token: string): string | null;
|
|
84
|
+
/**
|
|
85
|
+
* Where the user was headed before the link was issued (#1049).
|
|
86
|
+
*
|
|
87
|
+
* Named for the capability rather than the mechanism: GoogleOIDCProvider
|
|
88
|
+
* answers the same question keyed by a state nonce, and the two used to be
|
|
89
|
+
* `getTokenRedirect` / `getStateRedirect`, which is why AuthManager needed a
|
|
90
|
+
* method per provider to ask it.
|
|
91
|
+
*
|
|
92
|
+
* Must be read BEFORE consumeToken(), which deletes the entry.
|
|
93
|
+
*/
|
|
94
|
+
getFlowRedirect(token: string): string;
|
|
95
|
+
/**
|
|
96
|
+
* Create the account behind a new-user token (#1026).
|
|
97
|
+
*
|
|
98
|
+
* Called from the POST that completes sign-in, never from the GET — so a mail
|
|
99
|
+
* scanner pre-fetching the link cannot bring an account into existence, for
|
|
100
|
+
* the same reason it cannot consume the token (#1019).
|
|
101
|
+
*
|
|
102
|
+
* The account is created with `isExternal: true`, which stores an empty
|
|
103
|
+
* password hash. `UserManager.verifyPassword` refuses an empty stored hash
|
|
104
|
+
* outright (#1042), so these accounts cannot be logged into with a password
|
|
105
|
+
* by any input — magic link is structurally their only way in.
|
|
106
|
+
*
|
|
107
|
+
* Idempotent: a token whose account already exists is a no-op, so a double
|
|
108
|
+
* submit cannot fail the second time.
|
|
109
|
+
*
|
|
110
|
+
* @returns true if the token is usable (existing user, or newly created)
|
|
111
|
+
*/
|
|
112
|
+
provisionIfNew(token: string): Promise<boolean>;
|
|
113
|
+
/** Whether an unknown email may create an account by verifying a link (#1026). */
|
|
114
|
+
private isAutoProvisionEnabled;
|
|
115
|
+
/**
|
|
116
|
+
* Role for auto-provisioned users. An unknown role name falls back to
|
|
117
|
+
* `reader` with a warning — a typo should degrade to read-only, not throw
|
|
118
|
+
* mid-signup or silently mint a more privileged account than intended.
|
|
119
|
+
*/
|
|
120
|
+
private resolveDefaultRole;
|
|
121
|
+
private isRateLimited;
|
|
122
|
+
private cleanupExpired;
|
|
123
|
+
/** Exposed for testing */
|
|
124
|
+
getTokenCount(): number;
|
|
125
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import BaseCacheProvider, { CacheStats } from './BaseCacheProvider.js';
|
|
2
|
+
import type { ProviderInfo } from '../types/Provider.js';
|
|
3
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
4
|
+
/**
|
|
5
|
+
* NullCacheProvider - No-op cache provider
|
|
6
|
+
*
|
|
7
|
+
* Used when caching is disabled or for testing.
|
|
8
|
+
* All cache operations are no-ops that return immediately.
|
|
9
|
+
*/
|
|
10
|
+
declare class NullCacheProvider extends BaseCacheProvider {
|
|
11
|
+
constructor(engine: WikiEngine);
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the null cache provider (no-op)
|
|
14
|
+
* @returns {Promise<void>}
|
|
15
|
+
*/
|
|
16
|
+
initialize(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Get provider information
|
|
19
|
+
* @returns {ProviderInfo} Provider metadata
|
|
20
|
+
*/
|
|
21
|
+
getProviderInfo(): ProviderInfo;
|
|
22
|
+
/**
|
|
23
|
+
* Get a value from the cache (always returns undefined)
|
|
24
|
+
* @template T
|
|
25
|
+
* @param {string} _key - The cache key
|
|
26
|
+
* @returns {Promise<T | undefined>} Always undefined
|
|
27
|
+
*/
|
|
28
|
+
get<T = unknown>(_key: string): Promise<T | undefined>;
|
|
29
|
+
/**
|
|
30
|
+
* Set a value in the cache (no-op)
|
|
31
|
+
* @template T
|
|
32
|
+
* @param {string} _key - The cache key
|
|
33
|
+
* @param {T} _value - The value to cache
|
|
34
|
+
* @param {number} [_ttlSec] - Time to live in seconds
|
|
35
|
+
* @returns {Promise<void>}
|
|
36
|
+
*/
|
|
37
|
+
set<T = unknown>(_key: string, _value: T, _ttlSec?: number): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Delete one or more keys from the cache (no-op)
|
|
40
|
+
* @param {string | string[]} _keys - Single key or array of keys to delete
|
|
41
|
+
* @returns {Promise<void>}
|
|
42
|
+
*/
|
|
43
|
+
del(_keys: string | string[]): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Clear cache entries (no-op)
|
|
46
|
+
* @param {string} [_pattern] - Optional pattern to match keys
|
|
47
|
+
* @returns {Promise<void>}
|
|
48
|
+
*/
|
|
49
|
+
clear(_pattern?: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Get keys matching a pattern (always returns empty array)
|
|
52
|
+
* @param {string} [_pattern='*'] - Pattern to match
|
|
53
|
+
* @returns {Promise<string[]>} Empty array
|
|
54
|
+
*/
|
|
55
|
+
keys(_pattern?: string): Promise<string[]>;
|
|
56
|
+
/**
|
|
57
|
+
* Get cache statistics (all zeros)
|
|
58
|
+
* @returns {Promise<CacheStats>} Cache statistics with all zeros
|
|
59
|
+
*/
|
|
60
|
+
stats(): Promise<CacheStats>;
|
|
61
|
+
/**
|
|
62
|
+
* Check if the cache provider is healthy (always true)
|
|
63
|
+
* @returns {Promise<boolean>} Always true
|
|
64
|
+
*/
|
|
65
|
+
isHealthy(): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Close/cleanup the cache provider (no-op)
|
|
68
|
+
* @returns {Promise<void>}
|
|
69
|
+
*/
|
|
70
|
+
close(): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
export default NullCacheProvider;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OidcAuthProvider — signing in through an outside OpenID Connect provider (#1743).
|
|
3
|
+
*
|
|
4
|
+
* One class, one registered instance per configured issuer (`oidc:<name>`),
|
|
5
|
+
* registered by AuthManager from `ngdpbase.auth.oidc.providers` (and the old
|
|
6
|
+
* `ngdpbase.auth.google-oidc.*` keys, read as the `google` entry). Routes never
|
|
7
|
+
* call it: AuthManager is the one door.
|
|
8
|
+
*
|
|
9
|
+
* What an outside sign-in may do (decided 2026-10-10, docs/planning/authentication.md):
|
|
10
|
+
* - The identity is the issuer plus the subject (`iss` + `sub`, OIDC Core §5.7),
|
|
11
|
+
* stored as a credentials-store row of kind `identity`. Only an identity
|
|
12
|
+
* already connected to an account signs anyone in. Email never matches or
|
|
13
|
+
* signs anyone in here.
|
|
14
|
+
* - It gives no roles: a provider's groups and roles claims are never read.
|
|
15
|
+
* - No access or refresh token is kept; nothing needs them.
|
|
16
|
+
*
|
|
17
|
+
* Each flow is protected as `GoogleOIDCProvider` was (#1630): a single-use
|
|
18
|
+
* `state` bound to the starting browser by an HTTP-only cookie, PKCE (S256),
|
|
19
|
+
* an OIDC `nonce`, and the ID token's signature, issuer, audience and expiry.
|
|
20
|
+
*
|
|
21
|
+
* Apple (slice 4b) differs in two ways handled here: its client secret is a
|
|
22
|
+
* JWT signed per exchange (`SignedClientSecret`), and it POSTs the callback
|
|
23
|
+
* (`responseMode: 'form_post'`), which `middleware/outsideCallbackRelay.ts`
|
|
24
|
+
* turns into the usual GET before the session and CSRF see it.
|
|
25
|
+
*
|
|
26
|
+
* Discovery (`/.well-known/openid-configuration`) and the issuer's keys are
|
|
27
|
+
* fetched through `guardedFetch`, like every outbound request (#1133), and
|
|
28
|
+
* cached. Flow state is in memory: a restart drops a sign-in in progress.
|
|
29
|
+
*
|
|
30
|
+
* @see AuthManager
|
|
31
|
+
* @see BaseAuthProvider
|
|
32
|
+
*/
|
|
33
|
+
import * as crypto from 'crypto';
|
|
34
|
+
import type { EgressPolicy } from '../http/ssrf.js';
|
|
35
|
+
import type { AuthInitiateContext, AuthProvider, AuthResult, AuthVerifyCredentials, FactorDescription, ReportedAssurance } from './BaseAuthProvider.js';
|
|
36
|
+
/**
|
|
37
|
+
* A provider button's icon (#1781): an image (an `https://` address or a site
|
|
38
|
+
* path), a Font Awesome class, or a logo the button partial draws itself.
|
|
39
|
+
*/
|
|
40
|
+
export type OutsideProviderIcon = {
|
|
41
|
+
kind: 'image';
|
|
42
|
+
src: string;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'fa';
|
|
45
|
+
className: string;
|
|
46
|
+
} | {
|
|
47
|
+
kind: 'builtin';
|
|
48
|
+
id: 'google';
|
|
49
|
+
};
|
|
50
|
+
/** One configured provider, as AuthManager resolved it from configuration. */
|
|
51
|
+
export interface OidcProviderConfig {
|
|
52
|
+
/** The entry's name; the provider's id is `oidc:<name>`, its URLs `/auth/oauth/<name>`. */
|
|
53
|
+
name: string;
|
|
54
|
+
displayName: string;
|
|
55
|
+
/** What its sign-in and Connect buttons show beside `displayName` (#1781). */
|
|
56
|
+
icon: OutsideProviderIcon;
|
|
57
|
+
/** The issuer identifier, exactly as the issuer's discovery document states it. */
|
|
58
|
+
issuer: string;
|
|
59
|
+
clientId: string;
|
|
60
|
+
/**
|
|
61
|
+
* How the site proves itself at the token endpoint: a shared secret,
|
|
62
|
+
* already resolved from its environment reference, or (Apple) a key it signs
|
|
63
|
+
* a short-lived client secret with. Never logged.
|
|
64
|
+
*/
|
|
65
|
+
clientSecret: string | SignedClientSecret;
|
|
66
|
+
/** This site's callback, registered with the provider. */
|
|
67
|
+
redirectUri: string;
|
|
68
|
+
/** Where outbound requests may go (ngdpbase.security.egress.*). */
|
|
69
|
+
policy: EgressPolicy;
|
|
70
|
+
/**
|
|
71
|
+
* `form_post`: the issuer POSTs the code and state back (Apple, whenever
|
|
72
|
+
* scopes are asked for). AuthManager answers `callbackIsPosted` from it, and
|
|
73
|
+
* the relay in `middleware/outsideCallbackRelay.ts` turns the POST into the
|
|
74
|
+
* usual GET callback.
|
|
75
|
+
*/
|
|
76
|
+
responseMode?: 'form_post';
|
|
77
|
+
/** The issuer may send `email_verified` as the string `"true"` / `"false"` (Apple); only true counts either way. */
|
|
78
|
+
emailVerifiedMayBeString?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Apple's client secret is not a shared secret but a JWT the site signs
|
|
82
|
+
* (ES256) with a key from its Apple developer account: `kid` the key's id,
|
|
83
|
+
* `iss` the team id, `sub` the client id, `aud` the issuer, and an expiry
|
|
84
|
+
* of at most six months. Signed afresh for each code exchange, valid 5 minutes.
|
|
85
|
+
*/
|
|
86
|
+
export interface SignedClientSecret {
|
|
87
|
+
teamId: string;
|
|
88
|
+
keyId: string;
|
|
89
|
+
/** The EC P-256 private key (Apple's `.p8`), checked by AuthManager when it registers the provider. */
|
|
90
|
+
privateKey: crypto.KeyObject;
|
|
91
|
+
}
|
|
92
|
+
/** An outside identity, as the provider vouched for it. */
|
|
93
|
+
export interface OidcIdentity {
|
|
94
|
+
/** `${iss}#${sub}`, with the configured issuer — the credentials-store subject. */
|
|
95
|
+
subject: string;
|
|
96
|
+
/** The provider's email, only when it reports it verified; '' otherwise. */
|
|
97
|
+
email: string;
|
|
98
|
+
/** The ID token's `amr` and `acr`, as sent (#1633); AuthManager decides what they count for. */
|
|
99
|
+
reported: ReportedAssurance;
|
|
100
|
+
}
|
|
101
|
+
/** How the provider reaches connected identities: the credentials store, through AuthManager. */
|
|
102
|
+
export interface IdentityStore {
|
|
103
|
+
/** The row connecting this subject to an account, or null. */
|
|
104
|
+
find(subject: string): {
|
|
105
|
+
id: string;
|
|
106
|
+
username: string;
|
|
107
|
+
} | null;
|
|
108
|
+
/** Record a sign-in with it, and the provider's current verified email. */
|
|
109
|
+
used(id: string, at: string, email: string): Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
export declare class OidcAuthProvider implements AuthProvider {
|
|
112
|
+
private readonly config;
|
|
113
|
+
private readonly identities;
|
|
114
|
+
readonly id: string;
|
|
115
|
+
readonly displayName: string;
|
|
116
|
+
readonly icon: OutsideProviderIcon;
|
|
117
|
+
/**
|
|
118
|
+
* AAL1 with no `amr`: what is offered, and what a sign-in counts for unless
|
|
119
|
+
* the admin trusts this provider's own second factor (`trust-mfa`, #1633),
|
|
120
|
+
* when AuthManager maps the reported `amr` / `acr` instead
|
|
121
|
+
* (`outsideLevel`). Primary: it starts a sign-in on its own.
|
|
122
|
+
*/
|
|
123
|
+
readonly factor: FactorDescription;
|
|
124
|
+
private readonly flows;
|
|
125
|
+
private discovery;
|
|
126
|
+
private keys;
|
|
127
|
+
constructor(config: OidcProviderConfig, identities: IdentityStore);
|
|
128
|
+
/** The configured issuer; an identity row's subject starts with it and `#`. */
|
|
129
|
+
get issuer(): string;
|
|
130
|
+
/** The entry's name, as in its URLs. */
|
|
131
|
+
get name(): string;
|
|
132
|
+
/** Whether the issuer POSTs the callback (`response_mode=form_post`) instead of redirecting to it. */
|
|
133
|
+
get callbackIsPosted(): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Begin a sign-in, or a connection when `connectFor` names the signed-in
|
|
136
|
+
* account: keep the flow's state and return the provider's authorization URL.
|
|
137
|
+
*/
|
|
138
|
+
startFlow({ redirect, deviceState, connectFor }?: AuthInitiateContext): Promise<string>;
|
|
139
|
+
/** Where the person was headed when the flow began; read before `consumeToken`. */
|
|
140
|
+
getFlowRedirect(state: string): string;
|
|
141
|
+
/** The account this flow is connecting an identity to, or null for a sign-in or an unknown flow. */
|
|
142
|
+
getFlowConnectFor(state: string): string | null;
|
|
143
|
+
/**
|
|
144
|
+
* Finish a sign-in: the account whose connected identity this is, or null.
|
|
145
|
+
* A flow started to connect never signs in. An identity connected to no
|
|
146
|
+
* account is refused, and kept on the flow until it is consumed, so
|
|
147
|
+
* AuthManager can decide what happens next (`unconnectedIdentity`);
|
|
148
|
+
* nothing here says whether an account exists.
|
|
149
|
+
*/
|
|
150
|
+
verify(credentials: AuthVerifyCredentials): Promise<AuthResult | null>;
|
|
151
|
+
/**
|
|
152
|
+
* Finish a connection for `username`: the identity the provider vouched
|
|
153
|
+
* for, or null. Only a flow started to connect that same account counts.
|
|
154
|
+
*/
|
|
155
|
+
verifyIdentity(credentials: AuthVerifyCredentials, username: string): Promise<OidcIdentity | null>;
|
|
156
|
+
/**
|
|
157
|
+
* The verified identity a refused sign-in brought that no account has
|
|
158
|
+
* connected, or null; read before `consumeToken` (#1743 slice 3).
|
|
159
|
+
*/
|
|
160
|
+
unconnectedIdentity(state: string): OidcIdentity | null;
|
|
161
|
+
/** Spend the flow's state; true only for the caller that did (#1021). */
|
|
162
|
+
consumeToken(state: string): boolean;
|
|
163
|
+
/**
|
|
164
|
+
* The state, the browser, the code exchange and the ID token, checked in
|
|
165
|
+
* that order. `connectFor` is the account a connection is for, or null for
|
|
166
|
+
* a sign-in; a flow of the other kind is refused.
|
|
167
|
+
*/
|
|
168
|
+
private redeem;
|
|
169
|
+
/** Exchange the code for the ID token. No access or refresh token is kept. */
|
|
170
|
+
private exchange;
|
|
171
|
+
/** The client secret for one code exchange: the shared one, or a freshly signed JWT (Apple). */
|
|
172
|
+
private clientSecret;
|
|
173
|
+
/** Signature, issuer, audience and expiry; the keys are fetched again once if none matches (rotation). */
|
|
174
|
+
private verifyIdToken;
|
|
175
|
+
private keySet;
|
|
176
|
+
/** The issuer's discovery document, cached; its `issuer` must be the configured one (OIDC Discovery §4.3). */
|
|
177
|
+
private discover;
|
|
178
|
+
private getJson;
|
|
179
|
+
private sweep;
|
|
180
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OidcBearerAuthProvider — accepts access tokens issued by this instance's own
|
|
3
|
+
* OpenID Connect provider on ngdpbase's API (#1576).
|
|
4
|
+
*
|
|
5
|
+
* An app that a person allowed through /oidc holds a token for the audience
|
|
6
|
+
* `<base-url>/api`. That token is a delegation from the person, like an agent
|
|
7
|
+
* token: its scopes are permission names and ride on `viaToken`, so the PDP
|
|
8
|
+
* ceiling and the edge gate (#1173) bound it exactly as they bound an agent
|
|
9
|
+
* token, and authority stays the person's, resolved live. Unlike an agent
|
|
10
|
+
* token it also carries how the person signed in, so roles above that level
|
|
11
|
+
* step down (`aal`), as in a web session.
|
|
12
|
+
*
|
|
13
|
+
* Verification is OidcManager's, in process; this provider only adapts it.
|
|
14
|
+
*/
|
|
15
|
+
import type { AuthProvider, AuthResult, AuthVerifyCredentials, ViaToken } from './BaseAuthProvider.js';
|
|
16
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
17
|
+
export interface OidcBearerAuthResult extends AuthResult {
|
|
18
|
+
viaToken: ViaToken;
|
|
19
|
+
aal: 1 | 2 | 3;
|
|
20
|
+
}
|
|
21
|
+
export declare class OidcBearerAuthProvider implements AuthProvider {
|
|
22
|
+
private readonly engine;
|
|
23
|
+
readonly id = "oidc-bearer";
|
|
24
|
+
readonly displayName = "OpenID Connect access token";
|
|
25
|
+
/** Marks this provider as usable by the bearer middleware. */
|
|
26
|
+
readonly acceptsBearer = true;
|
|
27
|
+
constructor(engine: WikiEngine);
|
|
28
|
+
verify(credentials: AuthVerifyCredentials): Promise<OidcBearerAuthResult | null>;
|
|
29
|
+
}
|
|
30
|
+
export default OidcBearerAuthProvider;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON, RegistrationResponseJSON } from '@simplewebauthn/server';
|
|
2
|
+
import type { AuthProvider, AuthResult, AuthVerifyCredentials, FactorDescription } from './BaseAuthProvider.js';
|
|
3
|
+
import type { CredentialRecord } from './BaseCredentialsProvider.js';
|
|
4
|
+
/** What a passkey row's `secret` holds. Public material only. */
|
|
5
|
+
export interface PasskeySecret {
|
|
6
|
+
publicKey: string;
|
|
7
|
+
counter: number;
|
|
8
|
+
/** As the authenticator reported them; v14 types transports as plain strings. */
|
|
9
|
+
transports?: string[];
|
|
10
|
+
deviceType: 'singleDevice' | 'multiDevice';
|
|
11
|
+
backedUp: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface PasskeyRelyingParty {
|
|
14
|
+
/** The host of base-url — the relying-party id. */
|
|
15
|
+
rpID: string;
|
|
16
|
+
/** The origin of base-url — what the browser must report. */
|
|
17
|
+
origin: string;
|
|
18
|
+
/** Shown by the authenticator when creating a passkey. */
|
|
19
|
+
rpName: string;
|
|
20
|
+
}
|
|
21
|
+
/** The store access AuthManager grants: find by credential id, record a use. */
|
|
22
|
+
export interface PasskeyStoreAccess {
|
|
23
|
+
find(credentialId: string): CredentialRecord | null;
|
|
24
|
+
used(id: string, at: string, secret: string): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
/** The base-url origin, its host, and whether WebAuthn may run there (https, or localhost). */
|
|
27
|
+
export declare function relyingPartyFrom(baseUrl: string, rpName: string): PasskeyRelyingParty | null;
|
|
28
|
+
export declare class PasskeyAuthProvider implements AuthProvider {
|
|
29
|
+
private readonly rp;
|
|
30
|
+
private readonly store;
|
|
31
|
+
readonly id = "passkey";
|
|
32
|
+
readonly displayName = "Passkey";
|
|
33
|
+
/**
|
|
34
|
+
* Something you have (the key) unlocked by something you know or are (the
|
|
35
|
+
* PIN or biometric the authenticator requires): AAL2, phishing-resistant.
|
|
36
|
+
* `swk` because most passkeys sync; a device-bound key is stronger, never
|
|
37
|
+
* weaker, than what is claimed here.
|
|
38
|
+
*/
|
|
39
|
+
readonly factor: FactorDescription;
|
|
40
|
+
constructor(rp: PasskeyRelyingParty, store: PasskeyStoreAccess);
|
|
41
|
+
/** The relying party passkeys are tied to — for the admin page and the enrol screen. */
|
|
42
|
+
relyingParty(): PasskeyRelyingParty;
|
|
43
|
+
/** Options for enrolling a passkey; `existing` keeps one authenticator from enrolling twice. */
|
|
44
|
+
registrationOptions(username: string, displayName: string, existing: readonly CredentialRecord[]): Promise<PublicKeyCredentialCreationOptionsJSON>;
|
|
45
|
+
/** Verify an enrolment; returns what the credentials store keeps, or null when it does not verify. */
|
|
46
|
+
verifyRegistration(response: RegistrationResponseJSON, expectedChallenge: string): Promise<{
|
|
47
|
+
subject: string;
|
|
48
|
+
secret: string;
|
|
49
|
+
} | null>;
|
|
50
|
+
/** Options for signing in: no username asked, any passkey for this host may answer. */
|
|
51
|
+
authenticationOptions(): Promise<PublicKeyCredentialRequestOptionsJSON>;
|
|
52
|
+
/**
|
|
53
|
+
* Verify a sign-in. The credential id names the passkey; its public key and
|
|
54
|
+
* counter come from the store. A counter that does not move forward (where
|
|
55
|
+
* the authenticator keeps one) is a cloned key and fails.
|
|
56
|
+
*/
|
|
57
|
+
verify(credentials: AuthVerifyCredentials): Promise<AuthResult | null>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PasswordAuthProvider — username/password authentication.
|
|
3
|
+
*
|
|
4
|
+
* The password method's check (#1758): AuthManager is the one door for every
|
|
5
|
+
* check of a secret, and this provider is its connection to the password.
|
|
6
|
+
* The hash stays on the account (UserManager keeps it and hands it only to
|
|
7
|
+
* this check); the check, and the decision to re-hash at the current cost
|
|
8
|
+
* (#1042), are made here. Registered by AuthManager as the default provider.
|
|
9
|
+
*/
|
|
10
|
+
import type { AuthProvider, AuthVerifyCredentials, AuthResult, FactorDescription } from './BaseAuthProvider.js';
|
|
11
|
+
import type { WikiEngine } from '../types/WikiEngine.js';
|
|
12
|
+
export declare class PasswordAuthProvider implements AuthProvider {
|
|
13
|
+
private engine;
|
|
14
|
+
readonly id = "password";
|
|
15
|
+
readonly displayName = "Username & Password";
|
|
16
|
+
/** Something you know (#1523). */
|
|
17
|
+
readonly factor: FactorDescription;
|
|
18
|
+
constructor(engine: WikiEngine);
|
|
19
|
+
verify(credentials: AuthVerifyCredentials): Promise<AuthResult | null>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The application database as one SQLite file, encrypted with SQLCipher when a
|
|
3
|
+
* key is given (#1536). Ported from yourPHR (yourphr#617).
|
|
4
|
+
*
|
|
5
|
+
* Opened and migrated in the constructor, so every provider built over the
|
|
6
|
+
* handle sees a finished schema. WAL mode with `synchronous = FULL`: a
|
|
7
|
+
* committed transaction is on the device before the call returns. WAL needs
|
|
8
|
+
* shared memory, which network filesystems do not provide, so the file's
|
|
9
|
+
* location is checked first (`refuseNetworkFilesystem`).
|
|
10
|
+
*/
|
|
11
|
+
import Database from 'better-sqlite3-multiple-ciphers';
|
|
12
|
+
import BaseDatabaseProvider from './BaseDatabaseProvider.js';
|
|
13
|
+
import type { ProviderDurability } from './BaseProvider.js';
|
|
14
|
+
import { type Migration, type MigrationReport } from './sqliteMigrations.js';
|
|
15
|
+
export type SqliteHandle = InstanceType<typeof Database>;
|
|
16
|
+
/** The lock wait when none is configured: the driver's own default, stated (#1710). */
|
|
17
|
+
export declare const DEFAULT_BUSY_TIMEOUT_MS = 5000;
|
|
18
|
+
declare class SqliteDatabaseProvider extends BaseDatabaseProvider<SqliteHandle> {
|
|
19
|
+
private readonly file;
|
|
20
|
+
protected providerName: string;
|
|
21
|
+
protected providerDescription: string;
|
|
22
|
+
private readonly db;
|
|
23
|
+
private closed;
|
|
24
|
+
private readonly encrypted;
|
|
25
|
+
/** What the ledger did at open: the ids applied now, and how many were already there. */
|
|
26
|
+
readonly migrations: MigrationReport;
|
|
27
|
+
/**
|
|
28
|
+
* @param file the database file; created when absent
|
|
29
|
+
* @param key the SQLCipher key; '' opens the file unencrypted
|
|
30
|
+
* @param ledger the migrations, strictly ascending by id
|
|
31
|
+
* @param ledgerTable where applied ids are recorded; an add-on's database names its own (#13)
|
|
32
|
+
* @param options.busyTimeoutMs how long a statement waits for a lock another connection holds
|
|
33
|
+
* (`ngdpbase.database.busy-timeout-ms`, #1710) before failing with SQLITE_BUSY
|
|
34
|
+
*/
|
|
35
|
+
constructor(file: string, key: string, ledger: Migration[], ledgerTable?: string, options?: {
|
|
36
|
+
busyTimeoutMs?: number;
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Run a further migration ledger on this connection, recorded in its own
|
|
40
|
+
* table: an add-on that depends on the add-on owning this database brings
|
|
41
|
+
* its own tables (ngdp-accounting-addons#13). Same rules as the ledger run at
|
|
42
|
+
* open: once, in order, transactional, and a newer schema is refused.
|
|
43
|
+
*/
|
|
44
|
+
migrate(ledger: Migration[], ledgerTable: string): MigrationReport;
|
|
45
|
+
get handle(): SqliteHandle;
|
|
46
|
+
storage(): {
|
|
47
|
+
location: string;
|
|
48
|
+
sizeBytes: number;
|
|
49
|
+
encrypted: boolean;
|
|
50
|
+
};
|
|
51
|
+
integrityOk(): boolean;
|
|
52
|
+
getDurability(): ProviderDurability;
|
|
53
|
+
close(): void;
|
|
54
|
+
}
|
|
55
|
+
export default SqliteDatabaseProvider;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The application database as one SQLite file, encrypted with SQLCipher when a
|
|
3
|
+
* key is given (#1536). Ported from yourPHR (yourphr#617).
|
|
4
|
+
*
|
|
5
|
+
* Opened and migrated in the constructor, so every provider built over the
|
|
6
|
+
* handle sees a finished schema. WAL mode with `synchronous = FULL`: a
|
|
7
|
+
* committed transaction is on the device before the call returns. WAL needs
|
|
8
|
+
* shared memory, which network filesystems do not provide, so the file's
|
|
9
|
+
* location is checked first (`refuseNetworkFilesystem`).
|
|
10
|
+
*/
|
|
11
|
+
import Database from 'better-sqlite3-multiple-ciphers';
|
|
12
|
+
import { existsSync, statSync } from 'node:fs';
|
|
13
|
+
import BaseDatabaseProvider from './BaseDatabaseProvider.js';
|
|
14
|
+
import { runMigrations } from './sqliteMigrations.js';
|
|
15
|
+
import { refuseNetworkFilesystem } from './sqliteLocation.js';
|
|
16
|
+
/** The lock wait when none is configured: the driver's own default, stated (#1710). */
|
|
17
|
+
export const DEFAULT_BUSY_TIMEOUT_MS = 5000;
|
|
18
|
+
class SqliteDatabaseProvider extends BaseDatabaseProvider {
|
|
19
|
+
file;
|
|
20
|
+
providerName = 'SqliteDatabaseProvider';
|
|
21
|
+
providerDescription = 'SQLite application database, SQLCipher-encrypted when a key is set';
|
|
22
|
+
db;
|
|
23
|
+
closed = false;
|
|
24
|
+
encrypted;
|
|
25
|
+
/** What the ledger did at open: the ids applied now, and how many were already there. */
|
|
26
|
+
migrations;
|
|
27
|
+
/**
|
|
28
|
+
* @param file the database file; created when absent
|
|
29
|
+
* @param key the SQLCipher key; '' opens the file unencrypted
|
|
30
|
+
* @param ledger the migrations, strictly ascending by id
|
|
31
|
+
* @param ledgerTable where applied ids are recorded; an add-on's database names its own (#13)
|
|
32
|
+
* @param options.busyTimeoutMs how long a statement waits for a lock another connection holds
|
|
33
|
+
* (`ngdpbase.database.busy-timeout-ms`, #1710) before failing with SQLITE_BUSY
|
|
34
|
+
*/
|
|
35
|
+
constructor(file, key, ledger, ledgerTable = 'schema_migrations', options = {}) {
|
|
36
|
+
super();
|
|
37
|
+
this.file = file;
|
|
38
|
+
refuseNetworkFilesystem(file);
|
|
39
|
+
this.db = new Database(file, { timeout: options.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS });
|
|
40
|
+
this.encrypted = key !== '';
|
|
41
|
+
try {
|
|
42
|
+
if (this.encrypted) {
|
|
43
|
+
this.db.pragma("cipher='sqlcipher'");
|
|
44
|
+
this.db.pragma(`key='${key.replace(/'/g, "''")}'`);
|
|
45
|
+
}
|
|
46
|
+
// The first read: a wrong key fails here, before anything is written.
|
|
47
|
+
this.db.pragma('journal_mode = WAL');
|
|
48
|
+
this.db.pragma('synchronous = FULL');
|
|
49
|
+
this.migrations = runMigrations(this.db, ledger, ledgerTable);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
this.db.close();
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Run a further migration ledger on this connection, recorded in its own
|
|
58
|
+
* table: an add-on that depends on the add-on owning this database brings
|
|
59
|
+
* its own tables (ngdp-accounting-addons#13). Same rules as the ledger run at
|
|
60
|
+
* open: once, in order, transactional, and a newer schema is refused.
|
|
61
|
+
*/
|
|
62
|
+
migrate(ledger, ledgerTable) {
|
|
63
|
+
return runMigrations(this.db, ledger, ledgerTable);
|
|
64
|
+
}
|
|
65
|
+
get handle() {
|
|
66
|
+
return this.db;
|
|
67
|
+
}
|
|
68
|
+
storage() {
|
|
69
|
+
return { location: this.file, sizeBytes: existsSync(this.file) ? statSync(this.file).size : 0, encrypted: this.encrypted };
|
|
70
|
+
}
|
|
71
|
+
integrityOk() {
|
|
72
|
+
try {
|
|
73
|
+
const rows = this.db.pragma('quick_check');
|
|
74
|
+
return String(rows[0]?.quick_check ?? '').toLowerCase() === 'ok';
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
getDurability() {
|
|
81
|
+
return { bufferedForMs: 0, bufferedRecords: 0, fsync: true };
|
|
82
|
+
}
|
|
83
|
+
close() {
|
|
84
|
+
if (this.closed)
|
|
85
|
+
return;
|
|
86
|
+
this.closed = true;
|
|
87
|
+
this.db.close();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export default SqliteDatabaseProvider;
|