diff --git a/oauth2_http/java/com/google/auth/oauth2/DefaultPKCEProvider.java b/oauth2_http/java/com/google/auth/oauth2/DefaultPKCEProvider.java index c114383ff..d4671dbe2 100644 --- a/oauth2_http/java/com/google/auth/oauth2/DefaultPKCEProvider.java +++ b/oauth2_http/java/com/google/auth/oauth2/DefaultPKCEProvider.java @@ -36,40 +36,16 @@ import java.security.SecureRandom; import java.util.Base64; +/** + * Implements PKCE using only the Java standard library. See https://www.rfc-editor.org/rfc/rfc7636. + * + *

https://developers.google.com/identity/protocols/oauth2/native-app#step1-code-verifier. + */ public class DefaultPKCEProvider implements PKCEProvider { private String codeVerifier; private CodeChallenge codeChallenge; private static final int MAX_CODE_VERIFIER_LENGTH = 127; - private class CodeChallenge { - private String codeChallenge; - private String codeChallengeMethod; - - CodeChallenge(String codeVerifier) { - try { - byte[] bytes = codeVerifier.getBytes(); - MessageDigest md = MessageDigest.getInstance("SHA-256"); - md.update(bytes); - - byte[] digest = md.digest(); - - this.codeChallenge = Base64.getUrlEncoder().encodeToString(digest); - this.codeChallengeMethod = "S256"; - } catch (NoSuchAlgorithmException e) { - this.codeChallenge = codeVerifier; - this.codeChallengeMethod = "plain"; - } - } - - public String getCodeChallenge() { - return codeChallenge; - } - - public String getCodeChallengeMethod() { - return codeChallengeMethod; - } - } - private String createCodeVerifier() { SecureRandom sr = new SecureRandom(); byte[] code = new byte[MAX_CODE_VERIFIER_LENGTH]; @@ -100,4 +76,34 @@ public String getCodeChallenge() { public String getCodeChallengeMethod() { return codeChallenge.getCodeChallengeMethod(); } + + /** Class representing the Code Challenge derived from a Code Verifier string. */ + private class CodeChallenge { + private String codeChallenge; + private String codeChallengeMethod; + + CodeChallenge(String codeVerifier) { + try { + byte[] bytes = codeVerifier.getBytes(); + MessageDigest md = MessageDigest.getInstance("SHA-256"); + md.update(bytes); + + byte[] digest = md.digest(); + + this.codeChallenge = Base64.getUrlEncoder().encodeToString(digest); + this.codeChallengeMethod = "S256"; + } catch (NoSuchAlgorithmException e) { + this.codeChallenge = codeVerifier; + this.codeChallengeMethod = "plain"; + } + } + + public String getCodeChallenge() { + return codeChallenge; + } + + public String getCodeChallengeMethod() { + return codeChallengeMethod; + } + } } diff --git a/oauth2_http/javatests/com/google/auth/oauth2/UserAuthorizerTest.java b/oauth2_http/javatests/com/google/auth/oauth2/UserAuthorizerTest.java index 60dd8f464..7f444330f 100644 --- a/oauth2_http/javatests/com/google/auth/oauth2/UserAuthorizerTest.java +++ b/oauth2_http/javatests/com/google/auth/oauth2/UserAuthorizerTest.java @@ -477,7 +477,7 @@ public void revokeAuthorization_revokesAndClears() throws IOException { } @Test(expected = IllegalArgumentException.class) - public void illegalPKCEProvider() { + public void nullCodeVerifierPKCEProvider() { PKCEProvider pkce = new PKCEProvider() { @Override @@ -487,7 +487,36 @@ public String getCodeVerifier() { @Override public String getCodeChallengeMethod() { - return null; + return "dummy string"; + } + + @Override + public String getCodeChallenge() { + return "dummy string"; + } + }; + + UserAuthorizer authorizer = + UserAuthorizer.newBuilder() + .setClientId(CLIENT_ID) + .setScopes(DUMMY_SCOPES) + .setTokenStore(new MemoryTokensStorage()) + .setPKCEProvider(pkce) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void nullCodeChallengePKCEProvider() { + PKCEProvider pkce = + new PKCEProvider() { + @Override + public String getCodeVerifier() { + return "dummy string"; + } + + @Override + public String getCodeChallengeMethod() { + return "dummy string"; } @Override @@ -504,4 +533,33 @@ public String getCodeChallenge() { .setPKCEProvider(pkce) .build(); } + + @Test(expected = IllegalArgumentException.class) + public void nullCodeChallengeMethodPKCEProvider() { + PKCEProvider pkce = + new PKCEProvider() { + @Override + public String getCodeVerifier() { + return "dummy string"; + } + + @Override + public String getCodeChallengeMethod() { + return null; + } + + @Override + public String getCodeChallenge() { + return "dummy string"; + } + }; + + UserAuthorizer authorizer = + UserAuthorizer.newBuilder() + .setClientId(CLIENT_ID) + .setScopes(DUMMY_SCOPES) + .setTokenStore(new MemoryTokensStorage()) + .setPKCEProvider(pkce) + .build(); + } }