Tratamento de renovação de token / expiração de sessão em uma API RESTful

14

Estou criando uma API RESTful que usa tokens JWT para autenticação do usuário (emitida por um login endpoint e enviada em todos os cabeçalhos depois), e os tokens precisam ser atualizados após um período fixo de tempo (invocando um renew endpoint, que retorna um token renovado).

É possível que a sessão da API de um usuário se torne inválida antes que o token expire, portanto, todos os endpoints começam verificando se: 1) o token ainda é válido e 2) a sessão do usuário ainda é válida. Não há como invalidar diretamente o token, porque os clientes o armazenam localmente.

Portanto, todos os endpoints devem sinalizar aos meus clientes duas condições possíveis: 1) que é hora de renovar o token ou 2) que a sessão se tornou inválida e que eles não têm mais permissão para acessar o sistema. Posso pensar em duas alternativas para os endpoints sinalizarem seus clientes quando uma das duas condições ocorrer (suponha que os clientes possam ser adaptados a qualquer opção):

  1. Retorna um código http 401 (não autorizado) se a sessão se tornar inválida ou retorna um código 412 (falha na pré-condição) quando o token expirou e é hora de chamar o renew endpoint, que retornará um 200 (ok) código.
  2. Retorna 401 para sinalizar que a sessão é inválida ou o token expirou. Nesse caso, o cliente chamará imediatamente o ponto% renew , se ele retornar 200, o token será atualizado, mas se renew também retornar 401, significa que o cliente está fora do sistema.

Qual das duas alternativas acima você recomendaria? Qual deles seria mais padrão, mais simples de entender e / ou mais RESTful? Ou você recomendaria uma abordagem diferente? Você vê algum problema óbvio ou risco de segurança com qualquer uma das opções? Pontos extras se sua resposta incluir referências externas que apóiam sua opinião.

UPDATE

Gente, por favor, focar na questão real - qual das duas alternativas de código http para sinalizar uma invalidação de renovação / sessão é a melhor? Não se importe com o fato de meu sistema usar o JWT e sessões do lado do servidor, isso é uma peculiaridade da minha API para regras de negócios muito específicas, e não a parte em que estou procurando ajuda;)

    
por Óscar López 17.12.2016 / 17:02
fonte

3 respostas

17

Isso soa como um caso de autenticação versus autorização .

As JWTs são assinaturas assinadas criptograficamente sobre o originador de uma solicitação. Um JWT pode conter declarações como "Esta solicitação é para o usuário X" e "O usuário X tem uma função de administrador". Obter e fornecer essa prova por meio de senhas, assinaturas e TLS é o domínio da autenticação - provando que você é quem você diz que é.

O que essas reivindicações significam para o seu servidor - o que usuários e funções específicas podem fazer - é o problema da autorização . A diferença entre os dois pode ser descrita com dois cenários. Suponha que Bob queira entrar na seção de armazenamento restrito do depósito de sua empresa, mas primeiro ele deve lidar com um guarda chamado Jim.

Cenário A - Autenticação

  • Bob: "Olá, Jim, gostaria de entrar no armazenamento restrito".
  • Jim: "Você tem seu distintivo?"
  • Bob: "Não, esqueci."
  • Jim: "Desculpe amigo, sem entrada sem um crachá".

Cenário B - Autorização

  • Bob: "Olá, Jim, gostaria de entrar no armazenamento restrito. Aqui está meu distintivo".
  • Jim: "Ei Bob, você precisa de autorização de nível 2 para entrar aqui. Desculpe."

Os tempos de expiração do JWT são um dispositivo de autenticação usado para impedir que outras pessoas os roubem. Se todos os seus JWTs tiverem cinco minutos de expiração, isso não será tão grande se eles forem roubados porque eles rapidamente se tornarão inúteis. No entanto, a regra de "expiração da sessão" discutida por você parece um problema de autorização. Alguma mudança no estado significa que o usuário X não tem mais permissão para fazer algo que costumava ser capaz de fazer. Por exemplo, o usuário Bob pode ter sido demitido - não importa que seu crachá diga que ele é o Bob, porque simplesmente ser Bob não lhe dá mais autoridade sobre a empresa.

Esses dois casos têm códigos de resposta HTTP distintos: 401 Unauthorized e 403 Forbidden . O código 401, infelizmente chamado, é para problemas de autenticação, como credenciais ausentes, expiradas ou revogadas. 403 é para autorização, onde o servidor sabe exatamente quem você é, mas você simplesmente não tem permissão para fazer o que está tentando fazer. No caso de uma conta de usuário ser excluída, tentar fazer algo com um JWT em um terminal resultaria em uma resposta 403 Proibida. No entanto, se o JWT estiver vencido, o resultado correto seria 401 Unauthorized.

