Skip to content

Instantly share code, notes, and snippets.

@thiagofa
Last active October 15, 2022 16:58
Show Gist options
  • Save thiagofa/daca4f4790b5b18fed800b83747127ca to your computer and use it in GitHub Desktop.
Save thiagofa/daca4f4790b5b18fed800b83747127ca to your computer and use it in GitHub Desktop.
Suporte a PKCE com Authorization Code no Spring Security OAuth2
// Parte da configuração do Authorization Server, para adicionar o TokenGranter customizado
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenGranter(tokenGranter(endpoints));
}
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
var pkceAuthorizationCodeTokenGranter = new PkceAuthorizationCodeTokenGranter(endpoints.getTokenServices(),
endpoints.getAuthorizationCodeServices(), endpoints.getClientDetailsService(),
endpoints.getOAuth2RequestFactory());
var granters = Arrays.asList(
pkceAuthorizationCodeTokenGranter, endpoints.getTokenGranter());
return new CompositeTokenGranter(granters);
}
}
// Solução baseada em: https://github.com/spring-projects/spring-security-oauth/pull/675/files
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
public class PkceAuthorizationCodeTokenGranter extends AuthorizationCodeTokenGranter {
public PkceAuthorizationCodeTokenGranter(AuthorizationServerTokenServices tokenServices,
AuthorizationCodeServices authorizationCodeServices, ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory) {
super(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory);
}
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
OAuth2Authentication authentication = super.getOAuth2Authentication(client, tokenRequest);
OAuth2Request request = authentication.getOAuth2Request();
String codeChallenge = request.getRequestParameters().get("code_challenge");
String codeChallengeMethod = request.getRequestParameters().get("code_challenge_method");
String codeVerifier = request.getRequestParameters().get("code_verifier");
if (codeChallenge != null || codeChallengeMethod != null) {
if (codeVerifier == null) {
throw new InvalidGrantException("Code verifier expected.");
}
if (!validateCodeVerifier(codeVerifier, codeChallenge, codeChallengeMethod)) {
throw new InvalidGrantException(codeVerifier + " does not match expected code verifier.");
}
}
return authentication;
}
private boolean validateCodeVerifier(String codeVerifier, String codeChallenge,
String codeChallengeMethod) {
String generatedCodeChallenge = null;
if ("plain".equalsIgnoreCase(codeChallengeMethod)) {
generatedCodeChallenge = codeVerifier;
} else if ("s256".equalsIgnoreCase(codeChallengeMethod)) {
generatedCodeChallenge = generateHashSha256(codeVerifier);
} else {
throw new InvalidGrantException(codeChallengeMethod + " is not a valid challenge method.");
}
return generatedCodeChallenge.equals(codeChallenge);
}
private static String generateHashSha256(String plainText) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(Utf8.encode(plainText));
return Base64.encodeBase64URLSafeString(hash);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment