@google/clasp
Version:
Develop Apps Script Projects locally
88 lines (87 loc) • 3.61 kB
JavaScript
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* Base class for managing the OAuth 2.0 Authorization Code Flow.
* It provides common logic for generating authorization URLs,
* handling user authorization, and exchanging codes for tokens.
* Specific implementations will override methods to define how the
* redirect URI is obtained and how the user is prompted for the code.
*/
export class AuthorizationCodeFlow {
constructor(oauth2client) {
this.oauth2Client = oauth2client;
}
/**
* Initiates the authorization process.
* This method generates an authorization URL, prompts the user for authorization,
* exchanges the authorization code for tokens, and sets the credentials
* on the OAuth2 client.
* @param {string | string[]} scopes - The scope(s) for which authorization is requested.
* @returns {Promise<OAuth2Client>} The authorized OAuth2 client.
*/
async authorize(scopes) {
const scope = Array.isArray(scopes) ? scopes.join(' ') : scopes;
const redirectUri = await this.getRedirectUri();
const authUrl = this.oauth2Client.generateAuthUrl({
redirect_uri: redirectUri,
access_type: 'offline',
scope: scope,
});
const code = await this.promptAndReturnCode(authUrl);
const tokens = await this.oauth2Client.getToken({
code,
redirect_uri: redirectUri,
});
this.oauth2Client.setCredentials(tokens.tokens);
return this.oauth2Client;
}
/**
* Abstract method to get the redirect URI.
* Subclasses must implement this to provide the specific redirect URI
* for their authorization flow.
* @returns {Promise<string>} The redirect URI.
* @throws {Error} If not implemented by the subclass.
*/
async getRedirectUri() {
throw new Error('Not implemented');
}
/**
* Abstract method to prompt the user for the authorization code.
* Subclasses must implement this to define how the user is prompted
* (e.g., via a local server, manual input).
* @param {string} _authorizationUrl - The URL to which the user should be directed
* for authorization.
* @returns {Promise<string>} The authorization code obtained from the user.
* @throws {Error} If not implemented by the subclass.
*/
async promptAndReturnCode(_authorizationUrl) {
throw new Error('Not implemented');
}
}
/**
* Parses an authorization response URL (typically from a redirect)
* to extract the authorization code or an error.
* @param {string} url - The full URL from the authorization server's redirect.
* @returns {{code: string | null; error: string | null}} An object containing
* the 'code' if successful, or an 'error' if the authorization failed.
*/
export function parseAuthResponseUrl(url) {
const urlParts = new URL(url, 'http://localhost/').searchParams;
const code = urlParts.get('code');
const error = urlParts.get('error');
return {
code,
error,
};
}