diff --git a/src/main/java/dev/kruhlmann/imgfloat/config/OAuth2AuthorizedClientPersistenceConfig.java b/src/main/java/dev/kruhlmann/imgfloat/config/OAuth2AuthorizedClientPersistenceConfig.java index 9c58d1e..296a52c 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/config/OAuth2AuthorizedClientPersistenceConfig.java +++ b/src/main/java/dev/kruhlmann/imgfloat/config/OAuth2AuthorizedClientPersistenceConfig.java @@ -2,6 +2,7 @@ package dev.kruhlmann.imgfloat.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; @@ -14,9 +15,14 @@ public class OAuth2AuthorizedClientPersistenceConfig { @Bean OAuth2AuthorizedClientService oauth2AuthorizedClientService( JdbcOperations jdbcOperations, - ClientRegistrationRepository clientRegistrationRepository + ClientRegistrationRepository clientRegistrationRepository, + Environment environment ) { - return new SQLiteOAuth2AuthorizedClientService(jdbcOperations, clientRegistrationRepository); + return new SQLiteOAuth2AuthorizedClientService( + jdbcOperations, + clientRegistrationRepository, + OAuthTokenCipher.fromEnvironment(environment) + ); } @Bean diff --git a/src/main/java/dev/kruhlmann/imgfloat/config/OAuthTokenCipher.java b/src/main/java/dev/kruhlmann/imgfloat/config/OAuthTokenCipher.java index 3e8147c..d12a3b9 100644 --- a/src/main/java/dev/kruhlmann/imgfloat/config/OAuthTokenCipher.java +++ b/src/main/java/dev/kruhlmann/imgfloat/config/OAuthTokenCipher.java @@ -12,6 +12,7 @@ import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; public class OAuthTokenCipher { @@ -52,6 +53,28 @@ public class OAuthTokenCipher { return new OAuthTokenCipher(primaryKey, keys); } + public static OAuthTokenCipher fromEnvironment(Environment environment) { + String base64Key = environment.getProperty(KEY_ENV); + if (base64Key == null || base64Key.isBlank()) { + throw new IllegalStateException(KEY_ENV + " is required to encrypt OAuth tokens"); + } + SecretKey primaryKey = decodeKey(base64Key, KEY_ENV); + List keys = new ArrayList<>(); + keys.add(primaryKey); + + String previousKeys = environment.getProperty(PREVIOUS_KEYS_ENV); + if (previousKeys != null && !previousKeys.isBlank()) { + for (String value : previousKeys.split(",")) { + String trimmed = value.trim(); + if (!trimmed.isEmpty()) { + keys.add(decodeKey(trimmed, PREVIOUS_KEYS_ENV)); + } + } + } + + return new OAuthTokenCipher(primaryKey, keys); + } + public String encrypt(String plaintext) { if (plaintext == null) { return null; diff --git a/src/test/java/dev/kruhlmann/imgfloat/ChannelApiIntegrationTest.java b/src/test/java/dev/kruhlmann/imgfloat/ChannelApiIntegrationTest.java index 632f239..3dc8b30 100644 --- a/src/test/java/dev/kruhlmann/imgfloat/ChannelApiIntegrationTest.java +++ b/src/test/java/dev/kruhlmann/imgfloat/ChannelApiIntegrationTest.java @@ -29,6 +29,8 @@ import org.springframework.test.web.servlet.MockMvc; properties = { "spring.security.oauth2.client.registration.twitch.client-id=test-client-id", "spring.security.oauth2.client.registration.twitch.client-secret=test-client-secret", + "spring.datasource.url=jdbc:sqlite:target/test-${random.uuid}.db", + "IMGFLOAT_TOKEN_ENCRYPTION_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", } ) @AutoConfigureMockMvc diff --git a/src/test/java/dev/kruhlmann/imgfloat/ChannelDirectoryApiIntegrationTest.java b/src/test/java/dev/kruhlmann/imgfloat/ChannelDirectoryApiIntegrationTest.java index 95fb4d6..a9f9bfd 100644 --- a/src/test/java/dev/kruhlmann/imgfloat/ChannelDirectoryApiIntegrationTest.java +++ b/src/test/java/dev/kruhlmann/imgfloat/ChannelDirectoryApiIntegrationTest.java @@ -18,6 +18,8 @@ import org.springframework.test.web.servlet.MockMvc; properties = { "spring.security.oauth2.client.registration.twitch.client-id=test-client-id", "spring.security.oauth2.client.registration.twitch.client-secret=test-client-secret", + "spring.datasource.url=jdbc:sqlite:target/test-${random.uuid}.db", + "IMGFLOAT_TOKEN_ENCRYPTION_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", } ) @AutoConfigureMockMvc