Blog Post

Exchange Team Blog
1 MIN READ

Announcing Client Credential Flow for SMTP AUTH in Exchange Online

The_Exchange_Team's avatar
The_Exchange_Team
Platinum Contributor
Jul 10, 2023

We are excited to announce support of Client Credential Flow (CCF) for SMTP AUTH in Exchange Online. CCF for SMTP AUTH allows applications to use Modern authentication for submitting authenticated emails to Exchange Online without the need for interactive sign-on. Using OAuth reduces the chances of credentials being compromised during authentication.

The key benefit of CCF for SMTP is that it allows non-interactive sign-ins using OAuth. Non-interactive sign-in means that applications, scripts, and other automation can benefit from the increased security of Modern auth.

CCF for SMTP AUTH is available in all our environments today. You can use the steps in Authenticate an IMAP, POP or SMTP connection using OAuth for SMTP. The steps are summarized as:

  1. Register your application with Azure AD.
  2. Get an access token from a token server.
  3. Authenticate connection requests using the access token.

Exchange Online Team

Published Jul 10, 2023
Version 1.0

17 Comments

  • Debra_P's avatar
    Debra_P
    Brass Contributor

    Sunil Chauhan 

    Can you tell me what you did to get your implementation working?   What kind of errors were you seeing when it wasn't working?

     

    I cannot find any clues as to why it is failing....

     

    Thanks in Advance

  • Debra_P's avatar
    Debra_P
    Brass Contributor

     

    I am still having trouble accessing the token server.  I am not using the MSAL libraries, but rather am accessing it directly.  I am going to then use that token to send the email via java mail.

     

    Using the scope:  "https://outlook.office.com/SMTP.SendAsApp" causes the server to respond with "400 - Bad Request" and using the scope:  "https://outlook.office365.com/.default" (which was how it was previously documented) causes the server to respond with "401 - Unauthorized". 

     

    Here is my code that accesses the token server.   Please let me know if you see any issues with what I am doing.  Thanks in advance!

     

    public class OAuthToken {
    public String getAuthToken() throws IOException, NoSuchAlgorithmException, KeyManagementException {
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    return null;
    }
    @Override
    public void checkClientTrusted(X509Certificate[] certs, String authType) {
    }
    @Override
    public void checkServerTrusted(X509Certificate[] certs, String authType) {
    }
    }
    };

    // Install the all-trusting trust manager
    SSLContext sc = SSLContext.getInstance("TLS");
    sc.init(null, trustAllCerts, new java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

    // Create all-trusting host name verifier
    HostnameVerifier allHostsValid = new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
    return true;
    }
    };

    // These are dummy values. I have removed the actual values that allow connections to my server.
    String clientId = "my-client-id";
    String tenantId = "my-tenant-id";
    String scope = "https://outlook.office.com/SMTP.SendAsApp";
    String client_secret = "my-client-secret";
    String grant_type = "client_credentials";

    // Install the all-trusting host verifier
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    StringBuilder data = new StringBuilder();

    data.append("client_id=").append(clientId).append("&");
    data.append("scope=").append(scope).append("&");
    data.append("client_secret=").append(client_secret).append("&");
    data.append("grant_type=").append(grant_type);

    byte[] byteArray = data.toString().getBytes(StandardCharsets.UTF_8);

    String urlString = "https://login.microsoftonline.com/" + tenantId + "/oauth2/v2.0/token";
    URL url = new URL(urlString);

    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
    connection.setRequestMethod("POST");
    connection.setRequestProperty("Host", "login.microsoftonline.com");
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    connection.setRequestProperty("charset", "utf-8");
    connection.setRequestProperty("Content-Length", Integer.toString(byteArray.length));
    connection.setConnectTimeout(5000);
    connection.setUseCaches(false);
    connection.setDoOutput(true);
    try (OutputStream postStream = connection.getOutputStream()) {
    postStream.write(byteArray, 0, byteArray.length);
    }

    // Debug information...
    int responseCode = connection.getResponseCode();
    String responseMessage = connection.getResponseMessage();

    System.out.println("Response code = " + responseCode + ", Response description = " + responseMessage);

    InputStreamReader reader = new InputStreamReader(connection.getInputStream());
    String accessToken;
    try (BufferedReader in = new BufferedReader(reader)) {
    String json = in.readLine();
    if (json.length() > 0)
    System.out.println("Json String = " + json);
    else
    throw new IOException("No response returned from server while attempting to acquire access token.");

    // Parse the Json response and retrieve the Access Token
    Gson gson = new Gson();
    Type mapType = new TypeToken<Map<String,String>>(){}.getType();
    Map<String,String> response = gson.fromJson(json, mapType);
    accessToken = response.get("access_token");
    System.out.println("Access Token = " + accessToken);

    } finally {
    connection.disconnect();
    }

    return accessToken;
    }

     

  • Sunil Chauhan's avatar
    Sunil Chauhan
    Copper Contributor

    any body able to do a walk through post on this, i followed the steps in given links, added the permission to the service principal and full permission to mailbox but it still doesn't work for me"

     

    !! its working now !!

  • Many thanks for this.  With this now here, can we please have some commentary around this vs Anonymous SMTP relay via an Exchange Receive Connector?  Does Microsoft see CCF for SMTP as a proper option/ alternative to SMTP relaying?

     

    Seems like it to me but am not sure I am on the right path with this.

  • Navinsaini's avatar
    Navinsaini
    Copper Contributor

    AWeinmann You can use this - https://github.com/richfaj/SmtpClientDiag/blob/master/SmtpClientDiag.psm1 (https://github.com/richfaj/SmtpClientDiag/blob/master/README.md)