簡體   English   中英

使用 OAuth2.0 的客戶端憑據流的 IMAP 身份驗證失敗 | Java | 在線交流

[英]Authentication Failure for IMAP using Client Credential flow for OAuth2.0 | Java | Exchange Online

我在嘗試使用OAuth2.0的客戶端憑據授予流連接兩個 IMAP 協議時遇到身份驗證失敗問題。 在哪里,我一直按照微軟在其分步指南中建議的步驟進行操作,即“ 使用 OAuth 驗證 IMAP、POP 或 SMTP 連接

我一直在使用這個 github 項目使用客戶端憑據授予流程獲取訪問令牌MSAL Client Credential Grant using Java

Java IMAP代碼

public static void connectIMAP(String userEmail, String accessToken){
    String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
    Properties props= new Properties();
    
    props.put("mail.imap.ssl.enable", "true");
    props.put("mail.imap.sasl.enable", "true");
    props.put("mail.imap.port", "993");
    
    props.put("mail.imap.auth.mechanisms", "XOAUTH2");
    props.put("mail.imap.sasl.mechanisms", "XOAUTH2");
    
    props.put("mail.imap.auth.login.disable", "true");
    props.put("mail.imap.auth.plain.disable", "true");
    
    props.setProperty("mail.imap.socketFactory.class", SSL_FACTORY);
    props.setProperty("mail.imap.socketFactory.fallback", "false");
    props.setProperty("mail.imap.socketFactory.port", "993");
    props.setProperty("mail.imap.starttls.enable", "true");
    
    props.put("mail.debug", "true");
    props.put("mail.debug.auth", "true");

    Session session = Session.getInstance(props);
    session.setDebug(true);

    try {
        final Store store = session.getStore("imap");                   
        store.connect("outlook.office365.com",userEmail, accessToken);  
        
    } catch (NoSuchProviderException e) {   // session.getStore()
        e.printStackTrace();
    } catch (MessagingException e) {        // store.connect()
        e.printStackTrace();
    }
}

以下是我在使用 MSAL 庫執行客戶端憑據授予流程時使用的憑據

[注意:我一直在使用默認 Active Directory,以及我的 Azure 帳戶的默認用戶(管理員)。 這樣好嗎? 還是需要新的自定義 Azure AD 和單獨的租戶來執行客戶端憑據流]

下圖包含我在我的應用程序中應用的權限列表:應用權限列表的圖像

錯誤日志:

    *** IMAP *** 

DEBUG: JavaMail version 1.5.6
DEBUG: successfully loaded resource: /META-INF/javamail.default.providers
DEBUG: Tables of loaded providers
DEBUG: Providers Listed By Class Name: {com.sun.mail.smtp.SMTPSSLTransport=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], com.sun.mail.smtp.SMTPTransport=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle], com.sun.mail.imap.IMAPSSLStore=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], com.sun.mail.pop3.POP3SSLStore=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], com.sun.mail.imap.IMAPStore=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], com.sun.mail.pop3.POP3Store=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle]}
DEBUG: Providers Listed By Protocol: {imaps=javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle], imap=javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle], smtps=javax.mail.Provider[TRANSPORT,smtps,com.sun.mail.smtp.SMTPSSLTransport,Oracle], pop3=javax.mail.Provider[STORE,pop3,com.sun.mail.pop3.POP3Store,Oracle], pop3s=javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle], smtp=javax.mail.Provider[TRANSPORT,smtp,com.sun.mail.smtp.SMTPTransport,Oracle]}
DEBUG: successfully loaded resource: /META-INF/javamail.default.address.map
DEBUG: setDebug: JavaMail version 1.5.6
DEBUG: getProvider() returning javax.mail.Provider[STORE,imap,com.sun.mail.imap.IMAPStore,Oracle]
DEBUG IMAP: mail.imap.fetchsize: 16384
DEBUG IMAP: mail.imap.ignorebodystructuresize: false
DEBUG IMAP: mail.imap.statuscachetimeout: 1000
DEBUG IMAP: mail.imap.appendbuffersize: -1
DEBUG IMAP: mail.imap.minidletime: 10
DEBUG IMAP: enable STARTTLS
DEBUG IMAP: enable SASL
DEBUG IMAP: SASL mechanisms allowed: XOAUTH2
DEBUG IMAP: closeFoldersOnStoreFailure
DEBUG IMAP: trying to connect to host "outlook.office365.com", port 993, isSSL true
* OK The Microsoft Exchange IMAP4 service is ready. [UABO......]
A0 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
A0 OK CAPABILITY completed.
DEBUG IMAP: AUTH: PLAIN
DEBUG IMAP: AUTH: XOAUTH2
DEBUG IMAP: protocolConnect login, host=outlook.office365.com, user=ManishPrajapati@SampleOrg2022.onmicrosoft.com, password=<non-null>
DEBUG IMAP: SASL Mechanisms:
DEBUG IMAP:  XOAUTH2
DEBUG IMAP: 
DEBUG IMAP: SASL client XOAUTH2
DEBUG IMAP: SASL callback length: 2
DEBUG IMAP: SASL callback 0: javax.security.auth.callback.NameCallback@73f9ac
DEBUG IMAP: SASL callback 1: javax.security.auth.callback.PasswordCallback@1064425
A1 AUTHENTICATE XOAUTH2 dXNlcj.....
A1 NO AUTHENTICATE failed.
javax.mail.AuthenticationFailedException: AUTHENTICATE failed.
    at com.sun.mail.imap.IMAPStore.protocolConnect(IMAPStore.java:725)
    at javax.mail.Service.connect(Service.java:366)
    at javax.mail.Service.connect(Service.java:246)
    at test.ClientCredentialGrantAndConnect.connectIMAP(ClientCredentialGrantAndConnect.java:166)
    at test.ClientCredentialGrantAndConnect.main(ClientCredentialGrantAndConnect.java:45)

