Autorizzazione dei Campi nelle Mutations GraphQL: Guida Completa
L’autorizzazione nelle mutations di GraphQL è fondamentale per garantire che solo utenti autorizzati possano modificare o accedere a determinati dati. In un ambiente dove diverse tipologie di utenti interagiscono con la stessa API, è essenziale implementare controlli granulari non solo a livello di rotte o endpoint, ma anche a livello di singoli campi e operazioni all’interno delle mutations.
In questa guida, esploreremo come implementare l’autorizzazione dei campi nelle mutations di GraphQL, utilizzando tecniche come middleware, direttive personalizzate, e best practices per garantire la sicurezza dei dati.
Perché l’Autorizzazione nelle Mutations è Importante?
L’autorizzazione nelle mutations è cruciale per prevenire accessi non autorizzati o modifiche ai dati sensibili. Senza un adeguato controllo, un utente potrebbe tentare di manipolare dati che non gli appartengono o di eseguire operazioni non consentite. Garantire l’autorizzazione corretta per ogni campo nelle mutations è una pratica essenziale per mantenere l’integrità e la sicurezza del sistema.
Strategie di Autorizzazione nelle Mutations
1. Middleware di Autorizzazione
Una delle tecniche più comuni per gestire l’autorizzazione è l’utilizzo di middleware. I middleware possono essere applicati a livello di mutations per verificare se l’utente ha i permessi necessari per eseguire l’operazione.
Esempio di Middleware in Apollo Server
const { ApolloServer, gql } = require("apollo-server");
// Definizione dello schema
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
}
type Mutation {
updateUser(id: ID!, name: String, email: String): User
}
`;
// Mock del database
const users = [
{ id: "1", name: "Alice", email: "alice@example.com" },
{ id: "2", name: "Bob", email: "bob@example.com" },
];
// Resolvers con controllo di autorizzazione
const resolvers = {
Mutation: {
updateUser: (parent, args, context) => {
const user = users.find((user) => user.id === args.id);
if (!user) {
throw new Error("Utente non trovato");
}
// Controllo di autorizzazione
if (context.user.id !== args.id) {
throw new Error("Non autorizzato");
}
// Aggiornamento dei campi
if (args.name) user.name = args.name;
if (args.email) user.email = args.email;
return user;
},
},
};
// Middleware di autenticazione (esempio semplificato)
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
const token = req.headers.authorization || "";
// Mock di un controllo di autenticazione
const user = { id: "1", role: "user" }; // Dovrebbe essere verificato dal token
return { user };
},
});
server.listen().then(({ url }) => {
console.log(`Server pronto su ${url}`);
});
In questo esempio, un controllo di autorizzazione è stato implementato direttamente all’interno del resolver della mutation updateUser
. Se l’utente nel contesto non corrisponde all’ID dell’utente che si sta tentando di aggiornare, viene lanciato un errore di autorizzazione.
2. Direttive Personalizzate
Le direttive personalizzate di GraphQL offrono un modo elegante per implementare autorizzazioni direttamente nello schema. Le direttive possono essere applicate a specifici campi o mutations per garantire che solo utenti autorizzati possano accedere o modificarli.
Esempio di Direttiva Personalizzata
- Definizione della Direttiva
const { SchemaDirectiveVisitor } = require("apollo-server");
const { defaultFieldResolver } = require("graphql");
class AuthDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field;
const { role } = this.args;
field.resolve = async function (...args) {
const context = args[2];
if (context.user.role !== role) {
throw new Error("Non autorizzato");
}
return resolve.apply(this, args);
};
}
}
- Applicazione della Direttiva nello Schema
const typeDefs = gql`
directive @auth(role: String) on FIELD_DEFINITION
type User {
id: ID!
name: String! @auth(role: "admin")
email: String!
}
type Mutation {
updateUser(id: ID!, name: String, email: String): User @auth(role: "admin")
}
`;
- Configurazione di Apollo Server con la Direttiva
const server = new ApolloServer({
typeDefs,
resolvers,
schemaDirectives: {
auth: AuthDirective,
},
context: ({ req }) => {
const user = { id: "1", role: "user" }; // Simulazione di autenticazione
return { user };
},
});
Con questa implementazione, solo gli utenti con il ruolo “admin” possono modificare il campo name
dell’utente o eseguire la mutation updateUser
.
3. Controllo Granulare nei Resolvers
Un altro approccio consiste nell’implementare controlli di autorizzazione direttamente all’interno dei resolvers per ogni singolo campo o operazione.
Esempio di Controllo Granulare
const resolvers = {
Mutation: {
updateUser: (parent, args, context) => {
const user = users.find((user) => user.id === args.id);
if (!user) {
throw new Error("Utente non trovato");
}
// Controllo di autorizzazione per il nome
if (args.name && context.user.role !== "admin") {
throw new Error("Non autorizzato a modificare il nome");
}
// Controllo di autorizzazione per l'email
if (args.email && context.user.id !== args.id) {
throw new Error("Non autorizzato a modificare l'email");
}
// Aggiornamento dei campi
if (args.name) user.name = args.name;
if (args.email) user.email = args.email;
return user;
},
},
};
In questo caso, ogni campo che potrebbe essere aggiornato viene controllato singolarmente per garantire che l’utente abbia le autorizzazioni necessarie.
Best Practices per l’Autorizzazione nelle Mutations
- Principio del Minimo Privilegio: Concedi solo le autorizzazioni necessarie a ciascun utente o ruolo, limitando l’accesso ai campi sensibili.
- Modularità : Implementa l’autorizzazione in modo modulare, utilizzando middleware e direttive per mantenere il codice manutenibile e riutilizzabile.
- Logging e Monitoraggio: Registra le operazioni sensibili e monitora le attivitĂ per identificare accessi non autorizzati.
- Test di Sicurezza: Esegui test di sicurezza per verificare che le autorizzazioni siano applicate correttamente e che non ci siano falle.
- Gestione dei Ruoli: Implementa un sistema di gestione dei ruoli per facilitare l’assegnazione e il controllo delle autorizzazioni.
Conclusione
L’autorizzazione dei campi nelle mutations di GraphQL è essenziale per garantire che solo utenti autorizzati possano accedere e modificare i dati sensibili. Utilizzando middleware, direttive personalizzate e controlli granulari nei resolvers, puoi costruire un sistema sicuro e flessibile che protegge le risorse della tua API. Seguendo le best practices, sarai in grado di implementare autorizzazioni robuste e garantire la sicurezza del tuo sistema.