> ## Documentation Index
> Fetch the complete documentation index at: https://translations.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Auth0 を使用して MQTT でデバイスの認証と認可を行う方法。

# Auth0 を使用して MQTT でデバイスを認証および認可する

MQTT は、デバイスが他のシステムと通信する際によく使われる軽量なプロトコルです。**publish/subscribe** メッセージングパターン向けに設計されています。[MQTT](https://en.wikipedia.org/wiki/MQ_Telemetry_Transport) の詳細については、Wikipedia を参照してください。

一般に、構成要素は 3 つあります。

1. メッセージを送信する `パブリッシャー`
2. メッセージを受信する `サブスクライバー`
3. その両者を接続する `ブローカー`

`topics` (`channels` や `subjects` とも呼ばれます) という概念があり、メッセージはこれらに関連付けられます。トピックは、パブリッシャー と サブスクライバー の間でメッセージをルーティングするために使用されます。

MQTT プロトコルは、`usernames` と `passwords` に基づく基本的な認証メカニズムをサポートしています。これらの認証情報は `CONNECT` メッセージで送信されます。

この記事では、Node.js ベースの MQTT ブローカー である [mosca](https://github.com/moscajs/mosca) と [Auth0](https://auth0.com) の統合について説明します。この例では、Auth0 を使用して ブローカー に接続する `publishers` と `subscribers` を**認証**し、その後、メッセージのルーティングを**認可**します。

<Frame>
  <img src="https://mintcdn.com/translations/eVsQcTnbClN-oB7d/docs/images/cdy7uua7fh8z/1BAQm0lXvF7A3VqIciOagV/776a17e3372964acc3ac67a71a026e06/2023-09-22_13-15-29.png?fit=max&auto=format&n=eVsQcTnbClN-oB7d&q=85&s=7b02b87af22c615add6978757c853d85" alt="MQTT データフロー図" width="858" height="514" data-path="docs/images/cdy7uua7fh8z/1BAQm0lXvF7A3VqIciOagV/776a17e3372964acc3ac67a71a026e06/2023-09-22_13-15-29.png" />
</Frame>

<div id="components-of-the-solution">
  ## ソリューションの構成要素
</div>

<div id="the-broker">
  ### ブローカー
</div>

**mosca** は容易にホストでき、ほかのサーバーに組み込むこともできます。このサンプルでは、**mosca** サーバーを自前でホストするだけです。

```javascript lines expandable theme={null}
var mosca = require('mosca')
var Auth0Mosca = require('auth0mosca');

var settings = {
  port: 9999,
};

//'Thermostats' はすべてのデバイスが登録されているデータベース接続です。
var auth0 = new Auth0Mosca('https://eugeniop.auth0.com', '{Your Auth0 ClientID}', '{Your Auth0 Client Secret}','Thermostats');

//Moscaサーバーをセットアップする
var server = new mosca.Server(settings);

//認証と認可をmoscaに紐付ける
server.authenticate = auth0.authenticateWithCredentials();
server.authorizePublish = auth0.authorizePublish();
server.authorizeSubscribe = auth0.authorizeSubscribe();

server.on('ready', setup);

// mqttサーバーの準備が完了したときに発火する
function setup() {
    console.log('Mosca server is up and running');
}

server.on('clientConnected', function(client) {
  console.log('New connection: ', client.id );
});
```

これにより、ポート 9999 で MQTT メッセージを待ち受けるサーバーが作成されます。**mosca** では、操作の認証と認可に使用する 3 つの関数をオーバーライドできます。

このサンプルでは、これらの機能を実行するために、非常にシンプルなモジュール `auth0mosca` を使用しています。Auth0 は **mosca** と連携するように組み込まれています。

<div id="the-auth0mosca-module">
  ### Auth0Mosca モジュール
</div>

この小さな[モジュール](https://www.npmjs.org/package/auth0mosca)は、**mosca** で使用される 4 つの関数 `authenticateWithCredentials`、`authenticateWithJWT`、`authorizePublish`、`authorizeSubscribe` を提供します。

```javascript lines expandable theme={null}
var request = require('request');
var jwt = require('jsonwebtoken');

function Auth0Mosca(auth0Namespace, clientId, clientSecret, connection)
{
  this.auth0Namespace = auth0Namespace;
  this.connection = connection;
  this.clientId = clientId;
  this.clientSecret = clientSecret;
}

Auth0Mosca.prototype.authenticateWithJWT = function(){

  var self = this;

  return function(client, username, password, callback) {

    if( username !== 'JWT' ) { return callback("Invalid Credentials", false); }

    // console.log('Password:'+password);

    jwt.verify(password, self.clientSecret, function(err,profile){
          if( err ) { return callback("Error getting UserInfo", false); }
          console.log("Authenticated client " + profile.user_id);
          console.log(profile.topics);
          client.deviceProfile = profile;
          return callback(null, true);
        });
  }
}

Auth0Mosca.prototype.authenticateWithCredentials = function(){

  var self = this;

  return function(client, username, password, callback) {
    
    var data = {
        client_id:   self.clientId, // {client-name}
        username:    username.toString(),
        password:    password.toString(),
        connection:  self.connection,
        grant_type:  "password",
        scope: 'openid name email' //Details: https:///scopes
    };

    request.post({
        headers: {
                "Content-type": "application/json"
            },
        url: self.auth0Namespace + '/oauth/ro',
        body: JSON.stringify(data)
      }, function(e,r,b){
        if(e){
          console.log('Error in Authentication');
          return callback(e,false);
        }
        var r = JSON.parse(b);

        if( r.error ) { return callback( r, false); }

        jwt.verify(r.id_token, self.clientSecret, function(err,profile){
          if( err ) { return callback("Error getting UserInfo", false); }
          client.deviceProfile = profile;
          return callback(null, true);
        });
    });
  }
}

Auth0Mosca.prototype.authorizePublish = function() {
  return function (client, topic, payload, callback) {
   callback(null, client.deviceProfile && client.deviceProfile.topics && client.deviceProfile.topics.indexOf(topic) > -1);
  }
}

Auth0Mosca.prototype.authorizeSubscribe = function() {
  return function(client, topic, callback) {
  callback(null, client.deviceProfile && client.deviceProfile.topics && client.deviceProfile.topics.indexOf(topic) > -1);
}

module.exports = Auth0Mosca;
```

`authenticateWithCredentials` は、ブローカーとそれに対するすべての接続を認証するために [OAuth2 Resource Owner Password Credential Grant](/ja/docs/authenticate/protocols) を使用します。`パブリッシャー` または `サブスクライバー` がブローカーに **CONNECT** メッセージを送信するたびに、`authenticate` 関数が呼び出されます。この関数内で Auth0 の endpoint を呼び出し、デバイスの `username`/`password` を転送します。Auth0 はこれをアカウントストアに対して検証します (これはコード内の最初の `request.post` です) 。成功すると、デバイスのユーザープロファイルを取得するために <Tooltip tip="JSON Web Token (JWT): 2者間でクレームを安全に表現するために使用される標準的なIDトークン形式（多くの場合、アクセストークン形式でもあります）。" cta="用語集を見る" href="/ja/docs/glossary?term=JSON+Web+Token">JSON Web Token</Tooltip> (JWT) を検証して解析し、それを `サブスクライバー` または `パブリッシャー` を表す `client` オブジェクトに追加します。これは `jwt.verify` の呼び出しで行われます。

慣例として、ブローカーに接続されるすべてのデバイスは Auth0 にアカウントを持ちます。

Device Profile にも `topics` プロパティがあることに注目してください。これは、この特定のデバイスに許可されているすべてのトピックを含む配列です。上のスクリーンショットでは、`thermostat-1a` はトピック `temperature` と `config` へのパブリッシュ (またはサブスクライブ) を許可されます。

`authorizePublish` 関数と `authorizeSubscribe` 関数は、要求された特定のトピックがこのリストに含まれていることを確認するだけです。

`authenticateWithJWT` は、`password` フィールドに JWT が含まれていることを想定しています。この場合のフローは少し異なります。

1. パブリッシャー と サブスクライバー がトークンを取得します
2. JWT を送信して `mosca` に接続します
3. `mosca` が JWT を検証します
4. メッセージが送信され、サブスクライバー に再送信されます

<Frame>
  <img src="https://mintcdn.com/translations/3nS3prIggmJG9TUI/docs/images/cdy7uua7fh8z/38KV0HggAdQDkLxqZuAOph/e134f43c290d432064f288e549a59789/2023-09-22_13-15-47.png?fit=max&auto=format&n=3nS3prIggmJG9TUI&q=85&s=a44c30160f92208993b27d3d3061d037" alt="MQTT JSON Web Token データフロー " width="858" height="482" data-path="docs/images/cdy7uua7fh8z/38KV0HggAdQDkLxqZuAOph/e134f43c290d432064f288e549a59789/2023-09-22_13-15-47.png" />
</Frame>

パブリッシャーとサブスクライバーは、何らかの方法で JWT を取得します。ブローカーは、もはや Auth0 と通信する必要がないことに注意してください。JWT は自己完結型であり、署名に使用したシークレットを使って検証できます。

<div id="the-publisher">
  ### パブリッシャー
</div>

このサンプルでは、パブリッシャーは `mqtt` モジュールを使用し、適切な認証情報を設定したシンプルな Node.js プログラムです。

```javascript lines expandable theme={null}
var mqtt = require('mqtt')
  , host = 'localhost'
  , port = '9999';

var settings = {
  keepalive: 1000,
  protocolId: 'MQIsdp',
  protocolVersion: 3,
  clientId: 'Thermostat 1a',
  username:'thermostat-1a',
  password:'the password'
}

// クライアント接続
var client = mqtt.createClient(port, host, settings);

setInterval(sendTemperature, 2000, client);

function sendTemperature(client){
  var t = {
    T: Math.random() * 100,
    Units: "C"
  };

  client.publish('temperature', JSON.stringify(t));
}
```

もちろん、ここでの `username` と `password` は、Auth0 に保存されているものと一致している必要があります。

<div id="the-subscriber">
  ### サブスクライバー
</div>

サブスクライバーは、パブリッシャーと非常によく似ています。

```javascript lines expandable theme={null}
var mqtt = require('mqtt')
  , host = 'localhost'
  , port = '9999';

var settings = {
  keepalive: 1000,
  protocolId: 'MQIsdp',
  protocolVersion: 3,
  clientId: 'Reader-X1',
  username:'reader-X1',
  password:'the password'
}

// クライアント接続
var client = mqtt.createClient(port, host, settings);


client.subscribe('temperature');

client.on('message', function(topic, message) {

  if(topic ==='temperature')
  {
    console.log('New reading', message);
  }
});
```

<div id="summary">
  ## 概要
</div>

これは、さまざまなシナリオで Auth0 を簡単に利用できることを示しています。デバイスの管理には Auth0 のユーザーストアが使用されています。もちろん、時間、場所、device\_id などの別の条件に基づいて、より高度な認可ルールを記述することもできます。これらはすべて、追加のユーザープロファイル属性や Rules を使って、非常に簡単に実装できます。また、柔軟な Auth0 Profile を拡張して、任意の項目 (この例の `topics` など) をサポートできることも示しています。

Rules の詳細については、[Auth0 Rules](/ja/docs/customize/rules) を参照してください。

安全でないネットワーク上で認証情報 (`username`/`password`) を送信するのは、決してよい考えではありません。トランスポート層のセキュリティを提供し、メッセージ内容の漏えいを防ぐ実装もあります。たとえば、**mosca** は TLS をサポートしています。すべてのトラフィックが閉じたネットワーク内で完結するのでない限り、本番環境ではこちらが選ばれるでしょう。

<div id="acknowledgements">
  ### 謝辞
</div>

この記事のレビューをしてくださり、またすばらしい **mosca** を開発してくださった Matteo Collina 氏に深く感謝します。
