# apps-script-oauth2: getting an OAuth2 token inside a Google Apps Script

> apps-script-oauth2 is an Apache licensed library that creates, authorizes and refreshes OAuth2 tokens for Apps Script projects, using the platform's StateTokenBuilder and its /usercallback endpoint to close the redirect. The most useful thing in its README is the advice to check whether you need it at all, since Apps Script can often fetch a Google scoped token on its own.

**googleworkspace/apps-script-oauth2** — An OAuth2 library for Google Apps Script.

- Repository: https://github.com/googleworkspace/apps-script-oauth2
- Website: https://developers.google.com/apps-script/
- Stars: 1,751 · Forks: 459
- Language: JavaScript
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/googleworkspace-apps-script-oauth2

## First, check whether you need a third party token at all

The README opens with an argument against its own use, and it is the part of the documentation worth reading first. If you are connecting to a Google API from Apps Script, you might not need this library at all, because Apps Script has a number of easy to use built-in services and a variety of advanced services that wrap existing Google REST APIs. The alternative route is spelled out in full: edit the script's manifest to include the additional scopes your API requires, and when the user authorizes the script they are asked to approve those additional scopes as well. Then you use `ScriptApp.getOAuthToken()` to get the access token the script has acquired and pass it in the `Authorization` header of a `UrlFetchApp.fetch()` call. There is a sample directory called `NoLibrary` that shows exactly this. The practical consequence is that this library earns its place only when the target is a provider whose tokens Apps Script cannot mint for you, and the decision is per API rather than per project.

## The redirect is a script URL, and the platform provides the state token

The library uses Apps Script's StateTokenBuilder and the `/usercallback` endpoint to handle the redirects, which is the part that makes an OAuth flow work inside the platform at all. The mechanics are worth understanding because every Apps Script OAuth2 project hits them. Before authenticating against a provider you register your application there and obtain a client ID and secret, and the provider's registration screen usually asks for a Redirect URI, the URL the browser lands on after the user authorizes access. For this library, and for Apps Script generally, that URL always has the same shape, `https://script.google.com/macros/d/{SCRIPT ID}/usercallback`, where the script ID is the ID of the script using the library, which you can find in the Apps Script code editor under the File, Project properties menu. The library also lets you ask the service directly, and the README gives a small helper that logs the exact URL the service will use during the flow, which is the safest way to register it rather than assembling the string by hand:

```js
/**
 * Logs the redirect URI to register.
 */
function logRedirectUri() {
  var service = getService_();
  Logger.log(service.getRedirectUri());
}
```

Registering the wrong URI, or one that does not match exactly, is the usual reason an Apps Script OAuth flow appears to work and then silently fails when the provider sends the user back.

## Configuring a service is a chain of setters, and the factory must be private

The OAuth2Service class holds the configuration for a given provider, its endpoints, client IDs and secrets, and the README notes that this information is not persisted to any data store, so the object has to be created each time you want to use it. The example builds a service for the Google Drive API, and the shape of it is the same for any provider:

```js
  return OAuth2.createService('drive')
      .setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')
      .setClientId('...')
      .setClientSecret('...')
      .setCallbackFunction('authCallback')
      .setPropertyStore(PropertiesService.getUserProperties())
      .setScope('https://www.googleapis.com/auth/drive')
```

Two details in that chain are more than style. The service name is used when the authorized token is persisted, so it has to be unique within the scope of the property store. And the README asks you to make the factory method private, with an underscore at the end of its name, so that clients cannot call it and read your client ID and secret, which is a real concern in a script that ships as an add-on with a sidebar. The Google specific parameters come next: `login_hint` set to the effective user's email, which prevents the account chooser from appearing for people signed in with several accounts, `access_type` set to `offline` to request offline access, and `prompt` set to `consent`, which the README explains is required to ensure a refresh token is always returned when offline access is requested.

## Tokens live in a property store, and the service is rebuilt each run

The property store setter in the chain, `setPropertyStore(PropertiesService.getUserProperties())`, decides where the authorized token is kept, and the README's sample points at the user properties store, which means each user's token is stored against that user rather than shared across the project. That choice has consequences for how the script behaves, because the service object itself is not persisted. The configuration, the endpoints, the client ID and the secret all have to be reconstructed on every execution, which is why the factory function exists and why the README tells you to create the object each time you want to use it. The library's job is then to find the stored token, and to refresh it when it expires, refreshing being the reason the library exists at all rather than just the redirect handling. Because the token is per user, a script that runs as the developer on behalf of other users is not the same case as one that runs for each user, and the properties you choose decide which of those two you are building.

## Three steps at runtime: link, click, callback

