Självstudie: Lägga till autentisering och behörigheter i ditt program när du använder Azure Web PubSub

I Skapa en chattapp lärde du dig hur du använder WebSocket-API:er för att skicka och ta emot data med Azure Web PubSub. Du märker att det för enkelhetens skull inte kräver någon autentisering. Även om Azure Web PubSub kräver att en åtkomsttoken är ansluten, negotiate behöver det API som används i självstudien för att generera åtkomsttoken inte autentisering. Vem som helst kan anropa det här API:et för att hämta en åtkomsttoken.

I ett verkligt program vill du vanligtvis att användaren loggar in först innan de kan använda ditt program. I den här självstudien lär du dig att integrera Web PubSub med autentiserings- och auktoriseringssystemet i ditt program för att göra det säkrare.

Du hittar det fullständiga kodexemplet i den här självstudien på GitHub.

I den här självstudien lär du dig att:

  • Aktivera GitHub-autentisering
  • Lägga till mellanprogram för autentisering i ditt program
  • Lägga till behörigheter till klienterna

Lägga till autentisering i chattrumsappen

Den här självstudien återanvänder chattprogrammet som skapats i Skapa en chattapp. Du kan också klona det fullständiga kodexemplet för chattappen från GitHub.

I den här självstudien lägger du till autentisering i chattprogrammet och integrerar det med Web PubSub.

