diff --git a/dist/OAuth2.gs b/dist/OAuth2.gs
index adf3bb84..ce97757c 100644
--- a/dist/OAuth2.gs
+++ b/dist/OAuth2.gs
@@ -759,7 +759,6 @@ Service_.prototype.ensureExpiresAtSet_ = function(token) {
 Service_.prototype.refresh = function() {
   validate_({
     'Client ID': this.clientId_,
-    'Client Secret': this.clientSecret_,
     'Token URL': this.tokenUrl_
   });
 
diff --git a/samples/Twitter.gs b/samples/Twitter.gs
index d149882d..3c3ed32f 100644
--- a/samples/Twitter.gs
+++ b/samples/Twitter.gs
@@ -31,21 +31,17 @@ function run() {
  */
 function reset() {
   getService_().reset();
-  PropertiesService.getUserProperties().deleteProperty('code_challenge');
-  PropertiesService.getUserProperties().deleteProperty('code_verifier');
 }
 
 /**
  * Configures the service.
  */
 function getService_() {
-  pkceChallengeVerifier();
   var userProps = PropertiesService.getUserProperties();
   return OAuth2.createService('Twitter')
   // Set the endpoint URLs.
       .setAuthorizationBaseUrl('https://twitter.com/i/oauth2/authorize')
-      .setTokenUrl(
-          'https://api.twitter.com/2/oauth2/token?code_verifier=' + userProps.getProperty('code_verifier'))
+      .setTokenUrl('https://api.twitter.com/2/oauth2/token')
 
   // Set the client ID and secret.
       .setClientId(CLIENT_ID)
@@ -61,10 +57,8 @@ function getService_() {
   // Set the scopes to request (space-separated for Twitter services).
       .setScope('users.read tweet.read offline.access')
 
-  // Add parameters in the authorization url
-      .setParam('response_type', 'code')
-      .setParam('code_challenge_method', 'S256')
-      .setParam('code_challenge', userProps.getProperty('code_challenge'))
+  // Generate code verifier parameter
+      .generateCodeVerifier()
 
       .setTokenHeaders({
         'Authorization': 'Basic ' + Utilities.base64Encode(CLIENT_ID + ':' + CLIENT_SECRET),
@@ -91,27 +85,3 @@ function authCallback(request) {
 function logRedirectUri() {
   Logger.log(OAuth2.getRedirectUri());
 }
-
-/**
- * Generates code_verifier & code_challenge for PKCE
- */
-function pkceChallengeVerifier() {
-  var userProps = PropertiesService.getUserProperties();
-  if (!userProps.getProperty('code_verifier')) {
-    var verifier = '';
-    var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
-
-    for (var i = 0; i < 128; i++) {
-      verifier += possible.charAt(Math.floor(Math.random() * possible.length));
-    }
-
-    var sha256Hash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, verifier);
-
-    var challenge = Utilities.base64Encode(sha256Hash)
-        .replace(/\+/g, '-')
-        .replace(/\//g, '_')
-        .replace(/=+$/, '');
-    userProps.setProperty('code_verifier', verifier);
-    userProps.setProperty('code_challenge', challenge);
-  }
-}
diff --git a/src/Service.js b/src/Service.js
index 68bd3e7f..e7527fe1 100644
--- a/src/Service.js
+++ b/src/Service.js
@@ -666,7 +666,6 @@ Service_.prototype.ensureExpiresAtSet_ = function(token) {
 Service_.prototype.refresh = function() {
   validate_({
     'Client ID': this.clientId_,
-    'Client Secret': this.clientSecret_,
     'Token URL': this.tokenUrl_
   });
 
diff --git a/test/test.js b/test/test.js
index 1e8317f7..fe7aca71 100644
--- a/test/test.js
+++ b/test/test.js
@@ -387,6 +387,35 @@ describe('Service', () => {
       done();
     });
 
+    it('should refresh token granted for PKCE', () => {
+      const NOW_SECONDS = OAuth2.getTimeInSeconds_(new Date());
+      const ONE_HOUR_AGO_SECONDS = NOW_SECONDS - 360;
+      var token = {
+        granted_time: ONE_HOUR_AGO_SECONDS,
+        expires_in: 100,
+        refresh_token: 'bar',
+        refresh_token_expires_in: 720
+      };
+      var properties = new MockProperties({
+        'oauth2.test': JSON.stringify(token)
+      });
+
+      mocks.UrlFetchApp.resultFunction = () => JSON.stringify({
+        access_token: Math.random().toString(36)
+      });
+
+      OAuth2.createService('test')
+          .setClientId('test')
+          .setTokenUrl('http://www.example.com')
+          .setPropertyStore(properties)
+          .generateCodeVerifier()
+          .refresh();
+
+      var storedToken = JSON.parse(properties.getProperty('oauth2.test'));
+      assert.equal(storedToken.refresh_token, 'bar');
+      assert.equal(storedToken.refreshTokenExpiresAt, NOW_SECONDS + 360);
+    });
+
     it('should retain refresh expiry', () => {
       const NOW_SECONDS = OAuth2.getTimeInSeconds_(new Date());
       const ONE_HOUR_AGO_SECONDS = NOW_SECONDS - 360;
