Access Control

SonicJs has a flexible authentication system allowing you to set various data access controls at a table and/or field level.

Its meant to be easy to understand and use, but also powerful enough to handle complex access control requirements.

Access control is defined in the schema file for each table.

The access property defines access control rules for interacting with the data. It allows granular control over who can perform what actions.

By default, all operations (GET, POST, PUT, DELETE) are permitted. If you don’t define any access control rules, everything is allowed. This is fine during early development, but you should restrict access to your data as soon as possible—ideally well before deploying your site online.

The access property has two main sub-properties: operations and fields.

Operations Control

Let's check out the access operation control rules for the posts table:

src/custom/db/schema/posts.ts:

...
export const access: ApiConfig["access"] = {
  operation: {
    read: true,
    create: isAdminOrEditor,
    update: isAdminOrUser,
    delete: isAdminOrUser,
  },
  ...

As we can see, the posts table has the following access control rules:

  1. Everyone can read the posts (GET)
  2. Admin or Editors role can create posts (POST)
  3. Admin or User who create can update (PUT)
  4. Admin or User who created can delete (DELETE)

Access Control Helpers

The are 4 access control helpers and you can easily add your own: src/db/config-helpers.ts

  1. isUser
  2. isAdmin
  3. isAdminOrEditor
  4. isAdminOrUser

For example, here is the core source code for isAdminOrEditor:

export function isAdminOrEditor(ctx: AppContext) {
  const user = ctx.locals.user
  const role = user?.role?.toLowerCase() || ''
  if (role === 'admin' || role === 'editor') {
    return true
  }
  return false
}

Fields Control

Often times, you may want to restrict access to specific fields. For example, you may want to prevent users from updating their email address.

Let's check out the access fields control rules for the users table:

src/db/schema/users.ts:

...
export const access: ApiConfig["access"] = {
  operation: {...}
  fields: {
    id: {
      read: (ctx, value, doc) => {
        return isAdmin(ctx) || isAdminOrUser(ctx, doc.id);
      },
    },
    email: {
      read: (ctx, value, doc) => {
        return isAdminOrUser(ctx, doc.id);
      },
    },
    password: {
      read:false,
      update: isAdminOrUser,
    },
    role: {
      read: (ctx, value, doc) => {
        return isAdminOrUser(ctx, doc.id);
      },
      update: isAdmin,
    },
  },
};
  ...