GraphQL adalah alternatif populer untuk arsitektur RESTful API tradisional, menawarkan kueri data dan bahasa manipulasi yang fleksibel dan efisien untuk API. Dengan itu Dengan meningkatnya adopsi, prioritas keamanan API GraphQL menjadi semakin penting untuk melindungi aplikasi dari akses tidak sah dan potensi data pelanggaran.

Salah satu pendekatan efektif untuk mengamankan API GraphQL adalah dengan menerapkan JSON Web Tokens (JWTs). JWT menyediakan metode yang aman dan efisien untuk memberikan akses ke sumber daya yang dilindungi dan melakukan tindakan resmi, memastikan komunikasi yang aman antara klien dan API.

Otentikasi dan Otorisasi di API GraphQL

Berbeda dengan REST API, API GraphQL biasanya memiliki satu titik akhir yang memungkinkan klien meminta berbagai jumlah data secara dinamis dalam kueri mereka. Meskipun fleksibilitas ini adalah kekuatannya, hal ini juga meningkatkan risiko potensi serangan keamanan seperti rusaknya kerentanan kontrol akses.

Untuk memitigasi risiko ini, penting untuk menerapkan proses autentikasi dan otorisasi yang kuat, termasuk menentukan izin akses dengan benar. Dengan melakukan hal ini, Anda menjamin bahwa hanya pengguna resmi yang dapat mengakses sumber daya yang dilindungi, dan pada akhirnya, mengurangi risiko potensi pelanggaran keamanan dan kehilangan data.

Anda dapat menemukan kode proyek ini di dalamnya GitHub gudang.

Siapkan Server Apollo Express.js

Server Apollo adalah implementasi server GraphQL yang banyak digunakan untuk API GraphQL. Anda dapat menggunakannya untuk dengan mudah membuat skema GraphQL, menentukan penyelesai, dan mengelola berbagai sumber data untuk API Anda.

Untuk menyiapkan Server Apollo Express.js, buat dan buka folder proyek:

mkdir graphql-API-jwt
cd graphql-API-jwt

Selanjutnya, jalankan perintah ini untuk menginisialisasi proyek Node.js baru menggunakan npm, manajer paket Node:

npm init --yes

Sekarang, instal paket-paket ini.

npm install apollo-server graphql mongoose jsonwebtokens dotenv

Terakhir, buat a server.js file di direktori root, dan atur server Anda dengan kode ini:

const { ApolloServer } = require('apollo-server');
const mongoose = require('mongoose');
require('dotenv').config();

const typeDefs = require("./graphql/typeDefs");
const resolvers = require("./graphql/resolvers");

const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ req }),
});

const MONGO_URI = process.env.MONGO_URI;

mongoose
.connect(MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to DB");
return server.listen({ port: 5000 });
})
.then((res) => {
console.log(`Server running at ${res.url}`);
})
.catch(err => {
console.log(err.message);
});

Server GraphQL diatur dengan typeDefs Dan pemecah masalah parameter, menentukan skema dan operasi yang dapat ditangani API. Itu konteks opsi mengonfigurasi objek req ke konteks setiap penyelesai, yang akan memungkinkan server mengakses detail spesifik permintaan seperti nilai header.

Buat Basis Data MongoDB

Untuk membuat koneksi database, pertama membuat basis data MongoDB atau menyiapkan cluster di MongoDB Atlas. Kemudian, salin string URI koneksi database yang disediakan, buat a .env file, dan masukkan string koneksi sebagai berikut:

MONGO_URI=""

Tentukan Model Data

Tentukan model data menggunakan Mongoose. Buat yang baru model/pengguna.js file dan sertakan kode berikut:

const {model, Schema} = require('mongoose');

const userSchema = new Schema({
name: String,
password: String,
role: String
});

module.exports = model('user', userSchema);

Tentukan Skema GraphQL

Dalam API GraphQL, skema mendefinisikan struktur data yang dapat dikueri, serta menguraikannya operasi yang tersedia (kueri dan mutasi) yang dapat Anda lakukan untuk berinteraksi dengan data melalui API.

Untuk menentukan skema, buat folder baru di direktori root proyek Anda dan beri nama grafikql. Di dalam folder ini, tambahkan dua file: typeDefs.js Dan Resolver.js.

Dalam typeDefs.js file, sertakan kode berikut:

const { gql } = require("apollo-server");

const typeDefs = gql`
type User {
id: ID!
name: String!
password: String!
role: String!
}
input UserInput {
name: String!
password: String!
role: String!
}
type TokenResult {
message: String
token: String
}
type Query {
users: [User]
}
type Mutation {
register(userInput: UserInput): User
login(name: String!, password: String!, role: String!): TokenResult
}
`;

module.exports = typeDefs;

Buat Resolver untuk API GraphQL

Fungsi penyelesai menentukan bagaimana data diambil sebagai respons terhadap kueri dan mutasi klien, serta bidang lain yang ditentukan dalam skema. Saat klien mengirimkan kueri atau mutasi, server GraphQL memicu penyelesai terkait untuk memproses dan mengembalikan data yang diperlukan dari berbagai sumber, seperti database atau API.

