:index-group: MicroProfile
:jbake-type: page
:jbake-status: published

= MP REST JWT com Public key do MP Config

Este é um exemplo de como configurar e usar o MicroProfile JWT 1.1 no TomEE.

== Executando o teste

Este projeto inclui uma aplicação de exemplo e um teste do Arquillian para mostrar o controle de acesso baseado em função (RBAC) com JWT no MicroProfile.
Para executar o cenário, você pode executar o seguinte comando:

[source, bash]
----
mvn clean test
----

A aplicação representa um recurso REST de livraria com alguns endpoints.
Todos esperam que o cliente forneça um JSON Web Token (JWT) válido, representando um usuário com determinadas funções.
O teste do Arquillian é responsável por gerar os JWTs e anexá-los os requests.

== Configuração no TomEE

Para ativar o JWT, você precisa anotar sua classe de aplicação REST com a anotação `org.eclipse.microprofile.auth.LoginConfig`.
Neste exemplo, a classe é `ApplicationConfig`.

Outra coisa que precisa ser feita é configurar a `public key` para verificar a assinatura do JWT anexada no cabeçalho `Authorization'.
É assinado na criação com a `private key` do issuer.
Isso é feito para evitar adulteração do token enquanto ele trafega do chamador até o endpoint.
Geralmente, a emissão do JWT ocorre em um módulo ou microsserviço especial responsável pela autenticação dos usuários.
Neste projeto de exemplo, isso acontece no `BookstoreTest`.

Cada ambiente de execução que suporta o MicroProfile JWT deveria ser capaz de verificar se a assinatura está correta e se o conteúdo assinado não é alterado ao longo do caminho.
Para fazer isso, ele precisa ter acesso a uma `public key`.
Essa `public key` pode estar no formato `PKCS#8 PEM, JWK ou JWKS`.
Desde o MP JWT 1.1 (que é suportado pelo TomEE), a chave pode ser fornecida como uma string na propriedade de configuração `mp.jwt.verify.publickey` ou como um local ou URL de arquivo especificado em `mp.jwt.verify.publickey.location` na propriedade de configuração.

Neste projeto de exemplo, você pode ver a primeira opção.
O arquivo `src/main/resource/META-INF/microprofile-config.properties` contém a seguinte entrada:

[source,properties]
----
mp.jwt.verify.publickey=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y2jy0CfEqFyy46R0o7S8TKpsx5xbHKoU1VWg6QkQm+ntyIv1p4kE1sPEQO73+HY8+Bzs75XwRTYL1BmR1w8J5hmjVWjc6R2BTBGAYRPFRhor3kpM6ni2SPmNNhurEAHw7TaqszP5eUF/F9+KEBWkwVta+PZ37bwqSE4sCb1soZFrVz/UT/LF4tYpuVYt3YbqToZ3pZOZ9AX2o1GCG3xwOjkc4x0W7ezbQZdC9iftPxVHR8irOijJRRjcPDtA6vPKpzLl6CyYnsIYPd99ltwxTHjr3npfv/3Lw50bAkbT4HeLFxTx4flEoZLKO/g0bAoV2uqBhkA9xnQIDAQAB
----

== Trabalhando com JWT

A classe `BookResource` neste projeto de exemplo mostra dois casos em que você pode usar a especificação MP JWT: obter o valor de uma declaração JWT e o controle de acesso baseado em função dos endpoints REST.

=== Obtenção de valores de claim

O JSON Web Token (JWT) anexado no cabeçalho HTTP `Authorization` é essencialmente um objeto JSON que contém vários atributos.
Esses atributos são chamados _claims_.
Você pode obter o valor de cada claim dentro de um bean CDI injetando-o e qualificando-o com a anotação `@Claim`.

Por exemplo, se você deseja recuperar a claim de nome de usuário preferida, fazendo o seguinte:

[source,java]
----
    @Inject
    @Claim(standard = Claims.preferred_username)
    private String userName;
----

NOTE: Observe que você não pode injetar claims dessa maneira em uma classe de recurso REST que também contém endpoints não autenticados.
No entanto, o TomEE tentará extrair a claim do JWT.
Portanto, se não houver JWT ou se a claim não estiver lá, o request falhará.

=== Controle de acesso baseado em função (RBAC)

Uma das claims padrão definidas na especificação MP JWT é `groups`.
Ele contém uma lista de Strings, que representam os grupos aos quais o chamador pertence.
A especificação não distingue funções e grupos de usuários.
Portanto, a declaração `groups` também pode conter as funções atribuídas a um determinado usuário.

Nesse sentido, o MP JWT possui grande integração com os mecanismos de segurança Java EE existentes, como a anotação `@RolesAllowed`.
Portanto, o seguinte método `BookResource` pode ser chamado pelos usuários que estão na função `reader` ou `manager` (ou em ambos):

[source,java]
----
    @GET
    @Path("/{id}")
    @RolesAllowed({"manager", "reader"})
    public Book getBook(@PathParam("id") int id) {
        return booksBean.getBook(id);
    }
----

No entanto, o método abaixo resultará no código HTTP 403 se chamado por um usuário que não possui a função `manager` em sua declaração `groups`:

[source,java]
----
    @POST
    @RolesAllowed("manager")
    public void addBook(Book newBook) {
        booksBean.addBook(newBook);
    }
----

== O teste do bookstore

O projeto de exemplo contém um teste Arquillian (`org.superbiz.bookstore.BookstoreTest`) usado por alguns motivos:

* Geração do JSON Web Token (JWT)
* Apresentação do comportamento do TomEE em diferentes situações
** Recuperando um valor de Claim
** Chamando endpoints REST com funções apropriadas
** Chamando um endpoint REST com uma função incorreta (resultando no código de status HTTP 403)
** Chamando um endpoint REST sem JWT (resultando no código de status HTTP 401)