任何解決問題的幫助將不勝感激。

謝謝你。

我現在可以在交換在線服務器上為 IMAP 協議執行 OAuth2.0 身份驗證。 我發現我的方法的問題在於,由於缺乏使用 Azure 的經驗,我從錯誤的地方使用了一些參數。

按照分步指南中給出的說明並設置新創建的應用程序的權限就可以了。 但真正的問題是本文末尾給出的查詢,我們需要運行 3 個命令才能執行 OAuth2.0

據我了解,以下是執行服務主體相關查詢時使用的參數列表:

使用的參數(以及在哪里找到它們):

  • appId :應用程序(客戶端)ID [在應用程序概述屏幕中找到,來自企業和應用程序注冊]
  • entObjId :Object ID(企業應用程序)[僅在企業應用程序概述屏幕中找到]
  • orgId :目錄(租戶)ID [在 Azure AD 概覽屏幕中找到]

命令

  1. New-ServicePrincipal -AppId appId -ServiceId entObjId -Organization orgId
  2. Get-ServicePrincipal -Organization entObjId | 佛羅里達州
  3. Add-MailboxPermission -Identity "<email_id_here>" -User entObjId -AccessRights FullAccess

我面臨的困惑

  • 在 Add-MailboxPermission cmdlet 中,<SERVICE_PRINCIPAL_ID> 會造成混淆,因為為了應用“IMAP.AccessAsApp”之類的權限,互聯網告訴您“服務主體 ID”可以在 [Azure AD -> Enterprise Application ->(選擇的應用程序)中找到) -> 權限 -> IMAP.AccessAsApp -> 使用彈出菜單中的服務主體 ID ]
  • 可以在所有 3 個 cmdlet 中使用 Enterprise Object ID

我能夠使用客戶端憑據授予生成的 IMAP OAuth2 訪問令牌連接到郵箱。 以下是詳細信息:

Java 代碼(將以下代碼中的 clientid、secret、authority 和 email id 替換為您的 azure 應用注冊中的值)

