{"record":{"id":"2f32abf4ad5d1fd1","repo":"apereo/cas","slug":"storing-trusted-device-records-in-runtime-memory","errorCode":null,"errorMessage":"Storing trusted device records in runtime memory. Changes and records will be lost upon CAS restarts","messagePattern":"Storing trusted device records in runtime memory\\. Changes and records will be lost upon CAS restarts","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-trusted-mfa/src/main/java/org/apereo/cas/config/MultifactorAuthnTrustConfiguration.java","lineNumber":105,"sourceCode":"        public MultifactorAuthenticationTrustStorage mfaTrustEngine(\n            final CasConfigurationProperties casProperties,\n            @Qualifier(\"mfaTrustCipherExecutor\")\n            final CipherExecutor mfaTrustCipherExecutor,\n            @Qualifier(\"mfaTrustRecordKeyGenerator\")\n            final MultifactorAuthenticationTrustRecordKeyGenerator mfaTrustRecordKeyGenerator) {\n            val trusted = casProperties.getAuthn().getMfa().getTrusted();\n            val storage = Caffeine.newBuilder().initialCapacity(INITIAL_CACHE_SIZE)\n                .maximumSize(MAX_CACHE_SIZE).expireAfter(new MultifactorAuthenticationTrustRecordExpiry()).build(s -> {\n                    LOGGER.error(\"Load operation of the cache is not supported.\");\n                    return null;\n                });\n            return FunctionUtils.doIf(trusted.getJson().getLocation() != null, () -> {\n                LOGGER.debug(\"Storing trusted device records inside the JSON resource [{}]\", trusted.getJson().getLocation());\n                return new JsonMultifactorAuthenticationTrustStorage(casProperties.getAuthn().getMfa().getTrusted(),\n                    mfaTrustCipherExecutor, trusted.getJson().getLocation(),\n                    mfaTrustRecordKeyGenerator);\n            }, () -> {\n                LOGGER.warn(\"Storing trusted device records in runtime memory. Changes and records will be lost upon CAS restarts\");\n                return new InMemoryMultifactorAuthenticationTrustStorage(\n                    casProperties.getAuthn().getMfa().getTrusted(),\n                    mfaTrustCipherExecutor, storage, mfaTrustRecordKeyGenerator);\n            }).get();\n        }\n\n        @ConditionalOnMissingBean(name = \"transactionManagerMfaAuthnTrust\")\n        @Bean\n        @RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)\n        public PlatformTransactionManager transactionManagerMfaAuthnTrust() {\n            return new PseudoTransactionManager();\n        }\n    }\n\n    @Configuration(value = \"MultifactorAuthnTrustCryptoConfiguration\", proxyBeanMethods = false)\n    @EnableConfigurationProperties(CasConfigurationProperties.class)\n    static class MultifactorAuthnTrustCryptoConfiguration {\n        @Bean","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-trusted-mfa/src/main/java/org/apereo/cas/config/MultifactorAuthnTrustConfiguration.java#L87-L123","documentation":"CAS logs this warning when no persistent storage location is configured for trusted MFA device records, so it falls back to InMemoryMultifactorAuthenticationTrustStorage. Device trust records (which devices the user chose to 'remember' for MFA) live only in runtime memory and are wiped on every CAS restart, forcing users to re-authenticate with MFA.","triggerScenarios":"The mfaTrustEngine bean in MultifactorAuthnTrustConfiguration builds its trust storage; the fallback branch executes when casProperties.getAuthn().getMfa().getTrusted().getJson().getLocation() is null, i.e. no JSON storage file is configured (and no other persistent storage variant module is wired).","commonSituations":"Deployers enable CAS MFA trusted-device support but never set cas.authn.mfa.trusted.json.location; single-node dev/test environments where in-memory loss seems acceptable; Kubernetes/container deployments where pods restart frequently and trust records silently vanish.","solutions":["Set cas.authn.mfa.trusted.json.location=/etc/cas/config/trusted-devices.json (and ensure the path is writable and on persistent storage) so JsonMultifactorAuthenticationTrustStorage is used instead.","If running multiple CAS nodes or containers, use a database/redis-backed trusted MFA storage module (e.g. cas-server-support-trusted-mfa-mongo/jdbc/redis) and configure its location/connection instead of JSON.","If in-memory storage is intentional for a test environment, silence/ignore the warning, but do not use it in production."],"exampleFix":"// before (application.properties)\ncas.authn.mfa.trusted.enabled=true\n\n// after\ncas.authn.mfa.trusted.enabled=true\ncas.authn.mfa.trusted.json.location=/etc/cas/config/mfa-trusted-devices.json","handlingStrategy":"validation","validationCode":"// at startup, before enabling trusted MFA in prod\nString loc = casProperties.getAuthn().getMfa().getTrusted().getJson().getLocation();\nif (loc == null || loc.isBlank()) {\n    throw new IllegalStateException(\"cas.authn.mfa.trusted.json.location must be set for persistent trusted-device storage\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set cas.authn.mfa.trusted.json.location (or a database/redis-backed variant) in production config.","Store the trust file on persistent volume in containerized deployments.","Grep startup logs for 'Storing trusted device records in runtime memory' as a deployment checklist item."],"tags":["configuration","mfa","persistence","in-memory-storage"],"backgroundTag":"missing-config-value","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}