Como usar a autenticação com oAuth2 em REST

No artigo de hoje, vou mostrar em como utilizar a autenticação com oAuth2 em WebServices REST.

Antes de começarmos, esse artigo foi uma sugestão de tema enviada por Erick Landaverde. E o artigo foi redigido junto com a ajuda do grande Alison Lemes ( LinkedIn ).

Lembre-se de, no appserver.ini do REST, na seção HTTPREST, ative o SECURITY=1.

Agora vamos realizar um teste para pegar o token de acesso:

  1. Acesse a URL com os serviços REST, e procure pelo serviço TOKEN, clique nele para visualizar

API de Token

  1. Ao carregar, note que é um POST, e deixe anotado o caminho dele ( /api/oauth2/v1/token )

Pegando o caminho do TOKEN

  1. Abra um software para testarmos, no nosso caso iremos usar o Postman
  2. Adicione uma nova request, apontando para a URL, com o caminho do passo 3 na frente (e no tipo coloque como POST)

Informando a URL para buscar o TOKEN

  1. Clique na aba Params, informe os parâmetros grant_type (informe o texto “password”), password (senha do usuário do protheus) e username (nome do usuário do protheus)

Definindo o usuário e senha

  1. Execute o POST, se tudo der certo, irá ser retornado o token de acesso

Pegando o token gerado

Agora para testarmos algum método, basta informarmos esse token do passo 6. Para isso, iremos pegar qualquer outro método para testar (nesse exemplo, irei usar um padrão, o /api/framework/v1/systemParameters ).

  1. No Postman, adicione uma requisição do tipo GET, e coloque a URL junto com o da api citada acima

Informando a URL de uma API para ser consumida

  1. Clique na aba Authorization, no tipo coloque Bearer Token, e no conteúdo, coloque a chave gerada no item 6

Informando o Token de acesso

  1. Agora execute a requisição, e verifique o resultado

Resultado da execução

Update 11/10/2021:

A pedido do Walter Franco, segue um exemplo de um fonte para ser usado com REST. É uma classe simples que lista as transportadoras cadastradas na tabela SA4.

//Bibliotecas
#Include "TOTVS.ch"
#Include "RESTFul.ch"
#Include "TopConn.ch"
  
/*
    WS Rest de Exemplo para busca de Transportadoras
    Usando como base oAuth2
      
    Daniel Atilio
    https://terminaldeinformacao.com
*/
  
WSRESTFUL wTransporte DESCRIPTION "WS de Métodos de Transporte"
    //Atributos usados
    WSDATA limit AS INTEGER
    WSDATA page AS INTEGER
   
    //Métodos usados
    WSMETHOD GET ALL DESCRIPTION "Retorna todas as transportadoras" WSSYNTAX '/wTransporte?{limit, page}' PATH "all"       PRODUCES APPLICATION_JSON
END WSRESTFUL
  
