Authorization

在 SocketCluster 預設使用 JWT 處理驗證問題

在AGServer 之中有一個參數 authKey 是一個字串,提供 JWT 的 token 建立與驗證使用

Client 也可以使用 socket.authenticate

因為可能一個服務或多個服務 同時會有 HTTP 與 Websocket

所以會希望同一個 token 可以在各服務內使用

做使用者的驗證

建立 JWT Token

HTTP

最基本的使用方式是透過 Express 來建立 token

然後再將此 token 送到客戶短提供使用

客戶端獲得這個 token 的之後必須要加到 socketcluster.authToken

這是 SocketCluster 的預設 JWT localStorage token

建立新連線的時候或是重新連線時 SocketCluster 會自動在 localStorage 取得 JWT

Server

新增一個 Express 的 route

因為是 demo

所以先用 GET 來測試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

expressApp.get('/login', async (req, res) => {
const myTokenData = {
username: 'bob',
language: 'English',
company: 'Google',
groups: ['engineering', 'science', 'mathematics']
};

let signedTokenString = await agServer.auth.signToken(myTokenData, agServer.signatureKey);

res.status(200).json({
token: signedTokenString
});
});

之後在瀏覽器測試可以取得 token

1
2
3
{
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImJvYiIsImxhbmd1YWdlIjoiRW5nbGlzaCIsImNvbXBhbnkiOiJHb29nbGUiLCJncm91cHMiOlsiZW5naW5lZXJpbmciLCJzY2llbmNlIiwibWF0aGVtYXRpY3MiXSwiaWF0IjoxNTk5NTczMzA1fQ.TBwhqJlhVlpEwCcqsv9-JT5Vx7Z32D4YpCUebEDZSHQ"
}

Websocket

利用 WebSocket 建立 token 的範例

Server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(async () => {
for await (let request of socket.procedure('login')) {
try {
console.log("forawait -> request.data", request.data)
//chgeck use done



socket.setAuthToken({username: request.data.username});
request.end();
return;
} catch(error) {
console.log("forawait -> error", error)
let loginError = new Error(`Could not find a ${request.data.username} user`);
console.log("forawait -> loginError", loginError)
loginError.name = 'LoginError';
request.error(loginError);

return;
}
}
})();
Client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(async () => {

try {
// Invoke a custom 'login' procedure (RPC) on our server socket
// then wait for the socket to be authenticated.
const [, authResult] = await Promise.all([
socket.invoke("login", credentials),
socket.listener("authenticate").once(),
]);
console.log("authResult", JSON.stringify(authResult))
} catch (error) {
console.log("error", error)
// showLoginError(err);
return;
}
})();

Client 可以拿到的 Resonse 是

1
2
3
4
5
6
7
8
{
"signedAuthToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFsaWNlMTIzIiwiaWF0IjoxNTk5NTc2MDkyLCJleHAiOjE1OTk2NjI0OTJ9.fccJ4zBdCqpoXrHW-NWxEK9r5ykMYyA0aokQRZitUmw",
"authToken": {
"username": "alice123",
"iat": 1599576092,
"exp": 1599662492
}
}

驗證 JWT Token

在 SocketCluster 中不論是 HTTPWebSocket 的驗證方式都是一樣的

但是在這之前要先了解 SocketClsuter 的 Middleware 的使用方式

Middleware

SocketCluster 中可以註冊 Middleware

支援的類別總共四種

  • agServer.MIDDLEWARE_HANDSHAKE
  • agServer.MIDDLEWARE_INBOUND_RAW
  • agServer.MIDDLEWARE_INBOUND
    • from client -> server
  • agServer.MIDDLEWARE_OUTBOUND
    • from server -> client

註冊 Middleware

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
agServer.setMiddleware(agServer.MIDDLEWARE_INBOUND, async (middlewareStream) => {
for await (let action of middlewareStream) {
console.log("forawait -> action.type", action.type)
if (action.type === action.TRANSMIT) {
if (!action.data) {
let error = new Error(
'Transmit action must have a data object'
);
error.name = 'InvalidActionError';
action.block(error);
continue;
}
} else if (action.type === action.INVOKE) {
if (!action.data) {
let error = new Error(
'Invoke action must have a data object'
);
error.name = 'InvalidActionError';
action.block(error);
continue;
}
// token 的物件
console.log("forawait -> action.data", action.data)
}
action.allow();
}
});

利用註冊 IN_BOUINDOUT_BOUND 的註冊 middleware

來達成驗證 JWT token

參考資料

Authorization