In the world where hackers are trying to brute force user accounts (23andMe breach 2023) and session tokens are being stolen (OKTA breach 2023) to impersonate authenticated users and run critical user actions, it is highly important for developers to maintain a tamper-proof audit log of all authentication events to keep their apps secure.
An audit log is a log service that’s used to keep track of all critical and sensitive user actions that take place across your applications. Events being logged could include sensitive file access, user logins, deleting or updating database records, etc.
What are the Advantages of an Tamperproof Audit Log?
- Identify Risk of Exposure
A robust tamperproof audit log can help quickly assess the risk of exposure after a data breach since you have logs of all user events and critical user actions that occurred in our app.
- Can’t be Tampered with
Since message hashes of Pangea Audit Logs have been designed to be stored in a cryptographically secure hash-tree called “Merkle Trees”, the logs are tamperproof and cryptographically verifiable. Thus Pangea Audit Logs can neither be changed nor destroyed once created.
- Asynchronous Logging
Since Pangea Audit Logs can be implemented asynchronously with our authentication system and APIs, it doesn’t affect the performance of our web apps.
Adding the Audit Trail to Our NextAuth.js App
In this tutorial, I will demonstrate how easy it is to add a tamper-proof audit log to an authentication setup. For the purposes of this tutorial, I’m going to use NextAuth.js - a popular JavaScript authentication library - to demonstrate how we can add secure audit logging to our JavaScript apps in just a few lines of code.
To follow along, head over to next-auth.js.org and follow the “Getting Started” tutorial. Once we’ve setup our Next.js app with NextAuth, we’re going to update the [...next-auth].ts
file to include an events
code block that will send data to the Pangea Audit Log API asynchronously on every signIn
and signOut
event.
import NextAuth from "next-auth"
import { User } from "next-auth"
// Imports to add
import { JWT } from "next-auth/jwt";
import auditLogToPangea from "@/utils/auditLog";
export const authOptions = {
// Configure one or more authentication providers
providers: [
// List all your providers here
// ...
],
// events code block to add
events: {
async signIn({ user, isNewUser }: { user: User, isNewUser?: boolean | undefined }) {
const auditLogData = {
email: user.email as string,
name: user.name as string,
message: isNewUser ? "User signed up" : "User logged in",
event: isNewUser ? "signup" : "login",
}
// Call pangea secure audit log on sign in
await auditLogToPangea(auditLogData);
},
async signOut({token}: {token: JWT}) {
const auditLogData = {
email: token.email as string,
name: token.name as string,
message: "User logged out",
event: "logout",
}
// Call pangea secure audit log on sign out
await auditLogToPangea(auditLogData);
}
}
// end events code block
}
export default NextAuth(authOptions)
Now that we’ve added code to make an audit log happen on every signIn and signOut event, let’s create the auditLogToPangea
function in the src/utils
folder that uses the pangea-node-sdk
to send log data to the Pangea secure audit log API.
Create the file auditLog.ts
import { PangeaConfig, AuditService, PangeaErrors } from "pangea-node-sdk";
const auditLogToPangea = async (logData: any) => {
const token = process.env.PANGEA_TOKEN as string;
const config = new PangeaConfig({ domain: process.env.PANGEA_DOMAIN });
const audit = new AuditService(token, config);
const data = {
"message": logData.message,
"action": logData.event,
"email": logData.email,
"name": logData.name,
"target": logData.target ? logData.target : "",
}
try {
console.log("Logging: %s", data);
const logResponse = await audit.log(data as any, { verbose: true });
console.log("Response: %s", logResponse.result);
return true;
} catch (err) {
if (err instanceof PangeaErrors.APIError) {
console.log(err.summary, err.pangeaResponse);
return false;
} else {
throw err;
}
}
}
export default auditLogToPangea;
You’re all set 🎉, now every time any user logs into our Next.js app using next-auth authentication setup, the user information along with the event is automatically audit logged and if we go to View Logs
section in the Pangea User Console, we should see something like the following:
Further Steps:
Now that we’ve seen how easy it is to add an audit log to our authentication setup, we can go ahead and add audit logging to all the APIs used to perform critical user actions such as fetching PII, updating sensitive user fields, billing transactions, etc. We could either add this directly for each API in the app or run it as middleware to be able to log all user information going in and out of the applications APIs.