UNPKG

@dataroadinc/setup-auth

Version:

CLI tool and programmatic API for automated OAuth setup across cloud platforms

239 lines (238 loc) 9.72 kB
import { SetupAuthError } from "../../../utils/error.js"; import { GcpCloudCliClient } from "../../gcp/cloud-cli-client.js"; export class GcpOAuthWebClientManager { constructor(projectId) { this.authenticated = false; this.projectId = projectId; this.cli = new GcpCloudCliClient(); } async ensureAuthenticated() { if (this.authenticated) return; try { await this.cli.checkAuthenticated(); this.authenticated = true; } catch { console.log("gcloud CLI not authenticated. Attempting automatic authentication..."); await this.cli.autoAuthenticate(); this.authenticated = true; } } async ensureAlphaCommandAuth() { if (this.authenticated) { try { await this.cli.ensureAlphaCommandAuth(); } catch { this.authenticated = false; await this.cli.ensureAlphaCommandAuth(); this.authenticated = true; } } else { await this.cli.ensureAlphaCommandAuth(); this.authenticated = true; } } async runWithAuth(operation) { try { return await operation(); } catch (error) { if (error instanceof SetupAuthError && error.code === "GCP_AUTH_REQUIRED") { console.log("gcloud authentication expired. Attempting to re-authenticate..."); this.authenticated = false; try { const isInteractive = process.stdin.isTTY && process.stdout.isTTY; if (!isInteractive) { throw new SetupAuthError("GCP authentication expired and cannot be renewed automatically.\n" + "What went wrong: gcloud CLI authentication expired during execution and cannot prompt for login in non-interactive mode.\n" + "What to do: Run 'gcloud auth login' and 'gcloud auth application-default login' in your terminal, then re-run this command.\n" + "(If this could have been automated, it would have been.)", { code: "GCP_AUTH_REQUIRED", cause: error }); } await this.ensureAuthenticated(); return await operation(); } catch (reAuthError) { throw new SetupAuthError("GCP authentication expired and cannot be renewed automatically.\n" + "What went wrong: gcloud CLI authentication expired during execution and cannot prompt for login in non-interactive mode.\n" + "What to do: Run 'gcloud auth login' and 'gcloud auth application-default login' in your terminal, then re-run this command.\n" + "(If this could have been automated, it would have been.)", { code: "GCP_AUTH_REQUIRED", cause: reAuthError }); } } throw error; } } async createClient(displayName, redirectUris, origins) { await this.ensureAlphaCommandAuth(); await this.cli.checkInstalled(); return this.runWithAuth(async () => { const timestamp = Date.now(); const sanitizedName = displayName .toLowerCase() .replace(/[^a-z0-9-]/g, "-"); const clientId = `${sanitizedName}-${timestamp}`; const args = [ "alpha", "iam", "oauth-clients", "create", clientId, "--location=global", "--client-type=confidential-client", `--display-name=${displayName}`, "--allowed-grant-types=authorization-code-grant,refresh-token-grant", "--allowed-scopes=https://www.googleapis.com/auth/cloud-platform,openid,email", `--allowed-redirect-uris=${redirectUris.join(",")}`, `--project=${this.projectId}`, ]; if (origins && origins.length > 0) { console.log("Note: JavaScript origins may need to be set manually in the Google Cloud Console"); } try { await this.cli.run(args, false); console.log(`✅ OAuth client created successfully with ID: ${clientId}`); console.log("Creating OAuth client credential to retrieve secret..."); const credentialId = `auto-credential-${Date.now()}`; const credentialArgs = [ "alpha", "iam", "oauth-clients", "credentials", "create", credentialId, `--client-id=${clientId}`, "--location=global", `--project=${this.projectId}`, ]; await this.cli.run(credentialArgs, false); const secretArgs = [ "alpha", "iam", "oauth-clients", "credentials", "describe", credentialId, "--location=global", `--project=${this.projectId}`, ]; const secretResult = (await this.cli.run(secretArgs, true)); console.log("DEBUG secretResult:", secretResult); const clientSecret = secretResult.secret || secretResult.clientSecret || ""; console.log(`✅ OAuth client credential created successfully`); console.log(`Client ID: ${clientId}`); console.log(`Client Secret: ${clientSecret}`); return { clientId, clientSecret }; } catch (error) { console.error("Failed to create OAuth client:", error); throw error; } }); } async listClients() { await this.ensureAlphaCommandAuth(); await this.cli.checkInstalled(); return this.runWithAuth(async () => { const args = [ "alpha", "iam", "oauth-clients", "list", "--location=global", `--project=${this.projectId}`, ]; const result = (await this.cli.run(args, true)); const lines = result.trim().split("\n").slice(1); return lines .filter(line => line.trim()) .map(line => { const parts = line.split(/\s+/); return { clientId: parts[0] || "", displayName: parts.slice(1).join(" ") || "", }; }); }); } async getClientDetails(clientId) { await this.ensureAlphaCommandAuth(); await this.cli.checkInstalled(); return this.runWithAuth(async () => { const args = [ "alpha", "iam", "oauth-clients", "describe", clientId, "--location=global", `--project=${this.projectId}`, ]; const result = (await this.cli.run(args, true)); const lines = result.trim().split("\n"); let displayName = ""; let redirectUris = []; let origins = []; for (const line of lines) { if (line.includes("displayName:")) { displayName = line.split("displayName:")[1]?.trim() || ""; } else if (line.includes("redirectUris:")) { const uris = line.split("redirectUris:")[1]?.trim() || ""; redirectUris = uris ? uris.split(",").map((uri) => uri.trim()) : []; } else if (line.includes("origins:")) { const origs = line.split("origins:")[1]?.trim() || ""; origins = origs ? origs.split(",").map((origin) => origin.trim()) : []; } } return { clientId, displayName, redirectUris, origins, }; }); } async updateRedirectUris(clientId, redirectUris) { await this.ensureAlphaCommandAuth(); await this.cli.checkInstalled(); return this.runWithAuth(async () => { const args = [ "alpha", "iam", "oauth-clients", "update", clientId, "--location=global", `--allowed-redirect-uris=${redirectUris.join(",")}`, `--project=${this.projectId}`, ]; await this.cli.run(args, false); console.log(`✅ Updated redirect URIs for client: ${clientId}`); }); } async deleteClient(clientId) { await this.ensureAlphaCommandAuth(); await this.cli.checkInstalled(); return this.runWithAuth(async () => { const args = [ "alpha", "iam", "oauth-clients", "delete", clientId, "--location=global", `--project=${this.projectId}`, ]; await this.cli.run(args, false); console.log(`✅ Deleted OAuth client: ${clientId}`); }); } }