Acquisire un token di accesso (Python)

Questo esempio illustra come chiamare uno script Python esterno per ottenere un token OAuth 2.0. L'implementazione del delegato di autenticazione richiede un token di accesso OAuth 2.0 valido.

Prerequisiti

Per eseguire l'esempio:

  • Installare Python 3.10 o versione successiva.
  • Implementare utils.h/cpp nel progetto.
  • Aggiungere auth.py al progetto, nella stessa directory dei file binari in fase di compilazione.
  • Completare la configurazione e la configurazione di Microsoft Information Protection SDK (MIP). Tra le altre attività, tu registri l'applicazione client nel tenant di Microsoft Entra. Microsoft Entra ID fornisce un ID applicazione, noto anche come ID client, per la logica di acquisizione del token.

Questo codice non è destinato all'uso in produzione. Usarlo solo per lo sviluppo e per comprendere i concetti di autenticazione. L'esempio è multipiattaforma.

sample::auth::AcquireToken()

L'esempio di autenticazione semplice illustra una funzione semplice AcquireToken() che non accetta parametri e restituisce un valore di token hardcoded. Questo esempio esegue l'overload AcquireToken() per accettare i parametri di autenticazione e chiamare uno script di Python esterno per restituire il token.

auth.h

In auth.h, AcquireToken() è sovraccaricato. La funzione sovraccaricata e i parametri aggiornati sono i seguenti:

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

L'input dell'utente o l'applicazione fornisce i primi tre parametri. L'SDK fornisce gli ultimi due parametri al delegato di autenticazione.

auth.cpp

Il file auth.cpp aggiunge la definizione della funzione sovraccaricata e quindi definisce il codice che richiama lo script Python. La funzione accetta tutti i parametri forniti e li passa allo script di Python. Lo script viene eseguito e restituisce il token in formato stringa.

#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 di Python

Questo script acquisisce i token di autenticazione direttamente usando Libreria di Autenticazione Microsoft (MSAL) per Python. Questo codice è incluso solo per acquisire i token di autenticazione da usare dalle app di esempio e non è destinato all'uso nell'ambiente di produzione. Lo script funziona solo nei tenant che supportano l'autenticazione con nome utente/password. Lo script non supporta l'autenticazione a più fattori (MFA) o l'autenticazione basata su certificati.

Annotazioni

Prima di eseguire questo esempio, installare MSAL per Python eseguendo uno dei comandi seguenti:

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:])

Aggiornare AcquireOAuth2Token

Infine, aggiornare la AcquireOAuth2Token funzione in AuthDelegateImpl per chiamare la funzione sovraccaricata AcquireToken. Leggere challenge.GetResource() e challenge.GetAuthority() per ottenere gli URL della risorsa e dell'autorità. L’SDK passa OAuth2Challenge al delegato di autenticazione quando aggiunge il motore. Questo comportamento dell'SDK non richiede alcun lavoro aggiuntivo da parte dello sviluppatore.

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 l'SDK aggiunge engine, chiama la AcquireOAuth2Token funzione . La funzione passa il challenge, esegue lo script Python, riceve un token e fornisce il token al servizio.