Skip to content

Commit

Permalink
fix: retrieve internal user id instead and pass it to the UserSignIn …
Browse files Browse the repository at this point in the history
…audit log instead of using the Cognito sub id
  • Loading branch information
craigzour committed Feb 21, 2024
1 parent a7e75a8 commit d9cbd3f
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 18 deletions.
12 changes: 4 additions & 8 deletions lib/tests/users.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { Session } from "next-auth";
import { logEvent } from "@lib/auditLogs";
jest.mock("@lib/auditLogs");
const mockedLogEvent = jest.mocked(logEvent, { shallow: true });
import { JWT } from "next-auth/jwt";

describe("User query tests should fail gracefully", () => {
it("getOrCreateUser should fail gracefully - create", async () => {
Expand All @@ -26,7 +25,7 @@ describe("User query tests should fail gracefully", () => {
})
);

const result = await getOrCreateUser({ email: "[email protected]" } as JWT);
const result = await getOrCreateUser("", "[email protected]", undefined);
expect(result).toEqual(null);
expect(mockedLogEvent).not.toBeCalled();
});
Expand All @@ -45,7 +44,7 @@ describe("User query tests should fail gracefully", () => {
clientVersion: "4.3.2",
})
);
const result = await getOrCreateUser({ email: "[email protected]" } as JWT);
const result = await getOrCreateUser("", "[email protected]", undefined);
expect(result).toEqual(null);
expect(mockedLogEvent).not.toBeCalled();
});
Expand Down Expand Up @@ -80,7 +79,7 @@ describe("getOrCreateUser", () => {

(prismaMock.user.findUnique as jest.MockedFunction<any>).mockResolvedValue(user);

const result = await getOrCreateUser({ email: "[email protected]" } as JWT);
const result = await getOrCreateUser("", "[email protected]", undefined);
expect(result).toMatchObject(user);
expect(mockedLogEvent).not.toBeCalled();
});
Expand All @@ -99,10 +98,7 @@ describe("getOrCreateUser", () => {
});
(prismaMock.user.create as jest.MockedFunction<any>).mockResolvedValue(user);

const result = await getOrCreateUser({
name: "test",
email: "[email protected]",
} as JWT);
const result = await getOrCreateUser("test", "[email protected]", undefined);

expect(result).toMatchObject(user);
expect(prismaMock.user.create).toBeCalledWith({
Expand Down
11 changes: 5 additions & 6 deletions lib/users.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { prisma, prismaErrors } from "@lib/integration/prismaConnector";
import { JWT } from "next-auth/jwt";
import { AccessControlError, checkPrivileges } from "@lib/privileges";
import { NagwareResult, UserAbility } from "./types";
import { logEvent } from "./auditLogs";
Expand All @@ -16,11 +15,11 @@ import { activeStatusUpdate } from "@lib/cache/userActiveStatus";
* Get or Create a user if a record does not exist
* @returns A User Object
*/
export const getOrCreateUser = async ({
name,
email,
picture,
}: JWT): Promise<{
export const getOrCreateUser = async (
name: string,
email: string,
picture: string | null | undefined
): Promise<{
newlyRegistered?: boolean;
name: string | null;
email: string;
Expand Down
25 changes: 21 additions & 4 deletions pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,22 @@ export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma),
events: {
async signIn({ user }) {
logEvent(user.id, { type: "User", id: user.id }, "UserSignIn");
if (!user.name || !user.email)
throw new Error(
"Could not produce the UserSignIn audit log because of missing name and/or email"
);

const internalUser = await getOrCreateUser(user.name, user.email, user.image);

if (internalUser === null)
throw new Error(`Could not retrieve existing user with email: ${user.email}`);

logEvent(
internalUser.id,
{ type: "User", id: internalUser.id },
"UserSignIn",
`Cognito user unique identifier (sub): ${user.id}`
);
},
async signOut({ token }) {
logEvent(token.userId, { type: "User", id: token.userId }, "UserSignOut");
Expand All @@ -126,16 +141,18 @@ export const authOptions: NextAuthOptions = {
async jwt({ token, account }) {
// account is only available on the first call to the JWT function
if (account?.provider) {
if (!token.email) {
if (!token.email)
throw new Error(`JWT token does not have an email for user with name ${token.name}`);
}
const user = await getOrCreateUser(token);

const user = await getOrCreateUser(token.name, token.email, token.picture);

if (user === null)
throw new Error(`Could not get or create user with email: ${token.email}`);

token.userId = user.id;
token.lastLoginTime = new Date();
token.newlyRegistered = user.newlyRegistered;

// If name isn't passed in by the provider, use the name from the database
if (!token.name) {
token.name = user.name ?? "";
Expand Down

0 comments on commit d9cbd3f

Please sign in to comment.