| 
					
				 | 
			
			
				@@ -0,0 +1,554 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Copyright 2020-2021 the original author or authors. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Licensed under the Apache License, Version 2.0 (the "License"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * you may not use this file except in compliance with the License. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * You may obtain a copy of the License at 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *      https://www.apache.org/licenses/LICENSE-2.0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * Unless required by applicable law or agreed to in writing, software 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * distributed under the License is distributed on an "AS IS" BASIS, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * See the License for the specific language governing permissions and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * limitations under the License. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+package org.springframework.security.oauth2.server.authorization; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fasterxml.jackson.core.JsonProcessingException; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import com.fasterxml.jackson.databind.ObjectMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.dao.DataRetrievalFailureException; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.jdbc.core.ArgumentPreparedStatementSetter; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.jdbc.core.JdbcOperations; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.jdbc.core.PreparedStatementSetter; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.jdbc.core.RowMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.jdbc.core.SqlParameterValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.jdbc.support.lob.DefaultLobHandler; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.jdbc.support.lob.LobCreator; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.jdbc.support.lob.LobHandler; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.lang.Nullable; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.core.AbstractOAuth2Token; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.core.AuthorizationGrantType; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.core.OAuth2AccessToken; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.core.OAuth2RefreshToken; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.core.OAuth2RefreshToken2; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.core.OAuth2TokenType; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.core.oidc.OidcIdToken; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.util.Assert; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.util.CollectionUtils; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import org.springframework.util.StringUtils; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.nio.charset.StandardCharsets; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.sql.PreparedStatement; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.sql.ResultSet; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.sql.SQLException; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.sql.Timestamp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.sql.Types; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.time.Instant; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.ArrayList; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Collections; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.List; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Map; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.Set; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import java.util.function.Function; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * A JDBC implementation of an {@link OAuth2AuthorizationService} that uses a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <p> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * {@link JdbcOperations} for {@link OAuth2Authorization} persistence. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <p> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>NOTE:</b> This {@code OAuth2AuthorizationService} depends on the table definition 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * described in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql" and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * therefore MUST be defined in the database schema. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @author Ovidiu Popa 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @see OAuth2AuthorizationService 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @see OAuth2Authorization 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @see JdbcOperations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @see RowMapper 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * @since 0.1.2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+public final class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationService { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// @formatter:off 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String COLUMN_NAMES = "id, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "registered_client_id, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "principal_name, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "authorization_grant_type, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "attributes, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "state, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "authorization_code_value, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "authorization_code_issued_at, " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "authorization_code_expires_at," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "authorization_code_metadata," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "access_token_value," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "access_token_issued_at," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "access_token_expires_at," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "access_token_metadata," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "access_token_type," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "access_token_scopes," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "oidc_id_token_value," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "oidc_id_token_issued_at," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "oidc_id_token_expires_at," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "oidc_id_token_metadata," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "refresh_token_value," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "refresh_token_issued_at," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "refresh_token_expires_at," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ "refresh_token_metadata"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// @formatter:on 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String TABLE_NAME = "oauth2_authorization"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String PK_FILTER = "id = ?"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String UNKNOWN_TOKEN_TYPE_FILTER = "state = ? OR authorization_code_value = ? OR " + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			"access_token_value = ? OR " + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			"refresh_token_value = ?"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String STATE_FILTER = "state = ?"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String AUTHORIZATION_CODE_FILTER = "authorization_code_value = ?"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String ACCESS_TOKEN_FILTER = "access_token_value = ?"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String REFRESH_TOKEN_FILTER = "refresh_token_value = ?"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// @formatter:off 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String LOAD_AUTHORIZATION_SQL = "SELECT " + COLUMN_NAMES 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " FROM " + TABLE_NAME 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " WHERE "; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// @formatter:on 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// @formatter:off 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String SAVE_AUTHORIZATION_SQL = "INSERT INTO " + TABLE_NAME 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " (" + COLUMN_NAMES + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?, ?, ?,?, ?, ?, ?, ?, ?, ?, ?)"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// @formatter:on 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// @formatter:off 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String UPDATE_AUTHORIZATION_SQL = "UPDATE " + TABLE_NAME 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " SET registered_client_id = ?, principal_name = ?, authorization_grant_type = ?, attributes = ?, state = ?," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " authorization_code_value = ?, authorization_code_issued_at = ?, authorization_code_expires_at = ?, authorization_code_metadata = ?," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " access_token_value = ?, access_token_issued_at = ?, access_token_expires_at = ?, access_token_metadata = ?, access_token_type = ?, access_token_scopes = ?," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " oidc_id_token_value = ?, oidc_id_token_issued_at = ?, oidc_id_token_expires_at = ?, oidc_id_token_metadata = ?," 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " refresh_token_value = ?, refresh_token_issued_at = ?, refresh_token_expires_at = ?, refresh_token_metadata = ?" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			+ " WHERE " + PK_FILTER; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// @formatter:on 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final String REMOVE_AUTHORIZATION_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + PK_FILTER; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private final JdbcOperations jdbcOperations; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private final LobHandler lobHandler; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private RowMapper<OAuth2Authorization> authorizationRowMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private Function<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param jdbcOperations             the JDBC operations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param registeredClientRepository the registered client repository 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			RegisteredClientRepository registeredClientRepository) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this(jdbcOperations, registeredClientRepository, new DefaultLobHandler()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param jdbcOperations             the JDBC operations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param registeredClientRepository the registered client repository 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param lobHandler                 the handler for large binary fields and large text fields 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			RegisteredClientRepository registeredClientRepository, LobHandler lobHandler) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this(jdbcOperations, registeredClientRepository, lobHandler, new ObjectMapper()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param jdbcOperations             the JDBC operations 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param registeredClientRepository the registered client repository 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param lobHandler                 the handler for large binary fields and large text fields 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param objectMapper               the object mapper 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			RegisteredClientRepository registeredClientRepository, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			LobHandler lobHandler, ObjectMapper objectMapper) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.notNull(jdbcOperations, "jdbcOperations cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.notNull(lobHandler, "lobHandler cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.notNull(objectMapper, "objectMapper cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.jdbcOperations = jdbcOperations; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.lobHandler = lobHandler; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		OAuth2AuthorizationRowMapper authorizationRowMapper = new OAuth2AuthorizationRowMapper(registeredClientRepository, objectMapper); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		authorizationRowMapper.setLobHandler(lobHandler); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.authorizationRowMapper = authorizationRowMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.authorizationParametersMapper = new OAuth2AuthorizationParametersMapper(objectMapper); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public void save(OAuth2Authorization authorization) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.notNull(authorization, "authorization cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		OAuth2Authorization existingAuthorization = findById(authorization.getId()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if (existingAuthorization == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			insertAuthorization(authorization); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			updateAuthorization(authorization); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private void updateAuthorization(OAuth2Authorization authorization) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		List<SqlParameterValue> parameters = this.authorizationParametersMapper.apply(authorization); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		SqlParameterValue id = parameters.remove(0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		parameters.add(id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		try (LobCreator lobCreator = this.lobHandler.getLobCreator()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					parameters.toArray()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			this.jdbcOperations.update(UPDATE_AUTHORIZATION_SQL, pss); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private void insertAuthorization(OAuth2Authorization authorization) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		List<SqlParameterValue> parameters = this.authorizationParametersMapper.apply(authorization); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		try (LobCreator lobCreator = this.lobHandler.getLobCreator()) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					parameters.toArray()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			this.jdbcOperations.update(SAVE_AUTHORIZATION_SQL, pss); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public void remove(OAuth2Authorization authorization) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.notNull(authorization, "authorization cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		SqlParameterValue[] parameters = new SqlParameterValue[]{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				new SqlParameterValue(Types.VARCHAR, authorization.getId()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.jdbcOperations.update(REMOVE_AUTHORIZATION_SQL, pss); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@Nullable 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public OAuth2Authorization findById(String id) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.hasText(id, "id cannot be empty"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		List<SqlParameterValue> parameters = new ArrayList<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		parameters.add(new SqlParameterValue(Types.VARCHAR, id)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return findBy(PK_FILTER, parameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@Nullable 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	@Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.hasText(token, "token cannot be empty"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		List<SqlParameterValue> parameters = new ArrayList<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if (tokenType == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.VARCHAR, token)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return findBy(UNKNOWN_TOKEN_TYPE_FILTER, parameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.VARCHAR, token)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return findBy(STATE_FILTER, parameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return findBy(AUTHORIZATION_CODE_FILTER, parameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return findBy(ACCESS_TOKEN_FILTER, parameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return findBy(REFRESH_TOKEN_FILTER, parameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private OAuth2Authorization findBy(String filter, List<SqlParameterValue> parameters) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		List<OAuth2Authorization> result = this.jdbcOperations.query(LOAD_AUTHORIZATION_SQL + filter, pss, this.authorizationRowMapper); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		return !result.isEmpty() ? result.get(0) : null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * Sets the {@link RowMapper} used for mapping the current row in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * {@code java.sql.ResultSet} to {@link OAuth2Authorization}. The default is 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * {@link OAuth2AuthorizationRowMapper}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param authorizationRowMapper the {@link RowMapper} used for mapping the current 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 *                               row in {@code ResultSet} to {@link OAuth2Authorization} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public void setAuthorizationRowMapper(RowMapper<OAuth2Authorization> authorizationRowMapper) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.notNull(authorizationRowMapper, "authorizationRowMapper cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.authorizationRowMapper = authorizationRowMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * Sets the {@code Function} used for mapping {@link OAuth2Authorization} to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * a {@code List} of {@link SqlParameterValue}. The default is 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * {@link OAuth2AuthorizationParametersMapper}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * @param authorizationParametersMapper the {@code Function} used for mapping 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 *                                      {@link OAuth2Authorization} to a {@code List} of {@link SqlParameterValue} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public void setAuthorizationParametersMapper( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Function<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Assert.notNull(authorizationParametersMapper, "authorizationParametersMapper cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		this.authorizationParametersMapper = authorizationParametersMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * The default {@link RowMapper} that maps the current row in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * {@code java.sql.ResultSet} to {@link OAuth2Authorization}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public static class OAuth2AuthorizationRowMapper implements RowMapper<OAuth2Authorization> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		private final RegisteredClientRepository registeredClientRepository; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		private final ObjectMapper objectMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		private LobHandler lobHandler = new DefaultLobHandler(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		public OAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository, ObjectMapper objectMapper) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Assert.notNull(objectMapper, "objectMapper cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			this.registeredClientRepository = registeredClientRepository; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			this.objectMapper = objectMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		@Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		@SuppressWarnings("unchecked") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		public OAuth2Authorization mapRow(ResultSet rs, int rowNum) throws SQLException { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String registeredClientId = rs.getString("registered_client_id"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				RegisteredClient registeredClient = this.registeredClientRepository 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						.findById(registeredClientId); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (registeredClient == null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					throw new DataRetrievalFailureException( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							"The RegisteredClient with id '" + registeredClientId + "' it was not found in the RegisteredClientRepository."); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				OAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String id = rs.getString("id"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String principalName = rs.getString("principal_name"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String authorizationGrantType = rs.getString("authorization_grant_type"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				Map<String, Object> attributes = this.objectMapper.readValue(rs.getString("attributes"), Map.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				builder.id(id) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						.principalName(principalName) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						.authorizationGrantType(new AuthorizationGrantType(authorizationGrantType)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						.attributes(attrs -> attrs.putAll(attributes)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String state = rs.getString("state"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (StringUtils.hasText(state)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					builder.attribute(OAuth2ParameterNames.STATE, state); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String tokenValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				Instant tokenIssuedAt; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				Instant tokenExpiresAt; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				byte[] authorizationCodeValue = this.lobHandler.getBlobAsBytes(rs, "authorization_code_value"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (authorizationCodeValue != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenValue = new String(authorizationCodeValue, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							StandardCharsets.UTF_8); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenIssuedAt = rs.getTimestamp("authorization_code_issued_at").toInstant(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenExpiresAt = rs.getTimestamp("authorization_code_expires_at").toInstant(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					Map<String, Object> authorizationCodeMetadata = this.objectMapper.readValue(rs.getString("authorization_code_metadata"), Map.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							tokenValue, tokenIssuedAt, tokenExpiresAt); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					builder 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							.token(authorizationCode, (metadata) -> metadata.putAll(authorizationCodeMetadata)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				byte[] accessTokenValue = this.lobHandler.getBlobAsBytes(rs, "access_token_value"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (accessTokenValue != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenValue = new String(accessTokenValue, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							StandardCharsets.UTF_8); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenIssuedAt = rs.getTimestamp("access_token_issued_at").toInstant(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenExpiresAt = rs.getTimestamp("access_token_expires_at").toInstant(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					Map<String, Object> accessTokenMetadata = this.objectMapper.readValue(rs.getString("access_token_metadata"), Map.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					OAuth2AccessToken.TokenType tokenType = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString("access_token_type"))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						tokenType = OAuth2AccessToken.TokenType.BEARER; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					Set<String> scopes = Collections.emptySet(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					String accessTokenScopes = rs.getString("access_token_scopes"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					if (accessTokenScopes != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						scopes = StringUtils.commaDelimitedListToSet(accessTokenScopes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, tokenIssuedAt, tokenExpiresAt, scopes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					builder 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							.token(accessToken, (metadata) -> metadata.putAll(accessTokenMetadata)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				byte[] oidcIdTokenValue = this.lobHandler.getBlobAsBytes(rs, "oidc_id_token_value"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (oidcIdTokenValue != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenValue = new String(oidcIdTokenValue, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							StandardCharsets.UTF_8); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenIssuedAt = rs.getTimestamp("oidc_id_token_issued_at").toInstant(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenExpiresAt = rs.getTimestamp("oidc_id_token_expires_at").toInstant(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					Map<String, Object> oidcTokenMetadata = this.objectMapper.readValue(rs.getString("oidc_id_token_metadata"), Map.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					OidcIdToken oidcToken = new OidcIdToken( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							tokenValue, tokenIssuedAt, tokenExpiresAt, (Map<String, Object>) oidcTokenMetadata.get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					builder 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							.token(oidcToken, (metadata) -> metadata.putAll(oidcTokenMetadata)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				byte[] refreshTokenValue = this.lobHandler.getBlobAsBytes(rs, "refresh_token_value"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (refreshTokenValue != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenValue = new String(refreshTokenValue, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							StandardCharsets.UTF_8); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenIssuedAt = rs.getTimestamp("refresh_token_issued_at").toInstant(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenExpiresAt = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					Timestamp refreshTokenExpiresAt = rs.getTimestamp("refresh_token_expires_at"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					if (refreshTokenExpiresAt != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						tokenExpiresAt = refreshTokenExpiresAt.toInstant(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					Map<String, Object> refreshTokenMetadata = this.objectMapper.readValue(rs.getString("refresh_token_metadata"), Map.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					OAuth2RefreshToken refreshToken = new OAuth2RefreshToken2( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							tokenValue, tokenIssuedAt, tokenExpiresAt); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					builder 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+							.token(refreshToken, (metadata) -> metadata.putAll(refreshTokenMetadata)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				return builder.build(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} catch (JsonProcessingException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				throw new IllegalArgumentException(e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		public final void setLobHandler(LobHandler lobHandler) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Assert.notNull(lobHandler, "lobHandler cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			this.lobHandler = lobHandler; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	/** 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * The default {@code Function} that maps {@link OAuth2Authorization} to a 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 * {@code List} of {@link SqlParameterValue}. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	public static class OAuth2AuthorizationParametersMapper implements Function<OAuth2Authorization, List<SqlParameterValue>> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		private final ObjectMapper objectMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		public OAuth2AuthorizationParametersMapper(ObjectMapper objectMapper) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Assert.notNull(objectMapper, "objectMapper cannot be null"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			this.objectMapper = objectMapper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		@Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		public List<SqlParameterValue> apply(OAuth2Authorization authorization) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			try { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				List<SqlParameterValue> parameters = new ArrayList<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getId())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getRegisteredClientId())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getPrincipalName())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getAuthorizationGrantType().getValue())); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String attributes = this.objectMapper.writeValueAsString(authorization.getAttributes()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.add(new SqlParameterValue(Types.VARCHAR, attributes)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String state = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String authorizationState = authorization.getAttribute(OAuth2ParameterNames.STATE); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (StringUtils.hasText(authorizationState)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					state = authorizationState; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.add(new SqlParameterValue(Types.VARCHAR, state)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						authorization.getToken(OAuth2AuthorizationCode.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				List<SqlParameterValue> authorizationCodeSqlParameters = toSqlParameterList(authorizationCode); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.addAll(authorizationCodeSqlParameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				OAuth2Authorization.Token<OAuth2AccessToken> accessToken = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						authorization.getToken(OAuth2AccessToken.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				List<SqlParameterValue> accessTokenSqlParameters = toSqlParameterList(accessToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.addAll(accessTokenSqlParameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String accessTokenType = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				String accessTokenScopes = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (accessToken != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					accessTokenType = accessToken.getToken().getTokenType().getValue(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					if (!CollectionUtils.isEmpty(accessToken.getToken().getScopes())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						accessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(), ","); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenType)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenScopes)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				OAuth2Authorization.Token<OidcIdToken> oidcIdToken = authorization.getToken(OidcIdToken.class); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				List<SqlParameterValue> oidcTokenSqlParameters = toSqlParameterList(oidcIdToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.addAll(oidcTokenSqlParameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getRefreshToken(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				List<SqlParameterValue> refreshTokenSqlParameters = toSqlParameterList(refreshToken); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				parameters.addAll(refreshTokenSqlParameters); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				return parameters; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} catch (JsonProcessingException e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				throw new IllegalArgumentException(e.getMessage(), e); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		private <T extends AbstractOAuth2Token> List<SqlParameterValue> toSqlParameterList(OAuth2Authorization.Token<T> token) throws JsonProcessingException { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			List<SqlParameterValue> parameters = new ArrayList<>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			byte[] tokenValue = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Timestamp tokenIssuedAt = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Timestamp tokenExpiresAt = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			String codeMetadata = null; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if (token != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				tokenValue = token.getToken().getTokenValue().getBytes(StandardCharsets.UTF_8); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (token.getToken().getIssuedAt() != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenIssuedAt = Timestamp.from(token.getToken().getIssuedAt()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (token.getToken().getExpiresAt() != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					tokenExpiresAt = Timestamp.from(token.getToken().getExpiresAt()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				codeMetadata = this.objectMapper.writeValueAsString(token.getMetadata()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.BLOB, tokenValue)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.TIMESTAMP, tokenIssuedAt)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.TIMESTAMP, tokenExpiresAt)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			parameters.add(new SqlParameterValue(Types.VARCHAR, codeMetadata)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return parameters; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		protected final LobCreator lobCreator; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		private LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			super(args); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			this.lobCreator = lobCreator; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		@Override 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			if (argValue instanceof SqlParameterValue) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				SqlParameterValue paramValue = (SqlParameterValue) argValue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if (paramValue.getSqlType() == Types.BLOB) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					if (paramValue.getValue() != null) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+						Assert.isInstanceOf(byte[].class, paramValue.getValue(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+								"Value of blob parameter must be byte[]"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					byte[] valueBytes = (byte[]) paramValue.getValue(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					this.lobCreator.setBlobAsBytes(ps, parameterPosition, valueBytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+					return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			super.doSetValue(ps, parameterPosition, argValue); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |