Tutorial: Menambahkan autentikasi dan izin ke aplikasi Anda saat menggunakan Azure Web PubSub

Dalam Membangun aplikasi obrolan, Anda mempelajari cara menggunakan API WebSocket untuk mengirim dan menerima data dengan Azure Web PubSub. Anda melihat bahwa, untuk kesederhanaan, itu tidak memerlukan autentikasi apa pun. Meskipun Azure Web PubSub memerlukan token akses untuk dihubungkan, negotiate API yang digunakan dalam tutorial untuk membuat token akses tidak memerlukan autentikasi. Siapa pun dapat menghubungi API ini untuk mendapatkan token akses.

Dalam aplikasi dunia nyata, Anda biasanya ingin pengguna untuk masuk terlebih dahulu, sebelum mereka dapat menggunakan aplikasi Anda. Dalam tutorial ini, Anda mempelajari cara mengintegrasikan Azure Web PubSub dengan sistem autentikasi dan otorisasi aplikasi Anda, untuk membuatnya lebih aman.

Anda dapat menemukan sampel kode lengkap dari tutorial ini di GitHub.

Dalam tutorial ini, Anda akan mempelajari cara:

  • Mengaktifkan autentikasi GitHub
  • Menambahkan middleware autentikasi ke aplikasi Anda
  • Menambahkan izin ke klien

Menambahkan autentikasi ke aplikasi ruang obrolan

Tutorial ini menggunakan kembali aplikasi obrolan yang dibuat di Membangun aplikasi obrolan. Anda juga dapat mengkloning contoh kode lengkap untuk aplikasi obrolan dari GitHub.

Dalam tutorial ini, Anda menambahkan autentikasi ke aplikasi obrolan dan mengintegrasikannya dengan Azure Web PubSub.

Pertama, mari kita tambahkan autentikasi GitHub ke ruang obrolan sehingga pengguna dapat menggunakan akun GitHub untuk masuk.

  1. Instal dependensi.

    npm install --save cookie-parser
    npm install --save express-session
    npm install --save passport
    npm install --save passport-github2
    
  2. server.js Temukan file di direktori Anda dan aktifkan autentikasi GitHub dengan menambahkan kode berikut ke 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: '/' }));
    

    Kode di atas menggunakan Passport.js untuk mengaktifkan autentikasi GitHub. Berikut adalah ilustrasi sederhana tentang cara kerjanya:

    1. /auth/github dialihkan ke github.com untuk masuk.
    2. Setelah Anda masuk, GitHub mengarahkan Anda ke /auth/github/callback dengan kode untuk aplikasi Anda guna menyelesaikan autentikasi. (Untuk melihat bagaimana profil yang dikembalikan dari GitHub diverifikasi dan bertahan di server, lihat panggilan balik terverifikasi di passport.use().)
    3. Setelah autentikasi selesai, Anda akan diarahkan ke beranda (/) situs.

    Untuk detail lebih lanjut GitHub OAuth dan Passport.js, lihat artikel berikut:

    Untuk menguji ini, Anda harus terlebih dahulu membuat aplikasi GitHub OAuth:

    1. Buka https://www.github.com, buka profil Anda, dan pilih Pengaturan>Pengaturan pengembang.
    2. Buka Aplikasi OAuth, dan pilih Aplikasi OAuth Baru.
    3. Isi nama aplikasi dan URL beranda (URL bisa apa saja yang Anda suka), dan setel URL panggilan balik Otorisasi ke http://localhost:8080/auth/github/callback. URL ini cocok dengan API panggilan balik yang Anda tunjukkan di server.
    4. Setelah aplikasi terdaftar, salin ID klien dan pilih Buat rahasia klien baru.

    Jalankan perintah di bawah ini untuk menguji pengaturan, jangan lupa untuk mengganti <connection-string>, <client-id>, dan <client-secret> dengan nilai Anda.

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

    Sekarang buka http://localhost:8080/auth/github. Anda diarahkan ke GitHub untuk masuk. Setelah Masuk, Anda diarahkan ke aplikasi obrolan.

  3. Perbarui ruang obrolan untuk memanfaatkan identitas yang Anda dapatkan dari GitHub, alih-alih meminta nama pengguna kepada pengguna.

    Perbarui public/index.html untuk langsung menelepon /negotiate tanpa memberikan ID pengguna.

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

    Saat pengguna masuk, permintaan secara otomatis membawa identitas pengguna melalui cookie. Sehingga Anda hanya perlu memeriksa apakah pengguna ada di objek req, dan menambahkan nama pengguna ke token akses Azure Web PubSub:

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

    Sekarang jalankan ulang server, dan Anda melihat pesan "tidak diotorisasi" untuk pertama kalinya Anda membuka ruang obrolan. Pilih tautan masuk untuk masuk, lalu Anda melihatnya berfungsi seperti sebelumnya.

Berfungsi dengan adanya izin

