Implementering av brukerautentisering i Express.js ved hjelp av JWT-er

GraphQL er et populært alternativ til tradisjonell RESTful API-arkitektur, og tilbyr et fleksibelt og effektivt dataspørrings- og manipulasjonsspråk for APIer. Med den økende bruken blir det stadig viktigere å prioritere sikkerheten til GraphQL APIer for å beskytte applikasjoner mot uautorisert tilgang og potensielle datainnbrudd.

En effektiv tilnærming for å sikre GraphQL APIer er å implementere JSON Web Tokens (JWTs). JWT-er gir en sikker og effektiv metode for å gi tilgang til beskyttede ressurser og utføre autoriserte handlinger, og sikrer sikker kommunikasjon mellom klienter og API-er.

Autentisering og autorisasjon i GraphQL APIer

I motsetning til REST APIer, har GraphQL APIer vanligvis et enkelt endepunkt som lar klienter dynamisk be om forskjellige mengder data i spørringene sine. Selv om denne fleksibiliteten er dens styrke, øker den også risikoen for potensielle sikkerhetsangrep som for eksempel ødelagte sikkerhetsproblemer.

For å redusere denne risikoen er det viktig å implementere robuste autentiserings- og autorisasjonsprosesser, inkludert riktig definering av tilgangstillatelser. Ved å gjøre det garanterer du at bare autoriserte brukere kan få tilgang til beskyttede ressurser, og til slutt reduserer du risikoen for potensielle sikkerhetsbrudd og tap av data.

Du kan finne dette prosjektets kode i sin GitHub oppbevaringssted.

Sett opp en Express.js Apollo-server

Apollo server er en mye brukt GraphQL-serverimplementering for GraphQL APIer. Du kan bruke den til å enkelt bygge GraphQL-skjemaer, definere resolvere og administrere forskjellige datakilder for API-ene dine.

For å sette opp en Express.js Apollo Server, opprette og åpne en prosjektmappe:

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

Kjør deretter denne kommandoen for å initialisere et nytt Node.js-prosjekt ved å bruke npm, Node-pakkebehandleren:

 npm init --yes 

Installer nå disse pakkene.

 npm install apollo-server graphql mongoose jsonwebtokens dotenv 

Til slutt oppretter du en server.js-fil i rotkatalogen, og setter opp serveren din med denne koden:

 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);
  });

GraphQL-serveren er satt opp med typeDefs og resolvers-parametere, og spesifiserer skjemaet og operasjonene som API-en kan håndtere. Kontekstalternativet konfigurerer req-objektet til konteksten til hver resolver, noe som vil tillate serveren å få tilgang til forespørselsspesifikke detaljer som overskriftsverdier.

  Løs Gmail-feil 78754 på Outlook

Opprett en MongoDB-database

For å etablere databaseforbindelsen må du først opprette en MongoDB-database eller sette opp en klynge på MongoDB Atlas. Kopier deretter den oppgitte URI-strengen for databasetilkoblingen, lag en .env-fil og skriv inn tilkoblingsstrengen som følger:

 MONGO_URI="<mongo_connection_uri>"

Definer datamodellen

Definer en datamodell ved å bruke Mongoose. Opprett en ny models/user.js-fil og inkluder følgende kode:

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

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

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

Definer GraphQL-skjemaet

I en GraphQL API definerer skjemaet strukturen til dataene som kan spørres, samt skisserer tilgjengelige operasjoner (spørringer og mutasjoner) som du kan utføre for å samhandle med data gjennom API.

For å definere et skjema, opprette en ny mappe i rotkatalogen til prosjektet og gi den navnet graphql. Inne i denne mappen legger du til to filer: typeDefs.js og resolvers.js.

Inkluder følgende kode i typeDefs.js-filen:

 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;

Opprett løsere for GraphQL API

Resolverfunksjoner bestemmer hvordan data hentes som svar på klientforespørsler og mutasjoner, samt andre felt definert i skjemaet. Når en klient sender en spørring eller mutasjon, utløser GraphQL-serveren de tilsvarende resolverne til å behandle og returnere de nødvendige dataene fra ulike kilder, for eksempel databaser eller APIer.

  Hvordan ta opp teammøter for fremtidig referanse [+6 Tools]

