mirror of
https://github.com/imgfloat/server.git
synced 2026-02-05 11:49:25 +00:00
Retain twitch tokens
This commit is contained in:
@@ -0,0 +1,24 @@
|
|||||||
|
package dev.kruhlmann.imgfloat.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository;
|
||||||
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class OAuth2AuthorizedClientPersistenceConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
OAuth2AuthorizedClientService oauth2AuthorizedClientService(JdbcOperations jdbcOperations,
|
||||||
|
ClientRegistrationRepository clientRegistrationRepository) {
|
||||||
|
return new SQLiteOAuth2AuthorizedClientService(jdbcOperations, clientRegistrationRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
OAuth2AuthorizedClientRepository authorizedClientRepository(OAuth2AuthorizedClientService authorizedClientService) {
|
||||||
|
return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package dev.kruhlmann.imgfloat.config;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.jdbc.core.JdbcOperations;
|
||||||
|
import org.springframework.jdbc.core.RowMapper;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||||
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||||
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||||
|
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||||
|
|
||||||
|
public class SQLiteOAuth2AuthorizedClientService implements OAuth2AuthorizedClientService {
|
||||||
|
private static final String TABLE_NAME = "oauth2_authorized_client";
|
||||||
|
|
||||||
|
private final JdbcOperations jdbcOperations;
|
||||||
|
private final ClientRegistrationRepository clientRegistrationRepository;
|
||||||
|
private final RowMapper<OAuth2AuthorizedClient> rowMapper;
|
||||||
|
|
||||||
|
public SQLiteOAuth2AuthorizedClientService(JdbcOperations jdbcOperations,
|
||||||
|
ClientRegistrationRepository clientRegistrationRepository) {
|
||||||
|
this.jdbcOperations = jdbcOperations;
|
||||||
|
this.clientRegistrationRepository = clientRegistrationRepository;
|
||||||
|
this.rowMapper = (rs, rowNum) -> {
|
||||||
|
String registrationId = rs.getString("client_registration_id");
|
||||||
|
String principalName = rs.getString("principal_name");
|
||||||
|
ClientRegistration registration = clientRegistrationRepository.findByRegistrationId(registrationId);
|
||||||
|
if (registration == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2AccessToken accessToken = new OAuth2AccessToken(
|
||||||
|
OAuth2AccessToken.TokenType.BEARER,
|
||||||
|
rs.getString("access_token_value"),
|
||||||
|
toInstant(rs.getObject("access_token_issued_at")),
|
||||||
|
toInstant(rs.getObject("access_token_expires_at")),
|
||||||
|
scopesFrom(rs.getString("access_token_scopes"))
|
||||||
|
);
|
||||||
|
|
||||||
|
Object refreshValue = rs.getObject("refresh_token_value");
|
||||||
|
OAuth2RefreshToken refreshToken = refreshValue == null
|
||||||
|
? null
|
||||||
|
: new OAuth2RefreshToken(
|
||||||
|
refreshValue.toString(),
|
||||||
|
toInstant(rs.getObject("refresh_token_issued_at"))
|
||||||
|
);
|
||||||
|
|
||||||
|
return new OAuth2AuthorizedClient(registration, principalName, accessToken, refreshToken);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId, String principalName) {
|
||||||
|
return jdbcOperations.query(
|
||||||
|
"SELECT client_registration_id, principal_name, access_token_value, access_token_issued_at, access_token_expires_at, access_token_scopes, refresh_token_value, refresh_token_issued_at " +
|
||||||
|
"FROM " + TABLE_NAME + " WHERE client_registration_id = ? AND principal_name = ?",
|
||||||
|
ps -> {
|
||||||
|
ps.setString(1, clientRegistrationId);
|
||||||
|
ps.setString(2, principalName);
|
||||||
|
},
|
||||||
|
rs -> rs.next() ? (T) rowMapper.mapRow(rs, 0) : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
|
||||||
|
try {
|
||||||
|
int updated = jdbcOperations.update("""
|
||||||
|
INSERT INTO oauth2_authorized_client (
|
||||||
|
client_registration_id, principal_name,
|
||||||
|
access_token_value, access_token_issued_at, access_token_expires_at, access_token_scopes,
|
||||||
|
refresh_token_value, refresh_token_issued_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
ON CONFLICT(client_registration_id, principal_name) DO UPDATE SET
|
||||||
|
access_token_value=excluded.access_token_value,
|
||||||
|
access_token_issued_at=excluded.access_token_issued_at,
|
||||||
|
access_token_expires_at=excluded.access_token_expires_at,
|
||||||
|
access_token_scopes=excluded.access_token_scopes,
|
||||||
|
refresh_token_value=excluded.refresh_token_value,
|
||||||
|
refresh_token_issued_at=excluded.refresh_token_issued_at
|
||||||
|
""",
|
||||||
|
preparedStatement -> {
|
||||||
|
preparedStatement.setString(1, authorizedClient.getClientRegistration().getRegistrationId());
|
||||||
|
preparedStatement.setString(2, principal.getName());
|
||||||
|
setToken(preparedStatement, 3, authorizedClient.getAccessToken());
|
||||||
|
preparedStatement.setObject(5, toEpochMillis(authorizedClient.getAccessToken().getExpiresAt()), java.sql.Types.BIGINT);
|
||||||
|
preparedStatement.setString(6, scopesToString(authorizedClient.getAccessToken().getScopes()));
|
||||||
|
OAuth2RefreshToken refreshToken = authorizedClient.getRefreshToken();
|
||||||
|
if (refreshToken != null) {
|
||||||
|
preparedStatement.setString(7, refreshToken.getTokenValue());
|
||||||
|
preparedStatement.setObject(8, toEpochMillis(refreshToken.getIssuedAt()), java.sql.Types.BIGINT);
|
||||||
|
} else {
|
||||||
|
preparedStatement.setNull(7, java.sql.Types.VARCHAR);
|
||||||
|
preparedStatement.setNull(8, java.sql.Types.BIGINT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (DataAccessException ex) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAuthorizedClient(String clientRegistrationId, String principalName) {
|
||||||
|
jdbcOperations.update("DELETE FROM " + TABLE_NAME + " WHERE client_registration_id = ? AND principal_name = ?",
|
||||||
|
preparedStatement -> {
|
||||||
|
preparedStatement.setString(1, clientRegistrationId);
|
||||||
|
preparedStatement.setString(2, principalName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setToken(java.sql.PreparedStatement ps, int startIndex, OAuth2AccessToken token) throws java.sql.SQLException {
|
||||||
|
ps.setString(startIndex, token.getTokenValue());
|
||||||
|
ps.setObject(startIndex + 1, toEpochMillis(token.getIssuedAt()), java.sql.Types.BIGINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instant toInstant(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (value instanceof Timestamp ts) {
|
||||||
|
return ts.toInstant();
|
||||||
|
}
|
||||||
|
if (value instanceof Number num) {
|
||||||
|
return Instant.ofEpochMilli(num.longValue());
|
||||||
|
}
|
||||||
|
String text = value.toString();
|
||||||
|
try {
|
||||||
|
long millis = Long.parseLong(text);
|
||||||
|
return Instant.ofEpochMilli(millis);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
return Timestamp.valueOf(text).toInstant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long toEpochMillis(Instant instant) {
|
||||||
|
return instant == null ? null : instant.toEpochMilli();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> scopesFrom(String scopeString) {
|
||||||
|
if (scopeString == null || scopeString.isBlank()) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return Stream.of(scopeString.split(" "))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(s -> !s.isEmpty())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String scopesToString(Set<String> scopes) {
|
||||||
|
if (scopes == null || scopes.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return scopes.stream().sorted().collect(Collectors.joining(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,8 @@ public class SchemaMigration implements ApplicationRunner {
|
|||||||
ensureSessionAttributeUpsertTrigger();
|
ensureSessionAttributeUpsertTrigger();
|
||||||
ensureChannelCanvasColumns();
|
ensureChannelCanvasColumns();
|
||||||
ensureAssetMediaColumns();
|
ensureAssetMediaColumns();
|
||||||
|
ensureAuthorizedClientTable();
|
||||||
|
normalizeAuthorizedClientTimestamps();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureSessionAttributeUpsertTrigger() {
|
private void ensureSessionAttributeUpsertTrigger() {
|
||||||
@@ -101,4 +103,50 @@ public class SchemaMigration implements ApplicationRunner {
|
|||||||
logger.warn("Failed to add column '{}' to {} table", columnName, tableName, ex);
|
logger.warn("Failed to add column '{}' to {} table", columnName, tableName, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureAuthorizedClientTable() {
|
||||||
|
try {
|
||||||
|
jdbcTemplate.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS oauth2_authorized_client (
|
||||||
|
client_registration_id VARCHAR(100) NOT NULL,
|
||||||
|
principal_name VARCHAR(200) NOT NULL,
|
||||||
|
access_token_type VARCHAR(100),
|
||||||
|
access_token_value TEXT,
|
||||||
|
access_token_issued_at INTEGER,
|
||||||
|
access_token_expires_at INTEGER,
|
||||||
|
access_token_scopes VARCHAR(1000),
|
||||||
|
refresh_token_value TEXT,
|
||||||
|
refresh_token_issued_at INTEGER,
|
||||||
|
PRIMARY KEY (client_registration_id, principal_name)
|
||||||
|
)
|
||||||
|
""");
|
||||||
|
logger.info("Ensured oauth2_authorized_client table exists");
|
||||||
|
} catch (DataAccessException ex) {
|
||||||
|
logger.warn("Unable to ensure oauth2_authorized_client table", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void normalizeAuthorizedClientTimestamps() {
|
||||||
|
normalizeTimestampColumn("access_token_issued_at");
|
||||||
|
normalizeTimestampColumn("access_token_expires_at");
|
||||||
|
normalizeTimestampColumn("refresh_token_issued_at");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void normalizeTimestampColumn(String column) {
|
||||||
|
try {
|
||||||
|
int updated = jdbcTemplate.update(
|
||||||
|
"UPDATE oauth2_authorized_client " +
|
||||||
|
"SET " + column + " = CASE " +
|
||||||
|
"WHEN " + column + " LIKE '%-%' THEN CAST(strftime('%s', " + column + ") AS INTEGER) * 1000 " +
|
||||||
|
"WHEN typeof(" + column + ") = 'text' AND " + column + " GLOB '[0-9]*' THEN CAST(" + column + " AS INTEGER) " +
|
||||||
|
"WHEN typeof(" + column + ") = 'integer' THEN " + column + " " +
|
||||||
|
"ELSE " + column + " END " +
|
||||||
|
"WHERE " + column + " IS NOT NULL");
|
||||||
|
if (updated > 0) {
|
||||||
|
logger.info("Normalized {} rows in oauth2_authorized_client.{}", updated, column);
|
||||||
|
}
|
||||||
|
} catch (DataAccessException ex) {
|
||||||
|
logger.warn("Unable to normalize oauth2_authorized_client.{} timestamps", column, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
|||||||
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
|
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
|
||||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
|
||||||
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
@@ -21,7 +22,8 @@ import org.springframework.http.HttpStatus;
|
|||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
SecurityFilterChain securityFilterChain(HttpSecurity http,
|
||||||
|
OAuth2AuthorizedClientRepository authorizedClientRepository) throws Exception {
|
||||||
http
|
http
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers(
|
.requestMatchers(
|
||||||
@@ -44,6 +46,7 @@ public class SecurityConfig {
|
|||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.oauth2Login(oauth -> oauth
|
.oauth2Login(oauth -> oauth
|
||||||
|
.authorizedClientRepository(authorizedClientRepository)
|
||||||
.tokenEndpoint(token -> token.accessTokenResponseClient(twitchAccessTokenResponseClient()))
|
.tokenEndpoint(token -> token.accessTokenResponseClient(twitchAccessTokenResponseClient()))
|
||||||
.userInfoEndpoint(user -> user.userService(twitchOAuth2UserService())))
|
.userInfoEndpoint(user -> user.userService(twitchOAuth2UserService())))
|
||||||
.logout(logout -> logout.logoutSuccessUrl("/").permitAll())
|
.logout(logout -> logout.logoutSuccessUrl("/").permitAll())
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||||
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
|
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
@@ -33,6 +33,8 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -50,17 +52,20 @@ public class ChannelApiController {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(ChannelApiController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ChannelApiController.class);
|
||||||
private final ChannelDirectoryService channelDirectoryService;
|
private final ChannelDirectoryService channelDirectoryService;
|
||||||
private final OAuth2AuthorizedClientService authorizedClientService;
|
private final OAuth2AuthorizedClientService authorizedClientService;
|
||||||
|
private final OAuth2AuthorizedClientRepository authorizedClientRepository;
|
||||||
private final TwitchUserLookupService twitchUserLookupService;
|
private final TwitchUserLookupService twitchUserLookupService;
|
||||||
private final AuthorizationService authorizationService;
|
private final AuthorizationService authorizationService;
|
||||||
|
|
||||||
public ChannelApiController(
|
public ChannelApiController(
|
||||||
ChannelDirectoryService channelDirectoryService,
|
ChannelDirectoryService channelDirectoryService,
|
||||||
OAuth2AuthorizedClientService authorizedClientService,
|
OAuth2AuthorizedClientService authorizedClientService,
|
||||||
|
OAuth2AuthorizedClientRepository authorizedClientRepository,
|
||||||
TwitchUserLookupService twitchUserLookupService,
|
TwitchUserLookupService twitchUserLookupService,
|
||||||
AuthorizationService authorizationService
|
AuthorizationService authorizationService
|
||||||
) {
|
) {
|
||||||
this.channelDirectoryService = channelDirectoryService;
|
this.channelDirectoryService = channelDirectoryService;
|
||||||
this.authorizedClientService = authorizedClientService;
|
this.authorizedClientService = authorizedClientService;
|
||||||
|
this.authorizedClientRepository = authorizedClientRepository;
|
||||||
this.twitchUserLookupService = twitchUserLookupService;
|
this.twitchUserLookupService = twitchUserLookupService;
|
||||||
this.authorizationService = authorizationService;
|
this.authorizationService = authorizationService;
|
||||||
}
|
}
|
||||||
@@ -82,7 +87,7 @@ public class ChannelApiController {
|
|||||||
@GetMapping("/admins")
|
@GetMapping("/admins")
|
||||||
public Collection<TwitchUserProfile> listAdmins(@PathVariable("broadcaster") String broadcaster,
|
public Collection<TwitchUserProfile> listAdmins(@PathVariable("broadcaster") String broadcaster,
|
||||||
OAuth2AuthenticationToken oauthToken,
|
OAuth2AuthenticationToken oauthToken,
|
||||||
@RegisteredOAuth2AuthorizedClient("twitch") OAuth2AuthorizedClient authorizedClient) {
|
HttpServletRequest request) {
|
||||||
String sessionUsername = OauthSessionUser.from(oauthToken).login();
|
String sessionUsername = OauthSessionUser.from(oauthToken).login();
|
||||||
authorizationService.userMatchesSessionUsernameOrThrowHttpError(broadcaster, sessionUsername);
|
authorizationService.userMatchesSessionUsernameOrThrowHttpError(broadcaster, sessionUsername);
|
||||||
LOG.debug("Listing admins for {} by {}", broadcaster, sessionUsername);
|
LOG.debug("Listing admins for {} by {}", broadcaster, sessionUsername);
|
||||||
@@ -90,7 +95,7 @@ public class ChannelApiController {
|
|||||||
List<String> admins = channel.getAdmins().stream()
|
List<String> admins = channel.getAdmins().stream()
|
||||||
.sorted(Comparator.naturalOrder())
|
.sorted(Comparator.naturalOrder())
|
||||||
.toList();
|
.toList();
|
||||||
authorizedClient = resolveAuthorizedClient(oauthToken, authorizedClient);
|
OAuth2AuthorizedClient authorizedClient = resolveAuthorizedClient(oauthToken, null, request);
|
||||||
String accessToken = Optional.ofNullable(authorizedClient)
|
String accessToken = Optional.ofNullable(authorizedClient)
|
||||||
.map(OAuth2AuthorizedClient::getAccessToken)
|
.map(OAuth2AuthorizedClient::getAccessToken)
|
||||||
.map(token -> token.getTokenValue())
|
.map(token -> token.getTokenValue())
|
||||||
@@ -105,12 +110,12 @@ public class ChannelApiController {
|
|||||||
@GetMapping("/admins/suggestions")
|
@GetMapping("/admins/suggestions")
|
||||||
public Collection<TwitchUserProfile> listAdminSuggestions(@PathVariable("broadcaster") String broadcaster,
|
public Collection<TwitchUserProfile> listAdminSuggestions(@PathVariable("broadcaster") String broadcaster,
|
||||||
OAuth2AuthenticationToken oauthToken,
|
OAuth2AuthenticationToken oauthToken,
|
||||||
@RegisteredOAuth2AuthorizedClient("twitch") OAuth2AuthorizedClient authorizedClient) {
|
HttpServletRequest request) {
|
||||||
String sessionUsername = OauthSessionUser.from(oauthToken).login();
|
String sessionUsername = OauthSessionUser.from(oauthToken).login();
|
||||||
authorizationService.userMatchesSessionUsernameOrThrowHttpError(broadcaster, sessionUsername);
|
authorizationService.userMatchesSessionUsernameOrThrowHttpError(broadcaster, sessionUsername);
|
||||||
LOG.debug("Listing admin suggestions for {} by {}", broadcaster, sessionUsername);
|
LOG.debug("Listing admin suggestions for {} by {}", broadcaster, sessionUsername);
|
||||||
var channel = channelDirectoryService.getOrCreateChannel(broadcaster);
|
var channel = channelDirectoryService.getOrCreateChannel(broadcaster);
|
||||||
authorizedClient = resolveAuthorizedClient(oauthToken, authorizedClient);
|
OAuth2AuthorizedClient authorizedClient = resolveAuthorizedClient(oauthToken, null, request);
|
||||||
|
|
||||||
if (authorizedClient == null) {
|
if (authorizedClient == null) {
|
||||||
LOG.warn("No authorized Twitch client found for {} while fetching admin suggestions for {}", sessionUsername, broadcaster);
|
LOG.warn("No authorized Twitch client found for {} while fetching admin suggestions for {}", sessionUsername, broadcaster);
|
||||||
@@ -281,13 +286,21 @@ public class ChannelApiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private OAuth2AuthorizedClient resolveAuthorizedClient(OAuth2AuthenticationToken oauthToken,
|
private OAuth2AuthorizedClient resolveAuthorizedClient(OAuth2AuthenticationToken oauthToken,
|
||||||
OAuth2AuthorizedClient authorizedClient) {
|
OAuth2AuthorizedClient authorizedClient,
|
||||||
|
HttpServletRequest request) {
|
||||||
if (authorizedClient != null) {
|
if (authorizedClient != null) {
|
||||||
return authorizedClient;
|
return authorizedClient;
|
||||||
}
|
}
|
||||||
if (oauthToken == null) {
|
if (oauthToken == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
OAuth2AuthorizedClient sessionClient = authorizedClientRepository.loadAuthorizedClient(
|
||||||
|
oauthToken.getAuthorizedClientRegistrationId(),
|
||||||
|
oauthToken,
|
||||||
|
request);
|
||||||
|
if (sessionClient != null) {
|
||||||
|
return sessionClient;
|
||||||
|
}
|
||||||
return authorizedClientService.loadAuthorizedClient(oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName());
|
return authorizedClientService.loadAuthorizedClient(oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ server:
|
|||||||
key-store: ${SSL_KEYSTORE_PATH:classpath:keystore.p12}
|
key-store: ${SSL_KEYSTORE_PATH:classpath:keystore.p12}
|
||||||
key-store-password: ${SSL_KEYSTORE_PASSWORD:changeit}
|
key-store-password: ${SSL_KEYSTORE_PASSWORD:changeit}
|
||||||
key-store-type: ${SSL_KEYSTORE_TYPE:PKCS12}
|
key-store-type: ${SSL_KEYSTORE_TYPE:PKCS12}
|
||||||
|
error:
|
||||||
|
include-message: never
|
||||||
|
include-stacktrace: never
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
config:
|
config:
|
||||||
|
|||||||
Reference in New Issue
Block a user