Les emails expédiés par une application, un site, un webservice (…) sont généralement confiés à un serveur en utilisant le protocole SMTP (Simple Mail Transfer Protocol).

 

Les contraintes de sécurité actuelles peuvent rendre l'utilisation de ce protocole complexe. C'est le cas lorsque l'hébergeur du serveur SMTP ajoute une double authentification avec OAuth 2, comme Google pour Gmail, ou Microsoft avec Office365. En effet lorsqu'une authentification oAuth est demandée par un service de messagerie :

En fonction des besoins, on peut donc être amené à remplacer l'appel des fonctions EmailOuvreSession et EmailEnvoieMessage, par l'appel de la fonction RESTEnvoie afin d'envoyer les emails non pas à un serveur SMTP, mais à une API en ligne. Le principe de token sera identique :

  • une première requête à l'API en HTTPS va demander le token,
  • une seconde requête va envoyer l'email grâce au token obtenu.

 

Voici un exemple de code avec l'API Microsoft Graph :

 

Requête est une restRequête
Réponse est une httpRéponse
Token est un JSON

sClientId est une chaîne = "---" // Données du compte Azure dans lequel l'application est définie
sTenantId est une chaîne = "---"
sClientSecret est une chaîne = "---"
sURI est une chaîne = "https://login.microsoftonline.com/%1/oauth2/v2.0/token"
// Ou suivant le cas : "https://login.microsoftonline.com/common/oauth2/v2.0/token"


// Requête pour obtenir un token...
Requête..URL = ChaîneConstruit(sURI, sTenantId)
Requête..Méthode = httpPost
Requête..ContentType = "application/x-www-form-urlencoded"
HTTPCréeFormulaire("FORM_TOKEN")
HTTPAjouteParamètre("FORM_TOKEN","grant_type","client_credentials")
HTTPAjouteParamètre("FORM_TOKEN","client_id",sClientId)
HTTPAjouteParamètre("FORM_TOKEN","client_secret", sClientSecret)
HTTPAjouteParamètre("FORM_TOKEN","scope","https://graph.microsoft.com/.default")
Réponse = HTTPEnvoieFormulaire("FORM_TOKEN",Requête)
Token = Réponse..Contenu

// Requête pour envoyer l'email grâce au token d'autorisation obtenu...
VariableRAZ(Requête)
sExpéditeur est une chaîne = "adresse@expediteur.demo"
sURI = "https://graph.microsoft.com/v1.0/users/%1/sendMail"
sURI = ChaîneConstruit(sURI, sExpéditeur )
// Ou suivant le cas : "https://graph.microsoft.com/v1.0/me/sendMail"

// Construction de l'email avec le type Email standard du WLangage...

MonMessage est un Email

monAttache est un emailAttache

sFichier est une chaîne = "c:\temp\exemple-image.jpg"
MonMessage.Message = "Mon email texte"
MonMessage.HTML = "Message <b>HTML</b>"
MonMessage.Expediteur = sExpéditeur
MonMessage.Sujet = "Test : API Graph Microsoft"
MonMessage.Destinataire.Ajoute("adresse@destinataire.demo")
monAttache.Nom = fExtraitChemin(sFichier, fFichier + fExtension)
monAttache.Contenu = fChargeBuffer(sFichier)
monAttache.ContentType = "image/jpeg"
monAttache.ContentDescription = "Une image jointe"
Ajoute(MonMessage.Attache, monAttache)

// Construction d'un buffer contenant la totalité de l'email (comme un .EML)
MessageEnBuffer est un Buffer
MonMessage.ConstruitSource(emailOptionEncodeEntête)
MessageEnBuffer = MonMessage.Source
MessageEnBuffer = MessageEnBuffer.Encode(encodeBASE64)

// Requête pour envoyer le message

Requête..URL = sURI
Requête..Méthode = httpPost
Requête..ContentType = "text/plain"

Requête.Entête["Authorization"] = Token.token_type+" "+Token.access_token

Requête.Contenu = MessageEnBuffer
Réponse = RESTEnvoie(Requête)
SI Réponse.CodeEtat = 202 ALORS OK...

 

Variantes et astuces de mise au point :

  • La propriété ProcédureTrace du type RestRequete permet d'avoir le contenu complet de l'échange avec le serveur. Elle est pratique en phase de mise au point afin de recherche une éventuelle erreur dans le contenu envoyé :

    Requête..ProcédureTrace = RESTEnvoie_Trace


    Avec :

    PROCEDURE INTERNE RESTEnvoie_Trace( nTypeInfo est un entier, bufData est un Buffer )

    SELON nTypeInfo
    CAS httpTraceEntêteEnvoyé :
    Trace("Entête envoyée" + UTF8VersChaîne( bufData ) )

    CAS httpTraceDonnéeEnvoyée :
    Trace("Données envoyée" + bufData )

    CAS httpTraceEntêteRecu :
    Trace("Entête reçue" + UTF8VersChaîne( bufData ) )

    CAS httpTraceDonnéeRecue :
    Trace("DOnnées reçues" + bufData )
    FIN

    FIN

  • Important : les autorisations associées à un token renvoyé par un service, donne des possibilités dépendantes du "scope" indiqué envoyée à l'API. On peut donc avoir obtenu un token valide, mais avoir un échec pour envoyer le message car le scope du token n'autorise pas l'envoi. Si un token ne donne pas accès à une fonctionnalité, c'est donc :

    • que le "scope" donné pour l'authentification n'avait pas la fonctionnalité nécessaire dans l'application dans le portail Azure,

    • ou que l'application configurée dans le service d'authentification n'autorise pas la fonctionnalité demandée,

    • ou que l'application configurée dans le service d'authentification autorise la fonctionnalité demandée mais avec des caractéristiques inadaptées (ex dans le cas de Office365 un type "Déléguée" au lieu de "Application" dans Azure)...

  • Pour simplifier l'exemple ne traite pas les réponses du serveur. Il faut ajouter des contrôles systématique sur Réponse..CodeEtat et Token = Réponse..Contenu. Si le token n'est pas obtenu il ne faut pas envoyer l'email et à la place indiquer la réponse du serveur.

 

< Retour

1 commentaire

François Verney
10/10/2022 - 08:54 - Répondre
Merci pour ce code ! Petite question "EmailEnBuffer" et $emailFrom viennent d'ou ?

Publier un commentaire : 
Votre adresse email ne sera pas publiée