Um padrão comum de JWT é ter tokens de "vida longa" e "de curta duração". Os tokens de longa duração são armazenados no cliente como tokens de curta duração, mas eles são limitados em escopo e somente usados com seu sistema de autorização para obter tokens de curta duração. Os tokens de longa duração, como o nome indica, têm períodos de expiração muito longos - você pode usá-los para solicitar novos tokens por dias ou semanas a fio. Os tokens de curta duração são os tokens que você está descrevendo, usados com tempos de expiração muito curtos para interagir com o sistema. Os tokens de longa duração são úteis para implementar a funcionalidade Lembrar-me, portanto, você não precisa fornecer sua senha a cada cinco minutos para obter um novo token de curta duração.

O problema de "invalidação de sessão" que você está descrevendo é semelhante à tentativa de invalidar um JWT de longa duração, já que os de curta duração raramente são armazenados no servidor, enquanto os de longa duração são rastreados caso precisem ser revogados. Nesse sistema, a tentativa de adquirir credenciais com um token revogado de longa duração resultaria em 401 Unauthorized, porque o usuário pode tecnicamente adquirir credenciais, mas o token que está usando não é adequado para a tarefa. Então, quando o usuário tentar adquirir um novo token de longa duração usando seu nome de usuário e senha, o sistema poderá responder com 403 Forbidden se ele for expulso do sistema.

    
por 18.12.2016 / 00:40
fonte
13

Sua sessão de API é algo que não deveria existir em um mundo RESTful. As operações RESTful devem ser sem estado, a sessão contém o estado e, portanto, não tem lugar em um mundo RESTful.

O JWT deve ser sua única maneira de determinar se um usuário ainda está qualificado para acessar um endpoint ou não. Uma sessão deve desempenhar absolutamente nenhum papel nela. Em caso afirmativo, você não tem uma API RESTful.

Quando você elimina a sessão completamente, o que, se você estiver procurando uma API RESTful, e usar apenas o JWT como um fator de autenticação, um usuário está autorizado a usar seu endpoint ou não - nesse caso, o% O código de resposta401 Unauthorized é apropriado - e deve chamar o renew endpoint com grant_type=refresh_token ou qualquer identificação de renovação que você esteja usando.

Atualização:

No comentário, parece que o fluxo de validação do JWT que você está usando atualmente não está correto. A validação deve se parecer com isso:

  Client        RESTful API      JWT Issuer
     |              |                |
     |----- 1. ---->|                | 
     |              |------ 2. ----->|-----
     |              |                | 3. |
     |              |<----- 4. ------|<----
-----|<---- 5. -----|                |
| 6. |              |                |
---->|----- 7. ---->|                |
     |              |------ 8. ----->|-----
     |              |                | 9. |
     |              |<----- 10. -----|<----
     |              |                |
     |              |------          |
     |              | 11. |          |
     |<---- 12. ----|<-----          |
     |              |                |
     .              .                .

1. Ask RESTful API for a JWT using login endpoint.
2. Ask Issuer to create a new JWT.
3. Create JWT.
4. Return JWT to the RESTful API.
5. Return JWT to Client.
6. Store JWT to append it to all future API requests.
7. Ask for data from API providing JWT as authorization.
8. Send JWT to Issuer for verification.
9. Issuer verifies JWT.
10. Issuer returns 200 OK, verification successful.
11. Retrieve and format data for Client.
12. Return data to Client.

O servidor, RESTful API , deve verificar a validade do token que está sendo enviado como Autorização. Isso não é responsabilidade do Client . Parece que você atualmente não está fazendo isso. Implemente a verificação do JWT desta forma e você não precisa de sessões.

    
por 17.12.2016 / 18:23
fonte
1

Então, confesso que não faz muito sentido para mim ficar preocupado sobre qual abordagem é mais RESTful quando você já está quebrando as convenções REST com a sessão, mas eu entendo satisfazer suas necessidades de negócios.

Do ponto de vista do REST, o cliente é autenticado ou não é. A arquitetura não se importa muito com o porquê (ou seja, injetar estado desnecessário), então, para responder à sua pergunta principal, eu não teria um ponto final de renovação. Um cliente logado sempre enviará seu JWT sempre e o servidor sempre o validará e aceitará enviando o código Success apropriado com base na ação 200, 201, etc) ou rejeitará com um 401 ou 403 conforme apropriado.

Agora, o JWT será associado a uma conta de algum tipo. Essa conta pode ser bloqueada ou afogada ou qualquer outra coisa e, portanto, o próprio token pode ser válido, mas a ação ainda pode ser rejeitada em outro lugar. Se o caso é que a conta do usuário está bloqueada por causa das regras de negócios, então ainda é um 401 ou 403, dependendo de quantas informações você deseja dar ao cliente (empresas diferentes têm opiniões diferentes sobre isso).

Finalmente, se você estiver afirmando que a conta pode ser desbloqueada e válida, mas o JWT precisa ser revogado, eu ainda ficaria com o 401 ou 403 e manteria algo como uma Lista de Revogação de Certificados de JWTs inválidos que você puder coloque um, contanto que ele se limpe quando o JWT tiver expirado (a maioria dos bancos de dados tem uma maneira de fazer isso ou você pode ter eventos no código do aplicativo).

    
por 18.12.2016 / 00:50
fonte