For å implementere autentisering og autorisasjon ved hjelp av JSON Web Tokens (JWTs), definer resolvere for register- og påloggingsmutasjonene. Disse vil håndtere prosessene med brukerregistrering og autentisering. Deretter oppretter du en søkeløser for datahenting som bare vil være tilgjengelig for autentiserte og autoriserte brukere.

Men først, definer funksjonene for å generere og verifisere JWT-ene. I resolvers.js-filen begynner du med å legge til følgende importer.

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

Sørg for å legge til den hemmelige nøkkelen du vil bruke til å signere JSON-netttokens til .env-filen.

 SECRET_KEY = '<my_Secret_Key>'; 

For å generere et autentiseringstoken, inkluderer følgende funksjon, som også spesifiserer unike attributter for JWT-tokenet, f.eks. utløpstiden. I tillegg kan du inkludere andre attributter som utstedt på et tidspunkt basert på dine spesifikke applikasjonskrav.

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

  return token;
}

Implementer nå tokenverifiseringslogikken for å validere JWT-tokenene som er inkludert i påfølgende HTTP-forespørsler.

 function verifyToken(token) {
  if (!token) {
    throw new Error('Token not provided');
  }

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

Denne funksjonen vil ta et token som input, verifisere dets gyldighet ved hjelp av den spesifiserte hemmelige nøkkelen, og returnere det dekodede tokenet hvis det er gyldig, ellers gir en feilmelding som indikerer et ugyldig token.

Definer API-løsere

For å definere resolvere for GraphQL API, må du skissere de spesifikke operasjonene den skal administrere, i dette tilfellet brukerregistrering og påloggingsoperasjoner. Først oppretter du et resolverobjekt som skal inneholde resolverfunksjonene, og definer deretter følgende mutasjonsoperasjoner:

 const resolvers = {
  Mutation: {
    register: async (_, { userInput: { name, password, role } }) => {
      if (!name || !password || !role) {
        throw new Error('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);
        throw new Error('Failed to create user');
      }
    },
    login: async (_, { name, password }) => {
      try {
        const user = await User.findOne({ name: name });

        if (!user) {
          throw new Error('User not found');
       }

        if (password !== user.password) {
          throw new Error('Incorrect password');
        }

        const token = generateToken(user);

        if (!token) {
          throw new Error('Failed to generate token');
        }

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

Registermutasjonen håndterer registreringsprosessen ved å legge de nye brukerdataene til databasen. Mens påloggingsmutasjonen administrerer brukerpålogginger – ved vellykket autentisering vil den generere et JWT-token, samt returnere en suksessmelding i svaret.

  X Vanlige HTML-koder å vite for nybegynnere

Ta med spørringsløseren for å hente brukerdata. For å sikre at denne spørringen bare vil være tilgjengelig for autentiserte og autoriserte brukere, inkluderer du autorisasjonslogikk for å begrense tilgangen til kun brukere med en administratorrolle.

I hovedsak vil spørringen først sjekke gyldigheten til tokenet og deretter brukerrollen. Hvis autorisasjonssjekken er vellykket, fortsetter resolver-spørringen for å hente og returnere brukernes data fra databasen.

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

        if (decodedToken.role !== 'Admin') {
          throw new ('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);
        throw new Error('Failed to fetch users');
      }
    },
  },
};

Til slutt, start utviklingsserveren:

 node server.js 

Rått! Nå, fortsett og test funksjonaliteten til API ved å bruke Apollo Server API-sandkassen i nettleseren din. Du kan for eksempel bruke registermutasjonen til å legge til nye brukerdata i databasen, og deretter påloggingsmutasjonen for å autentisere brukeren.

Til slutt legger du til JWT-tokenet i autorisasjonshodedelen og fortsett med å spørre databasen etter brukerdata.

Sikring av GraphQL APIer

Autentisering og autorisasjon er avgjørende komponenter for å sikre GraphQL APIer. Ikke desto mindre er det viktig å erkjenne at de alene kanskje ikke er tilstrekkelige for å sikre omfattende sikkerhet. Du bør implementere ytterligere sikkerhetstiltak som inndatavalidering og kryptering av sensitive data.

Ved å ta i bruk en omfattende sikkerhetstilnærming kan du beskytte API-ene dine mot ulike potensielle angrep.