Skip to main content

Overview

PyqDeck uses Clerk for authentication. We leverage Clerk’s managed UI for sign-in/up and their Express middleware for backend session validation.

JWT Flow

  1. Frontend: Obtains a short-lived JWT (Session Token) from Clerk.
  2. Request: The token is sent in the Authorization: Bearer <token> header.
  3. Backend Middleware: clerkMiddleware() (from @clerk/express) validates the JWT using the CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY.
  4. Session Object: If valid, req.auth is populated with the user’s Clerk ID and metadata.

User Provisioning (syncUser)

We use a “lazy provisioning” strategy. Users are only created in our MongoDB when they make their first authenticated API request.

syncUser.middleware.js

This middleware runs after the Clerk middleware:
  1. Check Cache: It first checks if a user with the given clerkId exists in our database.
  2. Fetch Clerk Profile: If not found, it performs a server-side fetch to the Clerk API (api.clerk.com/v1/users/{id}) to get the user’s full profile (email, name, avatar).
  3. Upsert: It creates or updates the user record in MongoDB.
  4. Attach to Request: The database user object is attached to req.dbUser.
Note: We use a manual https request for the Clerk API fetch to avoid IPv6 resolution issues common in some Node.js environments.

Role-Based Access Control (RBAC)

We define three primary roles in the User model:
RolePermissions
normalView papers, search questions, add bookmarks, submit solutions.
editorAll normal permissions + Create/Edit universities, subjects, and papers.
adminFull system access, including user management and platform configuration.

Authorization Middleware

We use the authorize higher-order function in backend/src/middlewares/auth.middleware.js:
  • requireAuthentication: Ensures the user is logged in (has a valid Clerk session).
  • isEditor: Restricts access to users with editor or admin roles.
  • isAdmin: Restricts access to users with the admin role only.
// Example usage in routes
router.post("/", requireAuthentication, isAdmin, universityController.create);

Role Management

Roles can be managed in two ways:
  1. Clerk Public Metadata: If a role is set in the user’s public_metadata in Clerk, it will be synced to our database during the syncUser flow.
  2. Direct DB Edit: Admins can update roles directly in the database or via the Studio (Admin Panel).