Introdução

Essa publicação foi baseada no artigo [Securing Spring Boot REST APIs with Auth0 and OAuth 2.0]

Na publicação citada, foi utilizada a solução [Auth0] como plataforma de autenticação e autorização.

Nesta publicação nosso objetivo será demonstrar como utilizar o [IAM] da OCI, Oracle Cloud Infrastructure, para prover autenticação e autorização para nossa aplicação desenvolvida em Java, utilizando Spring Boot e Spring Security.

O que mudou?

Para utilizarmos o código de exemplo na OCI, realizamos as seguintes mudanças:  

  • Na classe [SecurityConfig.java](src/main/java/com/example/auth0demo/config/SecurityConfig.java):
    • incluímos um novo atributo: scope, porque iremos restringir a segurança com base nesta informação;
    • alteração no jwtDecoder, para utilizar a url JWK do nosso Identity Provider, com os certificados públicos.
  • Criação de Confidential Application, uma como resource server e outra como client, para os itens de autenticação e autorização.

Iremos descrever os passos a seguir.

Caso de Uso

Configuração JWK e Confidential Application

As seguintes atividades deverão ser realizadas na console administrativa da OCI.

Habilitando o JWK endpoint no seu Tenancy.

Para esse cenário vamos habilitar o endpoint /admin/v1/SigningCert/jwk do IAM para retornar o certificado de assinatura do tenancy em JWK.

JWK (JSON Web Key) é uma estrutura de dados JavaScript Object Notation (JSON) que representa uma chave criptográfica.

Execute os seguintes passos:

  •   Seu usuário deve pertencer ao grupo Identity Domain Administrator ou Security Administrator;
  •   Acesse Identity & Security;
  •   Clique em Domains, escolha o domain utilizado para os próximos procedimentos;
  •   Na aba Settings, procure pelo botão Edit domain settings e clique no mesmo;
  •   No item Access signing certificate, habilite a opção Configure client access e depois salve as alterações.

Para ilustar:

IAM e Domain:

JWK habilitado:

Realize o teste em um navegador, usando uma aba anônima, acessando a seguinte URL: 

  • [Domain URL]/admin/v1/SigningCert/jwk

A informação Domain URL, consta na aba Details do seu Domain.

#endpoint
https://idcs-yourDomain.identity.oraclecloud.com/admin/v1/SigningCert/jwk

#retorno da API
  {
    "keys": [
      {
        "kty": "RSA",
        "x5t#S256": "xyz",
        "e": "AQAB",
        "x5t": "xyz",
        "kid": "SIGNING_KEY",
        "x5c": [
          "xyz"
        ],
        "key_ops": [
          "wrapKey",
          "verify",
          "encrypt"
        ],
        "alg": "RS256",
        "n": "xyz"
      }
    ]
  }  

Confidential Application

Para autenticação e autorização, iremos utilizar [Confidential Application], realizando a criação através da console administrativa da OCI.

Para criação de Confidental Application, seguir:

  • Acesse Identity & Security;
  • Clique em Domains, escolha o domain utilizado para os próximos procedimentos;
  • Navegue até a aba Integrated applications, procure pelo botão Add Application e clique no mesmo;
  • Procure por Confidential Application e clique no botão Launch Workflow.

Executar esse procedimento para cada Confidental Application que criaremos a seguir.

Utilizaremos duas Confidental Applicatons, uma resource server e outra como client, para uma melhor organização e governança dentro do IAM.

Resource Server Application

Servidor de Recursos (Resource Server): É o componente que hospeda os dados e serviços que precisam ser protegidos. No contexto do Oracle IAM, o servidor de recursos é configurado para verificar se as requisições de acesso contêm tokens de autorização válidos.

Na criação desta Confidential Application, informe neste momento, os atributos Name e Description, depois clique em Submit.

Atenção: Ative sua aplicação, clicando em “Actions”, “Activate” e depois em “Activate application”.

Agora vá até a aba OAuth configuration e clique em Edit OAuth configuration:

Tela de Edit OAuth configuration:

Vamos escolher a opção Configure this application as a resource server now.

Deixe a opção No client configuration selecionada.

Configure o seguinte:

  • Primary audience: spring
    • Essa informação será utilizada no arquivo yaml, no atributo: iam.audience
  • Add scopes: habilite essa opção e clique em adicionar, vai abrir uma nova tela:
    • Scope**: api:get
      • Essa informação será utilizada no arquivo yaml, no atributo: iam.scope.
    •  Display name: get
    •  Description: scope para get nas API´s
  •  clique em adicionar.

Ao término, clique em Submit, para aplicar as alterações na sua aplicação.

Client Application

Aplicação Confidencial (Confidential Application): É uma aplicação de servidor que é confiável para manter suas credenciais seguras (ID do cliente e segredo do cliente + scope). 
Ao contrário de aplicações públicas (como as que rodam em um navegador), ela não expõe esses segredos ao usuário final. Ela usa o secret do cliente para autenticar-se junto ao servidor de autorização antes de fazer uma chamada para o servidor de recursos.

