Bir giriş qapısı olan klubu təsəvvür edin. Mühafizəçi sənədinizi yoxlayır və qolunuza bilərzik taxır — bu autentifikasiyadır: «siz kimsiniz?». İçəridə hər otağın öz işçisi var: o, bilərziyə baxıb sizi buraxıb-buraxmamağa qərar verir — bu avtorizasiyadır: «sizə nə etmək olar?». REST-də «otaqlar» marşrutlardır və hər birini öz middleware-i bağlayır. GraphQL-də qapı birdir, /graphql, «otaqlar» isə sxemin sahələridir, ona görə yoxlamalar context-də və resolver-lərdə yaşayır. Bu dərsdə blog istifadəçini token ilə tanımağı öyrənəcək, başqalarının postlarını dəyişikliyə qarşı qoruyacağıq, GraphQL xətaları necə bildirir onu araşdıracağıq və serveri bahalı sorğulardan qoruyacağıq.

Bir qapı, çoxlu otaq

AutentifikasiyaAvtorizasiya
SualSiz kimsiniz?Sizə nə etmək olar?
GraphQL-də haradacontext: sorğu başına bir dəfəresolver-lər: hər sahədə

Müştəri JWT-ni Authorization: Bearer <token> başlığında göndərir (JWT-nin quruluşunu Node.js mövzusunda öyrənmisiniz; kitabxana eynidir — jose). Server tokeni istifadəçiyə və ya null-a çevirib context-ə qoyur, resolver-lər isə context.user-ə baxır.

Başlıqdakı token context-ə düşür, resolver isə üç nəticədən birini seçir
Başlıqdakı token context-ə düşür, resolver isə üç nəticədən birini seçir

Addım 1. Token veririk: login mutation-u

User modelinə passwordHash və role (Role enum-u: USER, ADMIN) əlavə edin və miqrasiyanı icra edin. Sxemdə login və me yaranır, authorId isə CreatePostInput-dan çıxır: «mən kiməm?» sualında müştərinin sözünə inanmaq olmaz, müəllif tokendən götürüləcək. email sahəsi ixtiyari olacaq — səbəbini aşağıda görəcəksiniz. Kitabxanaları quraşdırın: npm install jose bcrypt və npm install -D @types/bcrypt.

graphql
type User {
  id: ID!
  name: String!
  email: String # optional on purpose, see below
  posts: [Post!]!
}

input CreatePostInput {
  title: String!
  body: String!
  published: Boolean = false # no authorId: the author is the logged-in user
}

input LoginInput { email: String!, password: String! }

type AuthPayload {
  token: String
  user: User
  errors: [UserError!]!
}

type Query {
  me: User # new, the other fields as before
}

type Mutation {
  login(input: LoginInput!): AuthPayload # new
}

tokens.ts tokenləri verir və yoxlayır — JWT mövzusundakı eyni SignJWT və jwtVerify; gizli açar .env-də saxlanılır:

ts
// src/auth/tokens.ts
import { SignJWT, jwtVerify } from 'jose';

const key = new TextEncoder().encode(process.env.JWT_SECRET); // 32+ characters

export type AuthUser = { id: number; role: 'USER' | 'ADMIN' };

export function signToken(user: AuthUser): Promise<string> {
  return new SignJWT({ role: user.role })
    .setProtectedHeader({ alg: 'HS256' })
    .setSubject(String(user.id))
    .setExpirationTime('2h')
    .sign(key);
}

export async function readUser(header?: string): Promise<AuthUser | null> {
  if (!header?.startsWith('Bearer ')) return null;
  try {
    const { payload } = await jwtVerify(header.slice(7), key, {
      algorithms: ['HS256'],
    });
    const id = Number(payload.sub);
    if (!Number.isInteger(id)) return null;
    return { id, role: payload.role === 'ADMIN' ? 'ADMIN' : 'USER' };
  } catch {
    return null; // expired, forged or broken: treat as anonymous
  }
}