Untuk menerapkan autentikasi dan otorisasi menggunakan JSON Web Tokens (JWTs), tentukan penyelesai mutasi register dan login. Ini akan menangani proses registrasi dan otentikasi pengguna. Kemudian, buat pemecah kueri pengambilan data yang hanya dapat diakses oleh pengguna yang diautentikasi dan diberi otorisasi.

Namun pertama-tama, tentukan fungsi untuk menghasilkan dan memverifikasi JWT. Dalam Resolver.js file, mulailah dengan menambahkan impor berikut.

const User = require("../models/user");
const jwt = require('jsonwebtoken');
const secretKey = process.env.SECRET_KEY;

Pastikan untuk menambahkan kunci rahasia yang akan Anda gunakan untuk menandatangani token web JSON ke file .env.

SECRET_KEY = '';

Untuk menghasilkan token autentikasi, sertakan fungsi berikut, yang juga menentukan atribut unik untuk token JWT, misalnya waktu kedaluwarsa. Selain itu, Anda dapat memasukkan atribut lain seperti dikeluarkan pada waktu berdasarkan persyaratan aplikasi spesifik Anda.

functiongenerateToken(user) {
const token = jwt.sign(
{ id: user.id, role: user.role },
secretKey,
{ expiresIn: '1h', algorithm: 'HS256' }
 );

return token;
}

Sekarang, terapkan logika verifikasi token untuk memvalidasi token JWT yang disertakan dalam permintaan HTTP berikutnya.

functionverifyToken(token) {
if (!token) {
thrownewError('Token not provided');
}

try {
const decoded = jwt.verify(token, secretKey, { algorithms: ['HS256'] });
return decoded;
} catch (err) {
thrownewError('Invalid token');
}
}

Fungsi ini akan mengambil token sebagai masukan, memverifikasi validitasnya menggunakan kunci rahasia yang ditentukan, dan mengembalikan token yang didekodekan jika valid, jika tidak, akan muncul kesalahan yang menunjukkan token tidak valid.

Tentukan Resolver API

Untuk menentukan penyelesai API GraphQL, Anda perlu menguraikan operasi spesifik yang akan dikelolanya, dalam hal ini, operasi pendaftaran dan login pengguna. Pertama, buat a pemecah masalah objek yang akan menampung fungsi penyelesai, lalu tentukan operasi mutasi berikut:

const resolvers = {
Mutation: {
register: async (_, { userInput: { name, password, role } }) => {
if (!name || !password || !role) {
thrownewError('Name password, and role required');
}

const newUser = new User({
name: name,
password: password,
role: role,
});

try {
const response = await newUser.save();

return {
id: response._id,
...response._doc,
};
} catch (error) {
console.error(error);
thrownewError('Failed to create user');
}
},
login: async (_, { name, password }) => {
try {
const user = await User.findOne({ name: name });

if (!user) {
thrownewError('User not found');
}

if (password !== user.password) {
thrownewError('Incorrect password');
}

const token = generateToken(user);

if (!token) {
thrownewError('Failed to generate token');
}

return {
message: 'Login successful',
token: token,
};
} catch (error) {
console.error(error);
thrownewError('Login failed');
}
}
},

Itu daftar mutasi menangani proses registrasi dengan menambahkan data pengguna baru ke database. Selagi Gabung mutasi mengelola login pengguna—jika autentikasi berhasil, ini akan menghasilkan token JWT, serta mengembalikan pesan sukses sebagai respons.

Sekarang, sertakan pemecah kueri untuk mengambil data pengguna. Untuk memastikan bahwa kueri ini hanya dapat diakses oleh pengguna yang diautentikasi dan diberi otorisasi, sertakan logika otorisasi untuk membatasi akses hanya kepada pengguna dengan Admin peran.

Pada dasarnya, kueri pertama-tama akan memeriksa validitas token dan kemudian, peran pengguna. Jika pemeriksaan otorisasi berhasil, kueri penyelesai akan melanjutkan untuk mengambil dan mengembalikan data pengguna dari database.

 Query: {
users: async (parent, args, context) => {
try {
const token = context.req.headers.authorization || '';
const decodedToken = verifyToken(token);

if (decodedToken.role !== 'Admin') {
thrownew ('Unauthorized. Only Admins can access this data.');
}

const users = await User.find({}, { name: 1, _id: 1, role:1 });
return users;
} catch (error) {
console.error(error);
thrownewError('Failed to fetch users');
}
},
},
};

Terakhir, mulai server pengembangan:

node server.js

Luar biasa! Sekarang, lanjutkan dan uji fungsionalitas API menggunakan kotak pasir API Server Apollo di browser Anda. Misalnya, Anda dapat menggunakan daftar mutasi untuk menambahkan data pengguna baru ke database, dan kemudian, Gabung mutasi untuk mengautentikasi pengguna.

Terakhir, tambahkan token JWT ke bagian header otorisasi dan lanjutkan untuk menanyakan database untuk data pengguna.

Mengamankan API GraphQL

Otentikasi dan otorisasi adalah komponen penting untuk mengamankan API GraphQL. Meskipun demikian, penting untuk menyadari bahwa hal-hal tersebut saja mungkin tidak cukup untuk menjamin keamanan yang komprehensif. Anda harus menerapkan langkah-langkah keamanan tambahan seperti validasi input, dan enkripsi data sensitif.

Dengan mengadopsi pendekatan keamanan komprehensif, Anda dapat melindungi API Anda dari berbagai potensi serangan.