mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 07:27:58 +00:00
feat: ui option to paste token on sending token to cli fails
This commit is contained in:
@@ -57,6 +57,10 @@ export const plans = plansProd || plansDev;
|
|||||||
export const leaveConfirmDefaultMessage =
|
export const leaveConfirmDefaultMessage =
|
||||||
"Your changes will be lost if you leave the page. Are you sure you want to continue?";
|
"Your changes will be lost if you leave the page. Are you sure you want to continue?";
|
||||||
|
|
||||||
|
export enum SessionStorageKeys {
|
||||||
|
CLI_TERMINAL_TOKEN = "CLI_TERMINAL_TOKEN"
|
||||||
|
}
|
||||||
|
|
||||||
export const secretTagsColors = [
|
export const secretTagsColors = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
import { SessionStorageKeys } from "@app/const";
|
||||||
import { setAuthToken } from "@app/reactQuery";
|
import { setAuthToken } from "@app/reactQuery";
|
||||||
|
|
||||||
import { APIKeyDataV2 } from "../apiKeys/types";
|
import { APIKeyDataV2 } from "../apiKeys/types";
|
||||||
@@ -293,6 +294,7 @@ export const useLogoutUser = (keepQueryClient?: boolean) => {
|
|||||||
localStorage.removeItem("PRIVATE_KEY");
|
localStorage.removeItem("PRIVATE_KEY");
|
||||||
localStorage.removeItem("orgData.id");
|
localStorage.removeItem("orgData.id");
|
||||||
localStorage.removeItem("projectData.id");
|
localStorage.removeItem("projectData.id");
|
||||||
|
sessionStorage.removeItem(SessionStorageKeys.CLI_TERMINAL_TOKEN);
|
||||||
|
|
||||||
if (!keepQueryClient) {
|
if (!keepQueryClient) {
|
||||||
queryClient.clear();
|
queryClient.clear();
|
||||||
|
|||||||
@@ -1,23 +1,89 @@
|
|||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { IconButton, SecretInput } from "@app/components/v2";
|
||||||
|
import { SessionStorageKeys } from "@app/const";
|
||||||
|
import { useTimedReset } from "@app/hooks";
|
||||||
|
|
||||||
|
const getTerminalCliToken = () => {
|
||||||
|
const cliTerminalTokenInfo = sessionStorage.getItem(SessionStorageKeys.CLI_TERMINAL_TOKEN);
|
||||||
|
if (!cliTerminalTokenInfo) return;
|
||||||
|
|
||||||
|
const { expiry, data } = JSON.parse(cliTerminalTokenInfo);
|
||||||
|
if (new Date() > new Date(expiry)) {
|
||||||
|
sessionStorage.removeItem(SessionStorageKeys.CLI_TERMINAL_TOKEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
return data as string;
|
||||||
|
};
|
||||||
|
|
||||||
export default function CliRedirect() {
|
export default function CliRedirect() {
|
||||||
|
const [isUrlCopied, , setIsUrlCopied] = useTimedReset<boolean>({
|
||||||
|
initialState: false
|
||||||
|
});
|
||||||
|
const cliToken = getTerminalCliToken();
|
||||||
|
|
||||||
|
const copyUrlToClipboard = () => {
|
||||||
|
if (cliToken) {
|
||||||
|
navigator.clipboard.writeText(cliToken);
|
||||||
|
setIsUrlCopied(true);
|
||||||
|
sessionStorage.removeItem(SessionStorageKeys.CLI_TERMINAL_TOKEN);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col justify-between bg-bunker-800 md:h-screen">
|
<div className="flex flex-col justify-between bg-bunker-800 md:h-screen">
|
||||||
<Head>
|
<Head>
|
||||||
<title>Infisical CLI | Login Successful!</title>
|
<title>Infisical CLI | Login Successful!</title>
|
||||||
<link rel="icon" href="/infisical.ico" />
|
<link rel="icon" href="/infisical.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<div className="flex h-screen w-screen flex-col items-center justify-center text-gray-200">
|
<div className="flex h-screen w-screen flex-col items-center justify-center space-y-4 text-gray-200">
|
||||||
<div className="mb-8 flex justify-center">
|
<div className="mb-4 flex justify-center">
|
||||||
<Image src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical Logo" />
|
<Image src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical Logo" />
|
||||||
</div>
|
</div>
|
||||||
<p className="bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-3xl font-medium text-transparent">
|
{cliToken ? (
|
||||||
Head back to your terminal
|
<>
|
||||||
</p>
|
<div className="pb-4">
|
||||||
<p className="text-light mb-1 text-lg text-mineshaft-400">
|
<p className="bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-3xl font-medium text-transparent">
|
||||||
You've successfully logged in to the Infisical CLI
|
Login successful!
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-light mb-1 text-lg text-mineshaft-400">
|
||||||
|
However, your token could not be sent to the CLI
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="dark relative flex max-h-36 max-w-xl flex-col items-center space-y-4 overflow-y-auto rounded-md border border-mineshaft-700 bg-mineshaft-900 p-3">
|
||||||
|
<SecretInput value={cliToken as string} />
|
||||||
|
<div className="mx-1 flex">
|
||||||
|
<IconButton
|
||||||
|
variant="outline_bg"
|
||||||
|
colorSchema="primary"
|
||||||
|
ariaLabel="copy to clipboard"
|
||||||
|
onClick={copyUrlToClipboard}
|
||||||
|
className=" flex items-center rounded py-2"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon className="pr-2" icon={isUrlCopied ? faCheck : faCopy} /> Copy to
|
||||||
|
clipboard
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-light mb-1 text-lg text-mineshaft-400">
|
||||||
|
After copying, return to terminal screen and paste it there
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p className="bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-3xl font-medium text-transparent">
|
||||||
|
Head back to your terminal
|
||||||
|
</p>
|
||||||
|
<p className="text-light mb-1 text-lg text-mineshaft-400">
|
||||||
|
You've successfully logged in to the Infisical CLI
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ import { useRouter } from "next/router";
|
|||||||
import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { addSeconds, formatISO } from "date-fns";
|
||||||
import jwt_decode from "jwt-decode";
|
import jwt_decode from "jwt-decode";
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { IsCliLoginSuccessful } from "@app/components/utilities/attemptCliLogin";
|
import { IsCliLoginSuccessful } from "@app/components/utilities/attemptCliLogin";
|
||||||
import { Button, Spinner } from "@app/components/v2";
|
import { Button, Spinner } from "@app/components/v2";
|
||||||
|
import { SessionStorageKeys } from "@app/const";
|
||||||
import { useUser } from "@app/context";
|
import { useUser } from "@app/context";
|
||||||
import { useGetOrganizations, useLogoutUser, useSelectOrganization } from "@app/hooks/api";
|
import { useGetOrganizations, useLogoutUser, useSelectOrganization } from "@app/hooks/api";
|
||||||
import { Organization } from "@app/hooks/api/types";
|
import { Organization } from "@app/hooks/api/types";
|
||||||
@@ -94,9 +96,19 @@ export default function LoginPage() {
|
|||||||
|
|
||||||
// send request to server endpoint
|
// send request to server endpoint
|
||||||
const instance = axios.create();
|
const instance = axios.create();
|
||||||
await instance.post(`http://127.0.0.1:${callbackPort}/`, payload);
|
await instance.post(`http://127.0.0.1:${callbackPort}/`, payload).catch(() => {
|
||||||
// cli page
|
// if error happens to communicate we set the token with an expiry in sessino storage
|
||||||
|
// the cli-redirect page has logic to show this to user and ask them to paste it in terminal
|
||||||
|
sessionStorage.setItem(
|
||||||
|
SessionStorageKeys.CLI_TERMINAL_TOKEN,
|
||||||
|
JSON.stringify({
|
||||||
|
expiry: formatISO(addSeconds(new Date(), 30)),
|
||||||
|
data: window.btoa(JSON.stringify(payload))
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
router.push("/cli-redirect");
|
router.push("/cli-redirect");
|
||||||
|
// cli page
|
||||||
} else {
|
} else {
|
||||||
navigateUserToOrg(router, organization.id);
|
navigateUserToOrg(router, organization.id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import ReactCodeInput from "react-code-input";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { addSeconds, formatISO } from "date-fns";
|
||||||
import jwt_decode from "jwt-decode";
|
import jwt_decode from "jwt-decode";
|
||||||
|
|
||||||
import Error from "@app/components/basic/Error";
|
import Error from "@app/components/basic/Error";
|
||||||
@@ -11,6 +12,7 @@ import attemptCliLoginMfa from "@app/components/utilities/attemptCliLoginMfa";
|
|||||||
import attemptLoginMfa from "@app/components/utilities/attemptLoginMfa";
|
import attemptLoginMfa from "@app/components/utilities/attemptLoginMfa";
|
||||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||||
import { Button } from "@app/components/v2";
|
import { Button } from "@app/components/v2";
|
||||||
|
import { SessionStorageKeys } from "@app/const";
|
||||||
import { useSendMfaToken } from "@app/hooks/api/auth";
|
import { useSendMfaToken } from "@app/hooks/api/auth";
|
||||||
import { useSelectOrganization, verifyMfaToken } from "@app/hooks/api/auth/queries";
|
import { useSelectOrganization, verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
||||||
@@ -79,11 +81,24 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => {
|
|||||||
if (callbackPort) {
|
if (callbackPort) {
|
||||||
const cliUrl = `http://127.0.0.1:${callbackPort}/`;
|
const cliUrl = `http://127.0.0.1:${callbackPort}/`;
|
||||||
const instance = axios.create();
|
const instance = axios.create();
|
||||||
await instance.post(cliUrl, {
|
const payload = {
|
||||||
email,
|
email,
|
||||||
privateKey,
|
privateKey,
|
||||||
JTWToken: newJwtToken
|
JTWToken: newJwtToken
|
||||||
|
};
|
||||||
|
await instance.post(cliUrl, payload).catch(() => {
|
||||||
|
// if error happens to communicate we set the token with an expiry in sessino storage
|
||||||
|
// the cli-redirect page has logic to show this to user and ask them to paste it in terminal
|
||||||
|
sessionStorage.setItem(
|
||||||
|
SessionStorageKeys.CLI_TERMINAL_TOKEN,
|
||||||
|
JSON.stringify({
|
||||||
|
expiry: formatISO(addSeconds(new Date(), 30)),
|
||||||
|
data: window.btoa(JSON.stringify(payload))
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
router.push("/cli-redirect");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
await navigateUserToOrg(router, organizationId);
|
await navigateUserToOrg(router, organizationId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -20,7 +20,7 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
|
|||||||
const [hashedHex, key] = urlEncodedPublicKey
|
const [hashedHex, key] = urlEncodedPublicKey
|
||||||
? urlEncodedPublicKey.toString().split("-")
|
? urlEncodedPublicKey.toString().split("-")
|
||||||
: ["", ""];
|
: ["", ""];
|
||||||
|
|
||||||
const publicKey = decodeURIComponent(urlEncodedPublicKey as string);
|
const publicKey = decodeURIComponent(urlEncodedPublicKey as string);
|
||||||
const { isLoading, data } = useGetActiveSharedSecretByIdAndHashedHex(
|
const { isLoading, data } = useGetActiveSharedSecretByIdAndHashedHex(
|
||||||
id as string,
|
id as string,
|
||||||
@@ -46,12 +46,6 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
|
|||||||
initialState: false
|
initialState: false
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isUrlCopied) {
|
|
||||||
setTimeout(() => setIsUrlCopied(false), 2000);
|
|
||||||
}
|
|
||||||
}, [isUrlCopied]);
|
|
||||||
|
|
||||||
const copyUrlToClipboard = () => {
|
const copyUrlToClipboard = () => {
|
||||||
navigator.clipboard.writeText(decryptedSecret);
|
navigator.clipboard.writeText(decryptedSecret);
|
||||||
setIsUrlCopied(true);
|
setIsUrlCopied(true);
|
||||||
@@ -59,12 +53,12 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
|
|||||||
const { popUp, handlePopUpToggle } = usePopUp(["createSharedSecret"] as const);
|
const { popUp, handlePopUpToggle } = usePopUp(["createSharedSecret"] as const);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen dark:[color-scheme:dark] flex flex-col overflow-y-auto bg-gradient-to-tr from-mineshaft-700 to-bunker-800 text-gray-200">
|
<div className="flex h-screen flex-col overflow-y-auto bg-gradient-to-tr from-mineshaft-700 to-bunker-800 text-gray-200 dark:[color-scheme:dark]">
|
||||||
<Head>
|
<Head>
|
||||||
<title>Secret Shared | Infisical</title>
|
<title>Secret Shared | Infisical</title>
|
||||||
<link rel="icon" href="/infisical.ico" />
|
<link rel="icon" href="/infisical.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<div className="w-full flex flex-grow items-center justify-center dark:[color-scheme:dark]">
|
<div className="flex w-full flex-grow items-center justify-center dark:[color-scheme:dark]">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="mb-4 flex justify-center pt-8">
|
<div className="mb-4 flex justify-center pt-8">
|
||||||
<Link href="https://infisical.com">
|
<Link href="https://infisical.com">
|
||||||
@@ -77,9 +71,15 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
|
|||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex justify-center">
|
<div className="flex w-full justify-center">
|
||||||
<h1 className={`${id ? "max-w-sm mb-4": "max-w-md mt-4 mb-6"} bg-gradient-to-b from-white to-bunker-200 bg-clip-text px-4 text-center text-3xl font-medium text-transparent`}>
|
<h1
|
||||||
{id ? "Someone shared a secret via Infisical with you" : "Share a secret via Infisical"}
|
className={`${
|
||||||
|
id ? "mb-4 max-w-sm" : "mt-4 mb-6 max-w-md"
|
||||||
|
} bg-gradient-to-b from-white to-bunker-200 bg-clip-text px-4 text-center text-3xl font-medium text-transparent`}
|
||||||
|
>
|
||||||
|
{id
|
||||||
|
? "Someone shared a secret via Infisical with you"
|
||||||
|
: "Share a secret via Infisical"}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="m-auto mt-4 flex w-full max-w-2xl justify-center px-6">
|
<div className="m-auto mt-4 flex w-full max-w-2xl justify-center px-6">
|
||||||
@@ -114,7 +114,7 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
|
|||||||
className="mt-3 w-full text-sm font-normal leading-[1.2rem] text-mineshaft-300 hover:text-mineshaft-100"
|
className="mt-3 w-full text-sm font-normal leading-[1.2rem] text-mineshaft-300 hover:text-mineshaft-100"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className="bg-mineshaft-700 text-bunker-200 w-full py-3"
|
className="w-full bg-mineshaft-700 py-3 text-bunker-200"
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -126,34 +126,45 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="m-auto my-6 flex w-full max-w-xl justify-center px-8 px-4 sm:my-8">
|
<div className="m-auto my-6 flex w-full max-w-xl justify-center px-4 sm:my-8">
|
||||||
<div className="w-full border-t border-mineshaft-600" />
|
<div className="w-full border-t border-mineshaft-600" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col justify-center items-center m-auto max-w-2xl px-4 sm:px-6">
|
<div className="m-auto flex max-w-2xl flex-col items-center justify-center px-4 sm:px-6">
|
||||||
<div className="m-auto mb-12 flex flex max-w-2xl w-full flex-col justify-center rounded-md border border-primary-500/30 bg-primary/5 p-6 pt-5">
|
<div className="m-auto mb-12 flex w-full max-w-2xl flex-col justify-center rounded-md border border-primary-500/30 bg-primary/5 p-6 pt-5">
|
||||||
<p className="pb-2 font-semibold text-mineshaft-100 md:pb-3 text-lg md:text-xl w-full">
|
<p className="w-full pb-2 text-lg font-semibold text-mineshaft-100 md:pb-3 md:text-xl">
|
||||||
Open source <span className="bg-clip-text text-transparent bg-gradient-to-tr from-yellow-500 to-primary-500">secret management</span> for developers
|
Open source{" "}
|
||||||
|
<span className="bg-gradient-to-tr from-yellow-500 to-primary-500 bg-clip-text text-transparent">
|
||||||
|
secret management
|
||||||
|
</span>{" "}
|
||||||
|
for developers
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col sm:flex-row gap-x-4">
|
<div className="flex flex-col gap-x-4 sm:flex-row">
|
||||||
<p className="md:text-md text-md">
|
<p className="md:text-md text-md">
|
||||||
<a
|
<a
|
||||||
href="https://github.com/infisical/infisical"
|
href="https://github.com/infisical/infisical"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="bg-clip-text text-transparent bg-gradient-to-tr from-yellow-500 to-primary-500 text-bold"
|
className="text-bold bg-gradient-to-tr from-yellow-500 to-primary-500 bg-clip-text text-transparent"
|
||||||
>
|
>
|
||||||
Infisical
|
Infisical
|
||||||
</a>{" "} is the all-in-one secret management platform to securely manage secrets, configs, and certificates across your team and infrastructure.
|
</a>{" "}
|
||||||
|
is the all-in-one secret management platform to securely manage secrets, configs,
|
||||||
|
and certificates across your team and infrastructure.
|
||||||
</p>
|
</p>
|
||||||
<Link href="https://infisical.com">
|
<Link href="https://infisical.com">
|
||||||
<span className="mt-4 border border-mineshaft-400/40 w-[17.5rem] h-min py-2 px-3 rounded-md bg-mineshaft-600 cursor-pointer duration-200 hover:text-white hover:border-primary/60 hover:bg-primary/20">
|
<span className="mt-4 h-min w-[17.5rem] cursor-pointer rounded-md border border-mineshaft-400/40 bg-mineshaft-600 py-2 px-3 duration-200 hover:border-primary/60 hover:bg-primary/20 hover:text-white">
|
||||||
Try Infisical <FontAwesomeIcon icon={faArrowRight} className="pl-1"/>
|
Try Infisical <FontAwesomeIcon icon={faArrowRight} className="pl-1" />
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AddShareSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} isPublic inModal />
|
<AddShareSecretModal
|
||||||
|
popUp={popUp}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
isPublic
|
||||||
|
inModal
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-auto flex w-full items-center justify-center bg-mineshaft-600 p-2">
|
<div className="mt-auto flex w-full items-center justify-center bg-mineshaft-600 p-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user