@authup/server-core 1.0.0-beta.57 → 1.0.0-beta.58
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/dist/adapters/database/logger/module.mjs +2 -4
- package/dist/adapters/database/logger/module.mjs.map +1 -1
- package/dist/adapters/http/controllers/entities/identity-provider/module.d.ts.map +1 -1
- package/dist/adapters/http/controllers/entities/identity-provider/module.mjs +1 -1
- package/dist/adapters/http/controllers/entities/identity-provider/module.mjs.map +1 -1
- package/dist/adapters/http/middleware/built-in/authorization/module.mjs +2 -1
- package/dist/adapters/http/middleware/built-in/authorization/module.mjs.map +1 -1
- package/dist/adapters/http/middleware/built-in/rate-limit.mjs +2 -2
- package/dist/adapters/http/middleware/built-in/rate-limit.mjs.map +1 -1
- package/dist/adapters/http/request/client-certificate.mjs +1 -1
- package/dist/adapters/http/request/client-certificate.mjs.map +1 -1
- package/dist/adapters/http/request/helpers/locations.mjs +1 -3
- package/dist/adapters/http/request/helpers/locations.mjs.map +1 -1
- package/dist/app/modules/authentication/module.mjs +6 -3
- package/dist/app/modules/authentication/module.mjs.map +1 -1
- package/dist/app/modules/config/helpers.mjs +3 -2
- package/dist/app/modules/config/helpers.mjs.map +1 -1
- package/dist/app/modules/config/read/module.mjs +8 -3
- package/dist/app/modules/config/read/module.mjs.map +1 -1
- package/dist/app/modules/database/module.mjs +2 -1
- package/dist/app/modules/database/module.mjs.map +1 -1
- package/dist/app/modules/http/modules/controller.mjs +197 -126
- package/dist/app/modules/http/modules/controller.mjs.map +1 -1
- package/dist/app/modules/http/modules/middleware.mjs +10 -5
- package/dist/app/modules/http/modules/middleware.mjs.map +1 -1
- package/dist/app/modules/identity/module.mjs +6 -3
- package/dist/app/modules/identity/module.mjs.map +1 -1
- package/dist/app/modules/oauth2/module.mjs +37 -17
- package/dist/app/modules/oauth2/module.mjs.map +1 -1
- package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs +1 -1
- package/dist/app/modules/oauth2/repositories/authorize/state/repository.mjs.map +1 -1
- package/dist/app/modules/oauth2/repositories/token/repository.mjs +1 -1
- package/dist/app/modules/oauth2/repositories/token/repository.mjs.map +1 -1
- package/dist/app/modules/provisioning/module.mjs +9 -8
- package/dist/app/modules/provisioning/module.mjs.map +1 -1
- package/dist/app/modules/provisioning/sources/file/module.d.ts.map +1 -1
- package/dist/app/modules/provisioning/sources/file/module.mjs +11 -2
- package/dist/app/modules/provisioning/sources/file/module.mjs.map +1 -1
- package/dist/core/authentication/session/module.mjs +2 -4
- package/dist/core/authentication/session/module.mjs.map +1 -1
- package/dist/core/entities/client/service.d.ts.map +1 -1
- package/dist/core/entities/client/service.mjs +1 -1
- package/dist/core/entities/client/service.mjs.map +1 -1
- package/dist/core/entities/client-permission/service.mjs +12 -9
- package/dist/core/entities/client-permission/service.mjs.map +1 -1
- package/dist/core/entities/key/service.mjs +4 -2
- package/dist/core/entities/key/service.mjs.map +1 -1
- package/dist/core/entities/permission/service.d.ts.map +1 -1
- package/dist/core/entities/permission/service.mjs +1 -1
- package/dist/core/entities/permission/service.mjs.map +1 -1
- package/dist/core/entities/policy/service.d.ts.map +1 -1
- package/dist/core/entities/policy/service.mjs +3 -2
- package/dist/core/entities/policy/service.mjs.map +1 -1
- package/dist/core/entities/realm/service.d.ts.map +1 -1
- package/dist/core/entities/realm/service.mjs +1 -1
- package/dist/core/entities/realm/service.mjs.map +1 -1
- package/dist/core/entities/role/service.d.ts.map +1 -1
- package/dist/core/entities/role/service.mjs +1 -1
- package/dist/core/entities/role/service.mjs.map +1 -1
- package/dist/core/entities/role-permission/service.mjs +12 -9
- package/dist/core/entities/role-permission/service.mjs.map +1 -1
- package/dist/core/entities/scope/service.d.ts.map +1 -1
- package/dist/core/entities/scope/service.mjs +1 -1
- package/dist/core/entities/scope/service.mjs.map +1 -1
- package/dist/core/entities/user/service.d.ts.map +1 -1
- package/dist/core/entities/user/service.mjs +1 -1
- package/dist/core/entities/user/service.mjs.map +1 -1
- package/dist/core/entities/user-authenticator/helpers.mjs +1 -1
- package/dist/core/entities/user-authenticator/helpers.mjs.map +1 -1
- package/dist/core/entities/user-authenticator/service.mjs +7 -7
- package/dist/core/entities/user-authenticator/service.mjs.map +1 -1
- package/dist/core/entities/user-permission/service.mjs +12 -9
- package/dist/core/entities/user-permission/service.mjs.map +1 -1
- package/dist/core/identity/password-recovery/service.mjs +1 -1
- package/dist/core/identity/password-recovery/service.mjs.map +1 -1
- package/dist/core/provisioning/synchronizer/client/module.mjs +0 -1
- package/dist/core/provisioning/synchronizer/client/module.mjs.map +1 -1
- package/dist/core/provisioning/synchronizer/permission/module.mjs +0 -1
- package/dist/core/provisioning/synchronizer/permission/module.mjs.map +1 -1
- package/dist/core/provisioning/synchronizer/realm/module.mjs +0 -1
- package/dist/core/provisioning/synchronizer/realm/module.mjs.map +1 -1
- package/dist/core/provisioning/synchronizer/role/module.mjs +0 -1
- package/dist/core/provisioning/synchronizer/role/module.mjs.map +1 -1
- package/dist/core/provisioning/synchronizer/scope/module.mjs +0 -1
- package/dist/core/provisioning/synchronizer/scope/module.mjs.map +1 -1
- package/dist/core/provisioning/synchronizer/user/module.mjs +0 -1
- package/dist/core/provisioning/synchronizer/user/module.mjs.map +1 -1
- package/dist/path.mjs.map +1 -1
- package/dist/swagger.json +435 -11
- package/dist/ui/client/.vite/ssr-manifest.json +172 -10
- package/dist/ui/client/assets/index-BTK3GPZZ.js +106 -0
- package/dist/ui/client/assets/{index-Bk6-OZy9.css → index-Cx_pI7BS.css} +1 -1
- package/dist/ui/client/index.html +2 -2
- package/dist/ui/server/server.js +1894 -5045
- package/package.json +25 -24
- package/dist/ui/client/assets/index-DO6_7NTJ.js +0 -106
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":["SymmetricCipher","CacheInjectionKey","ModuleName","LoggerInjectionKey","ConsentRepositoryAdapter","OAuth2AuthorizationCodeRepository","OAuth2AuthorizationStateRepository","OAuth2ClientRepository","OAuth2ScopeRepository","OAuth2TokenRepository","SessionTokenRepositoryAdapter","KeyRepositoryAdapter","OAuth2AccessTokenIssuer","OAuth2AuthorizationCodeIssuer","OAuth2AuthorizationCodeRequestVerifier","OAuth2AuthorizationCodeVerifier","OAuth2AuthorizationStateManager","OAuth2MfaTokenIssuer","OAuth2OpenIDTokenIssuer","OAuth2RefreshTokenIssuer","OAuth2TokenRevoker","OAuth2TokenSigner","OAuth2TokenVerifier","OAuth2InjectionToken","IdentityInjectionKey","ClientEntity","ClientScopeEntity","ConfigInjectionKey","DatabaseInjectionKey","OAuth2Module","name","dependencies","OAUTH2","DATABASE","CACHE","CONFIG","IDENTITY","setup","container","config","resolve","register","ClientRepository","useFactory","c","repository","ScopeRepository","AuthorizationCodeRepository","cache","AuthorizationStateRepository","TokenRepository","SessionTokenRepository","DataSource","ConsentRepository","KeyStore","secretsCipher","secretsEncryptionKey","env","logger","warn","TokenSigner","keyStore","AuthorizationCodeIssuer","codeRepository","maxAge","tokenAccessMaxAge","AuthorizationCodeVerifier","AuthorizationCodeRequestVerifier","clientRepository","clientScopeRepository","scopeRepository","AuthorizationStateManager","stateRepository","TokenRevoker","tokenRepository","sessionTokenRepository","TokenVerifier","AccessTokenIssuer","tokenSigner","identityRoleProvider","RoleProvider","issuer","publicUrl","MfaTokenIssuer","mfaTicketMaxAge","RefreshTokenIssuer","tokenRefreshMaxAge","OpenIDTokenIssuer","signer","identityResolver","Resolver","options"],"sources":["../../../../src/app/modules/oauth2/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { SymmetricCipher } from '@authup/server-kit';\nimport type { Repository } from 'typeorm';\nimport { CacheInjectionKey } from '../cache/index.ts';\nimport type { IContainer } from 'eldin';\nimport type { IModule } from 'orkos';\nimport { ModuleName } from '../constants.ts';\nimport { LoggerInjectionKey } from '../logger/index.ts';\n\nimport {\n ConsentRepositoryAdapter,\n OAuth2AuthorizationCodeRepository,\n OAuth2AuthorizationStateRepository,\n OAuth2ClientRepository,\n OAuth2ScopeRepository,\n OAuth2TokenRepository,\n SessionTokenRepositoryAdapter,\n} from './repositories/index.ts';\nimport { KeyRepositoryAdapter } from '../database/repositories/index.ts';\nimport {\n OAuth2AccessTokenIssuer,\n OAuth2AuthorizationCodeIssuer,\n OAuth2AuthorizationCodeRequestVerifier,\n OAuth2AuthorizationCodeVerifier,\n OAuth2AuthorizationStateManager,\n OAuth2MfaTokenIssuer,\n OAuth2OpenIDTokenIssuer,\n OAuth2RefreshTokenIssuer,\n OAuth2TokenRevoker,\n OAuth2TokenSigner,\n OAuth2TokenVerifier,\n} from '../../../core/index.ts';\nimport { OAuth2InjectionToken } from './constants.ts';\nimport { IdentityInjectionKey } from '../identity/index.ts';\nimport { ClientEntity, ClientScopeEntity } from '../../../adapters/database/domains/index.ts';\nimport { ConfigInjectionKey } from '../config/index.ts';\nimport { DatabaseInjectionKey } from '../database/index.ts';\n\nexport class OAuth2Module implements IModule {\n readonly name: string;\n\n readonly dependencies: string[];\n\n constructor() {\n this.name = ModuleName.OAUTH2;\n this.dependencies = [ModuleName.DATABASE, ModuleName.CACHE, ModuleName.CONFIG, ModuleName.IDENTITY];\n }\n\n async setup(container: IContainer) : Promise<void> {\n const config = container.resolve(ConfigInjectionKey);\n\n container.register(OAuth2InjectionToken.ClientRepository, {\n useFactory: (c) => {\n const repository = c.resolve<Repository<ClientEntity>>(ClientEntity);\n return new OAuth2ClientRepository(repository);\n },\n });\n\n container.register(OAuth2InjectionToken.ScopeRepository, {\n useFactory: (c) => {\n const repository = c.resolve<Repository<ClientScopeEntity>>(ClientScopeEntity);\n return new OAuth2ScopeRepository(repository);\n },\n });\n\n container.register(OAuth2InjectionToken.AuthorizationCodeRepository, {\n useFactory: (c) => {\n const cache = c.resolve(CacheInjectionKey);\n return new OAuth2AuthorizationCodeRepository(cache);\n },\n });\n\n container.register(OAuth2InjectionToken.AuthorizationStateRepository, {\n useFactory: (c) => {\n const cache = c.resolve(CacheInjectionKey);\n return new OAuth2AuthorizationStateRepository(cache);\n },\n });\n\n container.register(OAuth2InjectionToken.TokenRepository, {\n useFactory: (c) => {\n const cache = c.resolve(CacheInjectionKey);\n return new OAuth2TokenRepository(cache);\n },\n });\n\n container.register(OAuth2InjectionToken.SessionTokenRepository, {\n useFactory: (c) => new SessionTokenRepositoryAdapter(\n c.resolve(DatabaseInjectionKey.DataSource),\n ),\n });\n\n container.register(OAuth2InjectionToken.ConsentRepository, {\n useFactory: (c) => new ConsentRepositoryAdapter(\n c.resolve(DatabaseInjectionKey.DataSource),\n ),\n });\n\n container.register(OAuth2InjectionToken.KeyStore, {\n // todo: cache use here\n useFactory: (c) => {\n const config = c.resolve(ConfigInjectionKey);\n\n let secretsCipher : SymmetricCipher | null = null;\n if (config.secretsEncryptionKey) {\n secretsCipher = new SymmetricCipher(config.secretsEncryptionKey);\n } else if (config.env === 'production') {\n // parity posture without a KEK (Keycloak/authentik store key\n // material plaintext too) — but say so once, loudly.\n const logger = c.resolve(LoggerInjectionKey);\n logger.warn(\n 'The realm key store persists key material unwrapped; ' +\n 'set SECRETS_ENCRYPTION_KEY to wrap it at rest.',\n );\n }\n\n return new KeyRepositoryAdapter(c.resolve(DatabaseInjectionKey.DataSource), { secretsCipher });\n },\n });\n\n container.register(OAuth2InjectionToken.TokenSigner, {\n useFactory: (c) => {\n const keyStore = c.resolve(OAuth2InjectionToken.KeyStore);\n return new OAuth2TokenSigner(keyStore);\n },\n });\n\n // authorization code issuer\n container.register(OAuth2InjectionToken.AuthorizationCodeIssuer, {\n useFactory: (c) => {\n const codeRepository = c.resolve(OAuth2InjectionToken.AuthorizationCodeRepository);\n\n return new OAuth2AuthorizationCodeIssuer(\n codeRepository,\n {\n // todo: own constant here\n maxAge: config.tokenAccessMaxAge,\n },\n );\n },\n });\n container.register(OAuth2InjectionToken.AuthorizationCodeVerifier, {\n useFactory: (c) => {\n const codeRepository = c.resolve(OAuth2InjectionToken.AuthorizationCodeRepository);\n\n return new OAuth2AuthorizationCodeVerifier(codeRepository);\n },\n });\n\n // authorization code request verifier\n container.register(OAuth2InjectionToken.AuthorizationCodeRequestVerifier, {\n useFactory: (c) => {\n const clientRepository = c.resolve(OAuth2InjectionToken.ClientRepository);\n const clientScopeRepository = c.resolve(OAuth2InjectionToken.ScopeRepository);\n\n return new OAuth2AuthorizationCodeRequestVerifier({\n clientRepository,\n scopeRepository: clientScopeRepository,\n });\n },\n });\n\n container.register(OAuth2InjectionToken.AuthorizationStateManager, {\n useFactory: (c) => {\n const stateRepository = c.resolve(OAuth2InjectionToken.AuthorizationStateRepository);\n\n return new OAuth2AuthorizationStateManager(stateRepository);\n },\n });\n\n // token revoker\n container.register(OAuth2InjectionToken.TokenRevoker, {\n useFactory: (c) => {\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const sessionTokenRepository = c.resolve(OAuth2InjectionToken.SessionTokenRepository);\n return new OAuth2TokenRevoker(tokenRepository, sessionTokenRepository);\n },\n });\n\n // token verifier\n container.register(OAuth2InjectionToken.TokenVerifier, {\n useFactory: (c) => {\n const keyStore = c.resolve(OAuth2InjectionToken.KeyStore);\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n\n return new OAuth2TokenVerifier(keyStore, tokenRepository);\n },\n });\n\n // access token issuer\n container.register(OAuth2InjectionToken.AccessTokenIssuer, {\n useFactory: (c) => {\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const tokenSigner = c.resolve(OAuth2InjectionToken.TokenSigner);\n const identityRoleProvider = c.resolve(IdentityInjectionKey.RoleProvider);\n const sessionTokenRepository = c.resolve(OAuth2InjectionToken.SessionTokenRepository);\n return new OAuth2AccessTokenIssuer(\n tokenRepository,\n tokenSigner,\n {\n maxAge: config.tokenAccessMaxAge,\n issuer: config.publicUrl,\n },\n identityRoleProvider,\n sessionTokenRepository,\n );\n },\n });\n\n // mfa login-ticket issuer\n container.register(OAuth2InjectionToken.MfaTokenIssuer, {\n useFactory: (c) => {\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const tokenSigner = c.resolve(OAuth2InjectionToken.TokenSigner);\n\n return new OAuth2MfaTokenIssuer(\n tokenRepository,\n tokenSigner,\n {\n maxAge: config.mfaTicketMaxAge,\n issuer: config.publicUrl,\n },\n );\n },\n });\n\n // refresh token issuer\n container.register(OAuth2InjectionToken.RefreshTokenIssuer, {\n useFactory: (c) => {\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const tokenSigner = c.resolve(OAuth2InjectionToken.TokenSigner);\n const sessionTokenRepository = c.resolve(OAuth2InjectionToken.SessionTokenRepository);\n\n return new OAuth2RefreshTokenIssuer(\n tokenRepository,\n tokenSigner,\n {\n maxAge: config.tokenRefreshMaxAge,\n issuer: config.publicUrl,\n },\n sessionTokenRepository,\n );\n },\n });\n\n // open-id token issuer\n container.register(OAuth2InjectionToken.OpenIDTokenIssuer, {\n useFactory: (c) => {\n const repository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const signer = c.resolve(OAuth2InjectionToken.TokenSigner);\n const identityResolver = c.resolve(IdentityInjectionKey.Resolver);\n\n return new OAuth2OpenIDTokenIssuer({\n repository,\n signer,\n\n identityResolver,\n\n options: {\n // todo: own constant here\n maxAge: config.tokenAccessMaxAge,\n issuer: config.publicUrl,\n },\n });\n },\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"module.mjs","names":["SymmetricCipher","CacheInjectionKey","ModuleName","LoggerInjectionKey","ConsentRepositoryAdapter","OAuth2AuthorizationCodeRepository","OAuth2AuthorizationStateRepository","OAuth2ClientRepository","OAuth2ScopeRepository","OAuth2TokenRepository","SessionTokenRepositoryAdapter","KeyRepositoryAdapter","OAuth2AccessTokenIssuer","OAuth2AuthorizationCodeIssuer","OAuth2AuthorizationCodeRequestVerifier","OAuth2AuthorizationCodeVerifier","OAuth2AuthorizationStateManager","OAuth2MfaTokenIssuer","OAuth2OpenIDTokenIssuer","OAuth2RefreshTokenIssuer","OAuth2TokenRevoker","OAuth2TokenSigner","OAuth2TokenVerifier","OAuth2InjectionToken","IdentityInjectionKey","ClientEntity","ClientScopeEntity","ConfigInjectionKey","DatabaseInjectionKey","OAuth2Module","name","dependencies","OAUTH2","DATABASE","CACHE","CONFIG","IDENTITY","setup","container","config","resolve","register","ClientRepository","useFactory","c","repository","ScopeRepository","AuthorizationCodeRepository","cache","AuthorizationStateRepository","TokenRepository","SessionTokenRepository","DataSource","ConsentRepository","KeyStore","secretsCipher","secretsEncryptionKey","env","logger","warn","TokenSigner","keyStore","AuthorizationCodeIssuer","codeRepository","maxAge","tokenAccessMaxAge","AuthorizationCodeVerifier","AuthorizationCodeRequestVerifier","clientRepository","clientScopeRepository","scopeRepository","AuthorizationStateManager","stateRepository","TokenRevoker","tokenRepository","sessionTokenRepository","TokenVerifier","AccessTokenIssuer","tokenSigner","identityRoleProvider","RoleProvider","issuer","publicUrl","MfaTokenIssuer","mfaTicketMaxAge","RefreshTokenIssuer","tokenRefreshMaxAge","OpenIDTokenIssuer","signer","identityResolver","Resolver","options"],"sources":["../../../../src/app/modules/oauth2/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { SymmetricCipher } from '@authup/server-kit';\nimport type { Repository } from 'typeorm';\nimport { CacheInjectionKey } from '../cache/index.ts';\nimport type { IContainer } from 'eldin';\nimport type { IModule } from 'orkos';\nimport { ModuleName } from '../constants.ts';\nimport { LoggerInjectionKey } from '../logger/index.ts';\n\nimport {\n ConsentRepositoryAdapter,\n OAuth2AuthorizationCodeRepository,\n OAuth2AuthorizationStateRepository,\n OAuth2ClientRepository,\n OAuth2ScopeRepository,\n OAuth2TokenRepository,\n SessionTokenRepositoryAdapter,\n} from './repositories/index.ts';\nimport { KeyRepositoryAdapter } from '../database/repositories/index.ts';\nimport {\n OAuth2AccessTokenIssuer,\n OAuth2AuthorizationCodeIssuer,\n OAuth2AuthorizationCodeRequestVerifier,\n OAuth2AuthorizationCodeVerifier,\n OAuth2AuthorizationStateManager,\n OAuth2MfaTokenIssuer,\n OAuth2OpenIDTokenIssuer,\n OAuth2RefreshTokenIssuer,\n OAuth2TokenRevoker,\n OAuth2TokenSigner,\n OAuth2TokenVerifier,\n} from '../../../core/index.ts';\nimport { OAuth2InjectionToken } from './constants.ts';\nimport { IdentityInjectionKey } from '../identity/index.ts';\nimport { ClientEntity, ClientScopeEntity } from '../../../adapters/database/domains/index.ts';\nimport { ConfigInjectionKey } from '../config/index.ts';\nimport { DatabaseInjectionKey } from '../database/index.ts';\n\nexport class OAuth2Module implements IModule {\n readonly name: string;\n\n readonly dependencies: string[];\n\n constructor() {\n this.name = ModuleName.OAUTH2;\n this.dependencies = [ModuleName.DATABASE, ModuleName.CACHE, ModuleName.CONFIG, ModuleName.IDENTITY];\n }\n\n async setup(container: IContainer) : Promise<void> {\n const config = container.resolve(ConfigInjectionKey);\n\n container.register(OAuth2InjectionToken.ClientRepository, {\n useFactory: (c) => {\n const repository = c.resolve<Repository<ClientEntity>>(ClientEntity);\n return new OAuth2ClientRepository(repository);\n },\n });\n\n container.register(OAuth2InjectionToken.ScopeRepository, {\n useFactory: (c) => {\n const repository = c.resolve<Repository<ClientScopeEntity>>(ClientScopeEntity);\n return new OAuth2ScopeRepository(repository);\n },\n });\n\n container.register(OAuth2InjectionToken.AuthorizationCodeRepository, {\n useFactory: (c) => {\n const cache = c.resolve(CacheInjectionKey);\n return new OAuth2AuthorizationCodeRepository(cache);\n },\n });\n\n container.register(OAuth2InjectionToken.AuthorizationStateRepository, {\n useFactory: (c) => {\n const cache = c.resolve(CacheInjectionKey);\n return new OAuth2AuthorizationStateRepository(cache);\n },\n });\n\n container.register(OAuth2InjectionToken.TokenRepository, {\n useFactory: (c) => {\n const cache = c.resolve(CacheInjectionKey);\n return new OAuth2TokenRepository(cache);\n },\n });\n\n container.register(OAuth2InjectionToken.SessionTokenRepository, {\n useFactory: (c) => new SessionTokenRepositoryAdapter(\n c.resolve(DatabaseInjectionKey.DataSource),\n ),\n });\n\n container.register(OAuth2InjectionToken.ConsentRepository, {\n useFactory: (c) => new ConsentRepositoryAdapter(\n c.resolve(DatabaseInjectionKey.DataSource),\n ),\n });\n\n container.register(OAuth2InjectionToken.KeyStore, {\n // todo: cache use here\n useFactory: (c) => {\n const config = c.resolve(ConfigInjectionKey);\n\n let secretsCipher : SymmetricCipher | null = null;\n if (config.secretsEncryptionKey) {\n secretsCipher = new SymmetricCipher(config.secretsEncryptionKey);\n } else if (config.env === 'production') {\n // parity posture without a KEK (Keycloak/authentik store key\n // material plaintext too) — but say so once, loudly.\n const logger = c.resolve(LoggerInjectionKey);\n logger.warn(\n 'The realm key store persists key material unwrapped; ' +\n 'set SECRETS_ENCRYPTION_KEY to wrap it at rest.',\n );\n }\n\n return new KeyRepositoryAdapter(c.resolve(DatabaseInjectionKey.DataSource), { secretsCipher });\n },\n });\n\n container.register(OAuth2InjectionToken.TokenSigner, {\n useFactory: (c) => {\n const keyStore = c.resolve(OAuth2InjectionToken.KeyStore);\n return new OAuth2TokenSigner(keyStore);\n },\n });\n\n // authorization code issuer\n container.register(OAuth2InjectionToken.AuthorizationCodeIssuer, {\n useFactory: (c) => {\n const codeRepository = c.resolve(OAuth2InjectionToken.AuthorizationCodeRepository);\n\n return new OAuth2AuthorizationCodeIssuer(\n codeRepository,\n {\n // todo: own constant here\n maxAge: config.tokenAccessMaxAge,\n },\n );\n },\n });\n container.register(OAuth2InjectionToken.AuthorizationCodeVerifier, {\n useFactory: (c) => {\n const codeRepository = c.resolve(OAuth2InjectionToken.AuthorizationCodeRepository);\n\n return new OAuth2AuthorizationCodeVerifier(codeRepository);\n },\n });\n\n // authorization code request verifier\n container.register(OAuth2InjectionToken.AuthorizationCodeRequestVerifier, {\n useFactory: (c) => {\n const clientRepository = c.resolve(OAuth2InjectionToken.ClientRepository);\n const clientScopeRepository = c.resolve(OAuth2InjectionToken.ScopeRepository);\n\n return new OAuth2AuthorizationCodeRequestVerifier({\n clientRepository,\n scopeRepository: clientScopeRepository,\n });\n },\n });\n\n container.register(OAuth2InjectionToken.AuthorizationStateManager, {\n useFactory: (c) => {\n const stateRepository = c.resolve(OAuth2InjectionToken.AuthorizationStateRepository);\n\n return new OAuth2AuthorizationStateManager(stateRepository);\n },\n });\n\n // token revoker\n container.register(OAuth2InjectionToken.TokenRevoker, {\n useFactory: (c) => {\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const sessionTokenRepository = c.resolve(OAuth2InjectionToken.SessionTokenRepository);\n return new OAuth2TokenRevoker(tokenRepository, sessionTokenRepository);\n },\n });\n\n // token verifier\n container.register(OAuth2InjectionToken.TokenVerifier, {\n useFactory: (c) => {\n const keyStore = c.resolve(OAuth2InjectionToken.KeyStore);\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n\n return new OAuth2TokenVerifier(keyStore, tokenRepository);\n },\n });\n\n // access token issuer\n container.register(OAuth2InjectionToken.AccessTokenIssuer, {\n useFactory: (c) => {\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const tokenSigner = c.resolve(OAuth2InjectionToken.TokenSigner);\n const identityRoleProvider = c.resolve(IdentityInjectionKey.RoleProvider);\n const sessionTokenRepository = c.resolve(OAuth2InjectionToken.SessionTokenRepository);\n return new OAuth2AccessTokenIssuer(\n tokenRepository,\n tokenSigner,\n {\n maxAge: config.tokenAccessMaxAge,\n issuer: config.publicUrl,\n },\n identityRoleProvider,\n sessionTokenRepository,\n );\n },\n });\n\n // mfa login-ticket issuer\n container.register(OAuth2InjectionToken.MfaTokenIssuer, {\n useFactory: (c) => {\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const tokenSigner = c.resolve(OAuth2InjectionToken.TokenSigner);\n\n return new OAuth2MfaTokenIssuer(\n tokenRepository,\n tokenSigner,\n {\n maxAge: config.mfaTicketMaxAge,\n issuer: config.publicUrl,\n },\n );\n },\n });\n\n // refresh token issuer\n container.register(OAuth2InjectionToken.RefreshTokenIssuer, {\n useFactory: (c) => {\n const tokenRepository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const tokenSigner = c.resolve(OAuth2InjectionToken.TokenSigner);\n const sessionTokenRepository = c.resolve(OAuth2InjectionToken.SessionTokenRepository);\n\n return new OAuth2RefreshTokenIssuer(\n tokenRepository,\n tokenSigner,\n {\n maxAge: config.tokenRefreshMaxAge,\n issuer: config.publicUrl,\n },\n sessionTokenRepository,\n );\n },\n });\n\n // open-id token issuer\n container.register(OAuth2InjectionToken.OpenIDTokenIssuer, {\n useFactory: (c) => {\n const repository = c.resolve(OAuth2InjectionToken.TokenRepository);\n const signer = c.resolve(OAuth2InjectionToken.TokenSigner);\n const identityResolver = c.resolve(IdentityInjectionKey.Resolver);\n\n return new OAuth2OpenIDTokenIssuer({\n repository,\n signer,\n\n identityResolver,\n\n options: {\n // todo: own constant here\n maxAge: config.tokenAccessMaxAge,\n issuer: config.publicUrl,\n },\n });\n },\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.mjs","names":["createNanoID","buildCacheKey","CacheOAuth2Prefix","OAuth2AuthorizationStateRepository","cache","findOneById","key","id","prefix","AUTHORIZATION_CODE","payload","get","remove","drop","insert","data","state","set","ttl"],"sources":["../../../../../../../src/app/modules/oauth2/repositories/authorize/state/repository.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { createNanoID } from '@authup/kit';\nimport type { ICache } from '@authup/server-kit';\nimport { buildCacheKey } from '@authup/server-kit';\nimport type { IOAuth2AuthorizeStateRepository, OAuth2AuthorizationState } from '../../../../../../core/index.ts';\nimport { CacheOAuth2Prefix } from '../../constants.ts';\n\nexport class OAuth2AuthorizationStateRepository implements IOAuth2AuthorizeStateRepository {\n protected cache : ICache;\n\n constructor(cache: ICache) {\n this.cache = cache;\n }\n\n async findOneById(key: string): Promise<OAuth2AuthorizationState | null> {\n const id = buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key, \n });\n const payload = await this.cache.get<OAuth2AuthorizationState>(id);\n if (payload) {\n return payload;\n }\n\n return null;\n }\n\n async remove(key: string): Promise<void> {\n await this.cache.drop(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key, \n }),\n );\n }\n\n async insert(data: OAuth2AuthorizationState): Promise<string> {\n const state = createNanoID();\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key: state, \n }),\n data,\n { ttl: 1000 * 60 * 30 }, // 30 min\n );\n\n return state;\n }\n}\n"],"mappings":";;;;;;;AAaA,IAAaG,qCAAb,MAAaA;CACCC;CAEV,YAAYA,OAAe;EACvB,KAAKA,QAAQA;CACjB;CAEA,MAAMC,YAAYC,KAAuD;EACrE,MAAMC,KAAKN,cAAc;GACrBO,QAAQN,kBAAkBO;GAC1BH;EACJ,CAAA;EACA,MAAMI,UAAU,MAAM,KAAKN,MAAMO,IAA8BJ,EAAAA;EAC/D,IAAIG,SACA,OAAOA;EAGX,OAAO;CACX;CAEA,MAAME,OAAON,KAA4B;EACrC,MAAM,KAAKF,MAAMS,KACbZ,cAAc;GACVO,QAAQN,kBAAkBO;GAC1BH;EACJ,CAAA,CAAA;CAER;CAEA,MAAMQ,OAAOC,MAAiD;EAC1D,MAAMC,QAAQhB,aAAAA;EAEd,MAAM,KAAKI,MAAMa,IACbhB,cAAc;GACVO,QAAQN,kBAAkBO;GAC1BH,KAAKU;EACT,CAAA,GACAD,MACA,EAAEG,KAAK,
|
|
1
|
+
{"version":3,"file":"repository.mjs","names":["createNanoID","buildCacheKey","CacheOAuth2Prefix","OAuth2AuthorizationStateRepository","cache","findOneById","key","id","prefix","AUTHORIZATION_CODE","payload","get","remove","drop","insert","data","state","set","ttl"],"sources":["../../../../../../../src/app/modules/oauth2/repositories/authorize/state/repository.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { createNanoID } from '@authup/kit';\nimport type { ICache } from '@authup/server-kit';\nimport { buildCacheKey } from '@authup/server-kit';\nimport type { IOAuth2AuthorizeStateRepository, OAuth2AuthorizationState } from '../../../../../../core/index.ts';\nimport { CacheOAuth2Prefix } from '../../constants.ts';\n\nexport class OAuth2AuthorizationStateRepository implements IOAuth2AuthorizeStateRepository {\n protected cache : ICache;\n\n constructor(cache: ICache) {\n this.cache = cache;\n }\n\n async findOneById(key: string): Promise<OAuth2AuthorizationState | null> {\n const id = buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key, \n });\n const payload = await this.cache.get<OAuth2AuthorizationState>(id);\n if (payload) {\n return payload;\n }\n\n return null;\n }\n\n async remove(key: string): Promise<void> {\n await this.cache.drop(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key, \n }),\n );\n }\n\n async insert(data: OAuth2AuthorizationState): Promise<string> {\n const state = createNanoID();\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.AUTHORIZATION_CODE,\n key: state, \n }),\n data,\n { ttl: 1000 * 60 * 30 }, // 30 min\n );\n\n return state;\n }\n}\n"],"mappings":";;;;;;;AAaA,IAAaG,qCAAb,MAAaA;CACCC;CAEV,YAAYA,OAAe;EACvB,KAAKA,QAAQA;CACjB;CAEA,MAAMC,YAAYC,KAAuD;EACrE,MAAMC,KAAKN,cAAc;GACrBO,QAAQN,kBAAkBO;GAC1BH;EACJ,CAAA;EACA,MAAMI,UAAU,MAAM,KAAKN,MAAMO,IAA8BJ,EAAAA;EAC/D,IAAIG,SACA,OAAOA;EAGX,OAAO;CACX;CAEA,MAAME,OAAON,KAA4B;EACrC,MAAM,KAAKF,MAAMS,KACbZ,cAAc;GACVO,QAAQN,kBAAkBO;GAC1BH;EACJ,CAAA,CAAA;CAER;CAEA,MAAMQ,OAAOC,MAAiD;EAC1D,MAAMC,QAAQhB,aAAAA;EAEd,MAAM,KAAKI,MAAMa,IACbhB,cAAc;GACVO,QAAQN,kBAAkBO;GAC1BH,KAAKU;EACT,CAAA,GACAD,MACA,EAAEG,KAAK,KAAe,CAAA;EAG1B,OAAOF;CACX;AACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.mjs","names":["buildCacheKey","randomUUID","CacheOAuth2Prefix","OAuth2TokenRepository","cache","findOneBySignature","signature","get","prefix","TOKEN_CLAIMS","key","findOneById","id","TOKEN","removeById","token","drop","setInactive","exp","insert","payload","jti","set","ttl","buildTTL","save","saveWithSignature","data","normalized","isInactive","cacheKey","TOKEN_INACTIVE","response","claimInactive","add","utc","Date","now"],"sources":["../../../../../../src/app/modules/oauth2/repositories/token/repository.ts"],"sourcesContent":["/*\n * Copyright (c) 2025-2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { ICache } from '@authup/server-kit';\nimport { buildCacheKey } from '@authup/server-kit';\nimport type { OAuth2TokenPayload } from '@authup/specs';\nimport { randomUUID } from 'node:crypto';\nimport type { IOAuth2TokenRepository } from '../../../../../core/index.ts';\nimport { CacheOAuth2Prefix } from '../constants.ts';\n\nexport class OAuth2TokenRepository implements IOAuth2TokenRepository {\n protected cache : ICache;\n\n constructor(cache: ICache) {\n this.cache = cache;\n }\n\n // -----------------------------------------------------\n\n async findOneBySignature(signature: string): Promise<OAuth2TokenPayload | null> {\n return this.cache.get<OAuth2TokenPayload>(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_CLAIMS,\n key: signature, \n }),\n );\n }\n\n async findOneById(id: string): Promise<OAuth2TokenPayload | null> {\n return this.cache.get<OAuth2TokenPayload>(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN,\n key: id, \n }),\n );\n }\n\n // -----------------------------------------------------\n\n async removeById(id: string): Promise<void> {\n const token = await this.findOneById(id);\n if (!token) {\n return;\n }\n\n const key = buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN,\n key: id, \n });\n await this.cache.drop(key);\n\n await this.setInactive(id, token.exp);\n }\n\n // -----------------------------------------------------\n\n async insert(payload: OAuth2TokenPayload): Promise<OAuth2TokenPayload> {\n payload.jti = randomUUID();\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN,\n key: payload.jti, \n }),\n payload,\n { ttl: this.buildTTL(payload.exp) },\n );\n\n return payload;\n }\n\n async save(payload: OAuth2TokenPayload): Promise<OAuth2TokenPayload> {\n if (payload.jti) {\n return payload;\n }\n\n return this.insert(payload);\n }\n\n async saveWithSignature(data: OAuth2TokenPayload, signature: string): Promise<OAuth2TokenPayload> {\n const normalized = await this.save(data);\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_CLAIMS,\n key: signature, \n }),\n normalized,\n { ttl: this.buildTTL(data.exp) },\n );\n\n return normalized;\n }\n\n // -----------------------------------------------------\n\n async isInactive(key: string): Promise<boolean> {\n const cacheKey = buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_INACTIVE,\n key,\n });\n\n const response = await this.cache.get(cacheKey);\n\n return !!response;\n }\n\n async setInactive(key: string, exp?: number): Promise<void> {\n const ttl = this.buildTTL(exp);\n\n const cacheKey = buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_INACTIVE,\n key,\n });\n\n await this.cache.set(\n cacheKey,\n true,\n { ttl },\n );\n }\n\n async claimInactive(key: string, exp?: number): Promise<boolean> {\n const ttl = this.buildTTL(exp);\n\n const cacheKey = buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_INACTIVE,\n key,\n });\n\n // set-if-absent (Redis SET NX / single-tick memory adapter) — returns\n // true only for the caller that actually transitioned the token to\n // inactive, so concurrent single-use claims cannot both succeed.\n return this.cache.add(\n cacheKey,\n true,\n { ttl },\n );\n }\n\n // -----------------------------------------------------\n\n /**\n * Transform timestamp in seconds to ttl in ms.\n *\n * @param utc\n * @protected\n */\n protected buildTTL(utc?: number) {\n if (typeof utc === 'number') {\n const ttl = (utc * 1000) - Date.now();\n if (ttl > 0) {\n return ttl;\n }\n }\n\n return 3_600 * 1000;\n }\n}\n"],"mappings":";;;;;;;AAcA,IAAaG,wBAAb,MAAaA;CACCC;CAEV,YAAYA,OAAe;EACvB,KAAKA,QAAQA;CACjB;CAIA,MAAMC,mBAAmBC,WAAuD;EAC5E,OAAO,KAAKF,MAAMG,IACdP,cAAc;GACVQ,QAAQN,kBAAkBO;GAC1BC,KAAKJ;EACT,CAAA,CAAA;CAER;CAEA,MAAMK,YAAYC,IAAgD;EAC9D,OAAO,KAAKR,MAAMG,IACdP,cAAc;GACVQ,QAAQN,kBAAkBW;GAC1BH,KAAKE;EACT,CAAA,CAAA;CAER;CAIA,MAAME,WAAWF,IAA2B;EACxC,MAAMG,QAAQ,MAAM,KAAKJ,YAAYC,EAAAA;EACrC,IAAI,CAACG,OACD;EAGJ,MAAML,MAAMV,cAAc;GACtBQ,QAAQN,kBAAkBW;GAC1BH,KAAKE;EACT,CAAA;EACA,MAAM,KAAKR,MAAMY,KAAKN,GAAAA;EAEtB,MAAM,KAAKO,YAAYL,IAAIG,MAAMG,GAAG;CACxC;CAIA,MAAMC,OAAOC,SAA0D;EACnEA,QAAQC,MAAMpB,WAAAA;EAEd,MAAM,KAAKG,MAAMkB,IACbtB,cAAc;GACVQ,QAAQN,kBAAkBW;GAC1BH,KAAKU,QAAQC;EACjB,CAAA,GACAD,SACA,EAAEG,KAAK,KAAKC,SAASJ,QAAQF,GAAG,EAAE,CAAA;EAGtC,OAAOE;CACX;CAEA,MAAMK,KAAKL,SAA0D;EACjE,IAAIA,QAAQC,KACR,OAAOD;EAGX,OAAO,KAAKD,OAAOC,OAAAA;CACvB;CAEA,MAAMM,kBAAkBC,MAA0BrB,WAAgD;EAC9F,MAAMsB,aAAa,MAAM,KAAKH,KAAKE,IAAAA;EAEnC,MAAM,KAAKvB,MAAMkB,IACbtB,cAAc;GACVQ,QAAQN,kBAAkBO;GAC1BC,KAAKJ;EACT,CAAA,GACAsB,YACA,EAAEL,KAAK,KAAKC,SAASG,KAAKT,GAAG,EAAE,CAAA;EAGnC,OAAOU;CACX;CAIA,MAAMC,WAAWnB,KAA+B;EAC5C,MAAMoB,WAAW9B,cAAc;GAC3BQ,QAAQN,kBAAkB6B;GAC1BrB;EACJ,CAAA;EAIA,OAAO,CAAC,CAACsB,MAFc,KAAK5B,MAAMG,IAAIuB,QAAAA;CAG1C;CAEA,MAAMb,YAAYP,KAAaQ,KAA6B;EACxD,MAAMK,MAAM,KAAKC,SAASN,GAAAA;EAE1B,MAAMY,WAAW9B,cAAc;GAC3BQ,QAAQN,kBAAkB6B;GAC1BrB;EACJ,CAAA;EAEA,MAAM,KAAKN,MAAMkB,IACbQ,UACA,MACA,EAAEP,IAAI,CAAA;CAEd;CAEA,MAAMU,cAAcvB,KAAaQ,KAAgC;EAC7D,MAAMK,MAAM,KAAKC,SAASN,GAAAA;EAE1B,MAAMY,WAAW9B,cAAc;GAC3BQ,QAAQN,kBAAkB6B;GAC1BrB;EACJ,CAAA;EAKA,OAAO,KAAKN,MAAM8B,IACdJ,UACA,MACA,EAAEP,IAAI,CAAA;CAEd;;;;;;IAUA,SAAmBY,KAAc;EAC7B,IAAI,OAAOA,QAAQ,UAAU;GACzB,MAAMZ,MAAM,MAAO,MAAQa,KAAKC,IAAG;GACnC,IAAId,MAAM,GACN,OAAOA;EAEf;EAEA,OAAO
|
|
1
|
+
{"version":3,"file":"repository.mjs","names":["buildCacheKey","randomUUID","CacheOAuth2Prefix","OAuth2TokenRepository","cache","findOneBySignature","signature","get","prefix","TOKEN_CLAIMS","key","findOneById","id","TOKEN","removeById","token","drop","setInactive","exp","insert","payload","jti","set","ttl","buildTTL","save","saveWithSignature","data","normalized","isInactive","cacheKey","TOKEN_INACTIVE","response","claimInactive","add","utc","Date","now"],"sources":["../../../../../../src/app/modules/oauth2/repositories/token/repository.ts"],"sourcesContent":["/*\n * Copyright (c) 2025-2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { ICache } from '@authup/server-kit';\nimport { buildCacheKey } from '@authup/server-kit';\nimport type { OAuth2TokenPayload } from '@authup/specs';\nimport { randomUUID } from 'node:crypto';\nimport type { IOAuth2TokenRepository } from '../../../../../core/index.ts';\nimport { CacheOAuth2Prefix } from '../constants.ts';\n\nexport class OAuth2TokenRepository implements IOAuth2TokenRepository {\n protected cache : ICache;\n\n constructor(cache: ICache) {\n this.cache = cache;\n }\n\n // -----------------------------------------------------\n\n async findOneBySignature(signature: string): Promise<OAuth2TokenPayload | null> {\n return this.cache.get<OAuth2TokenPayload>(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_CLAIMS,\n key: signature, \n }),\n );\n }\n\n async findOneById(id: string): Promise<OAuth2TokenPayload | null> {\n return this.cache.get<OAuth2TokenPayload>(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN,\n key: id, \n }),\n );\n }\n\n // -----------------------------------------------------\n\n async removeById(id: string): Promise<void> {\n const token = await this.findOneById(id);\n if (!token) {\n return;\n }\n\n const key = buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN,\n key: id, \n });\n await this.cache.drop(key);\n\n await this.setInactive(id, token.exp);\n }\n\n // -----------------------------------------------------\n\n async insert(payload: OAuth2TokenPayload): Promise<OAuth2TokenPayload> {\n payload.jti = randomUUID();\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN,\n key: payload.jti, \n }),\n payload,\n { ttl: this.buildTTL(payload.exp) },\n );\n\n return payload;\n }\n\n async save(payload: OAuth2TokenPayload): Promise<OAuth2TokenPayload> {\n if (payload.jti) {\n return payload;\n }\n\n return this.insert(payload);\n }\n\n async saveWithSignature(data: OAuth2TokenPayload, signature: string): Promise<OAuth2TokenPayload> {\n const normalized = await this.save(data);\n\n await this.cache.set(\n buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_CLAIMS,\n key: signature, \n }),\n normalized,\n { ttl: this.buildTTL(data.exp) },\n );\n\n return normalized;\n }\n\n // -----------------------------------------------------\n\n async isInactive(key: string): Promise<boolean> {\n const cacheKey = buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_INACTIVE,\n key,\n });\n\n const response = await this.cache.get(cacheKey);\n\n return !!response;\n }\n\n async setInactive(key: string, exp?: number): Promise<void> {\n const ttl = this.buildTTL(exp);\n\n const cacheKey = buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_INACTIVE,\n key,\n });\n\n await this.cache.set(\n cacheKey,\n true,\n { ttl },\n );\n }\n\n async claimInactive(key: string, exp?: number): Promise<boolean> {\n const ttl = this.buildTTL(exp);\n\n const cacheKey = buildCacheKey({\n prefix: CacheOAuth2Prefix.TOKEN_INACTIVE,\n key,\n });\n\n // set-if-absent (Redis SET NX / single-tick memory adapter) — returns\n // true only for the caller that actually transitioned the token to\n // inactive, so concurrent single-use claims cannot both succeed.\n return this.cache.add(\n cacheKey,\n true,\n { ttl },\n );\n }\n\n // -----------------------------------------------------\n\n /**\n * Transform timestamp in seconds to ttl in ms.\n *\n * @param utc\n * @protected\n */\n protected buildTTL(utc?: number) {\n if (typeof utc === 'number') {\n const ttl = (utc * 1000) - Date.now();\n if (ttl > 0) {\n return ttl;\n }\n }\n\n return 3_600 * 1000;\n }\n}\n"],"mappings":";;;;;;;AAcA,IAAaG,wBAAb,MAAaA;CACCC;CAEV,YAAYA,OAAe;EACvB,KAAKA,QAAQA;CACjB;CAIA,MAAMC,mBAAmBC,WAAuD;EAC5E,OAAO,KAAKF,MAAMG,IACdP,cAAc;GACVQ,QAAQN,kBAAkBO;GAC1BC,KAAKJ;EACT,CAAA,CAAA;CAER;CAEA,MAAMK,YAAYC,IAAgD;EAC9D,OAAO,KAAKR,MAAMG,IACdP,cAAc;GACVQ,QAAQN,kBAAkBW;GAC1BH,KAAKE;EACT,CAAA,CAAA;CAER;CAIA,MAAME,WAAWF,IAA2B;EACxC,MAAMG,QAAQ,MAAM,KAAKJ,YAAYC,EAAAA;EACrC,IAAI,CAACG,OACD;EAGJ,MAAML,MAAMV,cAAc;GACtBQ,QAAQN,kBAAkBW;GAC1BH,KAAKE;EACT,CAAA;EACA,MAAM,KAAKR,MAAMY,KAAKN,GAAAA;EAEtB,MAAM,KAAKO,YAAYL,IAAIG,MAAMG,GAAG;CACxC;CAIA,MAAMC,OAAOC,SAA0D;EACnEA,QAAQC,MAAMpB,WAAAA;EAEd,MAAM,KAAKG,MAAMkB,IACbtB,cAAc;GACVQ,QAAQN,kBAAkBW;GAC1BH,KAAKU,QAAQC;EACjB,CAAA,GACAD,SACA,EAAEG,KAAK,KAAKC,SAASJ,QAAQF,GAAG,EAAE,CAAA;EAGtC,OAAOE;CACX;CAEA,MAAMK,KAAKL,SAA0D;EACjE,IAAIA,QAAQC,KACR,OAAOD;EAGX,OAAO,KAAKD,OAAOC,OAAAA;CACvB;CAEA,MAAMM,kBAAkBC,MAA0BrB,WAAgD;EAC9F,MAAMsB,aAAa,MAAM,KAAKH,KAAKE,IAAAA;EAEnC,MAAM,KAAKvB,MAAMkB,IACbtB,cAAc;GACVQ,QAAQN,kBAAkBO;GAC1BC,KAAKJ;EACT,CAAA,GACAsB,YACA,EAAEL,KAAK,KAAKC,SAASG,KAAKT,GAAG,EAAE,CAAA;EAGnC,OAAOU;CACX;CAIA,MAAMC,WAAWnB,KAA+B;EAC5C,MAAMoB,WAAW9B,cAAc;GAC3BQ,QAAQN,kBAAkB6B;GAC1BrB;EACJ,CAAA;EAIA,OAAO,CAAC,CAACsB,MAFc,KAAK5B,MAAMG,IAAIuB,QAAAA;CAG1C;CAEA,MAAMb,YAAYP,KAAaQ,KAA6B;EACxD,MAAMK,MAAM,KAAKC,SAASN,GAAAA;EAE1B,MAAMY,WAAW9B,cAAc;GAC3BQ,QAAQN,kBAAkB6B;GAC1BrB;EACJ,CAAA;EAEA,MAAM,KAAKN,MAAMkB,IACbQ,UACA,MACA,EAAEP,IAAI,CAAA;CAEd;CAEA,MAAMU,cAAcvB,KAAaQ,KAAgC;EAC7D,MAAMK,MAAM,KAAKC,SAASN,GAAAA;EAE1B,MAAMY,WAAW9B,cAAc;GAC3BQ,QAAQN,kBAAkB6B;GAC1BrB;EACJ,CAAA;EAKA,OAAO,KAAKN,MAAM8B,IACdJ,UACA,MACA,EAAEP,IAAI,CAAA;CAEd;;;;;;IAUA,SAAmBY,KAAc;EAC7B,IAAI,OAAOA,QAAQ,UAAU;GACzB,MAAMZ,MAAM,MAAO,MAAQa,KAAKC,IAAG;GACnC,IAAId,MAAM,GACN,OAAOA;EAEf;EAEA,OAAO;CACX;AACJ"}
|
|
@@ -141,18 +141,19 @@ var ProvisionerModule = class {
|
|
|
141
141
|
permissionRepository
|
|
142
142
|
});
|
|
143
143
|
const scopeSynchronizer = new ScopeProvisioningSynchronizer({ repository: scopeRepository });
|
|
144
|
+
const realmSynchronizer = new RealmProvisioningSynchronizer({
|
|
145
|
+
repository: new RealmRepositoryAdapter(realmRepository),
|
|
146
|
+
clientSynchronizer,
|
|
147
|
+
roleSynchronizer,
|
|
148
|
+
permissionSynchronizer,
|
|
149
|
+
userSynchronizer,
|
|
150
|
+
scopeSynchronizer
|
|
151
|
+
});
|
|
144
152
|
await new GraphProvisioningSynchronizer({
|
|
145
153
|
policySynchronizer,
|
|
146
154
|
permissionSynchronizer,
|
|
147
155
|
roleSynchronizer,
|
|
148
|
-
realmSynchronizer
|
|
149
|
-
repository: new RealmRepositoryAdapter(realmRepository),
|
|
150
|
-
clientSynchronizer,
|
|
151
|
-
roleSynchronizer,
|
|
152
|
-
permissionSynchronizer,
|
|
153
|
-
userSynchronizer,
|
|
154
|
-
scopeSynchronizer
|
|
155
|
-
}),
|
|
156
|
+
realmSynchronizer,
|
|
156
157
|
scopeSynchronizer
|
|
157
158
|
}).synchronize(data);
|
|
158
159
|
const webClientProvisioner = new WebClientProvisioner({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":["ClientEntity","ClientPermissionEntity","ClientRoleEntity","ClientScopeEntity","PermissionEntity","RealmEntity","RoleEntity","RolePermissionEntity","ScopeEntity","UserPermissionEntity","UserRoleEntity","SystemPolicyName","PermissionPolicyEntity","PolicyRepository","UserRepository","ClientProvisioningSynchronizer","GraphProvisioningSynchronizer","PermissionProvisioningSynchronizer","PolicyProvisioningSynchronizer","RealmProvisioningSynchronizer","RoleProvisioningSynchronizer","ScopeProvisioningSynchronizer","UserProvisioningSynchronizer","ClientPermissionRepositoryAdapter","ClientRepositoryAdapter","ClientRoleRepositoryAdapter","ClientScopeRepositoryAdapter","KeyRepositoryAdapter","PermissionPolicyRepositoryAdapter","PermissionRepositoryAdapter","PolicyRepositoryAdapter","RealmRepositoryAdapter","RolePermissionRepositoryAdapter","RoleRepositoryAdapter","ScopeRepositoryAdapter","UserPermissionRepositoryAdapter","UserRepositoryAdapter","UserRoleRepositoryAdapter","DatabaseInjectionKey","ModuleName","fs","path","ConfigInjectionKey","getAppOrigins","SymmetricCipher","LoggerInjectionKey","WebClientProvisioner","KeyProvisioner","CompositeProvisioningSource","FileProvisioningSource","ProvisionerModule","name","dependencies","sources","PROVISIONING","CONFIG","DATABASE","setup","container","config","resolve","provisioningDir","join","writableDirectoryPath","existsSync","push","cwd","logger","composite","data","load","dataSource","DataSource","realmRepository","permissionRepository","repository","policyRepository","permissionPolicyRepository","policySynchronizer","roleRepository","clientRepository","scopeRepository","clientScopeRepository","permissionSynchronizer","roleSynchronizer","rolePermissionRepository","clientSynchronizer","clientRoleRepository","clientPermissionRepository","userSynchronizer","userRepository","userRoleRepository","userPermissionRepository","scopeSynchronizer","realmSynchronizer","rootSynchronizer","synchronize","webClientProvisioner","appOrigins","keyProvisioner","keyStore","secretsCipher","secretsEncryptionKey","realms","find","realm","ensureForRealm","permissionsDefaultPolicyAssignment","assignDefaultPolicy","defaultPolicy","findOneByName","DEFAULT","permissionRepo","getRepository","junctionRepo","permissions","permission","hasAnyPolicy","findOneBy","permissionId","id","save","create","permissionRealmId","realmId","policyId","policyRealmId"],"sources":["../../../../src/app/modules/provisioning/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\nimport type {\n Client,\n ClientPermission,\n ClientRole,\n ClientScope,\n PermissionPolicy,\n Realm,\n Role,\n RolePermission,\n Scope,\n UserPermission,\n UserRole,\n} from '@authup/core-kit';\nimport type { DataSource, Repository } from 'typeorm';\nimport {\n ClientEntity,\n ClientPermissionEntity,\n ClientRoleEntity,\n ClientScopeEntity,\n PermissionEntity,\n RealmEntity,\n RoleEntity,\n RolePermissionEntity,\n ScopeEntity,\n UserPermissionEntity,\n UserRoleEntity,\n} from '../../../adapters/database/index.ts';\nimport { SystemPolicyName } from '@authup/access';\nimport {\n PermissionPolicyEntity,\n PolicyRepository,\n UserRepository,\n} from '../../../adapters/database/domains/index.ts';\nimport type { IContainer } from 'eldin';\nimport {\n ClientProvisioningSynchronizer,\n GraphProvisioningSynchronizer,\n PermissionProvisioningSynchronizer,\n PolicyProvisioningSynchronizer,\n RealmProvisioningSynchronizer,\n RoleProvisioningSynchronizer,\n ScopeProvisioningSynchronizer,\n UserProvisioningSynchronizer,\n} from '../../../core/provisioning/synchronizer/index.ts';\nimport type { IProvisioningSource } from '../../../core/provisioning/types.ts';\nimport {\n ClientPermissionRepositoryAdapter,\n ClientRepositoryAdapter,\n ClientRoleRepositoryAdapter,\n ClientScopeRepositoryAdapter,\n KeyRepositoryAdapter,\n PermissionPolicyRepositoryAdapter,\n PermissionRepositoryAdapter,\n PolicyRepositoryAdapter,\n RealmRepositoryAdapter,\n RolePermissionRepositoryAdapter,\n RoleRepositoryAdapter,\n ScopeRepositoryAdapter,\n UserPermissionRepositoryAdapter,\n UserRepositoryAdapter, \n UserRoleRepositoryAdapter, \n} from '../database/repositories/index.ts';\nimport { DatabaseInjectionKey } from '../database/index.ts';\nimport type { IModule } from 'orkos';\nimport { ModuleName } from '../constants.ts';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { ConfigInjectionKey, getAppOrigins } from '../config/index.ts';\nimport { SymmetricCipher } from '@authup/server-kit';\nimport { LoggerInjectionKey } from '../logger/index.ts';\nimport { WebClientProvisioner } from '../../../core/entities/client/index.ts';\nimport { KeyProvisioner } from '../../../core/key/index.ts';\nimport { CompositeProvisioningSource, FileProvisioningSource } from './sources/index.ts';\n\nexport class ProvisionerModule implements IModule {\n readonly name: string;\n\n readonly dependencies: string[];\n\n protected sources: IProvisioningSource[];\n\n constructor(sources: IProvisioningSource[] = []) {\n this.name = ModuleName.PROVISIONING;\n this.dependencies = [ModuleName.CONFIG, ModuleName.DATABASE];\n this.sources = sources;\n }\n\n async setup(container: IContainer): Promise<void> {\n const sources = [...this.sources];\n\n const config = container.resolve(ConfigInjectionKey);\n const provisioningDir = path.join(config.writableDirectoryPath, 'provisioning');\n if (fs.existsSync(provisioningDir)) {\n sources.push(new FileProvisioningSource({\n cwd: provisioningDir,\n logger: container.resolve(LoggerInjectionKey),\n }));\n }\n\n const composite = new CompositeProvisioningSource(sources);\n const data = await composite.load(container);\n\n const dataSource = container.resolve(DatabaseInjectionKey.DataSource);\n const realmRepository = container.resolve<Repository<Realm>>(RealmEntity);\n\n const permissionRepository = new PermissionRepositoryAdapter({\n repository: container.resolve<Repository<PermissionEntity>>(PermissionEntity),\n realmRepository,\n });\n\n const policyRepository = new PolicyRepositoryAdapter({\n repository: new PolicyRepository(dataSource),\n realmRepository,\n });\n\n // ---------------------------------------------------------------\n // Synchronize all entities (policies → permissions → roles → ...)\n // ---------------------------------------------------------------\n\n const permissionPolicyRepository = new PermissionPolicyRepositoryAdapter(\n container.resolve<Repository<PermissionPolicy>>(PermissionPolicyEntity),\n );\n\n const policySynchronizer = new PolicyProvisioningSynchronizer({\n repository: policyRepository,\n permissionPolicyRepository,\n });\n\n const roleRepository = new RoleRepositoryAdapter({\n repository: container.resolve<Repository<Role>>(RoleEntity),\n realmRepository,\n });\n const clientRepository = new ClientRepositoryAdapter({\n repository: container.resolve<Repository<Client>>(ClientEntity),\n realmRepository,\n });\n const scopeRepository = new ScopeRepositoryAdapter({\n repository: container.resolve<Repository<Scope>>(ScopeEntity),\n realmRepository,\n });\n const clientScopeRepository = new ClientScopeRepositoryAdapter(\n container.resolve<Repository<ClientScope>>(ClientScopeEntity),\n );\n\n const permissionSynchronizer = new PermissionProvisioningSynchronizer({\n repository: permissionRepository,\n policyRepository,\n permissionPolicyRepository,\n });\n\n const roleSynchronizer = new RoleProvisioningSynchronizer({\n repository: roleRepository,\n permissionRepository,\n rolePermissionRepository: new RolePermissionRepositoryAdapter(\n container.resolve<Repository<RolePermission>>(RolePermissionEntity),\n ),\n });\n\n const clientSynchronizer = new ClientProvisioningSynchronizer({\n clientRepository,\n clientRoleRepository: new ClientRoleRepositoryAdapter(\n container.resolve<Repository<ClientRole>>(ClientRoleEntity),\n ),\n clientPermissionRepository: new ClientPermissionRepositoryAdapter(\n container.resolve<Repository<ClientPermission>>(ClientPermissionEntity),\n ),\n clientScopeRepository,\n\n roleRepository,\n permissionRepository,\n scopeRepository,\n\n roleSynchronizer,\n permissionSynchronizer,\n });\n\n const userSynchronizer = new UserProvisioningSynchronizer({\n userRepository: new UserRepositoryAdapter({\n repository: new UserRepository(dataSource),\n realmRepository,\n }),\n userRoleRepository: new UserRoleRepositoryAdapter(\n container.resolve<Repository<UserRole>>(UserRoleEntity),\n ),\n userPermissionRepository: new UserPermissionRepositoryAdapter(\n container.resolve<Repository<UserPermission>>(UserPermissionEntity),\n ),\n\n clientRepository,\n roleRepository,\n permissionRepository,\n });\n\n const scopeSynchronizer = new ScopeProvisioningSynchronizer({ repository: scopeRepository });\n\n const realmSynchronizer = new RealmProvisioningSynchronizer({\n repository: new RealmRepositoryAdapter(realmRepository),\n\n clientSynchronizer,\n roleSynchronizer,\n permissionSynchronizer,\n userSynchronizer,\n scopeSynchronizer,\n });\n\n const rootSynchronizer = new GraphProvisioningSynchronizer({\n policySynchronizer,\n permissionSynchronizer,\n roleSynchronizer,\n realmSynchronizer,\n scopeSynchronizer,\n });\n\n await rootSynchronizer.synchronize(data);\n\n // ---------------------------------------------------------------\n // Per-realm public `web` client. Single provisioning mechanism:\n // list every realm (incl. pre-existing) and upsert its web client.\n // Idempotent; guarded on builtIn inside the provisioner.\n // ---------------------------------------------------------------\n const webClientProvisioner = new WebClientProvisioner({\n clientRepository,\n scopeRepository,\n clientScopeRepository,\n appOrigins: getAppOrigins(config),\n logger: container.resolve(LoggerInjectionKey),\n });\n\n // Eager key minting (plan 071 hybrid model): every realm — incl.\n // pre-existing ones — holds sig + enc keys after startup, so the\n // management API shows them without waiting for first use. The\n // adapter is constructed locally (NOT resolved from the oauth2\n // module's registration) so provisioning stays runnable in minimal\n // module graphs (test setup, CLI); the KEK handling is identical.\n const keyProvisioner = new KeyProvisioner({\n keyStore: new KeyRepositoryAdapter(dataSource, {\n secretsCipher: config.secretsEncryptionKey ?\n new SymmetricCipher(config.secretsEncryptionKey) :\n null,\n }),\n logger: container.resolve(LoggerInjectionKey),\n });\n\n const realms = await realmRepository.find();\n for (const realm of realms) {\n await webClientProvisioner.ensureForRealm(realm);\n await keyProvisioner.ensureForRealm(realm);\n }\n\n if (config.permissionsDefaultPolicyAssignment) {\n await this.assignDefaultPolicy(dataSource, policyRepository);\n }\n }\n\n protected async assignDefaultPolicy(\n dataSource: DataSource,\n policyRepository: PolicyRepositoryAdapter,\n ): Promise<void> {\n const defaultPolicy = await policyRepository.findOneByName(SystemPolicyName.DEFAULT);\n if (!defaultPolicy) {\n return;\n }\n\n const permissionRepo = dataSource.getRepository(PermissionEntity);\n const junctionRepo = dataSource.getRepository(PermissionPolicyEntity);\n\n const permissions = await permissionRepo.find();\n for (const permission of permissions) {\n const hasAnyPolicy = await junctionRepo.findOneBy({ permissionId: permission.id });\n\n if (!hasAnyPolicy) {\n await junctionRepo.save(junctionRepo.create({\n permissionId: permission.id,\n permissionRealmId: permission.realmId,\n policyId: defaultPolicy.id,\n policyRealmId: defaultPolicy.realmId,\n }));\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"module.mjs","names":["ClientEntity","ClientPermissionEntity","ClientRoleEntity","ClientScopeEntity","PermissionEntity","RealmEntity","RoleEntity","RolePermissionEntity","ScopeEntity","UserPermissionEntity","UserRoleEntity","SystemPolicyName","PermissionPolicyEntity","PolicyRepository","UserRepository","ClientProvisioningSynchronizer","GraphProvisioningSynchronizer","PermissionProvisioningSynchronizer","PolicyProvisioningSynchronizer","RealmProvisioningSynchronizer","RoleProvisioningSynchronizer","ScopeProvisioningSynchronizer","UserProvisioningSynchronizer","ClientPermissionRepositoryAdapter","ClientRepositoryAdapter","ClientRoleRepositoryAdapter","ClientScopeRepositoryAdapter","KeyRepositoryAdapter","PermissionPolicyRepositoryAdapter","PermissionRepositoryAdapter","PolicyRepositoryAdapter","RealmRepositoryAdapter","RolePermissionRepositoryAdapter","RoleRepositoryAdapter","ScopeRepositoryAdapter","UserPermissionRepositoryAdapter","UserRepositoryAdapter","UserRoleRepositoryAdapter","DatabaseInjectionKey","ModuleName","fs","path","ConfigInjectionKey","getAppOrigins","SymmetricCipher","LoggerInjectionKey","WebClientProvisioner","KeyProvisioner","CompositeProvisioningSource","FileProvisioningSource","ProvisionerModule","name","dependencies","sources","PROVISIONING","CONFIG","DATABASE","setup","container","config","resolve","provisioningDir","join","writableDirectoryPath","existsSync","push","cwd","logger","composite","data","load","dataSource","DataSource","realmRepository","permissionRepository","repository","policyRepository","permissionPolicyRepository","policySynchronizer","roleRepository","clientRepository","scopeRepository","clientScopeRepository","permissionSynchronizer","roleSynchronizer","rolePermissionRepository","clientSynchronizer","clientRoleRepository","clientPermissionRepository","userSynchronizer","userRepository","userRoleRepository","userPermissionRepository","scopeSynchronizer","realmSynchronizer","rootSynchronizer","synchronize","webClientProvisioner","appOrigins","keyProvisioner","keyStore","secretsCipher","secretsEncryptionKey","realms","find","realm","ensureForRealm","permissionsDefaultPolicyAssignment","assignDefaultPolicy","defaultPolicy","findOneByName","DEFAULT","permissionRepo","getRepository","junctionRepo","permissions","permission","hasAnyPolicy","findOneBy","permissionId","id","save","create","permissionRealmId","realmId","policyId","policyRealmId"],"sources":["../../../../src/app/modules/provisioning/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\nimport type {\n Client,\n ClientPermission,\n ClientRole,\n ClientScope,\n PermissionPolicy,\n Realm,\n Role,\n RolePermission,\n Scope,\n UserPermission,\n UserRole,\n} from '@authup/core-kit';\nimport type { DataSource, Repository } from 'typeorm';\nimport {\n ClientEntity,\n ClientPermissionEntity,\n ClientRoleEntity,\n ClientScopeEntity,\n PermissionEntity,\n RealmEntity,\n RoleEntity,\n RolePermissionEntity,\n ScopeEntity,\n UserPermissionEntity,\n UserRoleEntity,\n} from '../../../adapters/database/index.ts';\nimport { SystemPolicyName } from '@authup/access';\nimport {\n PermissionPolicyEntity,\n PolicyRepository,\n UserRepository,\n} from '../../../adapters/database/domains/index.ts';\nimport type { IContainer } from 'eldin';\nimport {\n ClientProvisioningSynchronizer,\n GraphProvisioningSynchronizer,\n PermissionProvisioningSynchronizer,\n PolicyProvisioningSynchronizer,\n RealmProvisioningSynchronizer,\n RoleProvisioningSynchronizer,\n ScopeProvisioningSynchronizer,\n UserProvisioningSynchronizer,\n} from '../../../core/provisioning/synchronizer/index.ts';\nimport type { IProvisioningSource } from '../../../core/provisioning/types.ts';\nimport {\n ClientPermissionRepositoryAdapter,\n ClientRepositoryAdapter,\n ClientRoleRepositoryAdapter,\n ClientScopeRepositoryAdapter,\n KeyRepositoryAdapter,\n PermissionPolicyRepositoryAdapter,\n PermissionRepositoryAdapter,\n PolicyRepositoryAdapter,\n RealmRepositoryAdapter,\n RolePermissionRepositoryAdapter,\n RoleRepositoryAdapter,\n ScopeRepositoryAdapter,\n UserPermissionRepositoryAdapter,\n UserRepositoryAdapter, \n UserRoleRepositoryAdapter, \n} from '../database/repositories/index.ts';\nimport { DatabaseInjectionKey } from '../database/index.ts';\nimport type { IModule } from 'orkos';\nimport { ModuleName } from '../constants.ts';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { ConfigInjectionKey, getAppOrigins } from '../config/index.ts';\nimport { SymmetricCipher } from '@authup/server-kit';\nimport { LoggerInjectionKey } from '../logger/index.ts';\nimport { WebClientProvisioner } from '../../../core/entities/client/index.ts';\nimport { KeyProvisioner } from '../../../core/key/index.ts';\nimport { CompositeProvisioningSource, FileProvisioningSource } from './sources/index.ts';\n\nexport class ProvisionerModule implements IModule {\n readonly name: string;\n\n readonly dependencies: string[];\n\n protected sources: IProvisioningSource[];\n\n constructor(sources: IProvisioningSource[] = []) {\n this.name = ModuleName.PROVISIONING;\n this.dependencies = [ModuleName.CONFIG, ModuleName.DATABASE];\n this.sources = sources;\n }\n\n async setup(container: IContainer): Promise<void> {\n const sources = [...this.sources];\n\n const config = container.resolve(ConfigInjectionKey);\n const provisioningDir = path.join(config.writableDirectoryPath, 'provisioning');\n if (fs.existsSync(provisioningDir)) {\n sources.push(new FileProvisioningSource({\n cwd: provisioningDir,\n logger: container.resolve(LoggerInjectionKey),\n }));\n }\n\n const composite = new CompositeProvisioningSource(sources);\n const data = await composite.load(container);\n\n const dataSource = container.resolve(DatabaseInjectionKey.DataSource);\n const realmRepository = container.resolve<Repository<Realm>>(RealmEntity);\n\n const permissionRepository = new PermissionRepositoryAdapter({\n repository: container.resolve<Repository<PermissionEntity>>(PermissionEntity),\n realmRepository,\n });\n\n const policyRepository = new PolicyRepositoryAdapter({\n repository: new PolicyRepository(dataSource),\n realmRepository,\n });\n\n // ---------------------------------------------------------------\n // Synchronize all entities (policies → permissions → roles → ...)\n // ---------------------------------------------------------------\n\n const permissionPolicyRepository = new PermissionPolicyRepositoryAdapter(\n container.resolve<Repository<PermissionPolicy>>(PermissionPolicyEntity),\n );\n\n const policySynchronizer = new PolicyProvisioningSynchronizer({\n repository: policyRepository,\n permissionPolicyRepository,\n });\n\n const roleRepository = new RoleRepositoryAdapter({\n repository: container.resolve<Repository<Role>>(RoleEntity),\n realmRepository,\n });\n const clientRepository = new ClientRepositoryAdapter({\n repository: container.resolve<Repository<Client>>(ClientEntity),\n realmRepository,\n });\n const scopeRepository = new ScopeRepositoryAdapter({\n repository: container.resolve<Repository<Scope>>(ScopeEntity),\n realmRepository,\n });\n const clientScopeRepository = new ClientScopeRepositoryAdapter(\n container.resolve<Repository<ClientScope>>(ClientScopeEntity),\n );\n\n const permissionSynchronizer = new PermissionProvisioningSynchronizer({\n repository: permissionRepository,\n policyRepository,\n permissionPolicyRepository,\n });\n\n const roleSynchronizer = new RoleProvisioningSynchronizer({\n repository: roleRepository,\n permissionRepository,\n rolePermissionRepository: new RolePermissionRepositoryAdapter(\n container.resolve<Repository<RolePermission>>(RolePermissionEntity),\n ),\n });\n\n const clientSynchronizer = new ClientProvisioningSynchronizer({\n clientRepository,\n clientRoleRepository: new ClientRoleRepositoryAdapter(\n container.resolve<Repository<ClientRole>>(ClientRoleEntity),\n ),\n clientPermissionRepository: new ClientPermissionRepositoryAdapter(\n container.resolve<Repository<ClientPermission>>(ClientPermissionEntity),\n ),\n clientScopeRepository,\n\n roleRepository,\n permissionRepository,\n scopeRepository,\n\n roleSynchronizer,\n permissionSynchronizer,\n });\n\n const userSynchronizer = new UserProvisioningSynchronizer({\n userRepository: new UserRepositoryAdapter({\n repository: new UserRepository(dataSource),\n realmRepository,\n }),\n userRoleRepository: new UserRoleRepositoryAdapter(\n container.resolve<Repository<UserRole>>(UserRoleEntity),\n ),\n userPermissionRepository: new UserPermissionRepositoryAdapter(\n container.resolve<Repository<UserPermission>>(UserPermissionEntity),\n ),\n\n clientRepository,\n roleRepository,\n permissionRepository,\n });\n\n const scopeSynchronizer = new ScopeProvisioningSynchronizer({ repository: scopeRepository });\n\n const realmSynchronizer = new RealmProvisioningSynchronizer({\n repository: new RealmRepositoryAdapter(realmRepository),\n\n clientSynchronizer,\n roleSynchronizer,\n permissionSynchronizer,\n userSynchronizer,\n scopeSynchronizer,\n });\n\n const rootSynchronizer = new GraphProvisioningSynchronizer({\n policySynchronizer,\n permissionSynchronizer,\n roleSynchronizer,\n realmSynchronizer,\n scopeSynchronizer,\n });\n\n await rootSynchronizer.synchronize(data);\n\n // ---------------------------------------------------------------\n // Per-realm public `web` client. Single provisioning mechanism:\n // list every realm (incl. pre-existing) and upsert its web client.\n // Idempotent; guarded on builtIn inside the provisioner.\n // ---------------------------------------------------------------\n const webClientProvisioner = new WebClientProvisioner({\n clientRepository,\n scopeRepository,\n clientScopeRepository,\n appOrigins: getAppOrigins(config),\n logger: container.resolve(LoggerInjectionKey),\n });\n\n // Eager key minting (plan 071 hybrid model): every realm — incl.\n // pre-existing ones — holds sig + enc keys after startup, so the\n // management API shows them without waiting for first use. The\n // adapter is constructed locally (NOT resolved from the oauth2\n // module's registration) so provisioning stays runnable in minimal\n // module graphs (test setup, CLI); the KEK handling is identical.\n const keyProvisioner = new KeyProvisioner({\n keyStore: new KeyRepositoryAdapter(dataSource, {\n secretsCipher: config.secretsEncryptionKey ?\n new SymmetricCipher(config.secretsEncryptionKey) :\n null,\n }),\n logger: container.resolve(LoggerInjectionKey),\n });\n\n const realms = await realmRepository.find();\n for (const realm of realms) {\n await webClientProvisioner.ensureForRealm(realm);\n await keyProvisioner.ensureForRealm(realm);\n }\n\n if (config.permissionsDefaultPolicyAssignment) {\n await this.assignDefaultPolicy(dataSource, policyRepository);\n }\n }\n\n protected async assignDefaultPolicy(\n dataSource: DataSource,\n policyRepository: PolicyRepositoryAdapter,\n ): Promise<void> {\n const defaultPolicy = await policyRepository.findOneByName(SystemPolicyName.DEFAULT);\n if (!defaultPolicy) {\n return;\n }\n\n const permissionRepo = dataSource.getRepository(PermissionEntity);\n const junctionRepo = dataSource.getRepository(PermissionPolicyEntity);\n\n const permissions = await permissionRepo.find();\n for (const permission of permissions) {\n const hasAnyPolicy = await junctionRepo.findOneBy({ permissionId: permission.id });\n\n if (!hasAnyPolicy) {\n await junctionRepo.save(junctionRepo.create({\n permissionId: permission.id,\n permissionRealmId: permission.realmId,\n policyId: defaultPolicy.id,\n policyRealmId: defaultPolicy.realmId,\n }));\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/app/modules/provisioning/sources/file/module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../../../../../../src/app/modules/provisioning/sources/file/module.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oDAAoD,CAAC;AACjG,OAAO,EAAE,yBAAyB,EAAE,MAAM,oDAAoD,CAAC;AAC/F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAErF,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AAOlE,qBAAa,sBAAuB,YAAW,mBAAmB;IAC9D,SAAS,CAAC,OAAO,EAAE,+BAA+B,CAAC;IAEnD,SAAS,CAAC,aAAa,EAAE,yBAAyB,CAAC;gBAEvC,OAAO,EAAE,+BAA+B;IAM9C,IAAI,IAAI,OAAO,CAAC,sBAAsB,CAAC;CAiEhD"}
|
|
@@ -6,6 +6,7 @@ import "../../../../../core/provisioning/entities/index.mjs";
|
|
|
6
6
|
import { CompositeProvisioningSource } from "../composite/module.mjs";
|
|
7
7
|
import "../composite/index.mjs";
|
|
8
8
|
import { ValidatorGroup, isObject } from "@authup/kit";
|
|
9
|
+
import { isValidupError, stringifyPath } from "validup";
|
|
9
10
|
import path from "node:path";
|
|
10
11
|
import { locateMany, read } from "locter";
|
|
11
12
|
//#region src/app/modules/provisioning/sources/file/module.ts
|
|
@@ -25,14 +26,22 @@ var FileProvisioningSource = class {
|
|
|
25
26
|
this.rootValidator = new RootProvisioningValidator();
|
|
26
27
|
}
|
|
27
28
|
async load() {
|
|
28
|
-
const
|
|
29
|
+
const cwd = path.isAbsolute(this.options.cwd) ? this.options.cwd : path.join(process.cwd(), this.options.cwd);
|
|
30
|
+
const locations = await locateMany("*.{json,yaml,yml,ts,mts,mjs,js}", { cwd });
|
|
29
31
|
const compositeSource = new CompositeProvisioningSource([]);
|
|
30
32
|
const output = {};
|
|
31
33
|
for (const location of locations) {
|
|
32
34
|
const raw = await read(location);
|
|
33
35
|
const entity = typeof location.extension === "string" && MODULE_FILE_EXTENSIONS.has(location.extension.toLowerCase()) && isObject(raw) && "default" in raw ? raw.default : raw;
|
|
34
36
|
if (!isObject(entity)) throw new Error(`The provisioning file "${location.path}" must contain an object at its root.`);
|
|
35
|
-
|
|
37
|
+
let data;
|
|
38
|
+
try {
|
|
39
|
+
data = await this.rootValidator.run(entity, { group: ValidatorGroup.PROVISIONING });
|
|
40
|
+
} catch (e) {
|
|
41
|
+
if (!isValidupError(e)) throw e;
|
|
42
|
+
const issues = e.issues.map((issue) => ` ${stringifyPath(issue.path)}: ${issue.message}`).join("\n");
|
|
43
|
+
throw new Error(`The provisioning file "${location.path}" is invalid.\n${issues}`, { cause: e });
|
|
44
|
+
}
|
|
36
45
|
compositeSource.merge(output, data);
|
|
37
46
|
}
|
|
38
47
|
return output;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":["ValidatorGroup","isObject","locateMany","read","path","RootProvisioningValidator","CompositeProvisioningSource","MODULE_FILE_EXTENSIONS","Set","FileProvisioningSource","options","rootValidator","load","cwd","isAbsolute","join","process","locations","compositeSource","output","location","raw","isModule","extension","has","toLowerCase","entity","default","Error","data","run","group","PROVISIONING","merge"],"sources":["../../../../../../src/app/modules/provisioning/sources/file/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { ValidatorGroup, isObject } from '@authup/kit';\nimport { locateMany, read } from 'locter';\nimport path from 'node:path';\nimport type { RootProvisioningEntity } from '../../../../../core/provisioning/entities/index.ts';\nimport { RootProvisioningValidator } from '../../../../../core/provisioning/entities/index.ts';\nimport type { IProvisioningSource } from '../../../../../core/provisioning/types.ts';\nimport { CompositeProvisioningSource } from '../composite/index.ts';\nimport type { FileEntitySchemaImporterOptions } from './types.ts';\n\n// Extensions locter loads as an ES module namespace ({ default, ...named }),\n// as opposed to data files (json/yaml/yml) whose parsed value is returned\n// directly. Only the former should have their `default` export unwrapped.\nconst MODULE_FILE_EXTENSIONS = new Set(['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs']);\n\nexport class FileProvisioningSource implements IProvisioningSource {\n protected options: FileEntitySchemaImporterOptions;\n\n protected rootValidator: RootProvisioningValidator;\n\n constructor(options: FileEntitySchemaImporterOptions) {\n this.options = options;\n\n this.rootValidator = new RootProvisioningValidator();\n }\n\n async load(): Promise<RootProvisioningEntity> {\n const cwd = path.isAbsolute(this.options.cwd) ?\n this.options.cwd :\n path.join(process.cwd(), this.options.cwd);\n\n const locations = await locateMany('*.{json,yaml,yml,ts,mts,mjs,js}', { cwd });\n\n const compositeSource = new CompositeProvisioningSource([]);\n\n const output : RootProvisioningEntity = {};\n for (const location of locations) {\n const raw = await read(location);\n // Unwrap the default export only for module files; a data file\n // (json/yaml) whose root legitimately carries a `default` key must\n // keep all of its keys.\n const isModule = typeof location.extension === 'string' &&\n MODULE_FILE_EXTENSIONS.has(location.extension.toLowerCase());\n const entity = isModule && isObject(raw) && 'default' in raw ?\n raw.default :\n raw;\n\n // Fail closed on a malformed root (top-level list/scalar, or an\n // empty file parsing to null) instead of silently provisioning\n // nothing — the same silent-skip class the loader otherwise avoids.\n if (!isObject(entity)) {\n throw new Error(`The provisioning file \"${location.path}\" must contain an object at its root.`);\n }\n\n // Provisioning files use the camelCase management vocabulary\n // (plan 073). Keys are NOT rewritten here: a blanket snake->camel\n // pass cannot tell a schema property from a data-identifier map key\n // (`clientPermissions`/`clientRoles` are keyed by client name, and\n // client names may contain underscores), so it would silently\n // corrupt those keys. Unmounted keys are stripped by the validator.\n
|
|
1
|
+
{"version":3,"file":"module.mjs","names":["ValidatorGroup","isObject","locateMany","read","path","isValidupError","stringifyPath","RootProvisioningValidator","CompositeProvisioningSource","MODULE_FILE_EXTENSIONS","Set","FileProvisioningSource","options","rootValidator","load","cwd","isAbsolute","join","process","locations","compositeSource","output","location","raw","isModule","extension","has","toLowerCase","entity","default","Error","data","run","group","PROVISIONING","e","issues","map","issue","message","cause","merge"],"sources":["../../../../../../src/app/modules/provisioning/sources/file/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2026.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { ValidatorGroup, isObject } from '@authup/kit';\nimport { locateMany, read } from 'locter';\nimport path from 'node:path';\nimport { isValidupError, stringifyPath } from 'validup';\nimport type { RootProvisioningEntity } from '../../../../../core/provisioning/entities/index.ts';\nimport { RootProvisioningValidator } from '../../../../../core/provisioning/entities/index.ts';\nimport type { IProvisioningSource } from '../../../../../core/provisioning/types.ts';\nimport { CompositeProvisioningSource } from '../composite/index.ts';\nimport type { FileEntitySchemaImporterOptions } from './types.ts';\n\n// Extensions locter loads as an ES module namespace ({ default, ...named }),\n// as opposed to data files (json/yaml/yml) whose parsed value is returned\n// directly. Only the former should have their `default` export unwrapped.\nconst MODULE_FILE_EXTENSIONS = new Set(['.ts', '.mts', '.cts', '.js', '.mjs', '.cjs']);\n\nexport class FileProvisioningSource implements IProvisioningSource {\n protected options: FileEntitySchemaImporterOptions;\n\n protected rootValidator: RootProvisioningValidator;\n\n constructor(options: FileEntitySchemaImporterOptions) {\n this.options = options;\n\n this.rootValidator = new RootProvisioningValidator();\n }\n\n async load(): Promise<RootProvisioningEntity> {\n const cwd = path.isAbsolute(this.options.cwd) ?\n this.options.cwd :\n path.join(process.cwd(), this.options.cwd);\n\n const locations = await locateMany('*.{json,yaml,yml,ts,mts,mjs,js}', { cwd });\n\n const compositeSource = new CompositeProvisioningSource([]);\n\n const output : RootProvisioningEntity = {};\n for (const location of locations) {\n const raw = await read(location);\n // Unwrap the default export only for module files; a data file\n // (json/yaml) whose root legitimately carries a `default` key must\n // keep all of its keys.\n const isModule = typeof location.extension === 'string' &&\n MODULE_FILE_EXTENSIONS.has(location.extension.toLowerCase());\n const entity = isModule && isObject(raw) && 'default' in raw ?\n raw.default :\n raw;\n\n // Fail closed on a malformed root (top-level list/scalar, or an\n // empty file parsing to null) instead of silently provisioning\n // nothing — the same silent-skip class the loader otherwise avoids.\n if (!isObject(entity)) {\n throw new Error(`The provisioning file \"${location.path}\" must contain an object at its root.`);\n }\n\n // Provisioning files use the camelCase management vocabulary\n // (plan 073). Keys are NOT rewritten here: a blanket snake->camel\n // pass cannot tell a schema property from a data-identifier map key\n // (`clientPermissions`/`clientRoles` are keyed by client name, and\n // client names may contain underscores), so it would silently\n // corrupt those keys. Unmounted keys are stripped by the validator.\n let data : RootProvisioningEntity;\n try {\n data = await this.rootValidator.run(\n entity as Record<string, any>,\n { group: ValidatorGroup.PROVISIONING },\n );\n } catch (e) {\n // The raw ValidupError message is a generic\n // \"Property <path> is invalid\" and names neither the file nor\n // the reason, so a bad entry in one of several mounted files\n // aborted the boot with nothing to act on. Re-throw with the\n // file path and every issue message attached.\n if (!isValidupError(e)) {\n throw e;\n }\n\n const issues = e.issues\n .map((issue) => ` ${stringifyPath(issue.path)}: ${issue.message}`)\n .join('\\n');\n\n throw new Error(\n `The provisioning file \"${location.path}\" is invalid.\\n${issues}`,\n { cause: e },\n );\n }\n\n compositeSource.merge(output, data);\n }\n\n return output;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,MAAMS,yCAAyB,IAAIC,IAAI;CAAC;CAAO;CAAQ;CAAQ;CAAO;CAAQ;AAAO,CAAA;AAErF,IAAaC,yBAAb,MAAaA;CACCC;CAEAC;CAEV,YAAYD,SAA0C;EAClD,KAAKA,UAAUA;EAEf,KAAKC,gBAAgB,IAAIN,0BAAAA;CAC7B;CAEA,MAAMO,OAAwC;EAC1C,MAAMC,MAAMX,KAAKY,WAAW,KAAKJ,QAAQG,GAAG,IACxC,KAAKH,QAAQG,MACbX,KAAKa,KAAKC,QAAQH,IAAG,GAAI,KAAKH,QAAQG,GAAG;EAE7C,MAAMI,YAAY,MAAMjB,WAAW,mCAAmC,EAAEa,IAAI,CAAA;EAE5E,MAAMK,kBAAkB,IAAIZ,4BAA4B,CAAA,CAAE;EAE1D,MAAMa,SAAkC,CAAC;EACzC,KAAK,MAAMC,YAAYH,WAAW;GAC9B,MAAMI,MAAM,MAAMpB,KAAKmB,QAAAA;GAMvB,MAAMM,SAFW,OAAON,SAASG,cAAc,YAC3ChB,uBAAuBiB,IAAIJ,SAASG,UAAUE,YAAW,CAAA,KAClC1B,SAASsB,GAAAA,KAAQ,aAAaA,MACrDA,IAAIM,UACJN;GAKJ,IAAI,CAACtB,SAAS2B,MAAAA,GACV,MAAM,IAAIE,MAAM,0BAA0BR,SAASlB,KAAK,sCAAsC;GASlG,IAAI2B;GACJ,IAAI;IACAA,OAAO,MAAM,KAAKlB,cAAcmB,IAC5BJ,QACA,EAAEK,OAAOjC,eAAekC,aAAa,CAAA;GAE7C,SAASC,GAAG;IAMR,IAAI,CAAC9B,eAAe8B,CAAAA,GAChB,MAAMA;IAGV,MAAMC,SAASD,EAAEC,OACZC,KAAKC,UAAU,KAAKhC,cAAcgC,MAAMlC,IAAI,EAAE,IAAIkC,MAAMC,SAAS,CAAA,CACjEtB,KAAK,IAAA;IAEV,MAAM,IAAIa,MACN,0BAA0BR,SAASlB,KAAK,iBAAiBgC,UACzD,EAAEI,OAAOL,EAAE,CAAA;GAEnB;GAEAf,gBAAgBqB,MAAMpB,QAAQU,IAAAA;EAClC;EAEA,OAAOV;CACX;AACJ"}
|
|
@@ -23,15 +23,13 @@ var SessionManager = class {
|
|
|
23
23
|
case IdentityType.CLIENT:
|
|
24
24
|
input.clientId = input.sub;
|
|
25
25
|
break;
|
|
26
|
-
case IdentityType.USER:
|
|
27
|
-
input.userId = input.sub;
|
|
28
|
-
break;
|
|
26
|
+
case IdentityType.USER: input.userId = input.sub;
|
|
29
27
|
}
|
|
30
28
|
return this.repository.save(input);
|
|
31
29
|
}
|
|
32
30
|
async ping(session) {
|
|
33
31
|
if (session.seenAt) {
|
|
34
|
-
if (new Date(session.seenAt).getTime() +
|
|
32
|
+
if (new Date(session.seenAt).getTime() + 5e3 > Date.now()) return session;
|
|
35
33
|
}
|
|
36
34
|
session.seenAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
37
35
|
return this.repository.save(session);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.mjs","names":["IdentityType","JWTError","SessionManager","options","repository","ctx","create","input","ipAddress","userAgent","expiresAt","Date","now","maxAge","toISOString","subKind","CLIENT","clientId","sub","USER","userId","save","ping","session","seenAt","getTime","threshold","refresh","refreshedAt","markMfaVerified","mfaAt","findOneById","id","revoke","removeById","verify","ms","remove","expired"],"sources":["../../../../src/core/authentication/session/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Session } from '@authup/core-kit';\nimport { IdentityType } from '@authup/core-kit';\nimport { JWTError } from '@authup/specs';\nimport type {\n ISessionManager, \n ISessionRepository, \n SessionManagerContext, \n SessionManagerOptions,\n} from './types.ts';\n\nexport class SessionManager implements ISessionManager {\n protected options: SessionManagerOptions;\n\n protected repository: ISessionRepository;\n\n // -----------------------------------------------------\n\n constructor(ctx: SessionManagerContext) {\n this.options = ctx.options;\n this.repository = ctx.repository;\n }\n\n // -----------------------------------------------------\n\n /**\n * Create/Update session\n *\n * @param input\n */\n async create(input: Partial<Session>): Promise<Session> {\n input.ipAddress = input.ipAddress || '127.0.0.1';\n input.userAgent = input.userAgent || 'system';\n input.expiresAt = input.expiresAt || new Date(\n Date.now() + (this.options.maxAge * 1_000),\n ).toISOString();\n\n switch (input.subKind) {\n case IdentityType.CLIENT: {\n input.clientId = input.sub;\n break;\n }\n case IdentityType.USER: {\n input.userId = input.sub;\n break;\n }\n }\n\n return this.repository.save(input);\n }\n\n // -----------------------------------------------------\n\n async ping(session: Session): Promise<Session> {\n if (session.seenAt) {\n const seenAt = new Date(session.seenAt).getTime();\n const threshold = seenAt + (5 * 1_000);\n\n if (threshold > Date.now()) {\n return session;\n }\n }\n\n session.seenAt = new Date().toISOString();\n\n return this.repository.save(session);\n }\n\n // -----------------------------------------------------\n\n async refresh(session: Session): Promise<Session> {\n const now = new Date().toISOString();\n session.refreshedAt = now;\n session.seenAt = now;\n\n session.expiresAt = new Date(\n Date.now() + (this.options.maxAge * 1_000),\n ).toISOString();\n\n return this.repository.save(session);\n }\n\n // -----------------------------------------------------\n\n async markMfaVerified(session: Session): Promise<Session> {\n session.mfaAt = new Date().toISOString();\n\n return this.repository.save(session);\n }\n\n // -----------------------------------------------------\n\n /**\n * Verify session on token inspection/verification.\n *\n * @param id\n * @throws JWTError\n */\n async findOneById(id: string): Promise<Session | null> {\n return this.repository.findOneById(id);\n }\n\n // -----------------------------------------------------\n\n async revoke(id: string): Promise<void> {\n await this.repository.removeById(id);\n }\n\n // -----------------------------------------------------\n\n async verify(session: Session): Promise<void> {\n const ms = new Date(session.expiresAt).getTime();\n if (Date.now() > ms) {\n await this.repository.remove(session);\n\n // todo: better error\n throw JWTError.expired();\n }\n }\n}\n"],"mappings":";;;;;;AAiBA,IAAaE,iBAAb,MAAaA;CACCC;CAEAC;CAIV,YAAYC,KAA4B;EACpC,KAAKF,UAAUE,IAAIF;EACnB,KAAKC,aAAaC,IAAID;CAC1B;;;;;IASA,MAAME,OAAOC,OAA2C;EACpDA,MAAMC,YAAYD,MAAMC,aAAa;EACrCD,MAAME,YAAYF,MAAME,aAAa;EACrCF,MAAMG,YAAYH,MAAMG,aAAa,IAAIC,KACrCA,KAAKC,IAAG,IAAM,KAAKT,QAAQU,SAAS,GAAA,CAAA,CACtCC,YAAW;EAEb,QAAQP,MAAMQ,SAAd;GACI,KAAKf,aAAagB;IACdT,MAAMU,WAAWV,MAAMW;IACvB;GAEJ,KAAKlB,aAAamB
|
|
1
|
+
{"version":3,"file":"module.mjs","names":["IdentityType","JWTError","SessionManager","options","repository","ctx","create","input","ipAddress","userAgent","expiresAt","Date","now","maxAge","toISOString","subKind","CLIENT","clientId","sub","USER","userId","save","ping","session","seenAt","getTime","threshold","refresh","refreshedAt","markMfaVerified","mfaAt","findOneById","id","revoke","removeById","verify","ms","remove","expired"],"sources":["../../../../src/core/authentication/session/module.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport type { Session } from '@authup/core-kit';\nimport { IdentityType } from '@authup/core-kit';\nimport { JWTError } from '@authup/specs';\nimport type {\n ISessionManager, \n ISessionRepository, \n SessionManagerContext, \n SessionManagerOptions,\n} from './types.ts';\n\nexport class SessionManager implements ISessionManager {\n protected options: SessionManagerOptions;\n\n protected repository: ISessionRepository;\n\n // -----------------------------------------------------\n\n constructor(ctx: SessionManagerContext) {\n this.options = ctx.options;\n this.repository = ctx.repository;\n }\n\n // -----------------------------------------------------\n\n /**\n * Create/Update session\n *\n * @param input\n */\n async create(input: Partial<Session>): Promise<Session> {\n input.ipAddress = input.ipAddress || '127.0.0.1';\n input.userAgent = input.userAgent || 'system';\n input.expiresAt = input.expiresAt || new Date(\n Date.now() + (this.options.maxAge * 1_000),\n ).toISOString();\n\n switch (input.subKind) {\n case IdentityType.CLIENT: {\n input.clientId = input.sub;\n break;\n }\n case IdentityType.USER: {\n input.userId = input.sub;\n break;\n }\n }\n\n return this.repository.save(input);\n }\n\n // -----------------------------------------------------\n\n async ping(session: Session): Promise<Session> {\n if (session.seenAt) {\n const seenAt = new Date(session.seenAt).getTime();\n const threshold = seenAt + (5 * 1_000);\n\n if (threshold > Date.now()) {\n return session;\n }\n }\n\n session.seenAt = new Date().toISOString();\n\n return this.repository.save(session);\n }\n\n // -----------------------------------------------------\n\n async refresh(session: Session): Promise<Session> {\n const now = new Date().toISOString();\n session.refreshedAt = now;\n session.seenAt = now;\n\n session.expiresAt = new Date(\n Date.now() + (this.options.maxAge * 1_000),\n ).toISOString();\n\n return this.repository.save(session);\n }\n\n // -----------------------------------------------------\n\n async markMfaVerified(session: Session): Promise<Session> {\n session.mfaAt = new Date().toISOString();\n\n return this.repository.save(session);\n }\n\n // -----------------------------------------------------\n\n /**\n * Verify session on token inspection/verification.\n *\n * @param id\n * @throws JWTError\n */\n async findOneById(id: string): Promise<Session | null> {\n return this.repository.findOneById(id);\n }\n\n // -----------------------------------------------------\n\n async revoke(id: string): Promise<void> {\n await this.repository.removeById(id);\n }\n\n // -----------------------------------------------------\n\n async verify(session: Session): Promise<void> {\n const ms = new Date(session.expiresAt).getTime();\n if (Date.now() > ms) {\n await this.repository.remove(session);\n\n // todo: better error\n throw JWTError.expired();\n }\n }\n}\n"],"mappings":";;;;;;AAiBA,IAAaE,iBAAb,MAAaA;CACCC;CAEAC;CAIV,YAAYC,KAA4B;EACpC,KAAKF,UAAUE,IAAIF;EACnB,KAAKC,aAAaC,IAAID;CAC1B;;;;;IASA,MAAME,OAAOC,OAA2C;EACpDA,MAAMC,YAAYD,MAAMC,aAAa;EACrCD,MAAME,YAAYF,MAAME,aAAa;EACrCF,MAAMG,YAAYH,MAAMG,aAAa,IAAIC,KACrCA,KAAKC,IAAG,IAAM,KAAKT,QAAQU,SAAS,GAAA,CAAA,CACtCC,YAAW;EAEb,QAAQP,MAAMQ,SAAd;GACI,KAAKf,aAAagB;IACdT,MAAMU,WAAWV,MAAMW;IACvB;GAEJ,KAAKlB,aAAamB,MACdZ,MAAMa,SAASb,MAAMW;EAG7B;EAEA,OAAO,KAAKd,WAAWiB,KAAKd,KAAAA;CAChC;CAIA,MAAMe,KAAKC,SAAoC;EAC3C,IAAIA,QAAQC,QACO;OAAA,IAAIb,KAAKY,QAAQC,MAAM,CAAA,CAAEC,QACtBD,IAAU,MAEZb,KAAKC,IAAG,GACpB,OAAOW;EAAAA;EAIfA,QAAQC,0BAAS,IAAIb,KAAAA,EAAAA,CAAOG,YAAW;EAEvC,OAAO,KAAKV,WAAWiB,KAAKE,OAAAA;CAChC;CAIA,MAAMI,QAAQJ,SAAoC;EAC9C,MAAMX,uBAAM,IAAID,KAAAA,EAAAA,CAAOG,YAAW;EAClCS,QAAQK,cAAchB;EACtBW,QAAQC,SAASZ;EAEjBW,QAAQb,YAAY,IAAIC,KACpBA,KAAKC,IAAG,IAAM,KAAKT,QAAQU,SAAS,GAAA,CAAA,CACtCC,YAAW;EAEb,OAAO,KAAKV,WAAWiB,KAAKE,OAAAA;CAChC;CAIA,MAAMM,gBAAgBN,SAAoC;EACtDA,QAAQO,yBAAQ,IAAInB,KAAAA,EAAAA,CAAOG,YAAW;EAEtC,OAAO,KAAKV,WAAWiB,KAAKE,OAAAA;CAChC;;;;;;IAUA,MAAMQ,YAAYC,IAAqC;EACnD,OAAO,KAAK5B,WAAW2B,YAAYC,EAAAA;CACvC;CAIA,MAAMC,OAAOD,IAA2B;EACpC,MAAM,KAAK5B,WAAW8B,WAAWF,EAAAA;CACrC;CAIA,MAAMG,OAAOZ,SAAiC;EAC1C,MAAMa,KAAK,IAAIzB,KAAKY,QAAQb,SAAS,CAAA,CAAEe,QAAO;EAC9C,IAAId,KAAKC,IAAG,IAAKwB,IAAI;GACjB,MAAM,KAAKhC,WAAWiC,OAAOd,OAAAA;GAG7B,MAAMtB,SAASqC,QAAO;EAC1B;CACJ;AACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/service.ts"],"names":[],"mappings":"AAUA,OAAO,EAGH,eAAe,EAElB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B,EAAG,MAAM,oBAAoB,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKpE,MAAM,MAAM,oBAAoB,GAAG;IAC/B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,eAAe,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF,qBAAa,aAAc,SAAQ,qBAAsB,YAAW,cAAc;IAC9E,SAAS,CAAC,UAAU,EAAE,iBAAiB,CAAC;IAExC,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;IAE5C,SAAS,CAAC,SAAS,EAAE,eAAe,CAAC;gBAEzB,GAAG,EAAE,oBAAoB;IAO/B,OAAO,CACT,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;IAY5C,MAAM,CACR,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IA6CZ,MAAM,CACR,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,MAAM,CACR,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,IAAI,CACN,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAO,GACvC,OAAO,CAAC;QACP,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAA;KACnB,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/core/entities/client/service.ts"],"names":[],"mappings":"AAUA,OAAO,EAGH,eAAe,EAElB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,8BAA8B,EAAG,MAAM,oBAAoB,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKpE,MAAM,MAAM,oBAAoB,GAAG;IAC/B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,eAAe,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF,qBAAa,aAAc,SAAQ,qBAAsB,YAAW,cAAc;IAC9E,SAAS,CAAC,UAAU,EAAE,iBAAiB,CAAC;IAExC,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;IAE5C,SAAS,CAAC,SAAS,EAAE,eAAe,CAAC;gBAEzB,GAAG,EAAE,oBAAoB;IAO/B,OAAO,CACT,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1B,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC;IAY5C,MAAM,CACR,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,OAAO,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC;IA6CZ,MAAM,CACR,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,MAAM,CACR,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;IAKZ,IAAI,CACN,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAO,GACvC,OAAO,CAAC;QACP,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,OAAO,CAAA;KACnB,CAAC;IA6JI,MAAM,CACR,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,YAAY,GACpB,OAAO,CAAC,MAAM,CAAC;CAmBrB"}
|
|
@@ -69,7 +69,7 @@ var ClientService = class extends AbstractEntityService {
|
|
|
69
69
|
else where.name = idOrName;
|
|
70
70
|
if (realm) where.realmId = realm.id;
|
|
71
71
|
entity = await this.repository.findOneWithSecret(where);
|
|
72
|
-
if (!entity && options.updateOnly) throw new EntityNotFoundError();
|
|
72
|
+
if (!entity && (options.updateOnly || where.id)) throw new EntityNotFoundError();
|
|
73
73
|
} else if (options.updateOnly) throw new EntityNotFoundError();
|
|
74
74
|
let isSelfEdit = false;
|
|
75
75
|
if (entity) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.mjs","names":["BuiltInPolicyType","definePolicyData","ValidatorGroup","isUUID","EntityNotFoundError","ValidationError","CLIENT_RESERVED_NAMES","ClientAuthMethod","ClientValidator","PermissionName","AbstractEntityService","ClientCredentialsService","CLIENT_READ_PERMISSIONS","decodeQuery","clientSchema","ClientService","repository","realmRepository","validator","ctx","getMany","query","actor","permissionEvaluator","preEvaluateOneOf","name","findMany","schema","getOne","idOrName","realmId","isMe","identity","type","data","id","entity","findOne","parameters","secret","secretEncrypted","secretHashed","evaluateOneOf","ATTRIBUTES","resourceRealmMatch","create","save","undefined","update","updateOnly","options","group","realm","resolve","where","findOneWithSecret","isSelfEdit","preEvaluate","CLIENT_UPDATE","e","CLIENT_SELF_MANAGE","UPDATE","CLIENT_CREATE","CREATE","validated","run","includes","builtIn","validateJoinColumns","checkUniqueness","credentialsService","actorRealmId","getActorRealmId","evaluate","merge","authMethod","SECRET","generateSecret","protect","created","delete","CLIENT_DELETE","findOneBy","entityId","remove"],"sources":["../../../../src/core/entities/client/service.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { BuiltInPolicyType, definePolicyData } from '@authup/access';\nimport { ValidatorGroup, isUUID } from '@authup/kit';\nimport { EntityNotFoundError, ValidationError } from '@authup/errors';\nimport {\n CLIENT_RESERVED_NAMES,\n ClientAuthMethod,\n ClientValidator,\n PermissionName,\n} from '@authup/core-kit';\nimport type { Client } from '@authup/core-kit';\nimport type { ActorContext, EntityRepositoryFindManyResult } from '@authup/server-kit';\nimport type { IRealmRepository } from '../realm/types.ts';\nimport { AbstractEntityService } from '@authup/server-kit';\nimport { ClientCredentialsService } from '../../authentication/credential/entities/client/module.ts';\nimport type { IClientRepository, IClientService } from './types.ts';\nimport { CLIENT_READ_PERMISSIONS } from './constants.ts';\nimport { decodeQuery } from '../../query/index.ts';\nimport { clientSchema } from './schema.ts';\n\nexport type ClientServiceContext = {\n repository: IClientRepository;\n realmRepository: IRealmRepository;\n};\n\nexport class ClientService extends AbstractEntityService implements IClientService {\n protected repository: IClientRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected validator: ClientValidator;\n\n constructor(ctx: ClientServiceContext) {\n super();\n this.repository = ctx.repository;\n this.realmRepository = ctx.realmRepository;\n this.validator = new ClientValidator();\n }\n\n async getMany(\n query: Record<string, any>,\n actor: ActorContext,\n ): Promise<EntityRepositoryFindManyResult<Client>> {\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n\n // The per-row `secret` visibility gate lives on the client SCHEMA\n // (`fields.validateMany`, issue #3322), so it also covers the\n // `include=client` paths served by other services; the repository\n // layer redacts unauthorized values without dropping rows.\n return this.repository.findMany(\n await decodeQuery(query, { schema: clientSchema, actor }),\n );\n }\n\n async getOne(\n idOrName: string,\n actor: ActorContext,\n query?: Record<string, any>,\n realmId?: string,\n ): Promise<Client> {\n let isMe = !!actor.identity &&\n actor.identity.type === 'client' &&\n (\n actor.identity.data.id === idOrName ||\n actor.identity.data.name === idOrName\n );\n\n if (!isMe) {\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n }\n\n const entity = await this.repository.findOne(\n idOrName,\n await decodeQuery(query, {\n schema: clientSchema, \n parameters: ['fields', 'relations'], \n actor, \n }),\n realmId,\n );\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n if (isMe && actor.identity!.data.id !== entity.id) {\n isMe = false;\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n }\n\n if (\n !isMe &&\n entity.secret &&\n !entity.secretEncrypted &&\n !entity.secretHashed\n ) {\n await actor.permissionEvaluator.evaluateOneOf({\n name: CLIENT_READ_PERMISSIONS,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n }\n\n return entity;\n }\n\n async create(\n data: Record<string, any>,\n actor: ActorContext,\n ): Promise<Client> {\n const { entity } = await this.save(undefined, data, actor);\n return entity;\n }\n\n async update(\n idOrName: string,\n data: Record<string, any>,\n actor: ActorContext,\n ): Promise<Client> {\n const { entity } = await this.save(idOrName, data, actor, { updateOnly: true });\n return entity;\n }\n\n async save(\n idOrName: string | undefined,\n data: Record<string, any>,\n actor: ActorContext,\n options: { updateOnly?: boolean } = {},\n ): Promise<{\n entity: Client,\n created: boolean \n }> {\n let group: string;\n\n const realm = typeof data.realmId === 'string' ?\n await this.realmRepository.resolve(data.realmId) :\n undefined;\n\n let entity: Client | null | undefined;\n if (idOrName) {\n const where: Record<string, any> = {};\n if (isUUID(idOrName)) {\n where.id = idOrName;\n } else {\n where.name = idOrName;\n }\n\n if (realm) {\n where.realmId = realm.id;\n }\n\n entity = await this.repository.findOneWithSecret(where);\n if (!entity && options.updateOnly) {\n throw new EntityNotFoundError();\n }\n } else if (options.updateOnly) {\n throw new EntityNotFoundError();\n }\n\n let isSelfEdit = false;\n if (entity) {\n try {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_UPDATE });\n } catch (e) {\n if (\n !actor.identity ||\n actor.identity.type !== 'client' ||\n actor.identity.data.id !== entity.id\n ) {\n throw e;\n }\n isSelfEdit = true;\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_SELF_MANAGE });\n }\n group = ValidatorGroup.UPDATE;\n } else {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_CREATE });\n group = ValidatorGroup.CREATE;\n }\n\n const validated = await this.validator.run(data, { group });\n\n // Reserve the system-provisioned client names (`system`, `web`) so an\n // API caller can't create or rename a client onto them — that would\n // collide on unique(name, realmId) or shadow the builtIn client.\n // Provisioning and runtime hooks bypass this service, so they remain\n // free to manage the reserved clients. builtIn clients are exempt\n // (they ARE the provisioned ones) but API callers can never produce a\n // builtIn client since the validator strips the flag.\n if (\n typeof validated.name === 'string' &&\n CLIENT_RESERVED_NAMES.includes(validated.name) &&\n !(entity && entity.builtIn && entity.name === validated.name)\n ) {\n throw new ValidationError(`The client name '${validated.name}' is reserved.`);\n }\n\n await this.repository.validateJoinColumns(validated);\n await this.repository.checkUniqueness(validated, entity || undefined);\n\n const credentialsService = new ClientCredentialsService();\n\n if (entity) {\n if (\n !isSelfEdit &&\n !validated.realmId &&\n !entity.realmId\n ) {\n const actorRealmId = this.getActorRealmId(actor);\n if (actorRealmId) {\n validated.realmId = actorRealmId;\n }\n }\n\n if (isSelfEdit) {\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_SELF_MANAGE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: validated, ...this.resourceRealmMatch(validated) }),\n });\n }\n\n entity = this.repository.merge(entity, validated);\n\n if (!isSelfEdit) {\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_UPDATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n }\n\n if (entity.authMethod === ClientAuthMethod.SECRET) {\n if (!validated.secret && !entity.secret) {\n validated.secret = credentialsService.generateSecret();\n }\n\n if (validated.secret) {\n entity.secret = await credentialsService.protect(validated.secret, entity);\n }\n } else {\n entity.secret = null;\n entity.secretHashed = false;\n entity.secretEncrypted = false;\n }\n\n await this.repository.save(entity);\n\n return {\n entity,\n created: false, \n };\n }\n\n if (!validated.realmId) {\n const actorRealmId = this.getActorRealmId(actor);\n if (actorRealmId) {\n validated.realmId = actorRealmId;\n }\n }\n\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_CREATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: validated, ...this.resourceRealmMatch(validated) }),\n });\n\n entity = this.repository.create(validated);\n\n if (entity.authMethod === ClientAuthMethod.SECRET) {\n if (!validated.secret) {\n validated.secret = credentialsService.generateSecret();\n }\n\n entity.secret = await credentialsService.protect(validated.secret, validated);\n } else {\n entity.secret = null;\n entity.secretHashed = false;\n entity.secretEncrypted = false;\n }\n\n await this.repository.save(entity);\n\n return {\n entity,\n created: true, \n };\n }\n\n async delete(\n id: string,\n actor: ActorContext,\n ): Promise<Client> {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_DELETE });\n\n const entity = await this.repository.findOneBy({ id });\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_DELETE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n\n const { id: entityId } = entity;\n await this.repository.remove(entity);\n entity.id = entityId;\n\n return entity;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.mjs","names":["BuiltInPolicyType","definePolicyData","ValidatorGroup","isUUID","EntityNotFoundError","ValidationError","CLIENT_RESERVED_NAMES","ClientAuthMethod","ClientValidator","PermissionName","AbstractEntityService","ClientCredentialsService","CLIENT_READ_PERMISSIONS","decodeQuery","clientSchema","ClientService","repository","realmRepository","validator","ctx","getMany","query","actor","permissionEvaluator","preEvaluateOneOf","name","findMany","schema","getOne","idOrName","realmId","isMe","identity","type","data","id","entity","findOne","parameters","secret","secretEncrypted","secretHashed","evaluateOneOf","ATTRIBUTES","resourceRealmMatch","create","save","undefined","update","updateOnly","options","group","realm","resolve","where","findOneWithSecret","isSelfEdit","preEvaluate","CLIENT_UPDATE","e","CLIENT_SELF_MANAGE","UPDATE","CLIENT_CREATE","CREATE","validated","run","includes","builtIn","validateJoinColumns","checkUniqueness","credentialsService","actorRealmId","getActorRealmId","evaluate","merge","authMethod","SECRET","generateSecret","protect","created","delete","CLIENT_DELETE","findOneBy","entityId","remove"],"sources":["../../../../src/core/entities/client/service.ts"],"sourcesContent":["/*\n * Copyright (c) 2025.\n * Author Peter Placzek (tada5hi)\n * For the full copyright and license information,\n * view the LICENSE file that was distributed with this source code.\n */\n\nimport { BuiltInPolicyType, definePolicyData } from '@authup/access';\nimport { ValidatorGroup, isUUID } from '@authup/kit';\nimport { EntityNotFoundError, ValidationError } from '@authup/errors';\nimport {\n CLIENT_RESERVED_NAMES,\n ClientAuthMethod,\n ClientValidator,\n PermissionName,\n} from '@authup/core-kit';\nimport type { Client } from '@authup/core-kit';\nimport type { ActorContext, EntityRepositoryFindManyResult } from '@authup/server-kit';\nimport type { IRealmRepository } from '../realm/types.ts';\nimport { AbstractEntityService } from '@authup/server-kit';\nimport { ClientCredentialsService } from '../../authentication/credential/entities/client/module.ts';\nimport type { IClientRepository, IClientService } from './types.ts';\nimport { CLIENT_READ_PERMISSIONS } from './constants.ts';\nimport { decodeQuery } from '../../query/index.ts';\nimport { clientSchema } from './schema.ts';\n\nexport type ClientServiceContext = {\n repository: IClientRepository;\n realmRepository: IRealmRepository;\n};\n\nexport class ClientService extends AbstractEntityService implements IClientService {\n protected repository: IClientRepository;\n\n protected realmRepository: IRealmRepository;\n\n protected validator: ClientValidator;\n\n constructor(ctx: ClientServiceContext) {\n super();\n this.repository = ctx.repository;\n this.realmRepository = ctx.realmRepository;\n this.validator = new ClientValidator();\n }\n\n async getMany(\n query: Record<string, any>,\n actor: ActorContext,\n ): Promise<EntityRepositoryFindManyResult<Client>> {\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n\n // The per-row `secret` visibility gate lives on the client SCHEMA\n // (`fields.validateMany`, issue #3322), so it also covers the\n // `include=client` paths served by other services; the repository\n // layer redacts unauthorized values without dropping rows.\n return this.repository.findMany(\n await decodeQuery(query, { schema: clientSchema, actor }),\n );\n }\n\n async getOne(\n idOrName: string,\n actor: ActorContext,\n query?: Record<string, any>,\n realmId?: string,\n ): Promise<Client> {\n let isMe = !!actor.identity &&\n actor.identity.type === 'client' &&\n (\n actor.identity.data.id === idOrName ||\n actor.identity.data.name === idOrName\n );\n\n if (!isMe) {\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n }\n\n const entity = await this.repository.findOne(\n idOrName,\n await decodeQuery(query, {\n schema: clientSchema, \n parameters: ['fields', 'relations'], \n actor, \n }),\n realmId,\n );\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n if (isMe && actor.identity!.data.id !== entity.id) {\n isMe = false;\n await actor.permissionEvaluator.preEvaluateOneOf({ name: CLIENT_READ_PERMISSIONS });\n }\n\n if (\n !isMe &&\n entity.secret &&\n !entity.secretEncrypted &&\n !entity.secretHashed\n ) {\n await actor.permissionEvaluator.evaluateOneOf({\n name: CLIENT_READ_PERMISSIONS,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n }\n\n return entity;\n }\n\n async create(\n data: Record<string, any>,\n actor: ActorContext,\n ): Promise<Client> {\n const { entity } = await this.save(undefined, data, actor);\n return entity;\n }\n\n async update(\n idOrName: string,\n data: Record<string, any>,\n actor: ActorContext,\n ): Promise<Client> {\n const { entity } = await this.save(idOrName, data, actor, { updateOnly: true });\n return entity;\n }\n\n async save(\n idOrName: string | undefined,\n data: Record<string, any>,\n actor: ActorContext,\n options: { updateOnly?: boolean } = {},\n ): Promise<{\n entity: Client,\n created: boolean \n }> {\n let group: string;\n\n const realm = typeof data.realmId === 'string' ?\n await this.realmRepository.resolve(data.realmId) :\n undefined;\n\n let entity: Client | null | undefined;\n if (idOrName) {\n const where: Record<string, any> = {};\n if (isUUID(idOrName)) {\n where.id = idOrName;\n } else {\n where.name = idOrName;\n }\n\n if (realm) {\n where.realmId = realm.id;\n }\n\n entity = await this.repository.findOneWithSecret(where);\n // Only a NAME key may upsert-create. A UUID addresses one specific\n // row, so a miss is a 404 (creating would write a different id).\n if (!entity && (options.updateOnly || where.id)) {\n throw new EntityNotFoundError();\n }\n } else if (options.updateOnly) {\n throw new EntityNotFoundError();\n }\n\n let isSelfEdit = false;\n if (entity) {\n try {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_UPDATE });\n } catch (e) {\n if (\n !actor.identity ||\n actor.identity.type !== 'client' ||\n actor.identity.data.id !== entity.id\n ) {\n throw e;\n }\n isSelfEdit = true;\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_SELF_MANAGE });\n }\n group = ValidatorGroup.UPDATE;\n } else {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_CREATE });\n group = ValidatorGroup.CREATE;\n }\n\n const validated = await this.validator.run(data, { group });\n\n // Reserve the system-provisioned client names (`system`, `web`) so an\n // API caller can't create or rename a client onto them — that would\n // collide on unique(name, realmId) or shadow the builtIn client.\n // Provisioning and runtime hooks bypass this service, so they remain\n // free to manage the reserved clients. builtIn clients are exempt\n // (they ARE the provisioned ones) but API callers can never produce a\n // builtIn client since the validator strips the flag.\n if (\n typeof validated.name === 'string' &&\n CLIENT_RESERVED_NAMES.includes(validated.name) &&\n !(entity && entity.builtIn && entity.name === validated.name)\n ) {\n throw new ValidationError(`The client name '${validated.name}' is reserved.`);\n }\n\n await this.repository.validateJoinColumns(validated);\n await this.repository.checkUniqueness(validated, entity || undefined);\n\n const credentialsService = new ClientCredentialsService();\n\n if (entity) {\n if (\n !isSelfEdit &&\n !validated.realmId &&\n !entity.realmId\n ) {\n const actorRealmId = this.getActorRealmId(actor);\n if (actorRealmId) {\n validated.realmId = actorRealmId;\n }\n }\n\n if (isSelfEdit) {\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_SELF_MANAGE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: validated, ...this.resourceRealmMatch(validated) }),\n });\n }\n\n entity = this.repository.merge(entity, validated);\n\n if (!isSelfEdit) {\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_UPDATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n }\n\n if (entity.authMethod === ClientAuthMethod.SECRET) {\n if (!validated.secret && !entity.secret) {\n validated.secret = credentialsService.generateSecret();\n }\n\n if (validated.secret) {\n entity.secret = await credentialsService.protect(validated.secret, entity);\n }\n } else {\n entity.secret = null;\n entity.secretHashed = false;\n entity.secretEncrypted = false;\n }\n\n await this.repository.save(entity);\n\n return {\n entity,\n created: false, \n };\n }\n\n if (!validated.realmId) {\n const actorRealmId = this.getActorRealmId(actor);\n if (actorRealmId) {\n validated.realmId = actorRealmId;\n }\n }\n\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_CREATE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: validated, ...this.resourceRealmMatch(validated) }),\n });\n\n entity = this.repository.create(validated);\n\n if (entity.authMethod === ClientAuthMethod.SECRET) {\n if (!validated.secret) {\n validated.secret = credentialsService.generateSecret();\n }\n\n entity.secret = await credentialsService.protect(validated.secret, validated);\n } else {\n entity.secret = null;\n entity.secretHashed = false;\n entity.secretEncrypted = false;\n }\n\n await this.repository.save(entity);\n\n return {\n entity,\n created: true, \n };\n }\n\n async delete(\n id: string,\n actor: ActorContext,\n ): Promise<Client> {\n await actor.permissionEvaluator.preEvaluate({ name: PermissionName.CLIENT_DELETE });\n\n const entity = await this.repository.findOneBy({ id });\n if (!entity) {\n throw new EntityNotFoundError();\n }\n\n await actor.permissionEvaluator.evaluate({\n name: PermissionName.CLIENT_DELETE,\n data: definePolicyData({ [BuiltInPolicyType.ATTRIBUTES]: entity, ...this.resourceRealmMatch(entity) }),\n });\n\n const { id: entityId } = entity;\n await this.repository.remove(entity);\n entity.id = entityId;\n\n return entity;\n }\n}\n"],"mappings":"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"}
|
|
@@ -54,15 +54,18 @@ var ClientPermissionService = class extends JunctionEntityService {
|
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
if (validated.client) validated.clientRealmId = validated.client.realmId;
|
|
57
|
-
if (validated.permission && actor.identity)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
if (validated.permission && actor.identity) {
|
|
58
|
+
const grant = await this.identityPermissionProvider.resolveJunctionGrant({
|
|
59
|
+
type: actor.identity.type,
|
|
60
|
+
id: actor.identity.data.id
|
|
61
|
+
}, {
|
|
62
|
+
name: validated.permission.name,
|
|
63
|
+
realmId: validated.permission.realmId,
|
|
64
|
+
clientId: validated.permission.clientId,
|
|
65
|
+
realmScope: validated.realmScope ?? RealmScope.OWN
|
|
66
|
+
});
|
|
67
|
+
applyJunctionCreateGrant(validated, grant);
|
|
68
|
+
}
|
|
66
69
|
await actor.permissionEvaluator.evaluate({
|
|
67
70
|
name: PermissionName.CLIENT_PERMISSION_CREATE,
|
|
68
71
|
data: definePolicyData({
|