//this method returns the token
public String getAccessTokenByClientCredentialGrant()  {
        
    String accessToken = null;
    String clientId = "<client id from azure app registration>";
    String secret = "<client secret from azure app registration>";
    String authority = "https://login.microsoftonline.com/<tenant-id from azure>/oauth2/v2.0/token";
    String scope = "https://outlook.office365.com/.default";
    log.info("Client ID : "+clientId);
    log.info("Client Secret : "+secret);
    log.info("Auth Server: "+authority);
    log.info("Scope: "+scope);
    
    try {
        
    
        ConfidentialClientApplication app = ConfidentialClientApplication.builder(
                clientId,
                ClientCredentialFactory.createFromSecret(secret))
                .authority(authority)
                .build();   
        
        // With client credentials flows the scope is ALWAYS of the shape "resource/.default", as the
        // application permissions need to be set statically (in the portal), and then granted by a tenant administrator
        ClientCredentialParameters clientCredentialParam = ClientCredentialParameters.builder(
                Collections.singleton(scope))
                .build();
        
        CompletableFuture<IAuthenticationResult> future = app.acquireToken(clientCredentialParam);
        IAuthenticationResult result = future.get();
        accessToken = result.accessToken();
        
    } catch(Exception e) {
        log.error("Exception in acquiring token: "+e.getMessage());
        e.printStackTrace();
    }
    log.info("Access Token : "+accessToken);
    return accessToken;
}

//This method connects to store using the access token
public Store connect(String userEmailId, String oauth2AccessToken) throws Exception {

    String host = "outlook.office365.com";
    String port = "993";
    Store store = null;
    
    
    String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
    Properties props= new Properties();

    props.put("mail.imaps.ssl.enable", "true");
    props.put("mail.imaps.sasl.enable", "true");
    props.put("mail.imaps.port", port);

    props.put("mail.imaps.auth.mechanisms", "XOAUTH2");
    props.put("mail.imaps.sasl.mechanisms", "XOAUTH2");
    
    props.put("mail.imaps.auth.login.disable", "true");
    props.put("mail.imaps.auth.plain.disable", "true");

    props.setProperty("mail.imaps.socketFactory.class", SSL_FACTORY);
    props.setProperty("mail.imaps.socketFactory.fallback", "false");
    props.setProperty("mail.imaps.socketFactory.port", port);
    props.setProperty("mail.imaps.starttls.enable", "true");

    props.put("mail.debug", "true");
    props.put("mail.debug.auth", "true");

    Session session = Session.getInstance(props);
    session.setDebug(true);
    
    store = session.getStore("imaps");
    
    log.info("OAUTH2 IMAP trying to connect with system properties to Host:" + host + ", Port: "+ port
            + ", userEmailId: " + userEmailId+ ", AccessToken: " + oauth2AccessToken);
    try {
    
        store.connect(host, userEmailId, oauth2AccessToken);
        log.info("IMAP connected with system properties to Host:" + host + ", Port: "+ port
            + ", userEmailId: " + userEmailId+ ", AccessToken: " + oauth2AccessToken);
        if(store.isConnected()){
            log.info("Connection Established using imap protocol successfully !");      
        }
    } catch (Exception e) {
        log.error("Store.Connect failed with the errror: "+e.getMessage());
        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        String exceptionAsString = sw.toString();
        log.error(exceptionAsString);
        
    }

    return store;

}

//the main method which calls the above 2 methods.
public void getEmailContents() throws Exception {
    Store store = null;
    
    String accessToken = getAccessTokenByClientCredentialGrant();
    String emailId = "<email which needs to be read>";

    try {
        store = connect(emailId, accessToken );
    } catch (Exception ex) {
        log.error("Exception in connecting to email " + ex.getMessage());
        ex.printStackTrace();
        
    }

    //write code to read email using javax.mail code

}


確保您的應用程序在 Azure 應用程序注冊下注冊,並且在應用程序的“API 權限”中授予以下 API 權限(應用程序權限)。

Office 365 在線交換

  • full_access_as_app
  • IMAP.AccessAsApp
  • 郵件閱讀
  • 郵件讀寫
  • 郵件.發送

還要確保使用以下鏈接中的命令將郵箱鏈接到 Azure: https://docs.microsoft.com/en-us/graph/auth-limit-mailbox-access

Test-ApplicationAccessPolicy -Identity <email> -AppId <app id>

您還可以使用https://jwt.io/解碼從 java 代碼生成的訪問令牌,並驗證權限是否正確。

驗證 office365 email 配置並確保 IMAP 已啟用:可以在以下鏈接中找到幫助。 https://www.limilabs.com/blog/office365-enable-imap-pop3-smtp

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM