Skip to content

Commit

Permalink
fix: prevent samples from leaking OAuth client ID + Secret to users (#…
Browse files Browse the repository at this point in the history
…379)

Previously, the getService method was public in most of the samples, letting any user of any sample application execute the method using google.script.run to exfiltrate the OAuth Client ID and Secret from the server. Now, these methods are made private by appending an _ to their names, preventing this issue.
  • Loading branch information
AlnisS committed Jul 27, 2022
1 parent 72d4dbc commit a5480c1
Show file tree
Hide file tree
Showing 47 changed files with 214 additions and 210 deletions.
34 changes: 19 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ exact URL that the service will use when performing the OAuth flow:
* Logs the redirect URI to register.
*/
function logRedirectUri() {
var service = getService();
var service = getService_();
Logger.log(service.getRedirectUri());
}
```
Expand All @@ -95,8 +95,12 @@ information is not persisted to any data store, so you'll need to create this
object each time you want to use it. The example below shows how to create a
service for the Google Drive API.

Ensure the method is private (has an underscore at the end of the name) to
prevent clients from being able to call the method to read your client ID and
secret.

```js
function getDriveService() {
function getDriveService_() {
// Create a new service with the given name. The name will be used when
// persisting the authorized token, so ensure it is unique within the
// scope of the property store.
Expand Down Expand Up @@ -143,7 +147,7 @@ The URL is generated by the service, using the function `getAuthorizationUrl()`.

```js
function showSidebar() {
var driveService = getDriveService();
var driveService = getDriveService_();
if (!driveService.hasAccess()) {
var authorizationUrl = driveService.getAuthorizationUrl();
var template = HtmlService.createTemplate(
Expand All @@ -167,7 +171,7 @@ to the user.

```js
function authCallback(request) {
var driveService = getDriveService();
var driveService = getDriveService_();
var isAuthorized = driveService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
Expand All @@ -190,7 +194,7 @@ request in the "Authorization" header.

```js
function makeRequest() {
var driveService = getDriveService();
var driveService = getDriveService_();
var response = UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files?maxResults=10', {
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
Expand All @@ -208,7 +212,7 @@ different account, use the `reset()` method:

```js
function logout() {
var service = getDriveService()
var service = getDriveService_()
service.reset();
}
```
Expand Down Expand Up @@ -350,7 +354,7 @@ request parameters and saved saved into storage.

```js
function authCallback(request) {
var service = getService();
var service = getService_();
var authorized = service.handleCallback(request);
if (authorized) {
// Gets the authorized account ID from the scope string. Assumes the
Expand Down Expand Up @@ -401,7 +405,7 @@ optional hash of parameter names and values to the `getAuthorizationUrl()`
method:
```js
var authorizationUrl = getService().getAuthorizationUrl({
var authorizationUrl = getService_().getAuthorizationUrl({
// Pass the additional parameter "lang" with the value "fr".
lang: 'fr'
});
Expand Down Expand Up @@ -467,17 +471,17 @@ means selecting a service name that matches the API the user will authorize:
```js
function run() {
var gitHubService = getGitHubService();
var mediumService = getMediumService();
var gitHubService = getGitHubService_();
var mediumService = getMediumService_();
// ...
}

function getGitHubService() {
function getGitHubService_() {
return OAuth2.createService('GitHub')
// GitHub settings ...
}

function getMediumService() {
function getMediumService_() {
return OAuth2.createService('Medium')
// Medium settings ...
}
Expand All @@ -490,12 +494,12 @@ names:
```js
function run() {
var copyFromService = getGitHubService('from');
var copyToService = getGitHubService('to');
var copyFromService = getGitHubService_('from');
var copyToService = getGitHubService_('to');
// ...
}

function getGitHubService(label) {
function getGitHubService_(label) {
return OAuth2.createService('GitHub_' + label)
// GitHub settings ...
}
Expand Down
30 changes: 15 additions & 15 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ <h2>Redirect URI</h2>
* Logs the redirect URI to register.
*/
function logRedirectUri() {
var service = getService();
var service = getService_();
Logger.log(service.getRedirectUri());
}
</code></pre>
Expand All @@ -131,7 +131,7 @@ <h3>1. Create the OAuth2 service</h3>
information is not persisted to any data store, so you'll need to create this
object each time you want to use it. The example below shows how to create a
service for the Google Drive API.</p>
<pre class="prettyprint source lang-js"><code>function getDriveService() {
<pre class="prettyprint source lang-js"><code>function getDriveService_() {
// Create a new service with the given name. The name will be used when
// persisting the authorized token, so ensure it is unique within the
// scope of the property store.
Expand Down Expand Up @@ -174,7 +174,7 @@ <h3>2. Direct the user to the authorization URL</h3>
you'll need to present the authorization URL as a link for the user to click.
The URL is generated by the service, using the function <code>getAuthorizationUrl()</code>.</p>
<pre class="prettyprint source lang-js"><code>function showSidebar() {
var driveService = getDriveService();
var driveService = getDriveService_();
if (!driveService.hasAccess()) {
var authorizationUrl = driveService.getAuthorizationUrl();
var template = HtmlService.createTemplate(
Expand All @@ -194,7 +194,7 @@ <h3>3. Handle the callback</h3>
request object to the service's <code>handleCallback</code> function, and show a message
to the user.</p>
<pre class="prettyprint source lang-js"><code>function authCallback(request) {
var driveService = getDriveService();
var driveService = getDriveService_();
var isAuthorized = driveService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
Expand All @@ -212,7 +212,7 @@ <h3>4. Get the access token</h3>
requests to the API. The access token can be passed along with a <code>UrlFetchApp</code>
request in the &quot;Authorization&quot; header.</p>
<pre class="prettyprint source lang-js"><code>function makeRequest() {
var driveService = getDriveService();
var driveService = getDriveService_();
var response = UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files?maxResults=10', {
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
Expand All @@ -225,7 +225,7 @@ <h3>Logout</h3>
<p>To logout the user or disconnect the service, perhaps so the user can select a
different account, use the <code>reset()</code> method:</p>
<pre class="prettyprint source lang-js"><code>function logout() {
var service = getDriveService()
var service = getDriveService_()
service.reset();
}
</code></pre>
Expand Down Expand Up @@ -326,7 +326,7 @@ <h4>Storing token-related data</h4>
in the callback URL. In the following code the account ID is extracted from the
request parameters and saved saved into storage.</p>
<pre class="prettyprint source lang-js"><code>function authCallback(request) {
var service = getService();
var service = getService_();
var authorized = service.handleCallback(request);
if (authorized) {
// Gets the authorized account ID from the scope string. Assumes the
Expand Down Expand Up @@ -368,7 +368,7 @@ <h4>Passing additional parameters to the callback function</h4>
token, which is a standard mechanism for this purpose. To do so, pass an
optional hash of parameter names and values to the <code>getAuthorizationUrl()</code>
method:</p>
<pre class="prettyprint source lang-js"><code>var authorizationUrl = getService().getAuthorizationUrl({
<pre class="prettyprint source lang-js"><code>var authorizationUrl = getService_().getAuthorizationUrl({
// Pass the additional parameter &quot;lang&quot; with the value &quot;fr&quot;.
lang: 'fr'
});
Expand Down Expand Up @@ -419,17 +419,17 @@ <h3>How can I connect to multiple OAuth services?</h3>
multiple services merely ensure they have different service names. Often this
means selecting a service name that matches the API the user will authorize:</p>
<pre class="prettyprint source lang-js"><code>function run() {
var gitHubService = getGitHubService();
var mediumService = getMediumService();
var gitHubService = getGitHubService_();
var mediumService = getMediumService_();
// ...
}

function getGitHubService() {
function getGitHubService_() {
return OAuth2.createService('GitHub')
// GitHub settings ...
}

function getMediumService() {
function getMediumService_() {
return OAuth2.createService('Medium')
// Medium settings ...
}
Expand All @@ -439,12 +439,12 @@ <h3>How can I connect to multiple OAuth services?</h3>
those cases you'll need to devise your own method for creating unique service
names:</p>
<pre class="prettyprint source lang-js"><code>function run() {
var copyFromService = getGitHubService('from');
var copyToService = getGitHubService('to');
var copyFromService = getGitHubService_('from');
var copyToService = getGitHubService_('to');
// ...
}

function getGitHubService(label) {
function getGitHubService_(label) {
return OAuth2.createService('GitHub_' + label)
// GitHub settings ...
}
Expand Down
14 changes: 7 additions & 7 deletions samples/Add-on/Code.gs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function onInstall(e) {
* project file.
*/
function showSidebar() {
var service = getGitHubService();
var service = getGitHubService_();
var template = HtmlService.createTemplateFromFile('Sidebar');
template.email = Session.getEffectiveUser().getEmail();
template.isSignedIn = service.hasAccess();
Expand All @@ -64,22 +64,22 @@ function showSidebar() {
* @return {String} The authorization URL.
*/
function getAuthorizationUrl() {
return getGitHubService().getAuthorizationUrl();
return getGitHubService_().getAuthorizationUrl();
}

/**
* Resets the API service, forcing re-authorization before
* additional authorization-required API calls can be made.
*/
function signOut() {
getGitHubService().reset();
getGitHubService_().reset();
}

/**
* Gets the user's GitHub profile.
*/
function getGitHubProfile() {
var service = getGitHubService();
var service = getGitHubService_();
if (!service.hasAccess()) {
throw new Error('Error: Missing GitHub authorization.');
}
Expand All @@ -96,7 +96,7 @@ function getGitHubProfile() {
* Gets the user's GitHub repos.
*/
function getGitHubRepos() {
var service = getGitHubService();
var service = getGitHubService_();
if (!service.hasAccess()) {
throw new Error('Error: Missing GitHub authorization.');
}
Expand All @@ -113,7 +113,7 @@ function getGitHubRepos() {
* Gets an OAuth2 service configured for the GitHub API.
* @return {OAuth2.Service} The OAuth2 service
*/
function getGitHubService() {
function getGitHubService_() {
return OAuth2.createService('github')
// Set the endpoint URLs.
.setAuthorizationBaseUrl('https://github.com/login/oauth/authorize')
Expand Down Expand Up @@ -142,7 +142,7 @@ function authCallback(request) {
template.error = null;
var title;
try {
var service = getGitHubService();
var service = getGitHubService_();
var authorized = service.handleCallback(request);
template.isSignedIn = authorized;
title = authorized ? 'Access Granted' : 'Access Denied';
Expand Down
8 changes: 4 additions & 4 deletions samples/AdobeSign.gs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var API_ACCESS_POINT_KEY = 'api_access_point';
* Authorizes and makes a request to the Adobe Sign API.
*/
function run() {
var service = getService();
var service = getService_();
if (service.hasAccess()) {
// Retrieve the API access point from storage.
var apiAccessPoint = service.getStorage().getValue(API_ACCESS_POINT_KEY);
Expand All @@ -38,13 +38,13 @@ function run() {
* Reset the authorization state, so that it can be re-tested.
*/
function reset() {
getService().reset();
getService_().reset();
}

/**
* Configures the service.
*/
function getService(optApiAccessPoint) {
function getService_(optApiAccessPoint) {
var service = OAuth2.createService('AdobeSign')
// Set the endpoint URLs.
.setAuthorizationBaseUrl('https://secure.echosign.com/public/oauth')
Expand Down Expand Up @@ -82,7 +82,7 @@ function getService(optApiAccessPoint) {
function authCallback(request) {
// Get the API access point specified in the URL parameters.
var apiAccessPoint = request.parameter[API_ACCESS_POINT_KEY];
var service = getService(apiAccessPoint);
var service = getService_(apiAccessPoint);
var authorized = service.handleCallback(request);
if (authorized) {
// Save the API access point in the service's storage.
Expand Down
8 changes: 4 additions & 4 deletions samples/Basecamp.gs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var CLIENT_SECRET = '...';
* Authorizes and makes a request to the Basecamp 3 API.
*/
function run() {
var service = getService();
var service = getService_();
if (service.hasAccess()) {
var url = 'https://launchpad.37signals.com/authorization.json';
var response = UrlFetchApp.fetch(url, {
Expand All @@ -26,13 +26,13 @@ function run() {
* Reset the authorization state, so that it can be re-tested.
*/
function reset() {
getService().reset();
getService_().reset();
}

/**
* Configures the service.
*/
function getService() {
function getService_() {
return OAuth2.createService('Basecamp')
// Set the endpoint URLs.
.setAuthorizationBaseUrl(
Expand All @@ -56,7 +56,7 @@ function getService() {
* Handles the OAuth callback.
*/
function authCallback(request) {
var service = getService();
var service = getService_();
var authorized = service.handleCallback(request);
if (authorized) {
return HtmlService.createHtmlOutput('Success!');
Expand Down
8 changes: 4 additions & 4 deletions samples/ChatWork.gs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var CLIENT_SECRET = '...';
* Authorizes and makes a request to the ChatWork API.
*/
function run() {
var service = getService();
var service = getService_();
if (service.hasAccess()) {
var response = UrlFetchApp.fetch('https://api.chatwork.com/v2/me', {
headers: {
Expand All @@ -25,13 +25,13 @@ function run() {
* Reset the authorization state, so that it can be re-tested.
*/
function reset() {
getService().reset();
getService_().reset();
}

/**
* Configures the service.
*/
function getService() {
function getService_() {
var scope = 'users.profile.me:read rooms.messages:read';
return OAuth2.createService('ChatWork')
// Set the endpoint URLs.
Expand Down Expand Up @@ -67,7 +67,7 @@ function getService() {
* Handles the OAuth callback.
*/
function authCallback(request) {
var service = getService();
var service = getService_();
var authorized = service.handleCallback(request);
if (authorized) {
return HtmlService.createHtmlOutput('Success!');
Expand Down

0 comments on commit a5480c1

Please sign in to comment.