-
Notifications
You must be signed in to change notification settings - Fork 6k
Add JDBC implementation of OAuth2AuthorizedClientService #7855
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add JDBC implementation of OAuth2AuthorizedClientService #7855
Conversation
.../main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java
Show resolved
Hide resolved
authorizedClientData.clientRegistrationId, | ||
authorizedClientData.principalName, | ||
authorizedClientData.accessTokenType, | ||
authorizedClientData.accessTokenValue, // TODO Encrypt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwinch Do you have a preference/recommendation on the approach for encrypting/securing the access token and refresh token?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should externalize the mapping (reading/writing) of the data into one of Spring's strategies. Then user's can inject their own strategies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which specific strategy were you thinking? Converter
or you had another one in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot help to think that we might want to think about discriminating based on other attributes...i.e. the scopes associated with the token. Perhaps this is something we do later, but we should if nothing else make sure there is a ticket for it
* @param clientRegistrationRepository the repository of client registrations | ||
*/ | ||
public JdbcOAuth2AuthorizedClientService( | ||
DataSource dataSource, ClientRegistrationRepository clientRegistrationRepository) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good to allow injecting JdbcOperations
instead.
import org.springframework.security.oauth2.core.OAuth2RefreshToken; | ||
import org.springframework.stereotype.Repository; | ||
import org.springframework.transaction.annotation.EnableTransactionManagement; | ||
import org.springframework.transaction.annotation.Transactional; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use @Transactional
in Spring Security because that means users must have declarative transactions enabled and we have no way to enforce it (nor should we need to). JdbcTemplate
will handle the transactions for you.
.../main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java
Show resolved
Hide resolved
authorizedClientData.clientRegistrationId, | ||
authorizedClientData.principalName, | ||
authorizedClientData.accessTokenType, | ||
authorizedClientData.accessTokenValue, // TODO Encrypt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should externalize the mapping (reading/writing) of the data into one of Spring's strategies. Then user's can inject their own strategies.
@@ -0,0 +1,13 @@ | |||
CREATE TABLE oauth2_authorized_client ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we publish this on the classpath similar to how we do with the users.ddl? We could also update the Javadoc of the service to mention the classpath resource.
The only "finder" operation we have in
so this wouldn't work for searching based on It might make sense to log a ticket with the details you had in mind so we can address this separately? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I provided feedback inline.
* @see JdbcOperations | ||
* @see RowMapper | ||
*/ | ||
@Repository |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to @Transactional
we don't use @Repository
on implementations within Spring Security because it requires specific Spring configuration that we cannot guarantee.
/** | ||
* The data (entity) representation of an {@link OAuth2AuthorizedClient}. | ||
*/ | ||
public static class OAuth2AuthorizedClientData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the need for this type vs allowing the user to inject a RowMapper
that produces an OAuth2AuthorizedClient
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rwinch The OAuth2AuthorizedClientData
is an entity representation that IMO is simpler to deal with compared to returning an OAuth2AuthorizedClient
. The logic in OAuth2AuthorizedClientDataConverter
is a bit involved and would require a bit too much work for the user to implement. Instead they can simply reuse OAuth2AuthorizedClientDataConverter
and implement a RowMapper<OAuth2AuthorizedClientData>
that performs some data translation/conversion (if necessary), eg. decoding/decrypting the access/refresh token, access token scope storage format
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at the test JdbcOAuth2AuthorizedClientServiceTests.tableDefinitionWhenCustomThenAbleToOverride
. It demonstrates what the user would need to implement if they preferred to change the table definition from the standard definition it expects. The RowMapper<OAuth2AuthorizedClientData>
needs to be implemented but both Converter
's are reused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like having this additional type. If OAuth2AuthorizedClient
is difficult to use, we should just make that easier vs creating another type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed OAuth2AuthorizedClientData
List<OAuth2AuthorizedClientData> result = this.jdbcOperations.query( | ||
LOAD_AUTHORIZED_CLIENT_SQL, pss, this.authorizedClientDataRowMapper); | ||
|
||
return !result.isEmpty() ? (T) this.authorizedClientDataConverter.convert(result.get(0)) : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are only looking for 0 or 1 results, it seems like queryForObject might make more sense here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used queryForObject()
originally but it throws an IncorrectResultSizeDataAccessException
if not found, which is not ideal. With List query()
, I can avoid the try catch
and exception being thrown.
/** | ||
* The data (entity) representation of an {@link OAuth2AuthorizedClient}. | ||
*/ | ||
public static class OAuth2AuthorizedClientData { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like having this additional type. If OAuth2AuthorizedClient
is difficult to use, we should just make that easier vs creating another type.
@@ -0,0 +1,13 @@ | |||
CREATE TABLE oauth2_authorized_client ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not be shy about making values larger rather than shorter. Storage space is typically not an issue these days. I'd consider making the token value and the refresh token value's both blobs since we should not ever need to search by them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made both token columns blob
and increased the size for a couple of other columns too. This is now merged.
Merged via de8b558 |
Fixes gh-7655