socket.handshake.query
現在、データをconnect
に設定できますが、 reconnect
が起動される前に参照によって更新することはできません。 これにより、更新トークンを使用してソケット接続を承認するときに問題が発生します。
//client
io.connect("wss://socket.server.com", {
query : {
token:"something dynamic"
}
});
サーバーが再起動され、クライアントがreconnect
場合、ソケットを再認証するときにトークンが古くなっている可能性があります。
//server
io.use(function(socket, next) {
var query = socket.handshake.query;
if(isValidToken(query.token)){
next();//will never fire if the token has been updated
}
});
クエリをfunction
:
io.connect("wss://socket.server.com", {
query : function(){
return {
token:"something dynamic"
}
}
});
現在のSocketコンストラクターの場合:
if (opts && opts.query) {
this.query = opts.query;
}
これに変更できます:
if(opts && opts.query) {
this.query = typeof opts.query == 'function' && opts.query.call(this) || opts.query;
}
+1
バグ#1086を修正すると、問題も解決するはずです。 接続が成功した後にクエリオプションを変更すると、古いsocket.io-clientバージョンで機能していましたが、現在のバージョンではクエリオブジェクトへの参照が失われます:(
@reeltimedoktor現在、 reconnect_attempt
イベントでquery
オブジェクトを更新できるはずです。
socket.on('reconnect_attempt', function () {
socket.io.opts.query = { token: /* ... */ };
});
@darrachequesneの解決策は私にはうまくいかないようです:(
私はクライアントでsocket.io-client
バージョン2.3.0
しており、ノードサーバーにはsocket.io
バージョン2.3.0
ます。
クライアントで私はこれを行っています:
socket.on('reconnect_attempt', async () => {
const newToken = await getNewToken();
socket.io.opts.query = {
token: latestToken,
};
});
そして、サーバーはio.use()
渡されたミドルウェアを使用してトークンを認証しています(ソケットが接続または再接続しているときに起動する必要があります、afaik?)。
現時点では、サーバー認証が再接続に失敗しており、トークンの有効期限が切れていると言っています-新しいトークンがsocket.io.opts.query.token
に適切に割り当てられていないようです-理由は何ですか?
ありがとう :)
更新:認証トークンが保存されているソケットオブジェクトには、 socket.io.engine.query.token
、 socket.io.engine.transport.query.token
などのさまざまな場所があるようです。 socket.io.opts.query.token
を設定しても、これらは自動的に入力されません。 ただし、これらを変更しても、サーバーが「見る」ものには影響しません。それでも、古いトークンのみが見えます。
トークンは、ソケットオブジェクトの一部のurlフィールドにURLパラメータとして含まれているようです。 おそらく、これらも更新する必要がありますか?
さらに調査した後、上のトークンを設定しているようですsocket.io.opts.query.token
再接続の再認証のためのトークンを変更するのに十分である-何か他のものは、おそらく、私のアプリのためにfirebase認証トークンの生成とは何か問題があると思われます。 したがって、これに遭遇した他の人にとっては、 @ darrachequesneのソリューションが機能します!
わかりました。修正したと思います。「reconnect_attempt」コールバックで新しいトークンを同期的に取得していることを確認する必要があります。つまり、これを行わないでください。
socket.on('reconnect_attempt', async () => {
const newToken = await getNewToken(); // won't work
socket.io.opts.query = {
token: latestToken,
};
});
新しいトークンが設定される前にサーバー側の認証が行われていたようです。 トークンを同期的に取得する場合(以前に取得して保存し、リスナーコールバックで取得するだけで)、サーバーは再接続認証に間に合うように新しい認証トークンを取得します。
"socket.io-client": "3.0.4"
TS2341: Property 'opts' is private and only accessible within class 'Manager'.
エラーが発生します。 "typescript": "4.1.3"
わかりました。修正したと思います。「reconnect_attempt」コールバックで新しいトークンを_同期的に_取得していることを確認する必要があります。つまり、これを行わないでください。
socket.on('reconnect_attempt', async () => { const newToken = await getNewToken(); // won't work socket.io.opts.query = { token: latestToken, }; });
新しいトークンが設定される前にサーバー側の認証が行われていたようです。 トークンを同期的に取得する場合(以前に取得して保存し、リスナーコールバックで取得するだけで)、サーバーは再接続認証に間に合うように新しい認証トークンを取得します。
同様の問題があります。 しかし、私の現在の回避策は、認証が失敗したときにサーバー側からdisconnect
することです。 クライアントでdisconnect
イベントを受信すると、非同期promise呼び出しを使用してクエリ/ヘッダーを再構築します。 reconnecting
イベントに到達するまでに、クエリ/ヘッダーに新しい値がすでに含まれています。
将来の読者のために:
Socket.IO v2のquery
オプションの動作は、両方で使用されているため、少し奇妙です。
したがって、下位互換性のある方法で修正する方法がわかりません...
auth
オプションを使用できるようになったため、これはSocket.IOv3で修正されていることに注意してください。
// plain object
const socket = io({
auth: {
token: "abc"
}
});
// or with a function
const socket = io({
auth: (cb) => {
cb({
token: "abc"
});
}
});
async
関数でも機能するはずです。
const socket = io({
auth: async (cb) => {
cb({
token: "abc"
});
}
});
参照:
最も参考になるコメント
@reeltimedoktor現在、
reconnect_attempt
イベントでquery
オブジェクトを更新できるはずです。