Add GET /v1/credentials/totp to list recent 2FA codes per org (#3770)

This commit is contained in:
Marc Kelechava
2025-10-29 20:49:25 -07:00
committed by GitHub
parent c78ee6a8d0
commit cae59a3c19
10 changed files with 822 additions and 39 deletions

View File

@@ -0,0 +1,89 @@
import { getClient } from "@/api/AxiosClient";
import type { TotpCode, TotpCodeListParams } from "@/api/types";
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
import { isAxiosError } from "axios";
import { useMemo } from "react";
import { useQuery, type UseQueryOptions } from "@tanstack/react-query";
type QueryFnData = TotpCode[];
type QueryKey = ["totpCodes", TotpCodeListParams];
type Options = {
params: TotpCodeListParams;
enabled?: boolean;
queryOptions?: Omit<
UseQueryOptions<QueryFnData, unknown, QueryFnData, QueryKey>,
"queryKey" | "queryFn"
>;
};
type UseTotpCodesQueryReturn = ReturnType<
typeof useQuery<QueryFnData, unknown, QueryFnData, QueryKey>
> & {
isFeatureUnavailable: boolean;
isCredentialAccountMissing: boolean;
};
function useTotpCodesQuery({
params,
enabled = true,
queryOptions,
}: Options): UseTotpCodesQueryReturn {
const credentialGetter = useCredentialGetter();
const searchParams = useMemo(() => {
const result = new URLSearchParams();
if (params.totp_identifier) {
result.set("totp_identifier", params.totp_identifier);
}
if (params.workflow_run_id) {
result.set("workflow_run_id", params.workflow_run_id);
}
if (params.otp_type) {
result.set("otp_type", params.otp_type);
}
if (typeof params.limit === "number") {
result.set("limit", String(params.limit));
}
return result;
}, [params]);
const query = useQuery<QueryFnData, unknown, QueryFnData, QueryKey>({
queryKey: ["totpCodes", params],
enabled,
queryFn: async () => {
const client = await getClient(credentialGetter, "sans-api-v1");
const response = await client.get<QueryFnData>("/credentials/totp", {
params: searchParams,
});
return response.data;
},
retry(failureCount, error) {
if (isAxiosError(error) && error.response?.status === 404) {
return false;
}
return failureCount < 3;
},
...queryOptions,
});
const axiosError = isAxiosError(query.error) ? query.error : null;
const errorStatus = axiosError?.response?.status;
const errorDetail =
(axiosError?.response?.data as { detail?: string } | undefined)?.detail ??
"";
const isCredentialAccountMissing =
errorStatus === 404 && errorDetail.includes("Credential account not found");
const isFeatureUnavailable =
errorStatus === 404 && !isCredentialAccountMissing;
return {
...query,
isFeatureUnavailable,
isCredentialAccountMissing,
};
}
export { useTotpCodesQuery };