Skip to content

Commit a783fbc

Browse files
stavshamirjgrandja
authored andcommitted
Support update when saving with JdbcOAuth2AuthorizedClientService
Before this commit, JdbcOAuth2AuthorizedClientService threw DuplicateKeyException when re-authorizing or when authorizing the same user from a different client. This commit makes JdbcOAuth2AuthorizedClientService's saveAuthorizedClient method consistent with that of InMemoryOAuth2AuthorizedClientService. Fixes gh-8425
1 parent 4d63e2f commit a783fbc

File tree

2 files changed

+57
-5
lines changed

2 files changed

+57
-5
lines changed

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.security.oauth2.client;
1717

1818
import org.springframework.dao.DataRetrievalFailureException;
19+
import org.springframework.dao.DuplicateKeyException;
1920
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
2021
import org.springframework.jdbc.core.JdbcOperations;
2122
import org.springframework.jdbc.core.PreparedStatementSetter;
@@ -52,6 +53,7 @@
5253
* and therefore MUST be defined in the database schema.
5354
*
5455
* @author Joe Grandja
56+
* @author Stav Shamir
5557
* @since 5.3
5658
* @see OAuth2AuthorizedClientService
5759
* @see OAuth2AuthorizedClient
@@ -77,6 +79,11 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient
7779
" (" + COLUMN_NAMES + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
7880
private static final String REMOVE_AUTHORIZED_CLIENT_SQL = "DELETE FROM " + TABLE_NAME +
7981
" WHERE " + PK_FILTER;
82+
private static final String UPDATE_AUTHORIZED_CLIENT_SQL = "UPDATE " + TABLE_NAME +
83+
" SET access_token_type = ?, access_token_value = ?, access_token_issued_at = ?," +
84+
" access_token_expires_at = ?, access_token_scopes = ?," +
85+
" refresh_token_value = ?, refresh_token_issued_at = ?" +
86+
" WHERE " + PK_FILTER;
8087
protected final JdbcOperations jdbcOperations;
8188
protected RowMapper<OAuth2AuthorizedClient> authorizedClientRowMapper;
8289
protected Function<OAuth2AuthorizedClientHolder, List<SqlParameterValue>> authorizedClientParametersMapper;
@@ -120,6 +127,35 @@ public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authen
120127
Assert.notNull(authorizedClient, "authorizedClient cannot be null");
121128
Assert.notNull(principal, "principal cannot be null");
122129

130+
boolean existsAuthorizedClient = null != this.loadAuthorizedClient(
131+
authorizedClient.getClientRegistration().getRegistrationId(), principal.getName());
132+
133+
if (existsAuthorizedClient) {
134+
updateAuthorizedClient(authorizedClient, principal);
135+
} else {
136+
try {
137+
insertAuthorizedClient(authorizedClient, principal);
138+
} catch (DuplicateKeyException e) {
139+
updateAuthorizedClient(authorizedClient, principal);
140+
}
141+
}
142+
}
143+
144+
private void updateAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
145+
List<SqlParameterValue> parameters = this.authorizedClientParametersMapper.apply(
146+
new OAuth2AuthorizedClientHolder(authorizedClient, principal));
147+
148+
SqlParameterValue clientRegistrationIdParameter = parameters.remove(0);
149+
SqlParameterValue principalNameParameter = parameters.remove(0);
150+
parameters.add(clientRegistrationIdParameter);
151+
parameters.add(principalNameParameter);
152+
153+
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());
154+
155+
this.jdbcOperations.update(UPDATE_AUTHORIZED_CLIENT_SQL, pss);
156+
}
157+
158+
private void insertAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
123159
List<SqlParameterValue> parameters = this.authorizedClientParametersMapper.apply(
124160
new OAuth2AuthorizedClientHolder(authorizedClient, principal));
125161
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray());

oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientServiceTests.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import org.junit.Before;
2020
import org.junit.Test;
2121
import org.springframework.dao.DataRetrievalFailureException;
22-
import org.springframework.dao.DuplicateKeyException;
2322
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
2423
import org.springframework.jdbc.core.JdbcOperations;
2524
import org.springframework.jdbc.core.JdbcTemplate;
@@ -64,6 +63,7 @@
6463
* Tests for {@link JdbcOAuth2AuthorizedClientService}.
6564
*
6665
* @author Joe Grandja
66+
* @author Stav Shamir
6767
*/
6868
public class JdbcOAuth2AuthorizedClientServiceTests {
6969
private static final String OAUTH2_CLIENT_SCHEMA_SQL_RESOURCE = "org/springframework/security/oauth2/client/oauth2-client-schema.sql";
@@ -236,14 +236,30 @@ public void saveAuthorizedClientWhenSaveThenLoadReturnsSaved() {
236236
}
237237

238238
@Test
239-
public void saveAuthorizedClientWhenSaveDuplicateThenThrowDuplicateKeyException() {
239+
public void saveAuthorizedClientWhenSaveClientWithExistingPrimaryKeyThenUpdate() {
240+
// Given a saved authorized client
240241
Authentication principal = createPrincipal();
241242
OAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);
242-
243243
this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal);
244244

245-
assertThatThrownBy(() -> this.authorizedClientService.saveAuthorizedClient(authorizedClient, principal))
246-
.isInstanceOf(DuplicateKeyException.class);
245+
// When a client with the same principal and registration id is saved
246+
OAuth2AuthorizedClient updatedClient = createAuthorizedClient(principal, this.clientRegistration);
247+
this.authorizedClientService.saveAuthorizedClient(updatedClient, principal);
248+
249+
// Then the saved client is updated
250+
OAuth2AuthorizedClient savedClient = this.authorizedClientService.loadAuthorizedClient(
251+
this.clientRegistration.getRegistrationId(), principal.getName());
252+
253+
assertThat(savedClient).isNotNull();
254+
assertThat(savedClient.getClientRegistration()).isEqualTo(updatedClient.getClientRegistration());
255+
assertThat(savedClient.getPrincipalName()).isEqualTo(updatedClient.getPrincipalName());
256+
assertThat(savedClient.getAccessToken().getTokenType()).isEqualTo(updatedClient.getAccessToken().getTokenType());
257+
assertThat(savedClient.getAccessToken().getTokenValue()).isEqualTo(updatedClient.getAccessToken().getTokenValue());
258+
assertThat(savedClient.getAccessToken().getIssuedAt()).isEqualTo(updatedClient.getAccessToken().getIssuedAt());
259+
assertThat(savedClient.getAccessToken().getExpiresAt()).isEqualTo(updatedClient.getAccessToken().getExpiresAt());
260+
assertThat(savedClient.getAccessToken().getScopes()).isEqualTo(updatedClient.getAccessToken().getScopes());
261+
assertThat(savedClient.getRefreshToken().getTokenValue()).isEqualTo(updatedClient.getRefreshToken().getTokenValue());
262+
assertThat(savedClient.getRefreshToken().getIssuedAt()).isEqualTo(updatedClient.getRefreshToken().getIssuedAt());
247263
}
248264

249265
@Test

0 commit comments

Comments
 (0)