tutorial

50 Role-based Authorization di GraphQL


title: “50 Role-based Authorization di GraphQL: Membawa Keamanan & Skalabilitas ke Level Berikutnya” date: 2024-06-26 author: “Ricky Pratama” tags: [graphql, authorization, security, backend, graphql-security]

“Authorization bukan hanya tentang siapa yang bisa mengakses apa, namun juga tentang bagaimana membangun kontrol terbaik untuk tim dan produk Anda.”

Selama beberapa tahun terakhir, pengembangan backend semakin bergeser ke arah API-first, dan GraphQL telah menjelma menjadi bintang utama. Meski fleksibilitas GraphQL menghadirkan keuntungan besar, sisi authorization-nya tetap menjadi tantangan besar, khususnya ketika aplikasi makin tumbuh dan kebutuhan kontrol akses meningkat. Dalam artikel ini, saya ingin membagikan pengalaman dan beberapa best-practice dalam mengimplementasikan role-based authorization pada GraphQL — bahkan, jika dibutuhkan sampai ke 50 role lebih!

1. Kenapa Role-based Authorization Penting?

Sebelum kita deep-dive ke implementasi, kenapa sih role-based authorization begitu vital, apalagi di aplikasi berbasis GraphQL?

  • Fleksibilitas Query: GraphQL memungkinkan client request data apa saja; tanpa authorization, risiko data leakage tinggi.
  • Granular Access Control: Bisnis modern sering punya skenario kompleks, seperti peran user berbeda-beda, delegasi antar departemen, dan lain-lain — yang berarti rules-nya harus sangat granular.
  • Auditing & Compliance: Organisasi perlu mencatat siapa mengakses apa untuk audit dan kepatuhan (misal, GDPR, HIPAA).

2. Skema Role-based Authorization Umum

Jenis role yang sering kita jumpai biasanya sesederhana Admin, User, dan Guest. Namun, pada bisnis besar, role bisa jadi sangat spesifik:

  • Product Manager
  • Data Analyst
  • Sales Executive
  • Junior Engineer
  • Vendor Operator
  • dan seterusnya — bisa dengan mudah mencapai 20, 30, bahkan 50 role!

Setiap role membutuhkan kekuatan akses, query, bahkan mutation yang berbeda-beda.

3. Tantangannya di GraphQL

  1. Single Entry Point: GraphQL hanya punya satu endpoint (/graphql), berbeda dengan REST yang endpointnya bisa diprotek satu-satu.
  2. Dynamic Query: Client bisa bentuk query seenaknya; filtering field via schema mustahil tanpa middleware.
  3. Nested Authorization: Di query GraphQL, satu request bisa memanggil relasi dari beberapa tipe data sekaligus.

Lalu gimana solusinya kalau role bisnis mencapai 50 lebih? Mari kita breakdown step-nya satu-satu.


4. Strategi Implementation: Role-permission Matrix

Saya suka menggunakan Role-permission Matrix — sebuah mapping table yang mendefinisikan akses setiap role ke resource, baik di level query maupun field.

Contoh sederhana:

RoleQuery:usersQuery:reportsMutation:createPostField:user.email
Admin
User
Manager
Analyst

Sebagai backend engineer, Anda dapat menyimpannya dalam bentuk:

  • Konfigurasi hardcoded (file JS/TS/JSON/YAML)
  • Tabel database (untuk lebih dinamis)

5. Implementasi dengan Middleware / Directive

Salah satu cara paling fleksibel untuk mengimplementasikan role-based authorization di GraphQL adalah lewat custom directive (@auth(role: ...)) yang mudah ditempel di schema atau field tertentu.

Contoh Schema dengan Directive

type User {
  id: ID!
  name: String!
  email: String! @auth(role: ["Admin", "Manager"])
}

type Query {
  users: [User]! @auth(role: ["Admin", "Manager"])
  reports: [Report]! @auth(role: ["Admin", "Analyst"])
}

Implementasi Directive di Apollo Server (TypeScript):

import { SchemaDirectiveVisitor } from 'apollo-server-express';
import { defaultFieldResolver, GraphQLField } from 'graphql';

