Skip to content
Dashboard

Securing data in your Next.js app with Okta and OpenFGA

Principal Developer Advocate at Auth0

Link to headingClient-side vs. server-side code in Next.js

Link to headingConfidential vs. public environments

Link to headingServer and client components

Link to headingIntroducing a Data Access Layer into your application

Update data flow with additional checks in our authorization server.Update data flow with additional checks in our authorization server.
Update data flow with additional checks in our authorization server.

Link to headingAdding fine-grained authorization to our application

Link to headingGetting started with OpenFGA

model
schema 1.1
type user
type file
relations
define can_delete: owner or owner from parent
define can_share: owner or owner from parent
define can_view: viewer or owner or viewer from parent
define can_write: owner or owner from parent
define is_owned: owner
define is_shared: can_view but not owner
define owner: [user]
define parent: [folder]
define viewer: [user, user:*]
type folder
relations
define can_create_file: owner or owner from parent
define can_create_folder: owner or owner from parent
define can_share: owner or owner from parent
define can_view: viewer or owner or viewer from parent
define owner: [user]
define parent: [folder]
define viewer: [user, user:*] or owner or viewer from parent

import { OpenFgaClient } from '@openfga/sdk';
const fgaClient = new OpenFgaClient({
// A link to your OpenFGA instance
apiUrl: process.env.FGA_API_URL,
// The ID of your store, the collection of tuples, on OpenFGA
storeId: process.env.FGA_STORE_ID,
// The ID of your Authorization model. This changes with each change to the
// model, and can be overwritten with each check
authorizationModelId: process.env.FGA_MODEL_ID,
});

Link to headingChecking FGA in our DAL

export async function getFile(fileId) {
try {
// Check if the user is authenticated
if (await !isAuthenticated()) {
return { error: "Unauthorized" };
}
// The user is authenticated so we can grab their ID
const userId = await getUserId();
// Check with OpenFGA if the user can view the file we're trying to fetch
const { allowed } = await openfgaClient.check({
user: `user:${userId}`,
relation: "can_view",
object: `file:${fileId}`,
});
// If the user is not authorized, we'll show an error
if (!allowed) {
return { error: "Forbidden" };
}
// The user was authorized, so we'll fetch our file and return it
return await getFileFromStore(fileId);
} catch (error) {
return { error };
}
}

export async function uploadFile(parent, file) {
try {
// Check if the user is authenticated
if (await !isAuthenticated()) {
return { error: "Unauthorized" };
}
// The user is authenticated, so we can grab their ID
const userId = await getUserId();
// Check with OpenFGA if the user can create new files in the current location
const { allowed } = await openfgaClient.check({
user: `user:${userId}`,
relation: "can_create_file",
object: `folder:${parent}`,
});
// If the user is not authorized, we'll show an error
if (!allowed) {
return { error: "Forbidden" };
}
// Write the file to a persistent location
const {fileId} = await writeFile(file);
// Write OpenFGA tuples for the new file
await openfgaClient.writeTuples([
{
user: `user:${userId}`,
relation: "owner",
object: `file:${fileId}`,
},
{
user: `folder:${parent}`,
relation: "parent",
object: `file:${fileId}`,
},
]);
return file;
} catch (error) {
return { error };
}
}

Link to headingError handling

Link to headingConclusion