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


17

Estou criando uma API RESTful que usa tokens JWT para autenticação do usuário (emitidos por um loginnó de extremidade e enviados em todos os cabeçalhos posteriormente), e os tokens precisam ser atualizados após um período fixo de tempo (invocando um renewnó de extremidade, 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 meus pontos de extremidade começam verificando: 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 meus pontos de extremidade precisam sinalizar para 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 meus pontos de extremidade sinalizarem seus clientes quando uma das duas condições ocorrer (suponha que os clientes possam ser adaptados a qualquer uma das opções):

  1. Retorne um código http 401 (não autorizado) se a sessão se tornar inválida ou retorne um código 412 (falha na pré-condição) quando o token expirar e for hora de chamar o renewterminal, que retornará um código 200 (ok).
  2. Retorne 401 para sinalizar que a sessão é inválida ou que o token expirou. Nesse caso, o cliente chamará imediatamente o renewterminal, se retornar 200, o token será atualizado, mas se renewtambé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 completamente diferente? Você vê algum problema óbvio ou risco à segurança com qualquer uma das opções? Pontos extras se sua resposta incluir referências externas que apóiam sua opinião.

ATUALIZAR

Pessoal, por favor, concentrem-se 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 sessões JWT e do lado do servidor, essa é uma peculiaridade da minha API para regras de negócios muito específicas, e não a parte para a qual estou procurando ajuda;)


Como a sessão de um usuário se tornaria inválida antes que o token expire, assumindo um tempo de expiração curto (aproximadamente 5 minutos)?
Jack

Devido a uma regra de negócios, uma parte diferente do sistema pode invalidar a sessão.
Óscar López

1
Os JWTs são para prova de identidade, como em "esta solicitação provou ser do usuário X". Se sua regra de negócios é algo como "o usuário X não tem mais permissão para interagir com o recurso Y", é algo que deve ser verificado separadamente do JWT.
Jack

@ Jack exatamente! esse é o meu ponto precisamente e a razão pela qual tenho que usar uma camada adicional e estável para salvar as informações da sessão. O JWT comum, por melhor que seja, simplesmente não é adequado para o trabalho.
Óscar López

1
Você pode estar interessado na minha resposta então :)
Jack

Respostas:


22

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

JWTs são reivindicações criptograficamente assinadas 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 funções de administrador". Obter e fornecer essa prova por meio de senhas, assinaturas e TLS é o domínio da autenticação - provando que você é quem diz ser.

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

Cenário A - Autenticação

  • Bob: "Olá Jim, gostaria de inserir armazenamento restrito."
  • Jim: "Você recebeu seu distintivo?"
  • Bob: "Não, esqueci."
  • Jim: "Desculpe amigo, nenhuma entrada sem crachá."

Cenário B - Autorização

  • Bob: "Olá Jim, gostaria de entrar em armazenamento restrito. Aqui está o meu crachá."
  • 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 outros os roubem. Se todas as suas JWTs tiverem um prazo de validade de cinco minutos, não será tão grande coisa se elas forem roubadas porque elas rapidamente se tornarão inúteis. No entanto, a regra "expiração da sessão" que você discute parece um problema de autorização. Alguma mudança de estado significa que o usuário X não tem mais permissão para fazer algo que costumava fazer. Por exemplo, o usuário Bob pode ter sido demitido - não importa que seu crachá diga que ele é mais Bob, porque simplesmente ser Bob não lhe dá mais autoridade na empresa.

Esses dois casos têm códigos de resposta HTTP distintos: 401 Unauthorizede 403 Forbidden. Infelizmente, o código 401 é indicado 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 a conta de um usuário ser excluída, tentar fazer algo com um JWT em um nó de extremidade resultaria em uma resposta 403 Proibida. No entanto, se o JWT expirar, o resultado correto será 401 Não autorizado.

Um padrão JWT comum é ter tokens de "longa vida" e "curta duração". Os tokens de longa duração são armazenados no cliente como tokens de curta duração, mas têm escopo limitado e são usados apenas com o 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 validade muito longos - você pode usá-los para solicitar novos tokens por dias ou semanas a fio. Os tokens de vida curta são aqueles que você está descrevendo, usados ​​com tempos de expiração muito curtos para interagir com seu sistema. Os tokens de longa duração são úteis para implementar a funcionalidade Remember Me, para que você não precise fornecer sua senha a cada cinco minutos para obter um novo token de curta duração.

