Skip to content

Commit

Permalink
Merge pull request #5261 from mozilla/accounts-attached-clients
Browse files Browse the repository at this point in the history
Add admin view for clients attached to Monitor Accounts
  • Loading branch information
flozia authored Oct 29, 2024
2 parents 2ffbf2a + 1c48b16 commit 56f7c6b
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use client";

import { useEffect, useState } from "react";
import { getAttachedClientsAction } from "./actions";
import { FxaGetAccountAttachedClients } from "../../../../../../utils/fxa";

export const AttachedClients = () => {
const [data, setData] = useState<FxaGetAccountAttachedClients[]>();

useEffect(() => {
getAttachedClientsAction()
.then((attachedClients) => {
setData(attachedClients);
})
.catch((error) => {
console.error("Could not get attached clients", error);
});
}, []);

return data ? (
<pre>{JSON.stringify(data, null, 2)}</pre>
) : (
"No data available"
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use server";

import { notFound } from "next/navigation";
import { getAttachedClients } from "../../../../../../utils/fxa";
import { getServerSession } from "../../../../../functions/server/getServerSession";
import { isAdmin } from "../../../../../api/utils/auth";
import { logger } from "@sentry/utils";
import { captureException } from "@sentry/node";

export async function getAttachedClientsAction() {
const session = await getServerSession();

if (
!session?.user?.email ||
!isAdmin(session.user.email) ||
process.env.APP_ENV === "production"
) {
return notFound();
}

try {
const attachedClients = await getAttachedClients(
session?.user.subscriber?.fxa_access_token ?? "",
);
return attachedClients;
} catch (error) {
captureException(error);
logger.error("Could not get attached clients", {
error: JSON.stringify(error),
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { getServerSession } from "../../../../../functions/server/getServerSession";
import { notFound } from "next/navigation";
import { isAdmin } from "../../../../../api/utils/auth";
import { AttachedClients } from "./AttachedClients";

export default async function DevPage() {
const session = await getServerSession();

if (
!session?.user?.email ||
!isAdmin(session.user.email) ||
process.env.APP_ENV === "productions"
) {
return notFound();
}

return <AttachedClients />;
}
56 changes: 56 additions & 0 deletions src/utils/fxa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,61 @@ async function applyCoupon(
}
/* c8 ignore stop */

/**
* @see https://mozilla.github.io/ecosystem-platform/api#tag/Devices-and-Sessions/operation/getAccountAttached_clients
*/
export type FxaGetAccountAttachedClients = {
clientId: string;
deviceId: number;
sessionTokenId: string;
refreshTokenId: string;
isCurrentSession: boolean;
deviceType: string;
name: string;
createdTime: string;
lastAccessTime: string;
scope: string[];
userAgent: string;
createdTimeFormatted?: string;
approximateLastAccessTime?: number;
location?: {
city: string;
country: string;
state: string;
stateCode: string;
};
os?: string;
};

// Not covered by tests; mostly side-effects. See test-coverage.md#mock-heavy
/* c8 ignore start */
async function getAttachedClients(
bearerToken: string,
): Promise<FxaGetAccountAttachedClients[]> {
const endpointUrl = `${envVars.OAUTH_ACCOUNT_URI}/account/attached_clients`;
try {
const response = await fetch(endpointUrl, {
headers: {
Accept: "application/json",
Authorization: `Bearer ${bearerToken}`,
},
});
const responseJson = await response.json();
if (!response.ok) throw new Error(JSON.stringify(responseJson));
logger.info("get_fxa_attached_clients_success");
return responseJson as FxaGetAccountAttachedClients[];
} catch (e) {
if (e instanceof Error) {
logger.error("get_fxa_attached_clients", {
stack: e.stack,
message: e.message,
});
}
throw e;
}
}
/* c8 ignore stop */

// TODO: Add unit test when changing this code:
/* c8 ignore next 3 */
function getSha1(email: crypto.BinaryLike) {
Expand All @@ -364,4 +419,5 @@ export {
getBillingAndSubscriptions,
deleteSubscription,
applyCoupon,
getAttachedClients,
};

0 comments on commit 56f7c6b

Please sign in to comment.