Skip to content

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

Conversation

jgrandja
Copy link
Contributor

Fixes gh-7655

authorizedClientData.clientRegistrationId,
authorizedClientData.principalName,
authorizedClientData.accessTokenType,
authorizedClientData.accessTokenValue, // TODO Encrypt
Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Contributor Author

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?

@jgrandja jgrandja requested a review from rwinch January 22, 2020 14:59
@jgrandja jgrandja added in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement labels Jan 22, 2020
@jgrandja jgrandja added this to the 5.3.0.RC1 milestone Jan 22, 2020
Copy link
Member

@rwinch rwinch left a 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) {
Copy link
Member

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;
Copy link
Member

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.

authorizedClientData.clientRegistrationId,
authorizedClientData.principalName,
authorizedClientData.accessTokenType,
authorizedClientData.accessTokenValue, // TODO Encrypt
Copy link
Member

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 (
Copy link
Member

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.

@jgrandja
Copy link
Contributor Author

jgrandja commented Jan 23, 2020

@rwinch

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

The only "finder" operation we have in OAuth2AuthorizedClientService is:

loadAuthorizedClient(String clientRegistrationId, String principalName)

so this wouldn't work for searching based on scopes. We would either need to add another operation or likely create another API that provides various "finder" operations. Is there a specific use case that you are trying to address that has been reported by the community?

It might make sense to log a ticket with the details you had in mind so we can address this separately?

@jgrandja jgrandja self-assigned this Jan 23, 2020
@jgrandja jgrandja requested a review from rwinch January 24, 2020 21:34
Copy link
Member

@rwinch rwinch left a 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
Copy link
Member

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 {
Copy link
Member

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?

Copy link
Contributor Author

@jgrandja jgrandja Jan 29, 2020

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

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

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;
Copy link
Member

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.

Copy link
Contributor Author

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 {
Copy link
Member

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.

@eleftherias eleftherias modified the milestones: 5.3.0.RC1, 5.3.0 Feb 5, 2020
@jgrandja jgrandja requested a review from rwinch February 6, 2020 12:22
@@ -0,0 +1,13 @@
CREATE TABLE oauth2_authorized_client (
Copy link
Member

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.

Copy link
Contributor Author

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.

@jgrandja
Copy link
Contributor Author

Merged via de8b558

@jgrandja jgrandja closed this Feb 13, 2020
@jgrandja jgrandja deleted the gh-7655-jdbc-authz-client-service branch February 13, 2020 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide JDBC implementation of OAuth2AuthorizedClientService
3 participants