Adquisición de un token de acceso (Python)

En este ejemplo se muestra cómo llamar a un script externo de Python para obtener un token de OAuth2. La implementación del delegado de autenticación requiere un token de acceso de OAuth2 válido.

Requisitos previos

Para ejecutar el ejemplo:

  • Instale Python 3.10 o posterior.
  • Implemente utils.h/cpp en el proyecto.
  • Auth.py debe agregarse al proyecto y existir en el mismo directorio que los archivos binarios de la compilación.
  • Complete la configuración y configuración del SDK de Microsoft Information Protection (MIP). Entre otras tareas, registrará la aplicación cliente en el inquilino de Microsoft Entra. Microsoft Entra ID proporciona un identificador de aplicación, también conocido como id. de cliente, que se usa en la lógica de adquisición de tokens.

Este código no está pensado para su uso en producción. Solo se puede usar para conceptos de desarrollo y comprensión de la autenticación. El ejemplo es multiplataforma.

sample::auth::AcquireToken()

En el ejemplo de autenticación simple, mostramos una función simple AcquireToken() que no obtuvo parámetros y devolvió un valor de token codificado de forma rígida. En este ejemplo, sobrecargamos AcquireToken() para aceptar parámetros de autenticación y llamamos a un script de Python externo para devolver el token.

auth.h

En auth.h, se sobrecarga AcquireToken(), y la función sobrecargada y los parámetros actualizados son los siguientes:

//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.
    }
}

Los tres primeros parámetros los proporciona la entrada del usuario o codifican de forma rígida en la aplicación. El SDK proporciona los dos últimos parámetros al delegado de autenticación.

auth.cpp

En auth.cpp, agregamos la definición de función sobrecargada y, a continuación, definimos el código necesario para llamar al script de Python. La función acepta todos los parámetros proporcionados y los pasa al script de Python. El script se ejecuta y devuelve el token en formato de cadena.

#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

Este script adquiere tokens de autenticación directamente a través de la Biblioteca de autenticación de Microsoft (MSAL) para Python. Este código solo se incluye como medio para adquirir tokens de autenticación para su uso por parte de las aplicaciones de ejemplo y no está pensado para su uso en producción. El script solo funciona con inquilinos que admiten la autenticación de nombre de usuario y contraseña antiguas sin formato. La autenticación basada en certificados o MFA no se admite a través de este script.

Nota:

Antes de ejecutar este ejemplo, debe instalar MSAL para Python mediante la ejecución de uno de los siguientes 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:])

Actualizar AcquireOAuth2Token

Por último, actualice la función AcquireOAuth2Token en AuthDelegateImpl para llamar a la función AcquireToken sobrecargada. Las direcciones URL de recursos y autoridad se obtienen leyendo challenge.GetResource() y challenge.GetAuthority(). OAuth2Challenge se pasa al delegado de autenticación cuando se agrega el motor. El SDK realiza este trabajo y no requiere ningún trabajo adicional por parte del desarrollador.

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;
}

engine Cuando se agrega , el SDK llama a la función "AcquireOAuth2Token, pasando el desafío, ejecutando el script de Python, recibiendo un token y presentándole el token al servicio.