mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-03 00:29:10 +00:00
add pass access token in headers
This commit is contained in:
105
src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx
Normal file
105
src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Check, Copy, Info, InfoIcon } from "lucide-react";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import CopyTextBox from "@app/components/CopyTextBox";
|
||||
|
||||
interface AccessTokenSectionProps {
|
||||
token: string;
|
||||
tokenId: string;
|
||||
resourceUrl: string;
|
||||
}
|
||||
|
||||
export default function AccessTokenSection({
|
||||
token,
|
||||
tokenId,
|
||||
resourceUrl
|
||||
}: AccessTokenSectionProps) {
|
||||
const { env } = useEnvContext();
|
||||
|
||||
const [copied, setCopied] = useState<string | null>(null);
|
||||
|
||||
const copyToClipboard = (text: string, type: string) => {
|
||||
navigator.clipboard.writeText(text);
|
||||
setCopied(type);
|
||||
setTimeout(() => setCopied(null), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-start space-x-2">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Your access token can be passed in two ways: as a query
|
||||
parameter or in the request headers. These must be passed
|
||||
from the client on every request for authenticated access.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="token" className="w-full mt-4">
|
||||
<TabsList className="grid grid-cols-2">
|
||||
<TabsTrigger value="token">Access Token</TabsTrigger>
|
||||
<TabsTrigger value="usage">Usage Examples</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="token" className="space-y-4">
|
||||
<div className="space-y-1">
|
||||
<div className="font-bold">Token ID</div>
|
||||
<CopyToClipboard text={tokenId} isLink={false} />
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<div className="font-bold">Token</div>
|
||||
<CopyToClipboard text={token} isLink={false} />
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="usage" className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-sm font-medium">Request Headers</h3>
|
||||
<CopyTextBox
|
||||
text={`${env.server.resourceAccessTokenHeadersId}: ${tokenId}
|
||||
${env.server.resourceAccessTokenHeadersToken}: ${token}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-sm font-medium">Query Parameter</h3>
|
||||
<CopyTextBox
|
||||
text={`${resourceUrl}?${env.server.resourceAccessTokenParam}=${tokenId}.${token}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Alert variant="neutral">
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
<AlertTitle className="font-semibold">
|
||||
Important Note
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
For security reasons, using headers is recommended
|
||||
over query parameters when possible, as query
|
||||
parameters may be logged in server logs or browser
|
||||
history.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<div className="text-sm text-muted-foreground mt-4">
|
||||
Keep your access token secure. Do not share it in publicly
|
||||
accessible areas or client-side code.
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
@@ -20,7 +19,6 @@ import {
|
||||
} from "@app/components/ui/select";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { InviteUserBody, InviteUserResponse } from "@server/routers/user";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -37,7 +35,6 @@ import {
|
||||
CredenzaTitle
|
||||
} from "@app/components/Credenza";
|
||||
import { useOrgContext } from "@app/hooks/useOrgContext";
|
||||
import { ListRolesResponse } from "@server/routers/role";
|
||||
import { formatAxiosError } from "@app/lib/api";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
@@ -58,12 +55,9 @@ import {
|
||||
CommandList
|
||||
} from "@app/components/ui/command";
|
||||
import { CheckIcon, ChevronsUpDown } from "lucide-react";
|
||||
import { register } from "module";
|
||||
import { Label } from "@app/components/ui/label";
|
||||
import { Checkbox } from "@app/components/ui/checkbox";
|
||||
import { GenerateAccessTokenResponse } from "@server/routers/accessToken";
|
||||
import {
|
||||
constructDirectShareLink,
|
||||
constructShareLink
|
||||
} from "@app/lib/shareLinks";
|
||||
import { ShareLinkRow } from "./ShareLinksTable";
|
||||
@@ -73,6 +67,7 @@ import {
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger
|
||||
} from "@app/components/ui/collapsible";
|
||||
import AccessTokenSection from "./AccessTokenUsage";
|
||||
|
||||
type FormProps = {
|
||||
open: boolean;
|
||||
@@ -100,7 +95,8 @@ export default function CreateShareLinkForm({
|
||||
const api = createApiClient({ env });
|
||||
|
||||
const [link, setLink] = useState<string | null>(null);
|
||||
const [directLink, setDirectLink] = useState<string | null>(null);
|
||||
const [accessTokenId, setAccessTokenId] = useState<string | null>(null);
|
||||
const [accessToken, setAccessToken] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [neverExpire, setNeverExpire] = useState(false);
|
||||
|
||||
@@ -226,12 +222,9 @@ export default function CreateShareLinkForm({
|
||||
const token = res.data.data;
|
||||
const link = constructShareLink(token.accessToken);
|
||||
setLink(link);
|
||||
const directLink = constructDirectShareLink(
|
||||
env.server.resourceAccessTokenParam,
|
||||
values.resourceUrl,
|
||||
token.accessToken
|
||||
);
|
||||
setDirectLink(directLink);
|
||||
|
||||
setAccessToken(token.accessToken);
|
||||
setAccessTokenId(token.accessTokenId);
|
||||
|
||||
const resource = resources.find(
|
||||
(r) => r.resourceId === values.resourceId
|
||||
@@ -515,8 +508,7 @@ export default function CreateShareLinkForm({
|
||||
className="p-0 flex items-center justify-between w-full"
|
||||
>
|
||||
<h4 className="text-sm font-semibold">
|
||||
See alternative share
|
||||
links
|
||||
See Access Token Usage
|
||||
</h4>
|
||||
<div>
|
||||
<ChevronsUpDown className="h-4 w-4" />
|
||||
@@ -528,26 +520,21 @@ export default function CreateShareLinkForm({
|
||||
</CollapsibleTrigger>
|
||||
</div>
|
||||
<CollapsibleContent className="space-y-2">
|
||||
{directLink && (
|
||||
{accessTokenId && accessToken && (
|
||||
<div className="space-y-2">
|
||||
<div className="mx-auto">
|
||||
<CopyTextBox
|
||||
text={directLink}
|
||||
wrapText={false}
|
||||
<AccessTokenSection
|
||||
tokenId={
|
||||
accessTokenId
|
||||
}
|
||||
token={accessToken}
|
||||
resourceUrl={
|
||||
form.getValues(
|
||||
"resourceUrl"
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
This link does not
|
||||
require visiting in a
|
||||
browser to complete the
|
||||
redirect. It contains
|
||||
the access token
|
||||
directly in the URL,
|
||||
which can be useful for
|
||||
sharing with clients
|
||||
that do not support
|
||||
redirects.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</CollapsibleContent>
|
||||
|
||||
@@ -617,7 +617,7 @@ PersistentKeepalive = 5`;
|
||||
</InfoSection>
|
||||
</InfoSections>
|
||||
|
||||
<Alert variant="default" className="">
|
||||
<Alert variant="neutral" className="">
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
<AlertTitle className="font-semibold">
|
||||
Save Your Credentials
|
||||
@@ -777,7 +777,7 @@ PersistentKeepalive = 5`;
|
||||
<SettingsSectionBody>
|
||||
<CopyTextBox text={wgConfig} />
|
||||
|
||||
<Alert variant="default">
|
||||
<Alert variant="neutral">
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
<AlertTitle className="font-semibold">
|
||||
Save Your Credentials
|
||||
|
||||
Reference in New Issue
Block a user