Adquirir um token de acesso (Python)

Este exemplo demonstra como chamar um script de Python externo para obter um token OAuth 2.0. A implementação do delegado de autenticação requer um token de acesso OAuth 2.0 válido.

Pré-requisitos

Para executar o exemplo:

  • Instale o Python 3.10 ou mais recente.
  • Implemente utils.h/cpp em seu projeto.
  • Adicione auth.py ao seu projeto, no mesmo diretório que os binários no momento da compilação.
  • Conclua a instalação e a configuração do SDK da Proteção de Informações da Microsoft (MIP). Entre outras tarefas, você registra seu aplicativo cliente em seu locatário do Microsoft Entra. Microsoft Entra ID fornece uma ID do aplicativo, também conhecida como ID do cliente, para sua lógica de aquisição de token.

Esse código não se destina ao uso de produção. Use-o apenas para desenvolvimento e para entender os conceitos de autenticação. A amostra é multiplataforma.

sample::auth::AcquireToken()

O exemplo de autenticação simples demonstra uma função simples AcquireToken() que não usa parâmetros e retorna um valor de token codificado em código. Este exemplo sobrecarrega AcquireToken() para aceitar parâmetros de autenticação e chamar um script Python externo para retornar o token.

auth.h

Em auth.h, AcquireToken() está sobrecarregado. A função sobrecarregada e os parâmetros atualizados são os seguintes:

//auth.h
#include <string>

namespace sample {
  namespace auth {
    std::string AcquireToken(
        const std::string& userName, //A string value containing the user's UPN.
        const std::string& password, //The user's password in plaintext
        const std::string& clientId, //The Azure AD client ID (also known as Application ID) of your application.
        const std::string& resource, //The resource URL for which an OAuth2 token is required. Provided by challenge object.
        const std::string& authority); //The authentication authority endpoint. Provided by challenge object.
    }
}

A entrada do usuário ou seu aplicativo fornece os três primeiros parâmetros. O SDK fornece os dois últimos parâmetros para o delegado de autenticação.

auth.cpp

O arquivo auth.cpp adiciona a definição de função sobrecarregada e define o código que chama o script Python. A função aceita todos os parâmetros fornecidos e os passa para o script Python. O script é executado e retorna o token no formato de cadeia de caracteres.

#include "auth.h"
#include "utils.h"

#include <fstream>
#include <functional>
#include <memory>
#include <string>

using std::string;
using std::runtime_error;

namespace sample {
    namespace auth {

    //This function implements token acquisition in the application by calling an external Python script.
    //The Python script requires username, password, clientId, resource, and authority.
    //Username, Password, and ClientId are provided by the user/developer
    //Resource and Authority are provided as part of the OAuth2Challenge object that is passed in by the SDK to the AuthDelegate.
    string AcquireToken(
        const string& userName,
        const string& password,
        const string& clientId,
        const string& resource,
        const string& authority) {

    string cmd = "python";
    if (sample::FileExists("auth.py"))
        cmd += " auth.py -u ";

    else
        throw runtime_error("Unable to find auth script.");

    cmd += userName;
    cmd += " -p ";
    cmd += password;
    cmd += " -a ";
    cmd += authority;
    cmd += " -r ";
    cmd += resource;
    cmd += " -c ";
    // Replace <application-id> with the Application ID provided during your Azure AD application registration.
    cmd += (!clientId.empty() ? clientId : "<application-id>");

    string result = sample::Execute(cmd.c_str());
    if (result.empty())
        throw runtime_error("Failed to acquire token. Ensure Python is installed correctly.");

    return result;
    }
    }
}

Script de Python

Esse script adquire tokens de autenticação diretamente usando Biblioteca do Microsoft Authenticator (MSAL) para Python. Esse código é incluído apenas para adquirir tokens de autenticação para uso pelos aplicativos de exemplo e não se destina a ser usado em produção. O script funciona somente em locatários que dão suporte à autenticação de nome de usuário/senha. O script não dá suporte à MFA (autenticação multifator) nem à autenticação baseada em certificado.

Observação

Antes de executar este exemplo, instale a MSAL para Python executando um dos seguintes comandos:

pip install msal
pip3 install msal
import getopt
import sys
import json
import re
from msal import PublicClientApplication

def printUsage():
  print('auth.py -u <username> -p <password> -a <authority> -r <resource> -c <clientId>')

def main(argv):
  try:
    options, args = getopt.getopt(argv, 'hu:p:a:r:c:')
  except getopt.GetoptError:
    printUsage()
    sys.exit(-1)

  username = ''
  password = ''
  authority = ''
  resource = ''

  clientId = ''
    
  for option, arg in options:
    if option == '-h':
      printUsage()
      sys.exit()
    elif option == '-u':
      username = arg
    elif option == '-p':
      password = arg
    elif option == '-a':
      authority = arg
    elif option == '-r':
      resource = arg
    elif option == '-c':
      clientId = arg

  if username == '' or password == '' or authority == '' or resource == '' or clientId == '':
    printUsage()
    sys.exit(-1)

  # ONLY FOR DEMO PURPOSES AND MSAL FOR PYTHON
  # This shouldn't be required when using proper auth flows in production.  
  if authority.find('common') > 1:
    authority = authority.split('/common')[0] + "/organizations"
   
  app = PublicClientApplication(client_id=clientId, authority=authority)  
  
  result = None  

  if resource.endswith('/'):
    resource += ".default"    
  else:
    resource += "/.default"
  
  # *DO NOT* use username/password authentication in production system.
  # Instead, consider auth code flow and using a browser to fetch the token.
  result = app.acquire_token_by_username_password(username=username, password=password, scopes=[resource])
  print(result['access_token'])

if __name__ == '__main__':  
  main(sys.argv[1:])

Atualizar AcquireOAuth2Token

Por fim, atualize a função AcquireOAuth2Token em AuthDelegateImpl para chamar a função sobrecarregada AcquireToken. Leia challenge.GetResource() e challenge.GetAuthority() para obter as URLs do recurso e da autoridade. O SDK passa o OAuth2Challenge ao delegado de autenticação quando adiciona o mecanismo. Esse comportamento do SDK não requer nenhum trabalho extra do desenvolvedor.

bool AuthDelegateImpl::AcquireOAuth2Token(
    const mip::Identity& /*identity*/,
    const OAuth2Challenge& challenge,
    OAuth2Token& token) {

    //call our AcquireToken function, passing in username, password, clientId, and getting the resource/authority from the OAuth2Challenge object
    string accessToken = sample::auth::AcquireToken(mUserName, mPassword, mClientId, challenge.GetResource(), challenge.GetAuthority());
    token.SetAccessToken(accessToken);
    return true;
}

Quando o SDK adiciona o engine, ele chama a função AcquireOAuth2Token. A função passa no desafio, executa o script Python, recebe um token e apresenta o token para o serviço.