Dalam tutorial sebelumnya, Anda belajar menggunakan WebSocket.send() untuk memublikasikan pesan secara langsung ke klien lain dengan menggunakan subprotokol. Dalam aplikasi nyata, Anda mungkin tidak ingin klien dapat memublikasikan atau berlangganan grup mana pun tanpa kontrol izin. Di bagian ini, Anda akan melihat cara mengontrol klien dengan menggunakan sistem izin Web PubSub.

Di Azure Web PubSub, klien dapat melakukan jenis operasi berikut dengan subprotokol:

  • Mengirim kejadian ke server.
  • Menerbitkan pesan ke grup.
  • Bergabung (berlangganan ke) grup.

Mengirim kejadian ke server adalah operasi default klien. Tidak ada protokol yang digunakan, jadi tindakan tersebut selalu diperbolehkan. Untuk menerbitkan dan berlangganan ke suatu grup, klien perlu mendapatkan izin. Ada dua cara bagi server untuk memberikan izin kepada klien:

  • Menentukan peran saat klien terhubung (peran adalah konsep untuk mewakili izin awal saat klien terhubung).
  • Menggunakan API untuk memberikan izin kepada klien setelah tersambung.

Untuk izin bergabung dengan grup, klien masih perlu bergabung dengan grup dengan menggunakan pesan "bergabung dengan grup" setelah mendapat izin. Atau, server dapat menggunakan API untuk menambahkan klien ke grup, meskipun tidak memiliki izin bergabung.

Sekarang mari kita gunakan sistem izin ini untuk menambahkan fitur baru ke ruang obrolan. Anda menambahkan jenis pengguna baru yang disebut administrator ke ruang obrolan. Anda mengizinkan administrator mengirim pesan sistem (pesan yang dimulai dengan "[SYSTEM]") langsung dari klien.

Pertama, Anda perlu memisahkan sistem dan pesan pengguna ke dalam dua grup yang berbeda sehingga Anda dapat mengontrol izin mereka secara terpisah.

Ubah server.js untuk mengirim pesan yang berbeda ke grup yang berbeda:

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

Kode sebelumnya menggunakan WebPubSubServiceClient.group().sendToAll() untuk mengirim pesan ke grup, bukan ke hub.

Karena pesan sekarang dikirim ke grup, Anda perlu menambahkan klien ke grup sehingga mereka dapat terus menerima pesan. Gunakan penghandel handleConnect untuk menambahkan klien ke grup.

Catatan

handleConnect dipicu ketika klien mencoba tersambung ke Azure Web PubSub. Dalam penghandel ini, Anda dapat mengembalikan grup dan peran, sehingga layanan dapat menambahkan koneksi ke grup atau memberikan peran, segera setelah koneksi dibuat. Layanan juga dapat menggunakan res.fail() untuk menolak sambungan.

Untuk memicu handleConnect, buka setelan penghandel peristiwa di portal Microsoft Azure, dan pilih sambungkan di kejadian sistem.

Anda juga perlu memperbarui HTML klien karena sekarang server mengirim pesan JSON, bukan teks biasa:

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

Kemudian ubah kode klien untuk dikirim ke grup sistem ketika pengguna memilih pesan sistem:

<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>

Secara default, klien tidak memiliki izin untuk mengirim ke grup mana pun. Perbarui kode server untuk memberikan izin bagi pengguna admin (untuk kesederhanaan, ID admin disediakan sebagai argumen baris perintah).

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

Sekarang jalankan node server <admin-id>. Anda melihat bahwa Anda dapat mengirim pesan sistem ke setiap klien ketika Anda masuk sebagai <admin-id>.

Namun jika Anda masuk sebagai pengguna yang berbeda, saat Anda memilih pesan sistem, maka tidak akan terjadi apa-apa. Anda mungkin berharap layanan memberi kesalahan untuk memberi tahu Anda bahwa operasi tidak diizinkan. Untuk memberikan tanggapan ini, Anda dapat menyetel ackId ​​saat Anda memublikasikan pesan. Setiap kali ackId ditentukan, Web PubSub mengembalikan pesan dengan pencocokan ackId untuk menunjukkan apakah operasi telah berhasil atau tidak.

Ubah kode pengiriman pesan sistem ke kode berikut:

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

Juga ubah kode pesan pemrosesan untuk menangani pesan ack:

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

Sekarang jalankan kembali server, dan masuk sebagai pengguna yang berbeda. Anda melihat pesan kesalahan saat mencoba mengirim pesan sistem.

Sampel kode lengkap dari tutorial ini dapat ditemukan di GitHub.

Langkah berikutnya

Tutorial ini memberikan Anda ide dasar tentang cara menyambungkan ke layanan Azure Web PubSub, dan cara memublikasikan pesan ke klien yang tersambung dengan menggunakan subprotokol.

Untuk mempelajari lebih lanjut tentang penggunaan layanan Azure Web PubSub, baca tutorial lain yang tersedia di dokumentasi.