JWT Token
Introdução
O JWT (Json Web Token) é um padrão para garantir a segurança e contexto da autenticação em aplicações web, onde o frontend se encontra desacoplado do backend.
É comum ser utilizado quando o frontend de uma aplicação web é desenvolvida em ReactJS, AngularJS, VueJS, etc... e o backend proporciona uma API REST para fornecer os dados dinâmicos da aplicação.
A API REST do backend permite aos serviços privados obterem o contexto de autenticação associado ao utilizador autenticado através do JWT Token.
Normalmente a API REST fica num subdomínio diferente do website, o que implica configurações de CORS (Cross-Origin Resource Sharing).
Isto acontece devido aos browsers bloquearem a utilização de serviços e outros tipos de recursos em endereços externos, com o objetivo de evitar ataques de obtenção de dados confidenciais.
Ativação e Configuração
Para ativar e configurar o JWT Token na sua aplicação no Netuno é necessário editar o ficheiro de configuração da aplicação referente ao ambiente que está a utilizar, como:
📂 config/_development.json
📂 config/_production.json
Insira e ajuste os seguintes parâmetros:
...
"jwt": {
"enabled": true,
"secret": "@MyComp1exSecr3t",
"access_expires": 60,
"refresh_expires": 1440,
"algorithm": "HS512"
},
...
No parâmetro secret
coloque uma sequência de caracteres complexa e aleatória, visto ser a chave que vai garantir a segurança da encriptação do JWT Token.
Os parâmetros de expires
são definidos em minutos, por exemplo: 60
equivale a 1 hora e 1440
a um dia.
No algorithm
são suportados os seguintes mecanismos:
- ECDSA com SHA-2:
ES256
,ES384
ouES512
- HMAC com SHA-2:
HS256
,HS384
ouHS512
- RSASSA-PSS com SHA-2:
PS256
,PS384
ouPS512
- RSASSA-PKCS1 com SHA-2:
RS256
,RS384
ouRS512
Como Obter o Access Token
Para obter o Access Token o Netuno fornece o serviço _auth
que valida a autenticação e, se a mesma for bem sucedida, devolve o Access Token e o Refresh Token.
O Access Token é obtido da seguinte forma:
Exemplo de como obter o Access Token através do fetch:
let token = null;
yield fetch("http://localhost:9000/services/_auth", {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
username: payload.username,
password: payload.password,
jwt: true
})
}).then((response) => {
if (response.status != 403) {
raiseInvalidLogin();
return null;
}
if (response.status != 200) {
console.log(`Autenticação falhou com o status ${response.status}`);
raiseRequestFailed();
return null;
}
return response.json();
}).then((res) => {
token = res;
}).catch((error)=> {
console.log(error);
raiseConnectionError();
});
if (token && token.result === true) {
console.log(`Meu Acccess Token: ${token.access_token}`);
console.log(`Meu Refresh Token: ${token.refresh_token}`);
console.log(`Expira em: ${token.expires_in} minutos`);
sessionStorage.setItem("token", JSON.stringify(token));
}
Repare que o token
deverá ficar guardado como sessão, portanto é preferível utilizar a sessionStorage em vez do localStorage quando se tratar de informação sensível.
Com o refresh_token
é possível gerar um novo token
antes do tempo e expiração (expires_in
em minutos).
Como Utilizar o Access Token
Para executar serviços programados à medida que exijam autenticação prévia em aplicações Netuno, deve passar o Access Token no Header do protocolo HTTP:
Authorization: Bearer eyJhbGciOiJIUzU...
Exemplo de como executar um serviço programado à medida através do frontend utilizando o fetch:
const token = JSON.parse(sessionStorage.getItem("token"));
let data = null;
yield fetch("http://localhost:9000/services/meu-servico-programado-a-medida", {
method: 'post',
credentials: 'include',
headers: {
'Authorization': `${token.token_type} ${token.access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
meuParametro1: '...',
meuParametro2: '...'
})
}).then((response) => {
if (response.status != 200) {
console.log(`Serviço falhou com o status ${response.status}.`);
raiseRequestFailed();
return null;
}
return response.json();
}).then((res) => {
data = res;
}).catch((error)=> {
console.log(error);
raiseConnectionError();
});
if (data === true) {
console.log(`Dados de resposta do meu serviço:`, data);
}
Repare que, nos Headers do HTTP, no parâmetro Authorization
é utilizado o token_type
e o access_token
obtidos do objeto JWT Token guardado na sessionStorage.
Atualização (Refresh Token)
Para atualizar o token deve chamar novamente o serviço _auth
, mas além do parâmetro do jwt: true
é necessário passar também o parâmetro do refresh_token
com o valor recebido na autenticação bem sucedida anterior.
A atualização do token deve ser feita antes do tempo de expiração (expires_in
em minutos).
Por exemplo:
let token = null;
yield fetch("http://localhost:9000/services/_auth", {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
refresh_token: token.refresh_token,
jwt: true
})
}).then((response) => {
if (response.status != 403) {
raiseInvalidLogin();
return null;
}
if (response.status != 200) {
console.log(`Autenticação falhou com o status ${response.status}`);
raiseRequestFailed();
return null;
}
return response.json();
}).then((res) => {
token = res;
}).catch((error)=> {
console.log(error);
raiseConnectionError();
});
if (token && token.result === true) {
console.log(`Meu Novo Acccess Token: ${token.access_token}`);
console.log(`Meu Novo Refresh Token: ${token.refresh_token}`);
console.log(`Expira em: ${token.expires_in} minutos`);
sessionStorage.setItem("token", JSON.stringify(token));
}
Ao obter o novo token deve passar a utiliza-lo, ao invés do anterior (antigo) nas próximas chamadas de serviços.
Auth Client - NPM
Para facilmente realizar a integração com o frontend é disponibilizado o módulo do NPM:
Comando de instalação: npm i -S @netuno/auth-client
Este módulo depende do @netuno/service-client, ao definir o endereço dos serviços, como:
_service.config({
prefix: 'http://localhost:9000/services/'
});
Com isto poderá efetuar o login:
_auth.login({
username: "admin",
password: "secret",
success: ()=> {
alert("Success.");
},
fail: ()=> {
alert("Fail.");
}
});
E para atualizar o token:
_auth.refreshToken({
success: ()=> {
alert("Success.");
},
fail: ()=> {
alert("Fail.");
}
});
Por fim, para terminar a sessão:
_auth.logout();
Conclusão
De forma simples, é possível configurar e ativar a autenticação com JWT (Json Web Token) nas aplicações.
A integração do JWT no frontend pode ser feito ao mais baixo nível utilizando diretamente o fetch
.
Para agilizar e padronizar a implementação é disponibilizado o módulo NPM @netuno/auth-client.