Tutorial: Adicionar autenticação e permissões ao seu aplicativo ao usar o Azure Web PubSub
Em Criar um aplicativo de chat, você aprendeu como usar APIs WebSocket para enviar e receber dados com o Azure Web PubSub. Você percebe que, para simplificar, ele não requer nenhuma autenticação. Embora o Azure Web PubSub exija um token de acesso para ser conectado, a negotiate
API usada no tutorial para gerar o token de acesso não precisa de autenticação. Qualquer pessoa pode chamar essa API para obter um token de acesso.
Em um aplicativo do mundo real, você normalmente deseja que o usuário entre primeiro, antes que ele possa usar seu aplicativo. Neste tutorial, você aprenderá a integrar o Web PubSub com o sistema de autenticação e autorização do seu aplicativo, para torná-lo mais seguro.
Você pode encontrar o exemplo de código completo deste tutorial no GitHub.
Neste tutorial, irá aprender a:
- Ativar autenticação do GitHub
- Adicionar middleware de autenticação ao seu aplicativo
- Adicionar permissões aos clientes
Adicionar autenticação ao aplicativo de sala de chat
Este tutorial reutiliza o aplicativo de bate-papo criado em Criar um aplicativo de bate-papo. Você também pode clonar o exemplo de código completo para o aplicativo de bate-papo do GitHub.
Neste tutorial, você adiciona autenticação ao aplicativo de chat e o integra ao Web PubSub.
Primeiro, adicione a autenticação do GitHub à sala de chat para que o usuário possa usar uma conta do GitHub para entrar.
Instale dependências.
npm install --save cookie-parser npm install --save express-session npm install --save passport npm install --save passport-github2
Localize o arquivo em seu diretório e habilite a autenticação do GitHub adicionando o
server.js
seguinte código aoserver.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: '/' }));
O código anterior usa o Passport.js para habilitar a autenticação do GitHub. Aqui está uma ilustração simples de como funciona:
/auth/github
redireciona para github.com para entrar.- Depois de entrar, o GitHub redireciona você com
/auth/github/callback
um código para seu aplicativo para concluir a autenticação. (Para ver como o perfil retornado do GitHub é verificado e persistido no servidor, consulte o retorno de chamada verificado empassport.use()
.) - Depois que a autenticação for concluída, você será redirecionado para a página inicial (
/
) do site.
Para obter mais detalhes sobre o GitHub OAuth e o Passport.js, consulte os seguintes artigos:
Para testar isso, você precisa primeiro criar um aplicativo GitHub OAuth:
- Vá para https://www.github.com, abra seu perfil e selecione Configurações Configurações>do desenvolvedor.
- Vá para Aplicativos OAuth e selecione Novo Aplicativo OAuth.
- Preencha o nome do aplicativo e o URL da página inicial (o URL pode ser o que você quiser) e defina URL de retorno de chamada de autorização como
http://localhost:8080/auth/github/callback
. Esse URL corresponde à API de retorno de chamada que você expôs no servidor. - Depois que o aplicativo for registrado, copie o ID do cliente e selecione Gerar um novo segredo do cliente.
Execute o comando abaixo para testar as configurações, não se esqueça de substituir
<connection-string>
,<client-id>
e<client-secret>
com seus valores.export WebPubSubConnectionString="<connection-string>" export GitHubClientId="<client-id>" export GitHubClientSecret="<client-secret>" node server
Agora aberto
http://localhost:8080/auth/github
. Você será redirecionado para o GitHub para fazer login. Depois de entrar, você será redirecionado para o aplicativo de bate-papo.Atualize a sala de chat para usar a identidade obtida do GitHub, em vez de solicitar ao usuário um nome de usuário.
Atualize
public/index.html
para ligar/negotiate
diretamente sem passar um ID de usuário.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);
Quando um utilizador tem sessão iniciada, o pedido transporta automaticamente a identidade do utilizador através de um cookie. Então você só precisa verificar se o
req
usuário existe no objeto e adicionar o nome de usuário ao token de acesso 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 }); });
Agora execute novamente o servidor e você verá uma mensagem "não autorizado" pela primeira vez que abrir a sala de chat. Selecione o link de entrada para entrar e, em seguida, você verá que ele funciona como antes.
Trabalhar com permissões
Nos tutoriais anteriores, você aprendeu a usar WebSocket.send()
para publicar mensagens diretamente para outros clientes usando o subprotocolo. Em um aplicativo real, talvez você não queira que o cliente possa publicar ou assinar qualquer grupo sem controle de permissão. Nesta seção, você verá como controlar clientes usando o sistema de permissão do Web PubSub.
No Web PubSub, um cliente pode executar os seguintes tipos de operações com subprotocolo:
- Enviar eventos para o servidor.
- Publicar mensagens em um grupo.
- Junte-se (subscreva) um grupo.
Enviar um evento para o servidor é a operação padrão do cliente. Nenhum protocolo é usado, por isso é sempre permitido. Para publicar e se inscrever em um grupo, o cliente precisa obter permissão. Há duas maneiras de o servidor conceder permissão aos clientes:
- Especifique funções quando um cliente estiver conectado (função é um conceito para representar permissões iniciais quando um cliente está conectado).
- Use uma API para conceder permissão a um cliente depois que ele estiver conectado.
Para obter permissão para ingressar em um grupo, o cliente ainda precisa ingressar no grupo usando a mensagem "ingressar no grupo" depois de obter a permissão. Como alternativa, o servidor pode usar uma API para adicionar o cliente a um grupo, mesmo que ele não tenha a permissão de associação.
Agora vamos usar esse sistema de permissão para adicionar um novo recurso à sala de chat. Você adiciona um novo tipo de usuário chamado administrador à sala de chat. Você permite que o administrador envie mensagens do sistema (mensagens que começam com "[SYSTEM]") diretamente do cliente.
Primeiro, você precisa separar as mensagens do sistema e do usuário em dois grupos diferentes para poder controlar suas permissões separadamente.
Altere server.js
para enviar mensagens diferentes para grupos diferentes:
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();
}
});
O código anterior usa WebPubSubServiceClient.group().sendToAll()
para enviar a mensagem para um grupo em vez do hub.
Como a mensagem agora é enviada para grupos, você precisa adicionar clientes a grupos para que eles possam continuar recebendo mensagens. Use o handleConnect
manipulador para adicionar clientes a grupos.
Nota
handleConnect
é acionado quando um cliente está tentando se conectar ao Web PubSub. Nesse manipulador, você pode retornar grupos e funções, para que o serviço possa adicionar uma conexão a grupos ou conceder funções, assim que a conexão for estabelecida. O serviço também pode usar res.fail()
para negar a conexão.
Para acionar handleConnect
o , vá para as configurações do manipulador de eventos no portal do Azure e selecione conectar em eventos do sistema.
Você também precisa atualizar o HTML do cliente, porque agora o servidor envia mensagens JSON em vez de texto sem formatação:
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 = '';
});
Em seguida, altere o código do cliente para enviar ao grupo do sistema quando os usuários selecionarem a mensagem do sistema:
<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>
Por padrão, o cliente não tem permissão para enviar para nenhum grupo. Atualize o código do servidor para conceder permissão ao usuário administrador (para simplificar, a ID do administrador é fornecida como um argumento de linha de comando).
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);
});
Agora corra node server <admin-id>
. Você vê que pode enviar uma mensagem do sistema para cada cliente quando entrar como <admin-id>
.
Mas se você entrar como um usuário diferente, quando você selecionar a mensagem do sistema, nada acontece. Você pode esperar que o serviço forneça um erro para informar que a operação não é permitida. Para fornecer esse feedback, você pode definir ackId
quando está publicando a mensagem. Sempre que ackId
é especificado, Web PubSub retorna uma mensagem com uma correspondência ackId
para indicar se a operação foi bem-sucedida ou não.
Altere o código de envio de uma mensagem do sistema para o seguinte código:
let ackId = 0;
system.addEventListener('click', e => {
ws.send(JSON.stringify({
type: 'sendToGroup',
group: 'system',
ackId: ++ackId,
dataType: 'text',
data: message.value
}));
message.value = '';
});
Altere também o código de processamento de mensagens para lidar com uma ack
mensagem:
ws.onmessage = event => {
...
switch (message.type) {
case 'ack':
if (!message.success && message.error.name === 'Forbidden') m.innerText = 'No permission to send system message';
break;
}
};
Agora execute novamente o servidor e entre como um usuário diferente. Você vê uma mensagem de erro quando tenta enviar uma mensagem do sistema.
O exemplo de código completo deste tutorial pode ser encontrado no GitHub.
Próximos passos
Este tutorial fornece uma ideia básica de como se conectar ao serviço Web PubSub e como publicar mensagens para clientes conectados usando o subprotocolo.
Para saber mais sobre como usar o serviço Web PubSub, leia os outros tutoriais disponíveis na documentação.