Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PCC-1449 cli uses new auth flow for management access tokens #313

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 33 additions & 41 deletions packages/cli/src/cli/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
import http from "http";
import { dirname, join } from "path";
import url, { fileURLToPath } from "url";
import { parseJwt } from "@pantheon-systems/pcc-sdk-core";
import { OAuth2Client } from "google-auth-library";
import nunjucks from "nunjucks";
import open from "open";
import ora from "ora";
import destroyer from "server-destroy";
import AddOnApiHelper from "../../lib/addonApiHelper";
import { getApiConfig } from "../../lib/apiConfig";
import {
getLocalAuthDetails,
JwtCredentials,
persistAuthDetails,
} from "../../lib/localStorage";
import { errorHandler } from "../exceptions";
Expand All @@ -20,69 +17,61 @@

const OAUTH_SCOPES = ["https://www.googleapis.com/auth/userinfo.email"];

function login(extraScopes: string[]): Promise<void> {
function login(): Promise<void> {

Check warning on line 20 in packages/cli/src/cli/commands/login.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/cli/commands/login.ts#L20

non-arrow functions are forbidden
return new Promise(
// eslint-disable-next-line no-async-promise-executor -- Handling promise rejection in the executor
async (resolve, reject) => {
const spinner = ora("Logging you in...").start();
try {
const authData = await getLocalAuthDetails(extraScopes);
const authData = (await getLocalAuthDetails()) as JwtCredentials | null;
if (authData) {
const scopes = authData.scope?.split(" ");

if (
!extraScopes?.length ||
extraScopes.find((x) => scopes?.includes(x))
) {
const jwtPayload = parseJwt(authData.id_token as string);
spinner.succeed(
`You are already logged in as ${jwtPayload.email}.`,
);
return resolve();
}
console.log("already exists", JSON.stringify({ authData }, null, 4));

Check warning on line 28 in packages/cli/src/cli/commands/login.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/cli/commands/login.ts#L28

'magic numbers' are not allowed: 4
// spinner.succeed(
// `You are already logged in as ${authData.email}.`,
// );
// return resolve();
}

const apiConfig = await getApiConfig();
const oAuth2Client = new OAuth2Client({
clientId: apiConfig.googleClientId,
redirectUri: apiConfig.googleRedirectUri,
});

// Generate the url that will be used for the consent dialog.
const authorizeUrl = oAuth2Client.generateAuthUrl({
access_type: "offline",
scope: [...OAUTH_SCOPES, ...extraScopes],
});

const server = http.createServer(async (req, res) => {
try {
if (!req.url) {

Check failure

Code scanning / CodeQL

User-controlled bypass of security check High

This condition guards a sensitive
action
, but a
user-provided value
controls it.
throw new Error("No URL path provided");
}

if (req.url.indexOf("/oauth-redirect") > -1) {
if (req.url.indexOf("/auth-success") !== -1) {

Check warning on line 41 in packages/cli/src/cli/commands/login.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/cli/commands/login.ts#L41

'magic numbers' are not allowed: -1
const qs = new url.URL(req.url, "http://localhost:3030")
.searchParams;
const code = qs.get("code");
const idToken = qs.get("idToken") as string;
const oauthToken = qs.get("oauthToken") as string;
const email = qs.get("email") as string;
const expiration = qs.get("expiration") as string;
const currDir = dirname(fileURLToPath(import.meta.url));
const content = readFileSync(
join(currDir, "../templates/loginSuccess.html"),
);
const credentials = await AddOnApiHelper.getToken(code as string);
const jwtPayload = parseJwt(credentials.id_token as string);
await persistAuthDetails(credentials);

await persistAuthDetails(
{
idToken,
oauthToken,
email,

Check warning on line 57 in packages/cli/src/cli/commands/login.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/cli/commands/login.ts#L57

The key 'email' is not sorted alphabetically
expiration,
}

Check warning on line 59 in packages/cli/src/cli/commands/login.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/cli/commands/login.ts#L59

Missing trailing comma
);

res.end(
nunjucks.renderString(content.toString(), {
email: jwtPayload.email,
email: email,
}),
);
server.destroy();

spinner.succeed(
`You are successfully logged in as ${jwtPayload.email}`,
);
spinner.succeed(`You are successfully logged in as ${email}`);
resolve();
} else {
res.writeHead(200, { "Content-Type": "text/plain" });

Check warning on line 72 in packages/cli/src/cli/commands/login.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/cli/commands/login.ts#L72

'magic numbers' are not allowed: 200
res.end("Hello World\n");
return;
}
} catch (e) {
spinner.fail();
Expand All @@ -93,7 +82,10 @@
destroyer(server);

server.listen(3030, () => {
open(authorizeUrl, { wait: true }).then((cp) => cp.kill());
// const apiConfig = await getApiConfig();
open("http://localhost:3000/auth/cli", { wait: true }).then((cp) =>
cp.kill(),
);
});
} catch (e) {
spinner.fail();
Expand All @@ -102,7 +94,7 @@
},
);
}
export default errorHandler<string[]>(login);
export default errorHandler<void>(login);

Check warning on line 97 in packages/cli/src/cli/commands/login.ts

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

packages/cli/src/cli/commands/login.ts#L97

Use of default exports is forbidden
export const LOGIN_EXAMPLES = [
{ description: "Login the user", command: "$0 login" },
];
7 changes: 5 additions & 2 deletions packages/cli/src/cli/commands/logout.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { existsSync, rmSync } from "fs";
import ora from "ora";
import { AUTH_FILE_PATH } from "../../lib/localStorage";
import { AUTH_FOLDER_PATH } from "../../lib/localStorage";
import { errorHandler } from "../exceptions";

const logout = async () => {
const spinner = ora("Logging you out...").start();
try {
if (existsSync(AUTH_FILE_PATH)) rmSync(AUTH_FILE_PATH);
if (existsSync(AUTH_FOLDER_PATH))
rmSync(AUTH_FOLDER_PATH, {
recursive: true,
});
spinner.succeed("Successfully logged you out from PPC client!");
} catch (e) {
spinner.fail();
Expand Down
31 changes: 27 additions & 4 deletions packages/cli/src/cli/commands/whoAmI.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
import { parseJwt } from "@pantheon-systems/pcc-sdk-core";
import chalk from "chalk";
import { getLocalAuthDetails } from "../../lib/localStorage";
import { Credentials } from "google-auth-library";
import {
CREDENTIAL_TYPE,
getLocalAuthDetails,
JwtCredentials,
} from "../../lib/localStorage";
import { errorHandler } from "../exceptions";

const printWhoAmI = async () => {
try {
const authData = await getLocalAuthDetails();
if (!authData) {
const nextJwt = (await getLocalAuthDetails(
CREDENTIAL_TYPE.NEXT_JWT,
)) as JwtCredentials | null;
if (!nextJwt) {
console.log("You aren't logged in.");
} else {
console.log(`You're logged in as ${nextJwt.email}`);
}
} catch (e) {
chalk.red("Something went wrong - couldn't retrieve auth info.");
throw e;
}

try {
const authData = (await getLocalAuthDetails(
CREDENTIAL_TYPE.OAUTH,
)) as Credentials | null;
if (!authData) {
console.log(
"You aren't logged into oauth. For some actions, the oauth connection isn't necessary. If you run a command that requires it, you will be only prompted to log in with oauth at that point.",
);
} else {
const jwtPayload = parseJwt(authData.id_token as string);
console.log(`You're logged in as ${jwtPayload.email}`);
console.log(`Oauth: You're logged in as ${jwtPayload.email}`);
}
} catch (e) {
chalk.red("Something went wrong - couldn't retrieve auth info.");
Expand Down
Loading
Loading