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 Erick LandaverdeCancelar resposta

Terminal de Informação