O problema da "invalidação de sessão" que você está descrevendo parece semelhante à tentativa de invalidar uma JWT de longa duração, pois as de curta duração raramente são armazenadas no lado do servidor, enquanto as de longa duração são rastreadas caso precisem ser revogadas. Nesse sistema, tentar adquirir credenciais com um token de longa duração revogado resultaria em 401 não autorizado, 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 Proibido se for expulso do sistema.


3
Esta é a orientação que eu estava procurando e você trouxe uma visão muito relevante para a discussão - que este é um caso de autenticação versus autorização, e cada um deve ser tratado de maneira diferente. Obrigado!
Óscar López

16

Sua sessão da API é algo que não deveria existir em um mundo RESTful. As operações RESTful devem ser sem estado, a sessão contém 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 é elegível para acessar um terminal ou não. Uma sessão não deve desempenhar absolutamente nenhum papel nela. Caso isso aconteça, você não possui uma API RESTful.

Quando você elimina a sessão completamente, o que, se você deseja uma API RESTful, deve fazer e usar apenas o JWT como um fator de autenticação, um usuário está autorizado a usar seu terminal ou não - nesse caso, o 401 Unauthorizedcódigo de resposta é apropriado - e deve ligar para o renewterminal com grant_type=refresh_tokenou qualquer identificação de renovação que você esteja usando.

Atualizar:

Pelo comentário, parece que o fluxo de validação do JWT que você está usando no momento não está correto. A validação deve ficar assim:

  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 a autorização. Essa não é a responsabilidade do Client. Parece que você não está fazendo isso no momento. Implemente a verificação do JWT dessa maneira e você não precisa de sessões.


Obrigado pela sua resposta. Concordo, o uso de uma sessão não seria uma abordagem 100% RESTful, mas, como mencionei acima, preciso negar o acesso a alguns usuários antes que o token expire.
Óscar López

2
@ ÓscarLópez Em seguida, simplesmente invalide os tokens que os usuários estão usando. Eles não poderão mais acessar a API usando o token fornecido (que agora será invalidado) e você não precisa de uma sessão.
21716 Andy

1
Os tokens são armazenados no cliente, como posso invalidá-los lá? Eu teria que acompanhar quais são válidos ... e é aí que o estado eleva sua cabeça feia. Às vezes é inevitável.
Óscar López

Um cliente mal-intencionado pode continuar enviando um token válido anteriormente enquanto o tempo de expiração permitir, mas preciso expulsá-lo do sistema imediatamente - portanto, definir um tempo de renovação curto também não é uma opção.
Óscar López

4
@Laiv Fiz o diagrama de sequência no bloco de notas.
21716 Andy

1

Portanto, confesso que não faz muito sentido me preocupar com qual abordagem é mais RESTful quando você já está violando as convenções REST com a sessão, mas entendo satisfazer os requisitos de negócios.

Do ponto de vista REST, o cliente é autenticado ou não. A arquitetura não se importa muito com o motivo (que está injetando um estado desnecessário); portanto, para responder à sua pergunta principal, eu não teria um ponto de extremidade renovado. Um cliente conectado sempre envia seu JWT sempre e o servidor sempre o valida e aceita enviando o código de Sucesso apropriado com base nas ações 200, 201, etc.) ou rejeitando com um 401 ou 403, conforme apropriado.

Agora, o JWT será associado a uma conta de algum tipo. Essa conta pode estar bloqueada ou estrangulada ou o que for, e assim o próprio token pode ser válido, mas a ação ainda será rejeitada em outro lugar. Se for o caso de a conta do usuário estar bloqueada por causa das regras de negócios, ainda será 401 ou 403, dependendo da quantidade de informações que você deseja fornecer ao cliente (empresas diferentes têm opiniões diferentes sobre isso).

Finalmente, se você está afirmando que a conta pode ser desbloqueada e válida, mas o JWT só precisa ser revogado, AINDA continuarei com o 401 ou 403 e manteria algo como uma lista de revogação de certificado de JWTs inválidos nos quais você pode colocar um , desde que se limpe quando o JWT expirou (a maioria dos bancos de dados tem uma maneira de fazer isso ou você pode ter eventos no código do aplicativo).


jwt são feitos para serem apátridas. no momento em que você questiona seu conteúdo e o valida em um banco de dados, ele não é mais apátrida e, portanto, se torna redundante, pois existem soluções melhores para sessões com estado de estado
Stavm
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.