class AuthDirective extends SchemaDirectiveVisitor {
  visitFieldDefinition(field: GraphQLField<any, any>) {
    const { resolve = defaultFieldResolver } = field;
    const { role } = this.args;
    field.resolve = async function (...args) {
      const context = args[2];
      if (!context.user) throw new Error('Not authenticated');
      if (!role.includes(context.user.role)) {
        throw new Error('Not authorized');
      }
      return resolve.apply(this, args);
    };
  }
}

Contoh ini hanya untuk field-level; Anda bisa extend ke ObjectType dan Query/Mutation level.


6. Simulasi 50 Role

Bayangkan aplikasi Human Resource (HR) enterprise dengan 50 departments, masing-masing punya role sendiri-sendiri.

[
  { "role": "Admin", "permissions": ["*"] },
  { "role": "HR_Manager", "permissions": ["users", "reports"] },
  { "role": "Recruiter", "permissions": ["candidates", "interviews"] },
  ...
  { "role": "Sales_Executive", "permissions": ["salesData"] },
  { "role": "Engineer_Backend_Lead", "permissions": ["deploymentLogs"] }
  // up to 50!
]

Misal, users hanya bisa diakses oleh Admin & HR_Manager, sementara deploymentLogs hanya oleh Engineer_Backend_Lead.


7. Alur Authorisasi pada GraphQL Request

Mari kita visualisasikan proses authorisasi pada request GraphQL dengan 50 role berbeda:

flowchart TD
    A[Client: Send GraphQL Request] --> B[Parse JWT Token]
    B --> C{Valid User?}
    C -- No --> Z[Return 401]
    C -- Yes --> D[Extract User Role]
    D --> E[Matching pada Role-permission Matrix]
    E -- Allowed --> F[Proceed Resolver]
    E -- Denied --> Y[Return 403]
    F --> G[Return Data]

Pada setiap request:

  1. Token user diekstrak untuk mendapatkan role
  2. Role dicocokkan terhadap permission matrix
  3. Jika tidak match, request ditolak (403 Forbidden)
  4. Jika match, lanjut ke resolver dan response data

8. Scalable Approach untuk 50++ Roles

a. Gunakan Permission Pattern, Bukan Role Saja

Role bisa bertambah terus; debuah best-practice adalah memecah role menjadi permission group.

{
  "role": "Analyst_Accounting",
  "permissions": ["read:reports", "export:spreadsheets"]
}

Alih-alih hardcode role, sistem bisa cek permission exact.

b. Abstraction & Policy Layer

Gunakan policy engine seperti Casbin atau OPA untuk mengatur mapping role & permission.

c. Caching Role Matrix

Request ke DB setiap hit tidak efisien. Cache permission matrix di memory (misal Redis atau memory store simple) akan sangat membantu performa.


9. Contoh Middleware Authorisasi (Express + Apollo)

const rolePermissionMatrix = {
  Admin: ['users', 'reports', 'logs'],
  Analyst: ['reports'],
  Engineer: ['logs']
}

// context.user.role didapat dari JWT decoded
const authorizationMiddleware = (resolverName: string) => {
  return (parent, args, context, info) => {
    const userRole = context.user.role;
    const allowedPermissions = rolePermissionMatrix[userRole] || [];
    if (!allowedPermissions.includes(resolverName)) {
      throw new Error("Forbidden: No access");
    }
    return resolver(parent, args, context, info);
  }
}

// usage di resolver
const resolvers = {
  Query: {
    users: authorizationMiddleware('users')(async (parent, args, context) => {
      // ...
    }),
    reports: authorizationMiddleware('reports')(async (parent, args, context) => {
      // ...
    }),
  }
}

10. Kesimpulan

Dalam skenario enterprise, role-based authorization pada GraphQL bukan hanya “nice-to-have”, tapi must-have, apalagi ketika role mencapai puluhan atau bahkan ratusan. Menggunakan konsep role-permission matrix, directive, dan policy engine, arsitektur API kita menjadi scalable, maintainable, serta menjamin keamanan dan compliance terhadap bisnis.

Ingat: GraphQL itu powerful, tapi harus dibungkus dengan security mindset, terutama dalam urusan role dan permission!


Further Reading & Tools:


Mudah-mudahan insight dan best-practice di atas bisa membantu Anda membangun GraphQL APIs baru yang jauh lebih scalable, secure, dan siap menghadapi tantangan enterprise dengan 50 role+! 🚀

comments powered by Disqus