Na criação desta Confidential Application, informe neste momento, os atributos Name e Description, depois clique em Submit.

Atenção: Ative sua aplicação, clicando em “Actions”, “Activate” e depois em “Activate application”.

Agora vá até a aba OAuth configuration e clique em Edit OAuth configuration:

Tela de Edit OAuth configuration:

Deixe a opção No resource server configuration selecionada.

Vamos escolher a opção Configure this application as a client now.

Configure o seguinte:

  • Em **Authorization**, selecione os seguintes **Allowed grant types**:
    • Client credentials
    • Refresh token

Add resources: habilite essa opção e clique em Add scope;

Nesta nova tela, busque pela aplicação de resource server criada anteriormente.
Selecione o scope e ATENÇÃO: não se esqueça de clicar no botão Add.

Aqui a tela com o scope adicionado: 

ATENÇÃO

Perceba que o scope quando adicionado como resource nesta aplicação, ficou desta forma:

  • springapi:get: ocorreu uma concatenação entre [primary audience] + [scope], que constam na aplicação de Resource Server, criada anteriormente;
  • Esse é um detalhe que será necessário observar ao gerar o JWT nos comandos curl que estão a seguir.

 Ao término, clique em Submit, para aplicar as alterações na sua aplicação.

Colete as seguintes informações desta aplicação, na Aba OAuth configuration, pois as utilizaremos em breve, para gerar o token JWT no processo de OAuth Client Credencials:

  • Client ID
  • Client secret

Configurando a aplicação

Arquivo [application.yaml](src/main/resources/application.yaml) configurado para uso do IAM.

Verifique as seguintes propriedades:

  • issuer-uri: Se você não especificar um Issuer no IAM, o Issuer padrão será utilizado, neste caso: https://identity.oraclecloud.com/
  • jwk-set-uri: o endpoint JWK do IAM
  • audience: foi o valor que definimos na aplicação do tipo Resource Server no atributo Primary audience
  • scope: SCOPE_api:get concatenção do prefixo [SCOPE_] + o scope definido na aplicação Resource Server
    • de acordo com a documentação do Spring Security, devemos mapear cada scope com o prefixo SCOPE_
    • veja que aqui, usamos exatamente api:get como está na aplicação Resource Server, sem acrescentar nenhuma outra informação.
  spring:
    application:
      name: spring-boot-app

    security:
      oauth2:
        resourceserver:
          jwt:
            issuer-uri: https://identity.oraclecloud.com/
            jwk-set-uri: https://idcs-yourDomain.identity.oraclecloud.com/admin/v1/SigningCert/jwk

  server:
        port: 8585

  logging:
    level:
      root: INFO

  # Custom property
  iam:
    audience: spring
    scope: SCOPE_api:get

Testando a aplicação

Comandos para gerar o JWT e testes com as API´s:

#devemos gerar a informação para autenticação em base64 concatenando [Client ID + : + Client secret] da Confidential Application do tipo Client
echo -n "Client ID:Client secret" | openssl enc -base64 -A

#comando curl para retornar o token
#ATENÇÃO: devemos enviar o scope por completo
curl -k -i \
-H "Authorization: Basic [base64 -> client_id:cliente_secret]" \
-H "Content-Type: application/x-www-form-urlencoded;charset=UTF-8" \
--request POST https://idcs-your-instance.identity.oraclecloud.com/oauth2/v1/token \
-d "grant_type=client_credentials&scope=springapi:get" 

#aqui extraímos apenas o token do JSON retornado
curl -s \
-H "Authorization: Basic [base64 -> client_id:cliente_secret]" \
-H "Content-Type: application/x-www-form-urlencoded;charset=UTF-8" \
--request POST https://idcs-your-instance.identity.oraclecloud.com/oauth2/v1/token \
-d "grant_type=client_credentials&scope=springapi:get" \
| jq -r '.access_token'

#criamos uma variável com o token retornado, para utilizarmos na chamada da API protegida
TOKEN=$(curl -s --request POST \
  --url https://idcs-your-instance.identity.oraclecloud.com/oauth2/v1/token \
  --header 'Authorization: Basic [base64 -> client_id:cliente_secret]' \
  --header 'content-type: application/x-www-form-urlencoded;charset=UTF-8' \
  --data 'grant_type=client_credentials&scope=springapi:get' | jq -r '.access_token'); echo "The token is: " $TOKEN

#testando a API que não possui autenticação
curl -s http://localhost:8585/api/public/hello | jq

#testando a API protegida e enviando o token gerado no cabeçalho de autenticação
curl -s http://localhost:8585/api/private/hello -H "Authorization: Bearer $TOKEN" | jq

Referências