@dataroadinc/setup-auth
Version:
CLI tool and programmatic API for automated OAuth setup across cloud platforms
239 lines (238 loc) • 9.72 kB
JavaScript
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}`);
});
}
}