繁体   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