//Poderia ser usado o FWAdapterBaseV2(), porém a paginação foi feita manualmente
WSMETHOD GET ALL WSRECEIVE limit, page WSSERVICE wTransporte
    Local lRet       := .T.
    Local oResponse  := JsonObject():New()
    Local cQuerySA4  := ""
    Local nTamanho   := 10
    Local nTotal     := 0
    Local nPags      := 0
    Local nPagina    := 0
    Local nAtual     := 0
  
    //Efetua a busca dos transportadoras
    cQuerySA4 := " SELECT " + CRLF
    cQuerySA4 += "     SA4.R_E_C_N_O_ AS SA4REC " + CRLF
    cQuerySA4 += " FROM " + CRLF
    cQuerySA4 += "     " + RetSQLName("SA4") + " SA4 " + CRLF
    cQuerySA4 += " WHERE " + CRLF
    cQuerySA4 += "     A4_FILIAL = '" + FWxFilial("SA4") + "' " + CRLF
    cQuerySA4 += "     AND SA4.D_E_L_E_T_ = '' " + CRLF
    cQuerySA4 += " ORDER BY " + CRLF
    cQuerySA4 += "     SA4REC " + CRLF
    TCQuery cQuerySA4 New Alias "QRY_SA4"
  
    //Se não encontrar a transportadora
    If QRY_SA4->(EoF())
        SetRestFault(500, "Falha ao consultar transportadoras - A consulta de transportadoras nao retornou nenhuma informacao, revise os filtros")
    Else
  
        oResponse["objects"] := {}
  
        //Conta o total de registros
        Count To nTotal
        QRY_SA4->(DbGoTop())
  
        //O tamanho do retorno, será o limit, se ele estiver definido
        If ! Empty(::limit)
            nTamanho := ::limit
        EndIf
  
        //Pegando total de páginas
        nPags := NoRound(nTotal / nTamanho, 0)
        nPags += Iif(nTotal % nTamanho != 0, 1, 0)
          
        //Se vier página
        If ! Empty(::page)
            nPagina := ::page
        EndIf
  
        //Se a página vier zerada ou negativa ou for maior que o máximo, será 1 
        If nPagina <= 0 .Or. nPagina > nPags
            nPagina := 1
        EndIf
  
        //Se a página for diferente de 1, pula os registros
        If nPagina != 1
            QRY_SA4->(DbSkip((nPagina-1) * nTamanho))
        EndIf
  
        //Adiciona os dados para a meta
        oJsonMeta := JsonObject():New()
        oJsonMeta["total"]         := nTotal
        oJsonMeta["current_page"]  := nPagina
        oJsonMeta["total_page"]    := nPags
        oJsonMeta["total_items"]   := nTamanho
        oResponse["meta"] := oJsonMeta
  
        //Percorre os transportadoras
        While ! QRY_SA4->(EoF())
            nAtual++
              
            //Se ultrapassar o limite, encerra o laço
            If nAtual > nTamanho
                Exit
            EndIf
  
            //Posiciona a transportadora e adiciona no retorno
            DbSelectArea("SA4")
            SA4->(DbGoTo(QRY_SA4->SA4REC))
            oJsonObj := JsonObject():New()
            oJsonObj["id"]   := SA4->A4_COD
            oJsonObj["name"] := SA4->A4_NOME
            aAdd(oResponse["objects"], oJsonObj)
  
            QRY_SA4->(DbSkip())
        EndDo
    EndIf
    QRY_SA4->(DbCloseArea())
  
    //Define o retorno
    Self:SetContentType("application/json")
    Self:SetResponse(oResponse:toJSON())
Return lRet

Bom pessoal, por hoje é só.

Abraços e até a próxima.

Dan (Daniel Atilio)
Cristão de ramificação protestante. Especialista em Engenharia de Software pela FIB, graduado em Banco de Dados pela FATEC Bauru e técnico em informática pelo CTI da Unesp. Entusiasta de soluções Open Source e blogueiro nas horas vagas. Autor e mantenedor do portal Terminal de Informação.

13 Responses

  1. Muchas Gracias ! Esto será muy útil !!!

  2. Walter Franco disse:

    Olá Dan,
    Realizei os passos que você apresentou aí e tô gostando mto destas séries de posts sobre o REST.
    No meu ambiente, ainda não tenho o dicionário no BD, portanto este seu exemplo não funcionou completamente no meu teste, assim, pra eu poder ter certeza de que está correto o que estou fazendo aqui, eu deveria criar um outro método qualquer baseando consulta em alguma tabela existente no BD certo?
    Qual o procedimento devo executar aqui pra ter o resultado positivo do teste?
    Obrigado!

    Waltre Franco

  3. Walter Franco disse:

    Olá Dan!

    Depois de um pequeno ajuste na Query, pois usamos Oracle aqui e o SA4.D_E_L_E_T_ = ‘ ‘, precisa de um espaço entre as aspas, funcionou direitinho e entendi a ideia do processo.
    Muito obrigado mesmo pelo feedback e dedicação em atender à minha solicitação.
    Forte abraço.

    Walter Franco

  4. Percy disse:

    Bom dia, muito bons os seus artigos …. no seu exemplo da transportadora, nao vejo o TOKEN para validar se o usuario esta validado no sistema

    • Boa tarde Percy.
      Obrigado pelo feedback.
      No caso, usando o oAuth não é necessário validar o token via código fonte, quem fica responsável de fazer essa validação é diretamente o AppServer.
      Então conforme os tópicos 7 ao 9, só precisamos informar o token obtido, e se na camada do AppServer bater a informação correta, ai que ele vai executar o método.
      Grande abraço.

  5. ismael disse:

    Post excelente, a totvs deveria te pagar por fazer esse trabalho maravilhoso.

  6. Wanderson Fernandes de Souza disse:

    Bom dia Daniel,

    Uma duvida, de posse do código do Token, como devemos informar este código dentro do advpl

Deixe uma resposta para Wanderson Fernandes de SouzaCancelar resposta

Terminal de Informação