Lägg först till GitHub-autentisering i chattrummet så att användaren kan använda ett GitHub-konto för att logga in.

  1. Installera beroenden.

    npm install --save cookie-parser
    npm install --save express-session
    npm install --save passport
    npm install --save passport-github2
    
  2. Leta upp server.js filen i din katalog och aktivera GitHub-autentisering genom att lägga till följande kod i server.js:

    const app = express();
    
    const users = {};
    passport.use(
      new GitHubStrategy({
        clientID: process.argv[3],
        clientSecret: process.argv[4]
      },
      (accessToken, refreshToken, profile, done) => {
        users[profile.id] = profile;
        return done(null, profile);
      }
    ));
    
    passport.serializeUser((user, done) => {
      done(null, user.id);
    });
    
    passport.deserializeUser((id, done) => {
      if (users[id]) return done(null, users[id]);
      return done(`invalid user id: ${id}`);
    });
    
    app.use(cookieParser());
    app.use(session({
      resave: false,
      saveUninitialized: true,
      secret: 'keyboard cat'
    }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.get('/auth/github', passport.authenticate('github', { scope: ['user:email'] }));
    app.get('/auth/github/callback', passport.authenticate('github', { successRedirect: '/' }));
    

    Föregående kod använder Passport.js för att aktivera GitHub-autentisering. Här är en enkel illustration av hur det fungerar:

    1. /auth/github omdirigerar till github.com för inloggning.
    2. När du har loggat in omdirigerar GitHub dig till /auth/github/callback med en kod för ditt program för att slutföra autentiseringen. (Om du vill se hur profilen som returneras från GitHub verifieras och sparas på servern kan du läsa det verifierade återanropet i passport.use().)
    3. När autentiseringen har slutförts omdirigeras du till webbplatsens startsida (/).

    Mer information om GitHub OAuth och Passport.js finns i följande artiklar:

    För att testa detta måste du först skapa en GitHub OAuth-app:

    1. Gå till https://www.github.com, öppna din profil och välj Inställningar> Developer-inställningar.
    2. Gå till OAuth-appar och välj Ny OAuth-app.
    3. Fyll i programnamnet och webbadressen till startsidan (URL:en kan vara vad du vill) och ange URL för auktoriseringsåteranrop till http://localhost:8080/auth/github/callback. Den här URL:en matchar motringnings-API:et som du exponerade på servern.
    4. När programmet har registrerats kopierar du klient-ID:t och väljer Generera en ny klienthemlighet.

    Kör kommandot nedan för att testa inställningarna, glöm inte att ersätta <connection-string>, <client-id>och <client-secret> med dina värden.

    export WebPubSubConnectionString="<connection-string>"
    export GitHubClientId="<client-id>"
    export GitHubClientSecret="<client-secret>"
    node server
    

    http://localhost:8080/auth/githubÖppna nu . Du omdirigeras till GitHub för att logga in. När du har loggat in omdirigeras du till chattprogrammet.

  3. Uppdatera chattrummet så att du kan använda den identitet du får från GitHub i stället för att fråga användaren om ett användarnamn.

    Uppdatera public/index.html för att anropa /negotiate direkt utan att skicka in ett användar-ID.

    let messages = document.querySelector('#messages');
    let res = await fetch(`/negotiate`);
    if (res.status === 401) {
      let m = document.createElement('p');
      m.innerHTML = 'Not authorized, click <a href="/auth/github">here</a> to login';
      messages.append(m);
      return;
    }
    let data = await res.json();
    let ws = new WebSocket(data.url);
    

    När en användare är inloggad bär begäran automatiskt användarens identitet via en cookie. Så du behöver bara kontrollera om användaren finns i req objektet och lägga till användarnamnet i webbpubenSub-åtkomsttoken:

    app.get('/negotiate', async (req, res) => {
      if (!req.user || !req.user.username) {
        res.status(401).send('missing user id');
        return;
      }
      let options = {
        userId: req.user.username
      };
      let token = await serviceClient.getClientAccessToken(options);
      res.json({
        url: token.url
      });
    });
    

    Kör nu servern igen och du ser ett "ej auktoriserat" meddelande för första gången du öppnar chattrummet. Välj inloggningslänken för att logga in och sedan ser du att den fungerar som tidigare.

Arbeta med behörigheter

I de föregående självstudierna lärde du dig att använda WebSocket.send() för att publicera meddelanden direkt till andra klienter med hjälp av subprotokol. I ett riktigt program kanske du inte vill att klienten ska kunna publicera eller prenumerera på någon grupp utan behörighetskontroll. I det här avsnittet ser du hur du styr klienter med hjälp av behörighetssystemet för Web PubSub.

I Web PubSub kan en klient utföra följande typer av åtgärder med subprotokol:

  • Skicka händelser till servern.
  • Publicera meddelanden till en grupp.
  • Anslut (prenumerera på) en grupp.

Att skicka en händelse till servern är standardåtgärden för klienten. Inget protokoll används, så det är alltid tillåtet. För att kunna publicera och prenumerera på en grupp måste klienten få behörighet. Det finns två sätt för servern att bevilja behörighet till klienter:

  • Ange roller när en klient är ansluten (roll är ett begrepp som representerar inledande behörigheter när en klient är ansluten).
  • Använd ett API för att bevilja behörighet till en klient när den är ansluten.

Om du vill ha behörighet att ansluta till en grupp måste klienten fortfarande ansluta till gruppen med hjälp av meddelandet "kopplingsgrupp" när den har fått behörigheten. Alternativt kan servern använda ett API för att lägga till klienten i en grupp, även om den inte har kopplingsbehörigheten.

Nu ska vi använda det här behörighetssystemet för att lägga till en ny funktion i chattrummet. Du lägger till en ny typ av användare med namnet administratör i chattrummet. Du låter administratören skicka systemmeddelanden (meddelanden som börjar med "[SYSTEM]") direkt från klienten.

Först måste du separera system- och användarmeddelanden i två olika grupper så att du kan styra deras behörigheter separat.

Ändra server.js för att skicka olika meddelanden till olika grupper:

let handler = new WebPubSubEventHandler(hubName, {
  path: '/eventhandler',
  handleConnect: (req, res) => {
    res.success({
      groups: ['system', 'message'],
    });
  },
  onConnected: req => {
    console.log(`${req.context.userId} connected`);
    serviceClient.group('system').sendToAll(`${req.context.userId} joined`, { contentType: 'text/plain' });
  },
  handleUserEvent: (req, res) => {
    if (req.context.eventName === 'message') {
      serviceClient.group('message').sendToAll({
        user: req.context.userId,
        message: req.data
      });
    }
    res.success();
  }
});

Föregående kod använder WebPubSubServiceClient.group().sendToAll() för att skicka meddelandet till en grupp i stället för hubben.

Eftersom meddelandet nu skickas till grupper måste du lägga till klienter i grupper så att de kan fortsätta att ta emot meddelanden. handleConnect Använd hanteraren för att lägga till klienter i grupper.

Kommentar

handleConnect utlöses när en klient försöker ansluta till Web PubSub. I den här hanteraren kan du returnera grupper och roller, så att tjänsten kan lägga till en anslutning till grupper eller bevilja roller så snart anslutningen upprättas. Tjänsten kan också använda res.fail() för att neka anslutningen.

Om du vill utlösa handleConnectgår du till händelsehanterarinställningarna i Azure-portalen och väljer Anslut i systemhändelser.

Du måste också uppdatera klientens HTML eftersom servern nu skickar JSON-meddelanden i stället för oformaterad text:

let ws = new WebSocket(data.url, 'json.webpubsub.azure.v1');
ws.onopen = () => console.log('connected');

ws.onmessage = event => {
  let m = document.createElement('p');
  let message = JSON.parse(event.data);
  switch (message.type) {
    case 'message':
      if (message.group === 'system') m.innerText = `[SYSTEM] ${message.data}`;
      else if (message.group === 'message') m.innerText = `[${message.data.user}] ${message.data.message}`;
      break;
  }
  messages.appendChild(m);
};

let message = document.querySelector('#message');
message.addEventListener('keypress', e => {
  if (e.charCode !== 13) return;
  ws.send(JSON.stringify({
    type: 'event',
    event: 'message',
    dataType: 'text',
    data: message.value
  }));
  message.value = '';
});

Ändra sedan klientkoden så att den skickas till systemgruppen när användarna väljer systemmeddelande:

<button id="system">system message</button>
...
<script>
  (async function() {
    ...
    let system = document.querySelector('#system');
    system.addEventListener('click', e => {
      ws.send(JSON.stringify({
        type: 'sendToGroup',
        group: 'system',
        dataType: 'text',
        data: message.value
      }));
      message.value = '';
    });
  })();
</script>

Som standard har klienten inte behörighet att skicka till någon grupp. Uppdatera serverkoden för att bevilja behörighet för administratörsanvändaren (för enkelhetens skull tillhandahålls administratörens ID som ett kommandoradsargument).

app.get('/negotiate', async (req, res) => {
  ...
  if (req.user.username === process.argv[2]) options.claims = { role: ['webpubsub.sendToGroup.system'] };
  let token = await serviceClient.getClientAccessToken(options);
});

node server <admin-id>Kör nu . Du ser att du kan skicka ett systemmeddelande till varje klient när du loggar in som <admin-id>.

Men om du loggar in som en annan användare händer ingenting när du väljer systemmeddelande. Du kan förvänta dig att tjänsten ger dig ett felmeddelande om att åtgärden inte är tillåten. Om du vill ge den här feedbacken kan du ange ackId när du publicerar meddelandet. När det ackId anges returnerar Web PubSub ett meddelande med matchning ackId för att ange om åtgärden har slutförts eller inte.

Ändra koden för att skicka ett systemmeddelande till följande kod:

let ackId = 0;
system.addEventListener('click', e => {
  ws.send(JSON.stringify({
    type: 'sendToGroup',
    group: 'system',
    ackId: ++ackId,
    dataType: 'text',
    data: message.value
    }));
  message.value = '';
});

Ändra även koden för att bearbeta meddelanden för att hantera ett ack meddelande:

ws.onmessage = event => {
  ...
  switch (message.type) {
    case 'ack':
      if (!message.success && message.error.name === 'Forbidden') m.innerText = 'No permission to send system message';
      break;
  }
};

Kör nu servern igen och logga in som en annan användare. Ett felmeddelande visas när du försöker skicka ett systemmeddelande.

Det fullständiga kodexemplet för den här självstudien finns på GitHub.

Nästa steg

I den här självstudien får du en grundläggande uppfattning om hur du ansluter till Web PubSub-tjänsten och hur du publicerar meddelanden till anslutna klienter med hjälp av subprotokol.

Mer information om hur du använder tjänsten Web PubSub finns i de andra självstudierna i dokumentationen.