The usage section reduces the whole flow to three steps, and each one has a platform specific wrinkle. First, direct the user to the authorization URL, and the wrinkle is that Apps Script UIs are not allowed to redirect the user's window to a new URL, so you have to present the authorization URL as a link for the user to click. The URL is generated by the service with `getAuthorizationUrl()`, and the README's example builds an HTML template with `HtmlService.createTemplate`, puts the link in it, and shows it in a sidebar with `DocumentApp.getUi().showSidebar(page)`, and it also checks `hasAccess()` first so that a user who is already authorized never sees the link. Second, the callback: when the user completes the OAuth flow, the callback function you named with `setCallbackFunction` is invoked, and it should pass its request object to the service's `handleCallback` function and show a message to the user. Third, and implicitly throughout, you use the service to obtain and refresh tokens. The whole exchange happens in a sidebar and a browser tab, and the script itself is the party that never sees a password.

## How the library is delivered, and the samples that come with it

The library is already published as an Apps Script, so adding it is a UI operation rather than a dependency resolution. You open the Resources, Libraries menu item in the Apps Script code editor, enter the script ID `1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF` in the Find a Library box, pick a version from the dropdown, and save. The alternative, which the README also gives, is to copy the files in the `/dist` directory directly into your script project. If you set explicit scopes in your manifest, the README requires that the scope `https://www.googleapis.com/auth/script.external_request` is included, since that is what allows the script to make outbound requests on the user's behalf. The samples directory is the best argument for the library's generality, with ready made services for AdobeSign, Basecamp, ChatWork, DocuSign, Domo, Dropbox, Facebook, FitBit, GitHub, Google, Harvest, HubSpot, Jira, LINE, LinkedIn, Mailchimp, Medium and others, including a service account sample and a Cloud IAP sample. The build is driven by gulp, with a `dist` script that assembles the distributable, a `lint` script, a `doc` script that runs jsdoc over the sources and the README, a `push` script that runs clasp from the `src` directory, and `mocha` for the tests.

## Versioning, packaging and what the repository does not publish

The npm side of the project is private, with the `private` flag set and the export pointing at `./dist/OAuth2.gs`, so the package is a build pipeline rather than something you install from npm. The `src` directory holds the sources, `dist` holds the assembled Apps Script files, and the `samples` directory holds the per provider examples, with `docs/`, `test/`, a `gulpfile.js`, a `jsdoc.json` and an `.eslintrc.js` that uses the Google eslint config. The published version is 1.43.0, released on 2022-12-01, with 1.42.1 and 1.42.0 both from September 2022, while the last push to the branch was on 2026-06-16. So the source is being touched more recently than the last tagged version, which matters for a library that people add by script ID, since the version you select in that dropdown is what your users get. The repository also carries a `CHANGELOG.md`, a `CODEOWNERS` file and a `SECURITY.md`, and the licence is Apache 2.0, which is the permissive terms most Google samples use.

## Conclusion

Adopt apps-script-oauth2 when your Apps Script has to reach a third party API on behalf of a user, since the library handles the redirect, the token storage and the refresh cycle, and its samples cover a wide range of providers. Do not adopt it for Google APIs that Apps Script already covers, because the README says you may not need the library at all, and it is often simpler to add a scope to the manifest and use `ScriptApp.getOAuthToken()`. Before you copy it in, register your script's redirect URI with the provider in the documented `/usercallback` form, and make sure your service factory is a private function so the client ID and secret cannot be read from the client side. The licence is Apache 2.0, the published library version is 1.43.0 from 2022-12-01, and the last push was on 2026-06-16.

## FAQ

### Do I need apps-script-oauth2 to call a Google API from Apps Script?

Probably not. The README says you might not need the library, since Apps Script has built-in and advanced services, and for anything else you can add the required scopes to the script manifest and use `ScriptApp.getOAuthToken()` in the `Authorization` header of a `UrlFetchApp.fetch()` call. The `samples/NoLibrary` sample shows that approach.

### What redirect URI do I register with the OAuth2 provider?

For Apps Script it is always `https://script.google.com/macros/d/{SCRIPT ID}/usercallback`, where the script ID is found in the code editor under File, Project properties. You can also call the service's `getRedirectUri()` method to see the exact URL the service will use.

### How do I install apps-script-oauth2 in my Apps Script project?

The library is published as an Apps Script, so you use Resources, Libraries in the code editor, enter the script ID 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF, choose a version and save. Alternatively you can copy the files in the `/dist` directory into your project directly.

### Why should the service factory function be private?

The README asks you to make the method private, with an underscore at the end of the name, to prevent clients from being able to call it and read your client ID and secret. The service name is also used when the authorized token is persisted, so it must be unique within the scope of the property store.

### Which scope must be in my manifest when I set explicit scopes?

The README requires that `https://www.googleapis.com/auth/script.external_request` is included, alongside the scopes your target API needs. The library also sets `access_type` to `offline` and `prompt` to `consent` so that a refresh token is always returned for offline access.

## Sources

- [googleworkspace/apps-script-oauth2 on GitHub](https://github.com/googleworkspace/apps-script-oauth2)
- [License: Apache-2.0](https://github.com/googleworkspace/apps-script-oauth2/blob/main/LICENSE)
- [Project website](https://developers.google.com/apps-script/)
- [README](https://github.com/googleworkspace/apps-script-oauth2/blob/main/README.md)
- [Releases](https://github.com/googleworkspace/apps-script-oauth2/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/googleworkspace-apps-script-oauth2
