Exchange удаленный сервер возвратил ошибку 401 несанкционированный

Getting The request failed. The remote server returned an error: (401)Unauthorized when I try to connect to Microsoft Exchange Server
The error message I am getting is :

microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException: The request failed. The request failed. The remote server returned an error: (401)Unauthorized
    at microsoft.exchange.webservices.data.core.request.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:74)
    at microsoft.exchange.webservices.data.core.request.MultiResponseServiceRequest.execute(MultiResponseServiceRequest.java:158)
    at microsoft.exchange.webservices.data.core.ExchangeService.findItems(ExchangeService.java:981)
    at microsoft.exchange.webservices.data.core.ExchangeService.findItems(ExchangeService.java:1024)
    at microsoft.exchange.webservices.data.core.ExchangeService.findItems(ExchangeService.java:1100)
Caused by: microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException: The request failed. The remote server returned an error: (401)Unauthorized
    at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.validateAndEmitRequest(ServiceRequestBase.java:644)
    at microsoft.exchange.webservices.data.core.request.SimpleServiceRequestBase.internalExecute(SimpleServiceRequestBase.java:62)
    ... 6 more
Caused by: microsoft.exchange.webservices.data.core.exception.http.HttpErrorException: The remote server returned an error: (401)Unauthorized
    at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.getEwsHttpWebResponse(ServiceRequestBase.java:723)
    at microsoft.exchange.webservices.data.core.request.ServiceRequestBase.validateAndEmitRequest(ServiceRequestBase.java:639)
    ... 7 more

The code snippet which I have been using is:

System.setProperty("javax.net.ssl.trustStore","C:\Users\vermad\Downloads\Softz\jssecacerts");
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
ExchangeCredentials credentials = new WebCredentials("username.domain.com","password");
service.setCredentials(credentials);
service.setUrl(new URI("https:/domain/EWS/Exchange.asmx"));
ItemView view = new ItemView (5);
FindItemsResults<Item> findResults = service.findItems(WellKnownFolderName.Inbox, view);
for(Item item : findResults.getItems()){
    item.load(new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.MimeContent));
    System.out.println("id==========" + item.getId());
}

Всем привет, несколько дней пытаюсь понять, почему не работает autodiscover, причем если проверять из аутлука , то он его находит (внутри сети).

Имя домена .local , почта  .ru . Exchange 2019 Сертификат самоподписанный. Пока что сервер работает только внутри сети. 

Внутри  в днс сделал запись на autodiscover. Если захожу с браузера на https://autodiscover.mydomain.ru/Autodiscover/Autodiscover.xml, то просит ввести учетку доменную, не .ru , а .local domainusername , далее выводит  в браузере :

<Autodiscover xmlnshttp://schemas.microsoft.com/exchange/autodiscover/responseschema/2006«>

<Response>

<Error Time19:02:09.3919275« Id3007185755«>

<ErrorCode>600</ErrorCode>

<Message>Недопустимый запрос</Message>

<DebugData/>

</Error>

</Response>

</Autodiscover>

тут так и должно быть? думал что авторизация к автодискавери должна идти через username@mydomain.ru

Выводит из консоли:

[PS] C:Windowssystem32>Test-WebServicesconnectivity | fl

RunspaceId          : a2482324-671e-403b-9a4f-2c78f53b65e0
Source              : Exchange.mydomain.local
ServiceEndpoint     : autodiscover.mydomain.ru
Scenario            : AutoDiscoverSoapProvider
ScenarioDescription : Автообнаружение: поставщик SOAP
Result              : Failure
Latency             : 49
Error               : System.Net.WebException: Удаленный сервер возвратил ошибку: (401) Несанкционированный.
                         в System.Net.HttpWebRequest.GetResponse()
                         в Microsoft.Exchange.Management.SystemConfigurationTasks.ServiceValidatorBase.InternalInvoke()
                         в Microsoft.Exchange.Management.SystemConfigurationTasks.ServiceValidatorBase.Invoke()
Verbose             : [2023-01-18 14:42:33Z] Подключение Автообнаружение к «https://autodiscover.mydomain.ru/Autodiscover/Autodiscover.svc».
                      [2023-01-18 14:42:33Z] Тестовая учетная запись: extest_879f7275158e4@mydomain.local Пароль: ******
                      [2023-01-18 14:42:33Z] Запрос Автообнаружение:
                      User-Agent: EXCHANGE/Test-WebServicesConnectivity/extest_879f7275158e4@mydomain.ru
                      Content-Type: text/xml; charset=utf-8
                      Authorization: Negotiate TlRMTVNTUAADAAAAGAAYALAAAAByAXIByAAAAAAAAABYAAAASABIAFgAAAAQABAAoAAAABAAEAA6AgAAFYKI4goAY0UAAAAP7sXj2eD5WLq7L00hNhiNhmUAeAB0AGUAcwB0AF8AOAA3ADkAZgA3ADIANwA1ADEANQA4AGUANABAAGMAaABpAHMAdABvA
                      HYAaQBlAC4AbABvAGMAYQBsAEUAWABDAEgAQQBOAEcARQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCV66W9bRFCmyWrGnavkP9AQEAAAAAAADGLncZSyvZAUlkdHpfNW0vAAAAAAIAEgBDAEgASQBTAFQATwBWAEkARQABABAARQBYAEMASABBAE4ARwBFAAQAHgBjAGgAaQBzAHQAbwB
                      2AGkAZQAuAGwAbwBjAGEAbAADADAARQB4AGMAaABhAG4AZwBlAC4AYwBoAGkAcwB0AG8AdgBpAGUALgBsAG8AYwBhAGwABQAeAGMAaABpAHMAdABvAHYAaQBlAC4AbABvAGMAYQBsAAcACADGLncZSyvZAQYABAACAAAACAAwADAAAAAAAAAAAAAAAABAAABRNykLHpoppy8+GWozcnK7O
                      sdnq0PRNB1t3edWjBNMHwoAEABy78eA5RqPgmVnSBYGnmu0CQA8AEgAVABUAFAALwBhAHUAdABvAGQAaQBzAGMAbwB2AGUAcgAuAGMAaABpAHMAdABvAHYAaQBlAC4AcgB1AAAAAAAAAAAA8jezubC4kQuyE2Xoh9i1UA==
                      Host: autodiscover.mydomain.ru
                      Content-Length: 1147
                      Expect: 100-continue
                      [2023-01-18 14:42:33Z] Запрос Автообнаружение:
                      <?xml version=»1.0″ encoding=»utf-8″ standalone=»yes»?>
                      <soap:Envelope xmlns:a=»http://schemas.microsoft.com/exchange/2010/Autodiscover» xmlns:wsa=»http://www.w3.org/2005/08/addressing» xmlns:xsi=»http://www.w3.org/2001/XMLSchema-instance»
xmlns:soap=»http://schemas.xml
                      soap.org/soap/envelope/»>
                        <soap:Header>
                          <a:RequestedServerVersion>Exchange2010</a:RequestedServerVersion>
                          <wsa:Action>http://schemas.microsoft.com/exchange/2010/Autodiscover/Autodiscover/GetUserSettings</wsa:Action>
                          <wsa:To>https://autodiscover.mydomain.ru/Autodiscover/Autodiscover.svc</wsa:To>
                        </soap:Header>
                        <soap:Body>
                          <a:GetUserSettingsRequestMessage xmlns:a=»http://schemas.microsoft.com/exchange/2010/Autodiscover»>
                            <a:Request>
                              <a:Users>
                                <a:User>
                                  <a:Mailbox>extest_879f7275158e4@mydomain.ru</a:Mailbox>
                                </a:User>
                              </a:Users>
                              <a:RequestedSettings>
                                <a:Setting>ExternalEwsUrl</a:Setting>
                                <a:Setting>InternalEwsUrl</a:Setting>
                              </a:RequestedSettings>
                            </a:Request>
                          </a:GetUserSettingsRequestMessage>
                        </soap:Body>
                      </soap:Envelope>
                      [2023-01-18 14:42:33Z] Ответ Автообнаружение:
                      request-id: c06407c8-06ce-4390-8524-466c33fa206f
                      X-OWA-Version: 15.2.922.14
                      Server: Microsoft-IIS/10.0
                      WWW-Authenticate: Negotiate,NTLM,Basic realm=»autodiscover.mydomain.ru»
                      X-Powered-By: ASP.NET
                      X-FEServer: EXCHANGE
                      Date: Wed, 18 Jan 2023 14:42:32 GMT
                      Content-Length: 0
                      [2023-01-18 14:42:33Z] Ответ Автообнаружение:
                      System.Net.WebException: Удаленный сервер возвратил ошибку: (401) Несанкционированный.
                         в System.Net.HttpWebRequest.GetResponse()
                         в Microsoft.Exchange.Management.SystemConfigurationTasks.ServiceValidatorBase.InternalInvoke()
                         в Microsoft.Exchange.Management.SystemConfigurationTasks.ServiceValidatorBase.Invoke()
MonitoringEventId   : 6051

RunspaceId          : a2482324-671e-403b-9a4f-2c78f53b65e0
Source              : Exchange.mydomain.local
ServiceEndpoint     :
Scenario            : EwsGetFolder
ScenarioDescription : EWS: GetFolder
Result              : Skipped
Latency             : 0
Error               : Пропущено тестирование веб-служб Exchange из-за ошибки на шаге автообнаружения.
Verbose             :
MonitoringEventId   : 5053

  • Изменено

    18 января 2023 г. 16:06

  • Перемещено
    Иван ПродановMicrosoft contingent staff, Moderator
    25 января 2023 г. 9:53
    moved
  • Изменен тип
    Petko KrushevMicrosoft contingent staff, Owner
    25 апреля 2023 г. 7:33
  • Изменен тип
    Petko KrushevMicrosoft contingent staff, Owner
    25 апреля 2023 г. 7:33

I am extracting gmail contacts using opencontactsnet. I get The remote server returned an error: (401) Unauthorized. when executing this line

HttpWebResponse exportResponse = ( HttpWebResponse ) contactsRequest.GetResponse();

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Net;
using System.Web.UI;
using System.Web.UI.WebControls;
using OpenContactsNet;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        GmailExtract gm = new GmailExtract();
        NetworkCredential nw = new NetworkCredential("chendur.pandiya","**");
        MailContactList mc;
        if (gm.Extract(nw, out mc))
        {

        }
        else
        {

        }
     }
}

Here is the complete GmailExtract class,

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Utilities.Web;

namespace OpenContactsNet
{
    public class GmailExtract : IMailContactExtract
    {
        private const string ContinueUrl = "http://mail.google.com/mail?ui=html&amp;zy=l";
        private const string ExportUrl = "https://mail.google.com/mail/contacts/data/export?exportType=ALL&groupToExport=&out=GMAIL_CSV";
        private const string LoginRefererUrl = "https://www.google.com/accounts/ServiceLogin?service=mail&passive=true&rm=false&continue=http%3A%2F%2Fmail.google.com%2Fmail%2F%3Fui%3Dhtml%26zy%3Dl&ltmpl=default&ltmplcache=2";
        private const string LoginUrl = "https://www.google.com/accounts/ServiceLoginAuth?service=mail";
        private const string UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; EmbeddedWB 14.52 from: http://www.bsalsa.com/ EmbeddedWB 14.52; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1; .NET CLR 1.0.3705; .NET CLR 3.0.04506.30)";

        #region IMailContactExtract Members

        public bool Extract( NetworkCredential credential, out MailContactList list )
        {
            bool result = false;
            list = new MailContactList();

            try
            {
                CookieCollection cookies = new CookieCollection();

                // Prepare login form data
                HttpValueCollection loginFormValues = new HttpValueCollection();
                loginFormValues[ "ltmpl" ] = "default";
                loginFormValues[ "ltmplcache" ] = "2";
                loginFormValues[ "continue" ] = ContinueUrl;
                loginFormValues[ "service" ] = "mail";
                loginFormValues[ "rm" ] = "false";
                loginFormValues[ "hl" ] = "en";
                loginFormValues[ "Email" ] = credential.UserName;
                loginFormValues[ "Passwd" ] = credential.Password;
                loginFormValues[ "PersistentCookie" ] = "true";
                loginFormValues[ "rmShown" ] = "1";
                loginFormValues[ "null" ] = "Sign In";

                // Convert to bytes
                byte[] loginPostData = Encoding.UTF8.GetBytes( loginFormValues.ToString( true ) );

                HttpWebRequest loginRequest = ( HttpWebRequest ) WebRequest.Create( LoginUrl );
                loginRequest.Method = "POST";
                loginRequest.UserAgent = UserAgent;
                loginRequest.Referer = LoginRefererUrl;
                loginRequest.ContentType = "application/x-www-form-urlencoded";
                loginRequest.ContentLength = loginPostData.Length;
                loginRequest.AllowAutoRedirect = false;

                // Create cookie container
                loginRequest.CookieContainer = new CookieContainer();

                // Add post data to request
                Stream stream;
                using ( stream = loginRequest.GetRequestStream() )
                {
                    stream.Write( loginPostData, 0, loginPostData.Length );
                }

                HttpWebResponse loginResponse = ( HttpWebResponse ) loginRequest.GetResponse();

                cookies.Add( loginResponse.Cookies );

                // Create request to export Google CSV page
                HttpWebRequest contactsRequest = ( HttpWebRequest ) WebRequest.Create( ExportUrl );
                contactsRequest.Method = "GET";
                contactsRequest.UserAgent = UserAgent;
                contactsRequest.Referer = loginResponse.ResponseUri.ToString();

                // use cookie gotten from login page
                contactsRequest.CookieContainer = new CookieContainer();
                foreach ( Cookie cookie in cookies )
                {
                    contactsRequest.CookieContainer.Add( cookie );
                }

                HttpWebResponse exportResponse = ( HttpWebResponse ) contactsRequest.GetResponse();

                // Read data from response stream
                string csvData;
                using ( Stream responseStream = exportResponse.GetResponseStream() )
                {
                    using ( StreamReader streamRead = new StreamReader( responseStream ) )
                    {
                        csvData = streamRead.ReadToEnd();
                    }
                }

                // parse google csv
                string[] lines = csvData.Split( 'n' );
                foreach ( string line in lines )
                {
                    string[] values = line.Split( ',' );
                    if ( values.Length < 2 )
                    {
                        continue;
                    }

                    MailContact mailContact = new MailContact();
                    mailContact.Email = values[ 1 ];
                    mailContact.Name = values[ 0 ];
                    list.Add( mailContact );
                }

                result = true;
            }
            catch (Exception e)
            {
                throw e;
            }

            return result;
        }

        #endregion
    }
}

asked Aug 2, 2010 at 6:43

ACP's user avatar

I ran into the same problem. I had to change the GmailExtract class in opencontactsnet. Below is the new code which works for me.

public class GmailExtract : IMailContactExtract
    {
        private const string ContinueUrl = "https://mail.google.com/mail/?"; 
        private const string ExportUrl = "https://mail.google.com/mail/contacts/data/export?exportType=ALL&groupToExport=&out=GMAIL_CSV";
        private const string LoginRefererUrl = "https://www.google.com/accounts/ServiceLogin?service=mail&passive=true&rm=false&continue=http%3A%2F%2Fmail.google.com%2Fmail%2F%3Fui%3Dhtml%26zy%3Dl&ltmpl=default&ltmplcache=2";
        private const string LoginUrl = "https://www.google.com/accounts/ServiceLoginAuth?service=mail";
        private const string UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)";

        #region IMailContactExtract Members

        public bool Extract( NetworkCredential credential, out MailContactList list)
        {
            bool result = false;
            list = new MailContactList();

            DateTime jsStartDate = new DateTime(1970, 1, 1);
            TimeSpan endTs = DateTime.Now.Subtract(jsStartDate);
            TimeSpan startTs = DateTime.Now.AddMinutes(-2).Subtract(jsStartDate);

            try
            {
                CookieCollection cookies = new CookieCollection();

                // Prepare login form data
                HttpValueCollection loginFormValues = new HttpValueCollection();
                loginFormValues["Email"] = credential.UserName;
                loginFormValues["Passwd"] = credential.Password;
                loginFormValues["asts"] = "";
                loginFormValues["continue"] = ContinueUrl;
                loginFormValues["dsh"] = "1461574034599761425";
                loginFormValues["hl"] = "en";
                loginFormValues["ltmpl"] = "default";
                loginFormValues["ltmplcache"] = "2";
                loginFormValues["rm"] = "false";
                loginFormValues["rmShown"] = "1";
                loginFormValues["service"] = "mail";
                loginFormValues["signIn"] = "Sign In";
                loginFormValues["scc"] = "1";
                loginFormValues["ss"] = "1";
                loginFormValues["GALX"] = "rBTUs4OAJBI";
                loginFormValues["ltmpl"] = "default";
                loginFormValues["ltmpl"] = "default";

                // Convert to bytes
                byte[] loginPostData = Encoding.UTF8.GetBytes( loginFormValues.ToString( true ) );

                HttpWebRequest loginRequest = ( HttpWebRequest ) WebRequest.Create( LoginUrl );
                loginRequest.Method = "POST";
                loginRequest.UserAgent = UserAgent;
                loginRequest.Referer = LoginRefererUrl;
                loginRequest.ContentType = "application/x-www-form-urlencoded";
                loginRequest.ContentLength = loginPostData.Length;
                loginRequest.AllowAutoRedirect = false;

                // Create cookie container
                loginRequest.CookieContainer = new CookieContainer();
                loginRequest.CookieContainer.Add(new Cookie("GMAIL_LOGIN", "T" + startTs.Milliseconds.ToString() + "/" + startTs.Milliseconds.ToString() + "/" + endTs.Milliseconds.ToString(), "/", ".google.com"));
                loginRequest.CookieContainer.Add(new Cookie("GALX", "rBTUs4OAJBI", "/accounts", ".google.com"));

                // Add post data to request
                Stream stream;
                using ( stream = loginRequest.GetRequestStream())
                {
                    stream.Write( loginPostData, 0, loginPostData.Length);
                }

                HttpWebResponse loginResponse = ( HttpWebResponse ) loginRequest.GetResponse();

                cookies.Add( loginResponse.Cookies );

                // Create request to export Google CSV page
                HttpWebRequest contactsRequest = ( HttpWebRequest ) WebRequest.Create( ExportUrl );
                contactsRequest.Method = "GET";
                contactsRequest.UserAgent = UserAgent;
                contactsRequest.Referer = loginResponse.ResponseUri.ToString();

                // use cookie gotten from login page
                contactsRequest.CookieContainer = new CookieContainer();
                foreach ( Cookie cookie in cookies )
                {
                    contactsRequest.CookieContainer.Add( cookie );
                }

                HttpWebResponse exportResponse = ( HttpWebResponse ) contactsRequest.GetResponse();

                // Read data from response stream
                string csvData;
                using ( Stream responseStream = exportResponse.GetResponseStream())
                {
                    using ( StreamReader streamRead = new StreamReader( responseStream ) )
                    {
                        csvData = streamRead.ReadToEnd();
                    }
                }

                // parse google csv
                string[] lines = csvData.Split( 'n' );
                foreach ( string line in lines )
                {
                    string[] values = line.Split( ',' );
                    if ( values.Length < 2 )
                    {
                        continue;
                    }

                    MailContact mailContact = new MailContact();
                    mailContact.Email = values[ 28 ];
                    mailContact.Name = values[ 0 ];
                    if (mailContact.Email.Trim().Length > 0)
                    {

                         list.Add(mailContact);

                    }
                }

                result = true;
            }
            catch
            {
            }

            return result;
        }

        #endregion
    }

answered Aug 2, 2010 at 7:31

ajay_whiz's user avatar

ajay_whizajay_whiz

17.3k3 gold badges36 silver badges44 bronze badges

5

I am not familiar with google’s api nor do I know C#.
However check the URL you are sending your form to.
Looks like your authentication works. Otherwise you would have recieved a 403.
401 means that you (as an authenticated user) are not allowed to access the requested resource.

answered Aug 2, 2010 at 7:03

sprehn's user avatar

1

10.4.2 401 Unauthorized

The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information.

Is the LoginRefererUrl correctly formed? As that address isn’t found on google servers however the correctly formed version signs me straight into my mail box.

answered Aug 2, 2010 at 7:11

Confussedinwales's user avatar

I am extracting gmail contacts using opencontactsnet. I get The remote server returned an error: (401) Unauthorized. when executing this line

HttpWebResponse exportResponse = ( HttpWebResponse ) contactsRequest.GetResponse();

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Net;
using System.Web.UI;
using System.Web.UI.WebControls;
using OpenContactsNet;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        GmailExtract gm = new GmailExtract();
        NetworkCredential nw = new NetworkCredential("chendur.pandiya","**");
        MailContactList mc;
        if (gm.Extract(nw, out mc))
        {

        }
        else
        {

        }
     }
}

Here is the complete GmailExtract class,

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Utilities.Web;

namespace OpenContactsNet
{
    public class GmailExtract : IMailContactExtract
    {
        private const string ContinueUrl = "http://mail.google.com/mail?ui=html&amp;zy=l";
        private const string ExportUrl = "https://mail.google.com/mail/contacts/data/export?exportType=ALL&groupToExport=&out=GMAIL_CSV";
        private const string LoginRefererUrl = "https://www.google.com/accounts/ServiceLogin?service=mail&passive=true&rm=false&continue=http%3A%2F%2Fmail.google.com%2Fmail%2F%3Fui%3Dhtml%26zy%3Dl&ltmpl=default&ltmplcache=2";
        private const string LoginUrl = "https://www.google.com/accounts/ServiceLoginAuth?service=mail";
        private const string UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; EmbeddedWB 14.52 from: http://www.bsalsa.com/ EmbeddedWB 14.52; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1; .NET CLR 1.0.3705; .NET CLR 3.0.04506.30)";

        #region IMailContactExtract Members

        public bool Extract( NetworkCredential credential, out MailContactList list )
        {
            bool result = false;
            list = new MailContactList();

            try
            {
                CookieCollection cookies = new CookieCollection();

                // Prepare login form data
                HttpValueCollection loginFormValues = new HttpValueCollection();
                loginFormValues[ "ltmpl" ] = "default";
                loginFormValues[ "ltmplcache" ] = "2";
                loginFormValues[ "continue" ] = ContinueUrl;
                loginFormValues[ "service" ] = "mail";
                loginFormValues[ "rm" ] = "false";
                loginFormValues[ "hl" ] = "en";
                loginFormValues[ "Email" ] = credential.UserName;
                loginFormValues[ "Passwd" ] = credential.Password;
                loginFormValues[ "PersistentCookie" ] = "true";
                loginFormValues[ "rmShown" ] = "1";
                loginFormValues[ "null" ] = "Sign In";

                // Convert to bytes
                byte[] loginPostData = Encoding.UTF8.GetBytes( loginFormValues.ToString( true ) );

                HttpWebRequest loginRequest = ( HttpWebRequest ) WebRequest.Create( LoginUrl );
                loginRequest.Method = "POST";
                loginRequest.UserAgent = UserAgent;
                loginRequest.Referer = LoginRefererUrl;
                loginRequest.ContentType = "application/x-www-form-urlencoded";
                loginRequest.ContentLength = loginPostData.Length;
                loginRequest.AllowAutoRedirect = false;

                // Create cookie container
                loginRequest.CookieContainer = new CookieContainer();

                // Add post data to request
                Stream stream;
                using ( stream = loginRequest.GetRequestStream() )
                {
                    stream.Write( loginPostData, 0, loginPostData.Length );
                }

                HttpWebResponse loginResponse = ( HttpWebResponse ) loginRequest.GetResponse();

                cookies.Add( loginResponse.Cookies );

                // Create request to export Google CSV page
                HttpWebRequest contactsRequest = ( HttpWebRequest ) WebRequest.Create( ExportUrl );
                contactsRequest.Method = "GET";
                contactsRequest.UserAgent = UserAgent;
                contactsRequest.Referer = loginResponse.ResponseUri.ToString();

                // use cookie gotten from login page
                contactsRequest.CookieContainer = new CookieContainer();
                foreach ( Cookie cookie in cookies )
                {
                    contactsRequest.CookieContainer.Add( cookie );
                }

                HttpWebResponse exportResponse = ( HttpWebResponse ) contactsRequest.GetResponse();

                // Read data from response stream
                string csvData;
                using ( Stream responseStream = exportResponse.GetResponseStream() )
                {
                    using ( StreamReader streamRead = new StreamReader( responseStream ) )
                    {
                        csvData = streamRead.ReadToEnd();
                    }
                }

                // parse google csv
                string[] lines = csvData.Split( 'n' );
                foreach ( string line in lines )
                {
                    string[] values = line.Split( ',' );
                    if ( values.Length < 2 )
                    {
                        continue;
                    }

                    MailContact mailContact = new MailContact();
                    mailContact.Email = values[ 1 ];
                    mailContact.Name = values[ 0 ];
                    list.Add( mailContact );
                }

                result = true;
            }
            catch (Exception e)
            {
                throw e;
            }

            return result;
        }

        #endregion
    }
}

asked Aug 2, 2010 at 6:43

ACP's user avatar

I ran into the same problem. I had to change the GmailExtract class in opencontactsnet. Below is the new code which works for me.

public class GmailExtract : IMailContactExtract
    {
        private const string ContinueUrl = "https://mail.google.com/mail/?"; 
        private const string ExportUrl = "https://mail.google.com/mail/contacts/data/export?exportType=ALL&groupToExport=&out=GMAIL_CSV";
        private const string LoginRefererUrl = "https://www.google.com/accounts/ServiceLogin?service=mail&passive=true&rm=false&continue=http%3A%2F%2Fmail.google.com%2Fmail%2F%3Fui%3Dhtml%26zy%3Dl&ltmpl=default&ltmplcache=2";
        private const string LoginUrl = "https://www.google.com/accounts/ServiceLoginAuth?service=mail";
        private const string UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)";

        #region IMailContactExtract Members

        public bool Extract( NetworkCredential credential, out MailContactList list)
        {
            bool result = false;
            list = new MailContactList();

            DateTime jsStartDate = new DateTime(1970, 1, 1);
            TimeSpan endTs = DateTime.Now.Subtract(jsStartDate);
            TimeSpan startTs = DateTime.Now.AddMinutes(-2).Subtract(jsStartDate);

            try
            {
                CookieCollection cookies = new CookieCollection();

                // Prepare login form data
                HttpValueCollection loginFormValues = new HttpValueCollection();
                loginFormValues["Email"] = credential.UserName;
                loginFormValues["Passwd"] = credential.Password;
                loginFormValues["asts"] = "";
                loginFormValues["continue"] = ContinueUrl;
                loginFormValues["dsh"] = "1461574034599761425";
                loginFormValues["hl"] = "en";
                loginFormValues["ltmpl"] = "default";
                loginFormValues["ltmplcache"] = "2";
                loginFormValues["rm"] = "false";
                loginFormValues["rmShown"] = "1";
                loginFormValues["service"] = "mail";
                loginFormValues["signIn"] = "Sign In";
                loginFormValues["scc"] = "1";
                loginFormValues["ss"] = "1";
                loginFormValues["GALX"] = "rBTUs4OAJBI";
                loginFormValues["ltmpl"] = "default";
                loginFormValues["ltmpl"] = "default";

                // Convert to bytes
                byte[] loginPostData = Encoding.UTF8.GetBytes( loginFormValues.ToString( true ) );

                HttpWebRequest loginRequest = ( HttpWebRequest ) WebRequest.Create( LoginUrl );
                loginRequest.Method = "POST";
                loginRequest.UserAgent = UserAgent;
                loginRequest.Referer = LoginRefererUrl;
                loginRequest.ContentType = "application/x-www-form-urlencoded";
                loginRequest.ContentLength = loginPostData.Length;
                loginRequest.AllowAutoRedirect = false;

                // Create cookie container
                loginRequest.CookieContainer = new CookieContainer();
                loginRequest.CookieContainer.Add(new Cookie("GMAIL_LOGIN", "T" + startTs.Milliseconds.ToString() + "/" + startTs.Milliseconds.ToString() + "/" + endTs.Milliseconds.ToString(), "/", ".google.com"));
                loginRequest.CookieContainer.Add(new Cookie("GALX", "rBTUs4OAJBI", "/accounts", ".google.com"));

                // Add post data to request
                Stream stream;
                using ( stream = loginRequest.GetRequestStream())
                {
                    stream.Write( loginPostData, 0, loginPostData.Length);
                }

                HttpWebResponse loginResponse = ( HttpWebResponse ) loginRequest.GetResponse();

                cookies.Add( loginResponse.Cookies );

                // Create request to export Google CSV page
                HttpWebRequest contactsRequest = ( HttpWebRequest ) WebRequest.Create( ExportUrl );
                contactsRequest.Method = "GET";
                contactsRequest.UserAgent = UserAgent;
                contactsRequest.Referer = loginResponse.ResponseUri.ToString();

                // use cookie gotten from login page
                contactsRequest.CookieContainer = new CookieContainer();
                foreach ( Cookie cookie in cookies )
                {
                    contactsRequest.CookieContainer.Add( cookie );
                }

                HttpWebResponse exportResponse = ( HttpWebResponse ) contactsRequest.GetResponse();

                // Read data from response stream
                string csvData;
                using ( Stream responseStream = exportResponse.GetResponseStream())
                {
                    using ( StreamReader streamRead = new StreamReader( responseStream ) )
                    {
                        csvData = streamRead.ReadToEnd();
                    }
                }

                // parse google csv
                string[] lines = csvData.Split( 'n' );
                foreach ( string line in lines )
                {
                    string[] values = line.Split( ',' );
                    if ( values.Length < 2 )
                    {
                        continue;
                    }

                    MailContact mailContact = new MailContact();
                    mailContact.Email = values[ 28 ];
                    mailContact.Name = values[ 0 ];
                    if (mailContact.Email.Trim().Length > 0)
                    {

                         list.Add(mailContact);

                    }
                }

                result = true;
            }
            catch
            {
            }

            return result;
        }

        #endregion
    }

answered Aug 2, 2010 at 7:31

ajay_whiz's user avatar

ajay_whizajay_whiz

17.3k3 gold badges36 silver badges44 bronze badges

5

I am not familiar with google’s api nor do I know C#.
However check the URL you are sending your form to.
Looks like your authentication works. Otherwise you would have recieved a 403.
401 means that you (as an authenticated user) are not allowed to access the requested resource.

answered Aug 2, 2010 at 7:03

sprehn's user avatar

1

10.4.2 401 Unauthorized

The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information.

Is the LoginRefererUrl correctly formed? As that address isn’t found on google servers however the correctly formed version signs me straight into my mail box.

answered Aug 2, 2010 at 7:11

Confussedinwales's user avatar

Появление сообщения об ошибке 401 Unauthorized Error («отказ в доступе») при открытии страницы сайта означает неверную авторизацию или аутентификацию пользователя на стороне сервера при обращении к определенному url-адресу. Чаще всего она возникает при ошибочном вводе имени и/или пароля посетителем ресурса при входе в свой аккаунт. Другой причиной являются неправильные настройки, допущенные при администрировании web-ресурса. Данная ошибка отображается в браузере в виде отдельной страницы с соответствующим описанием. Некоторые разработчики интернет-ресурсов, в особенности крупных порталов, вводят собственную дополнительную кодировку данного сбоя:

  • 401 Unauthorized;
  • Authorization Required;
  • HTTP Error 401 – Ошибка авторизации.

Попробуем разобраться с наиболее распространенными причинами возникновения данной ошибки кода HTTP-соединения и обсудим способы их решения.

При доступе к некоторым сайтам (или отдельным страницам этих сайтов), посетитель должен пройти определенные этапы получения прав:

  1. Идентификация – получение вашей учетной записи («identity») по username/login или email.
  2. Аутентификация («authentic») – проверка того, что вы знаете пароль от этой учетной записи.
  3. Авторизация – проверка вашей роли (статуса) в системе и решение о предоставлении доступа к запрошенной странице или ресурсу на определенных условиях.

Большинство пользователей сохраняют свои данные по умолчанию в истории браузеров, что позволяет быстро идентифицироваться на наиболее часто посещаемых страницах и синхронизировать настройки между устройствами. Данный способ удобен для серфинга в интернете, но может привести к проблемам с безопасностью доступа к конфиденциальной информации. При наличии большого количества авторизованных регистрационных данных к различным сайтам используйте надежный мастер-пароль, который закрывает доступ к сохраненной в браузере информации.

Наиболее распространенной причиной появления ошибки с кодом 401 для рядового пользователя является ввод неверных данных при посещении определенного ресурса. В этом и других случаях нужно попробовать сделать следующее:

  1. Проверьте в адресной строке правильность написания URL. Особенно это касается перехода на подстраницы сайта, требующие авторизации. Введите правильный адрес. Если переход на страницу осуществлялся после входа в аккаунт, разлогинитесь, вернитесь на главную страницу и произведите повторный вход с правильными учетными данными.
  2. При осуществлении входа с сохраненными данными пользователя и появлении ошибки сервера 401 проверьте их корректность в соответствующих настройках данного браузера. Возможно, авторизационные данные были вами изменены в другом браузере. Также можно очистить кэш, удалить cookies и повторить попытку входа. При удалении истории браузера или очистке кэша потребуется ручное введение логина и пароля для получения доступа. Если вы не помните пароль, пройдите процедуру восстановления, следуя инструкциям.
  3. Если вы считаете, что вводите правильные регистрационные данные, но не можете получить доступ к сайту, обратитесь к администратору ресурса. В этом случае лучше всего сделать скриншот проблемной страницы.
  4. Иногда блокировка происходит на стороне провайдера, что тоже приводит к отказу в доступе и появлению сообщения с кодировкой 401. Для проверки можно попробовать авторизоваться на том же ресурсе с альтернативного ip-адреса (например, используя VPN). При подтверждении блокировки трафика свяжитесь с провайдером и следуйте его инструкциям.

Некоторые крупные интернет-ресурсы с большим количеством подписчиков используют дополнительные настройки для обеспечения безопасности доступа. К примеру, ваш аккаунт может быть заблокирован при многократных попытках неудачной авторизации. Слишком частые попытки законнектиться могут быть восприняты как действия бота. В этом случае вы увидите соответствующее сообщение, но можете быть просто переадресованы на страницу с кодом 401. Свяжитесь с администратором сайта и решите проблему.

Иногда простая перезагрузка проблемной страницы, выход из текущей сессии или использование другого веб-браузера полностью решают проблему с 401 ошибкой авторизации.

Ошибка 401 - отказ в доступе

Устранение ошибки 401 администратором веб-ресурса 

Для владельцев сайтов, столкнувшихся с появлением ошибки отказа доступа 401, решить ее порою намного сложнее, чем обычному посетителю ресурса. Есть несколько рекомендаций, которые помогут в этом:

  • Обращение в службу поддержки хостинга сайта. Как и в случае возникновения проблем с провайдером, лучше всего подробно описать последовательность действий, приведших к появлению ошибки 401, приложить скриншот.
  • При отсутствии проблем на стороне хостинг-провайдера можно внести следующие изменения в настройки сайта с помощью строки Disallow:/адрес проблемной страницы. Запретить индексацию страницам с ошибкой в «rоbоts.txt», после чего добавить в файл «.htассеss» строку такого типа:
Redirect 301 /oldpage.html http://site.com/newpage.html.

Где в поле /oldpage.html прописывается адрес проблемной страницы, а в http://site.com/newpage.html адрес страницы авторизации.

Таким образом вы перенаправите пользователей со всех страниц, которые выдают ошибку 401, на страницу начальной авторизации.

  • Если после выполнения предыдущих рекомендаций пользователи при попытках авторизации все равно видят ошибку 401, то найдите на сервере файл «php.ini» и увеличьте время жизни сессии, изменив значения следующих параметров: «session.gc_maxlifetime» и «session.cookie_lifetime» на 1440 и 0 соответственно.
  • Разработчики веб-ресурсов могут использовать более сложные методы авторизации и аутентификации доступа для создания дополнительной защиты по протоколу HTTP. Если устранить сбой простыми методами администрирования не удается, следует обратиться к специалистам, создававшим сайт, для внесения соответствующих изменений в код.

Хотя ошибка 401 и является проблемой на стороне клиента, ошибка пользователя на стороне сервера может привести к ложному требованию входа в систему. К примеру, сетевой администратор разрешит аутентификацию входа в систему всем пользователям, даже если это не требуется. В таком случае сообщение о несанкционированном доступе будет отображаться для всех, кто посещает сайт. Баг устраняется внесением соответствующих изменений в настройки.

Дополнительная информация об ошибке с кодом 401

Веб-серверы под управлением Microsoft IIS могут предоставить дополнительные данные об ошибке 401 Unauthorized в виде второго ряда цифр:

  • 401, 1 – войти не удалось;
  • 401, 2 – ошибка входа в систему из-за конфигурации сервера;
  • 401, 3 – несанкционированный доступ из-за ACL на ресурс;
  • 401, 501 – доступ запрещен: слишком много запросов с одного и того же клиентского IP; ограничение динамического IP-адреса – достигнут предел одновременных запросов и т.д.

Более подробную информацию об ошибке сервера 401 при использовании обычной проверки подлинности для подключения к веб-узлу, который размещен в службе MS IIS, смотрите здесь. 

Следующие сообщения также являются ошибками на стороне клиента и относятся к 401 ошибке:

  • 400 Bad Request; 
  • 403 Forbidden; 
  • 404 Not Found;
  • 408 Request Timeout.

Как видим, появление ошибки авторизации 401 Unauthorized не является критичным для рядового посетителя сайта и чаще всего устраняется самыми простыми способами. В более сложной ситуации оказываются администраторы и владельцы интернет-ресурсов, но и они в 100% случаев разберутся с данным багом путем изменения настроек или корректировки html-кода с привлечением разработчика сайта. 

  • Remove From My Forums
  • Вопрос

  • (Beginner) Creating a Provider-hosted calendar list programmatically.

    I have done this:

    Uri hostWeb = new Uri(Request.QueryString["SPHostUrl"]);
    
                using (var clientContext = TokenHelper.GetS2SClientContextWithWindowsIdentity(hostWeb, Request.LogonUserIdentity))
                {
                    Web web = clientContext.Web;
                    ListCreationInformation listCreator = new ListCreationInformation();
                    listCreator.Title = "CompanyCalendar";
                    listCreator.Description = "Workcalendar";
                    
                    listCreator.TemplateType = (int)ListTemplateType.GanttTasks; //106 = events 120=CustomGrid 140=WorkflowHistory 150=GanttTasks
                    web.Lists.Add(listCreator);
                    clientContext.ExecuteQuery();
    }

    If I remove the code above there are no problems, as the title says there is something wrong with my permission to modify but I have set the wevb permission to «Full control» so it should be something else.

    Some people thinks that it has to do with IIS so I tried to set the servers authorization rules to allow all users but that didnt do it (maybe I didn’t do it right).

    Im a beginner so please explain as you would for a absolute beginner that dont know IIS.How can I solv this problem, why doesent I get permission to create a list on my sharepointsite?

    thanks

    • Изменено

      22 мая 2014 г. 22:54
      language

    • Изменен тип
      Hemendra Agrawal
      23 мая 2014 г. 5:09
      q

Ответы

  • Problem solved!

    I used a SharePoint developer-site (in my case «https://mysite@company.onmicrosoft.com») to debugg with instead of the virtual machine server (in my case «http://SERVER1»). It should be the other way around.

    I login to my virtual machine as Developer and to be able to use that login «Request.LogonUserIdentity» with my App I have to be connected to the virtual server «SERVER1» in my case.

    Beginners misstake. Hope it can help other beginners atleast.

    • Помечено в качестве ответа
      Hannes Bergsten
      26 мая 2014 г. 9:39

In my previous post, you learned how to troubleshoot HTTP Error 503. Today, we will look into how to troubleshoot 401 – Unauthorized: Access is denied due to invalid credentials in Internet Information Services (IIS).

Contents

  1. 401 – Unauthorized
  2. How IIS authentication works
  3. Cause of error
  4. Debugging the error
  5. Resolving the error
  6. Common 401 substatus codes
  7. Conclusion
  • Author
  • Recent Posts

Surender Kumar has more than twelve years of experience in server and network administration. His fields of interest are Windows Servers, Active Directory, PowerShell, web servers, networking, Linux, virtualization, and penetration testing. He loves writing for his blog.

Surender Kumar has more than twelve years of experience in server and network administration. His fields of interest are Windows Servers, Active Directory, PowerShell, web servers, networking, Linux, virtualization, and penetration testing. He loves writing for his blog.

Latest posts by Surender Kumar (see all)

  • Backup in Proxmox VE — Thu, Jan 26 2023
  • Snapshots in Proxmox VE — Wed, Jan 25 2023
  • Create a Windows VM in Proxmox VE — Fri, Jan 13 2023

401 – Unauthorized

401 Unauthorized Access is denied due to invalid credentials

401 Unauthorized Access is denied due to invalid credentials

The 401 – Unauthorized: Access is denied due to invalid credentials error is a common access-related error that you may experience with a website hosted on IIS.

How IIS authentication works

The error itself indicates that it is caused by a failure to authorize access. Someone who is a beginner in IIS could find the error description «you do not have permission to view this directory or page using the credentials that you supplied» slightly confusing. If you think from an end user’s perspective, you might be wondering when you supplied any credentials while accessing the website. Well, this happened automatically on the server side. By default, every website hosted on IIS has anonymous authentication enabled.

If you open the IIS Manager, select your website, and then double-click Authentication under the IIS section in the Features view, you will see the various authentication modes (such as basic authentication, forms authentication, anonymous authentication, etc.) that are supported by IIS.

Viewing various authentication modes supported by a website in IIS

Viewing various authentication modes supported by a website in IIS

Each website has to have at least one authentication mode enabled and, by default, Anonymous Authentication mode enabled. Authentication is a mechanism that is used to verify the visitor’s identity to your website. See the following screenshot for reference:

Default authentication modes supported by a website in IIS

Default authentication modes supported by a website in IIS

Anonymous authentication allows visitors to access the public content of your website anonymously without having to supply any credentials. If you don’t want to use it, select the authentication mode, and then click Disable in the Actions pane on the right. To view or modify which credentials are used by anonymous authentication, click the Edit link on the right. You will see two options, as shown in the following screenshot:

View or modify the credentials used by anonymous authentication in IIS

View or modify the credentials used by anonymous authentication in IIS

By default, each website is set to use the IUSR user for anonymous user identity, which is a built-in account starting with IIS version 7. If you are using a custom username as the application pool identity, make sure you select the application pool identity option here. This way, you don’t have to worry about updating the user’s password in the website configuration over and over when the user’s password is changed.

Cause of error

The primary cause of the 401 – Unauthorized: Access is denied due to invalid credentials error is that you are using a custom username as the application pool identity. In simple terms, when you set the application pool to use a custom application pool identity (a custom username and password) rather than the default identity (which is ApplicationPoolIdentity), the Anonymous Authentication mode of the website continues using the IUSR user for authentication, which results in this error.

Debugging the error

As discussed in previous posts, the error page we saw above is a custom error page and does not reveal much helpful information for administrators. If you enable the detailed errors, you will see an error page with much detailed information, as shown in the following screenshot:

Error message 401.3 – You do not have permission to view this directory or page using the credentials you supplied (acc

Error message 401.3 – You do not have permission to view this directory or page using the credentials you supplied (access denied due to Access Control Lists)

The detailed error gave you the HTTP 401.3 status code, which will guide you in checking the access control permissions on the file system. Make sure that the user who is set as the application pool identity has the read and execute permissions on the website’s root directory. If the error persists, the most likely cause is incorrect anonymous user identity. The following screenshot shows the problematic scenario:

Mismatched application pool identity and anonymous authentication identity user in IIS

Mismatched application pool identity and anonymous authentication identity user in IIS

The screenshot shows a mismatched application pool identity and anonymous authentication identity on the website, which is causing the error. For a website to work properly, both should be the same.

Resolving the error

Now that you know the cause of the HTTP 401.3 status code, do the following to fix the error:

If you are using a custom username as the application pool identity, make sure that the user has read and execute permissions on the website’s root directory.

Ensuring that the custom application pool user has read and execute permissions on the root directory

Ensuring that the custom application pool user has read and execute permissions on the root directory

If you’re using the default ApplicationPoolIdentity for your application pool, make sure that the built-in IUSR user or IIS_IUSRS group has exactly the same permissions as shown in the screenshot below.

Ensuring that the default application pool identity has read and execute permissions on the root directory

Ensuring that the default application pool identity has read and execute permissions on the root directory

If the error persists, edit the Anonymous Authentication setting of your website, as explained in the How IIS authentication works section. Be sure it is set to use the Application pool identity. See the following screenshot for reference:

Modifying the anonymous authentication identity to match the application pool identity in IIS

Modifying the anonymous authentication identity to match the application pool identity in IIS

Common 401 substatus codes

The following table covers the most common HTTP 401 substatus codes, along with their possible causes and troubleshooting advice:

Subscribe to 4sysops newsletter!

Status Code Possible Cause Troubleshooting Advice
401.1 Logon failed The logon attempt failed, probably due to an invalid user name or password.
401.2 Logon failed due to server configuration The 401.2 status code indicates that there is a problem in the authentication configuration on the server.
401.3 Unauthorized due to ACL on resource We covered how to fix this error above.

Conclusion

I hope you’re enjoying this series of resolving common HTTP errors. In the next post, we will discuss how to fix the HTTP Error 403.14 – Forbidden error.

I am trying to get started with using IIS. I created a new site on IIS Manager, mapped it to a folder on my file system and added index.html to the folder. I have set the port to 85 for this site. When I try to access http://localhost:85/index.html, I get the following error message:

401.3 — unathorized — You do not have permission to view this directory or page because of the access control list (ACL)
configuration or encryption settings for this resource on the Web
server.

I gave read access to everybody on the folder and tried again. I could then access the page.

I then compared the properties of my folder with that of wwwroot. I found that wwwroot had read access on IIS_IUSRS…When I did the same on my folder and tried again, I got the above error again.
I checkedthat anonymous access is enabled by default, but I still get this error.

Why does this happen? What is the correct way to resolve the problem?

asked Oct 3, 2013 at 15:00

Aadith Ramia's user avatar

Aadith RamiaAadith Ramia

9,84519 gold badges65 silver badges86 bronze badges

0

I have struggled on this same issue for several days. It can be solved by modifying the security user access properties of the file system folder on which your site is mapped. But IIS_IUSRS is not the only account you must authorize.

  • In IIS management console, in the Authentication part of the configuration of your site, modify the «Anonymous authentication» line and check the account set as «Specific user» (mine is IUSR).
  • Give read and execution permission on the folder of your site to the account listed as the specific user.

OR

  • In IIS management console, in the Authentication part of the configuration of your site, modify the «Anonymous authentication» line by selecting «Identity of the application pool» instead of «Specific user».

ScottyG's user avatar

ScottyG

3,0963 gold badges30 silver badges42 bronze badges

answered May 6, 2014 at 12:29

groch's user avatar

2

Here is what worked for me.

  1. Set the app pool identity to an account that can be assigned
    permissions to a folder.
  2. Ensure the source directory and all related files have been granted
    read rights to the files to the account assigned to the app pool identity property
  3. In IIS, at the server root node, set anonymous user to inherit from
    app pool identity. (This was the part I struggled with)

To set the server anonymous to inherit from the app pool identity do the following..

  • Open IIS Manager (inetmgr)
  • In the left-hand pane select the root node (server host name)
  • In the middle pane open the ‘Authentication’ applet
  • Highlight ‘Anonymous Authentication’
  • In the right-hand pane select ‘Edit…’ (a dialog box should open)
  • select ‘Application pool identity’

answered Oct 19, 2015 at 18:23

barrypicker's user avatar

barrypickerbarrypicker

9,4928 gold badges62 silver badges78 bronze badges

6

TL;DR;

In most cases, granting access to the following account(s) (one|both) will be enough:

  1. IIS AppPoolDefaultAppPool
  2. IUSR

with Access Rights:

  1. Read & Execute
  2. List folder contents
  3. Read

That’s it!

Read on for a more detailed explanation…


  1. Open IIS and select your application.
  2. On the right side click on Authentication.
  3. Select «Anonymous authentication»
    here.
  4. The following dialog pops up.

enter image description here

Grant access to the web application folder’s ACL depending what is selected in the pic above:

  • Specific user: grant access for both IUSR (in my case) + IIS AppPoolDefaultAppPool
  • Application pool identity: grant access for IIS AppPoolDefaultAppPool only

IIS AppPoolDefaultAppPool account is the default AppPool account for new IIS web applications, if you have set a custom account, use the custom one.


Give the following permissions to the account(s):

  1. Read & Execute
  2. List folder contents
  3. Read

enter image description here

answered Jun 4, 2017 at 21:39

Legends's user avatar

LegendsLegends

20.3k12 gold badges93 silver badges120 bronze badges

1

Since you’re dealing with static content…

On the folder that acts as the root of your website- if you right click > properties > security, does «Users» show up in the list? if not click «Add…» and type it in, be sure to click «Apply» when you’re done.

answered Nov 8, 2013 at 23:34

joelmdev's user avatar

joelmdevjoelmdev

10.7k9 gold badges63 silver badges89 bronze badges

Community's user avatar

answered Jun 9, 2014 at 17:48

c-chavez's user avatar

c-chavezc-chavez

7,0705 gold badges33 silver badges48 bronze badges

Just in case anyone else runs into this. I troubleshooted all of these steps and it turns out because I unzipped some files from a MAC, Microsoft automatically without any notification Encrypted the files. After hours of trying to set folder permissions I went in and saw the file names were green which means the files were encrypted and IIS will throw the same error even if folder permissions are correct.

answered May 8, 2019 at 22:16

Joe's user avatar

JoeJoe

2244 silver badges18 bronze badges

1

  1. Create a new Site, Right Click on Sites folder then click add Site
  2. Enter the site name.
  3. Select physical path
  4. Select Ip Address
  5. Change Port
  6. Click OK
  7. Go to Application Pools
  8. Select the site pool
  9. Right-click the click Advance Settings
  10. Change the .Net CLR Version to «No Manage Code»
  11. Change the Identity to «ApplicationPoolIdentity»
  12. Go to Site home page then click «Authentication»
  13. Right-click to AnonymousAuthentication then click «Edit»
  14. Select Application Pool Identity
  15. Click ok
  16. boom!

for routes add a web.config

<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="React Routes" stopProcessing="true">
                    <match url=".*" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="/" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

answered Aug 12, 2020 at 11:56

Aevi Rontos's user avatar

Please enable the following items in Windows 2012 R2

enter image description here

answered Dec 30, 2019 at 17:28

user2907940's user avatar

If you are working with Application Pool authentication (instead of IUSR), which you should, then this list of checks by Jean Sun is the very best I could find to deal with 401 errors in IIS:


Open IIS Manager, navigate to your website or application folder where the site is deployed to.

  1. Open Advanced Settings (it’s on the right hand Actions pane).
  2. Note down the Application Pool name then close this window
  3. Double click on the Authentication icon to open the authentication settings
  4. Disable Windows Authentication
  5. Right click on Anonymous Authentication and click Edit
  6. Choose the Application pool identity radio button the click OK
  7. Select the Application Pools node from IIS manager tree on left and select the Application Pool name you noted down in step 3
  8. Right click and select Advanced Settings
  9. Expand the Process Model settings and choose ApplicationPoolIdentityfrom the «Built-in account» drop down list then click OK.
  10. Click OK again to save and dismiss the Application Pool advanced settings page
  11. Open an Administrator command line (right click on the CMD icon and select «Run As Administrator». It’ll be somewhere on your start menu, probably under Accessories.
  12. Run the following command:

    icacls <path_to_site> /grant "IIS APPPOOL<app_pool_name>"(CI)(OI)(M)
    

    For example:

    icacls C:inetpubwwwrootmysite /grant "IIS APPPOOLDEFAULTAPPPOOL":(CI)(OI)(M)
    

Especially steps 5. & 6. are often overlooked and rarely mentioned on the web.

answered Jan 10, 2020 at 10:38

Jpsy's user avatar

JpsyJpsy

19.6k7 gold badges116 silver badges112 bronze badges

  • Remove From My Forums
  • Question

  • User-649116597 posted

    When I run my web application. I got this error!!!!!!

    Line 133:            request.Method = "GET";
    Line 134:
    Line 135: using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) Line 136:            {
    Line 137:                StreamReader reader = new StreamReader(response.GetResponseStream());

    Source File: C:UsersAnuragDocumentsVisual Studio 2010ProjectsTicketSystemTicketSystemDefault.aspx.cs    Line:
    135

    Stack Trace:

    [WebException: The remote server returned an error: (401) Unauthorized.]
       System.Net.HttpWebRequest.GetResponse() +6111075
       TicketSystem._Default.Button2_Click(Object sender, EventArgs e) in C:UsersAnuragDocumentsVisual Studio 2010ProjectsTicketSystemTicketSystemDefault.aspx.cs:135
       System.Web.UI.WebControls.Button.OnClick(EventArgs e) +118
       System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +112
       System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10
       System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13
       System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +36
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5563
    

Answers

  • User-650628323 posted

    Hi,

    I agree with BrockAllen’s opinion. According to your description, you need to add the add Credentials for HttpWebRequest.  

    // Assign the credentials of the logged in user or the user being impersonated.
    request.Credentials = CredentialCache.DefaultCredentials;
    

    Or

    request.Credentials = new NetworkCredential("UserName", "PassWord"); 

    For details about it, please
    NetworkCredential Class and
    CredentialCache.DefaultCredentials Property.

    Best wishes,

    • Marked as answer by

      Thursday, October 7, 2021 12:00 AM

BaLahmuT

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 608

1

14.04.2021, 22:01. Показов 8693. Ответов 8

Метки нет (Все метки)


Получаю погоду с api, при запуске представления вылетает ошибка: System.Net.WebException: «Удаленный сервер возвратил ошибку: (401) Несанкционированный.»

Модель:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 public class Weather
    {
        public Object getWeather()
        {
            string url = "http://api.openweathermap.org/data/2.5/weather?q=Kharkiv&APPID=03e45b7211028e0119ce0b1b3fa9fa90units=imperial";
 
            var client = new WebClient();
            var content = client.DownloadString(url);
 
            var serializer = new JavaScriptSerializer();
            var jsonContent = serializer.Deserialize<Object>(content);
 
            return jsonContent;
        }
    }

Контроллер:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }
 
        public ActionResult Api()
        {
            return View();
        }
 
        public ActionResult Weather()
        {
            return View();
        }
 
        public JsonResult GetWeather()
        {
            Weather weath = new Weather();
            return Json(weath.getWeather(), JsonRequestBehavior.AllowGet);
        }
    }

Представление:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@{ 
    ViewBag.Title = "Weather"; 
}
 
<h2>Weather</h2>
<div class="col-md-12">
    <h1>Current Conditions in <span data-bind="text:name"></span></h1>
</div>
 
<div class="col-md-12">
    Temperature is <span data-bind="text:main.temp"></span>&deg; F
</div>
 
<script>
    var weather = Object();
    $(document).ready(function () {
            $.get("@Url.Action("GetWeather", "Home")", function (response) {
                console.log(response);
                weather = ko.mapping.fromJS(response);
                ko.applyBindings(weather);
 
            });
    });
 
</script>

Миниатюры

Ошибка "(401) Несанкционированный"
 

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь

0

647 / 582 / 170

Регистрация: 17.07.2012

Сообщений: 1,648

Записей в блоге: 1

14.04.2021, 23:25

2

Rudman132, 401 отдает API. Видимо ему ключ какой-то нужен.

1

BaLahmuT

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 608

15.04.2021, 15:13

 [ТС]

3

Cupko, Хмм..странно, если api ключ вынести в отдельную переменную то все работает

C#
1
string url = "http://api.openweathermap.org/data/2.5/weather?q=Cairo&APPID=" + key + "&units=imperial";

0

922 / 600 / 149

Регистрация: 09.09.2011

Сообщений: 1,879

Записей в блоге: 2

15.04.2021, 18:47

4

Лучший ответ Сообщение было отмечено Rudman132 как решение

Решение

Цитата
Сообщение от Rudman132
Посмотреть сообщение

странно, если api ключ вынести в отдельную переменную то все работает

Ничего странного. Пропущен разделитель.
&APPID=03e45b7211028e0119ce0b1b3fa9fa90<-тут-->units=imperial
Так что и первый вариант бы работал
Но второй правильнее с точки зрения программирования. Вы ж ключ менять будете и не раз. Не в тексте же его менять.

1

BaLahmuT

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 608

15.04.2021, 23:09

 [ТС]

5

Вопрос такой, как привязать форму к api? чтоб город можно было указывать любой

C#
1
string url = "http://api.openweathermap.org/data/2.5/weather?q=Cairo&APPID=" + key + "&units=imperial";

Вот форма:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
form action="Index" method="post" style="text-align: center">
        <table>
            <tr>
                <td style="font-size:18px">Enter city:</td>
                <td>
                    <input type="text" name="city" />
                    <input type="submit" id="search" value="Search"/>
                </td>
            </tr>
        </table>
        <hr />
    </form>

0

922 / 600 / 149

Регистрация: 09.09.2011

Сообщений: 1,879

Записей в блоге: 2

16.04.2021, 23:14

6

Цитата
Сообщение от Rudman132
Посмотреть сообщение

Вопрос такой, как привязать форму к api? чтоб город можно было указывать любой

Я давно не работал с погодным апи. Но наверное это с ним и не должно быть связано.
Объясните причину вопроса? Что такое привязать форму к АПИ? Или так — Зачем нужно привязать что-то к форме?

Форма — ввод города. Всё. Роль на этом заканчивается.
Запрос вы отправляете через бакэнд. Там вы и подставляете ключ. Собственно на бакэнде вы и можете манипулировать данными так как вам нужно.
И поэтому не понятно что за проблема вообще «можно было указывать любой город»

0

BaLahmuT

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 608

17.04.2021, 11:04

 [ТС]

7

HF, Может неправильно сформулировал, имел ввиду: как текст из inputa подставить вместо

C#
1
q=Cairo

0

922 / 600 / 149

Регистрация: 09.09.2011

Сообщений: 1,879

Записей в блоге: 2

17.04.2021, 22:12

8

Лучший ответ Сообщение было отмечено Rudman132 как решение

Решение

Цитата
Сообщение от Rudman132
Посмотреть сообщение

Может неправильно сформулировал, имел ввиду: как текст из inputa подставить вместо

Судя по представлению — у вас запрос сразу уходит. Значит
1) вы должны сделать событие onclick на кнопку формы
2) и отправлять запрос исходя из данных этой формы. А точнее поля City
Далее запрос. Сейчас он у вас вызывает голый метод. А вы же хотите город. Значит и должны город передавать.
3) обновляйте запрос $.get("@Url.Action("GetWeather", "Home")" и добавляйте аргументом поле «city» из формы
Соответственно и контроллер должен начать принимать это поле
4) добавляйте в метод контроллера параметр string city
5) ваша переменная uri теперь должна подставлять значение city в ключ «q» аналогично ключу авторизации.

0

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 608

17.04.2021, 23:17

 [ТС]

9

HF, понял, спасибо!

0

BaLahmuT

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 609

1

14.04.2021, 22:01. Показов 9515. Ответов 8

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

Получаю погоду с api, при запуске представления вылетает ошибка: System.Net.WebException: «Удаленный сервер возвратил ошибку: (401) Несанкционированный.»

Модель:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 public class Weather
    {
        public Object getWeather()
        {
            string url = "http://api.openweathermap.org/data/2.5/weather?q=Kharkiv&APPID=03e45b7211028e0119ce0b1b3fa9fa90units=imperial";
 
            var client = new WebClient();
            var content = client.DownloadString(url);
 
            var serializer = new JavaScriptSerializer();
            var jsonContent = serializer.Deserialize<Object>(content);
 
            return jsonContent;
        }
    }

Контроллер:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }
 
        public ActionResult Api()
        {
            return View();
        }
 
        public ActionResult Weather()
        {
            return View();
        }
 
        public JsonResult GetWeather()
        {
            Weather weath = new Weather();
            return Json(weath.getWeather(), JsonRequestBehavior.AllowGet);
        }
    }

Представление:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@{ 
    ViewBag.Title = "Weather"; 
}
 
<h2>Weather</h2>
<div class="col-md-12">
    <h1>Current Conditions in <span data-bind="text:name"></span></h1>
</div>
 
<div class="col-md-12">
    Temperature is <span data-bind="text:main.temp"></span>&deg; F
</div>
 
<script>
    var weather = Object();
    $(document).ready(function () {
            $.get("@Url.Action("GetWeather", "Home")", function (response) {
                console.log(response);
                weather = ko.mapping.fromJS(response);
                ko.applyBindings(weather);
 
            });
    });
 
</script>

Миниатюры

Ошибка "(401) Несанкционированный"
 



0



648 / 582 / 171

Регистрация: 17.07.2012

Сообщений: 1,651

Записей в блоге: 1

14.04.2021, 23:25

2

Rudman132, 401 отдает API. Видимо ему ключ какой-то нужен.



1



BaLahmuT

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 609

15.04.2021, 15:13

 [ТС]

3

Cupko, Хмм..странно, если api ключ вынести в отдельную переменную то все работает

C#
1
string url = "http://api.openweathermap.org/data/2.5/weather?q=Cairo&APPID=" + key + "&units=imperial";



0



972 / 639 / 160

Регистрация: 09.09.2011

Сообщений: 1,942

Записей в блоге: 2

15.04.2021, 18:47

4

Лучший ответ Сообщение было отмечено Rudman132 как решение

Решение

Цитата
Сообщение от Rudman132
Посмотреть сообщение

странно, если api ключ вынести в отдельную переменную то все работает

Ничего странного. Пропущен разделитель.
&APPID=03e45b7211028e0119ce0b1b3fa9fa90<-тут-->units=imperial
Так что и первый вариант бы работал
Но второй правильнее с точки зрения программирования. Вы ж ключ менять будете и не раз. Не в тексте же его менять.



1



BaLahmuT

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 609

15.04.2021, 23:09

 [ТС]

5

Вопрос такой, как привязать форму к api? чтоб город можно было указывать любой

C#
1
string url = "http://api.openweathermap.org/data/2.5/weather?q=Cairo&APPID=" + key + "&units=imperial";

Вот форма:

Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
form action="Index" method="post" style="text-align: center">
        <table>
            <tr>
                <td style="font-size:18px">Enter city:</td>
                <td>
                    <input type="text" name="city" />
                    <input type="submit" id="search" value="Search"/>
                </td>
            </tr>
        </table>
        <hr />
    </form>



0



972 / 639 / 160

Регистрация: 09.09.2011

Сообщений: 1,942

Записей в блоге: 2

16.04.2021, 23:14

6

Цитата
Сообщение от Rudman132
Посмотреть сообщение

Вопрос такой, как привязать форму к api? чтоб город можно было указывать любой

Я давно не работал с погодным апи. Но наверное это с ним и не должно быть связано.
Объясните причину вопроса? Что такое привязать форму к АПИ? Или так — Зачем нужно привязать что-то к форме?

Форма — ввод города. Всё. Роль на этом заканчивается.
Запрос вы отправляете через бакэнд. Там вы и подставляете ключ. Собственно на бакэнде вы и можете манипулировать данными так как вам нужно.
И поэтому не понятно что за проблема вообще «можно было указывать любой город»



0



BaLahmuT

85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 609

17.04.2021, 11:04

 [ТС]

7

HF, Может неправильно сформулировал, имел ввиду: как текст из inputa подставить вместо

C#
1
q=Cairo



0



972 / 639 / 160

Регистрация: 09.09.2011

Сообщений: 1,942

Записей в блоге: 2

17.04.2021, 22:12

8

Лучший ответ Сообщение было отмечено Rudman132 как решение

Решение

Цитата
Сообщение от Rudman132
Посмотреть сообщение

Может неправильно сформулировал, имел ввиду: как текст из inputa подставить вместо

Судя по представлению — у вас запрос сразу уходит. Значит
1) вы должны сделать событие onclick на кнопку формы
2) и отправлять запрос исходя из данных этой формы. А точнее поля City
Далее запрос. Сейчас он у вас вызывает голый метод. А вы же хотите город. Значит и должны город передавать.
3) обновляйте запрос $.get("@Url.Action("GetWeather", "Home")" и добавляйте аргументом поле «city» из формы
Соответственно и контроллер должен начать принимать это поле
4) добавляйте в метод контроллера параметр string city
5) ваша переменная uri теперь должна подставлять значение city в ключ «q» аналогично ключу авторизации.



0



85 / 29 / 16

Регистрация: 01.06.2019

Сообщений: 609

17.04.2021, 23:17

 [ТС]

9

HF, понял, спасибо!



0



#c# #.net #exchangewebservices

Вопрос:

Я работал над программой, которая сканирует почтовый ящик exchange на наличие определенных писем с указанного адреса. В настоящее время программа считывает входящие, загружает вложение и перемещает электронное письмо в другую папку. Однако примерно после 15 запросов с сервера EWS соединение начинает выдавать 401 несанкционированную ошибку, пока я не перезапущу программу. Программа настроена для входа в систему через OAuth, так как базовая аутентификация отключена системным администратором. Ниже приведен код, который я использую для получения подключения к exchange и чтения электронных писем из папки «Входящие».

Код подключения к Exchange:

 public static async Task<ExchangeService> GetExchangeConnection()
    {
        var pcaOptions = new PublicClientApplicationOptions
        {
            ClientId = AppID,
            TenantId = TenantID,
        };

        var pca = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build();

        var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" };

        var securePassword = new SecureString();
        foreach (char c in Pasword)
            securePassword.AppendChar(c);

        try
        {
            var authResult = await pca.AcquireTokenByUsernamePassword(ewsScopes, Username, securePassword).ExecuteAsync();

            ExchangeService exchangeService = new ExchangeService()
            {
                Credentials = new OAuthCredentials(authResult.AccessToken),
                Url = new Uri("https://outlook.office365.com/ews/exchange.asmx"),
            };

            return exchangeService;
        }
        catch
        {
            return null;
        }
    }
 

Ретривер электронной почты

 public static List<Email> RetreiveEmails()
    {
        ExchangeService exchangeConnection = GetExchangeConnection().Resu<

        try
        {
            List<Email> Emails = new List<Email>();
            TimeSpan ts = new TimeSpan(0, -5, 0, 0);
            DateTime date = DateTime.Now.Add(ts);
            SearchFilter.IsGreaterThanOrEqualTo EmailTimeFilter = new SearchFilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived, date);

            if (exchangeConnection != null)
            {
                FindItemsResults<Item> findResults = exchangeConnection.FindItems(WellKnownFolderName.Inbox, EmailTimeFilter, new ItemView(10));

                foreach (Item item in findResults)
                {
                    if (item.Subject != null)
                    {
                        EmailMessage message = EmailMessage.Bind(exchangeConnection, item.Id);
                        message.Load(new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.TextBody));
                        Emails.Add(new Email(message.DateTimeReceived, message.From.Name.ToString(), message.Subject, message.TextBody.ToString(), (message.HasAttachments) ? "Yes" : "No", message.Id.ToString()));
                    }
                }
            }

            exchangeConnection = null;
            return Emails;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
            return null;
        }
    }
 

Ошибка возникает, когда отправитель электронной почты пытается либо создать соединение exchange, либо запросить электронные письма из папки. В любом случае код выдаст ошибку и выдаст мне 401 несанкционированный при использовании учетных данных, которые работают в течение первой дюжины раз, а затем завершаются неудачей после стольких попыток. Я попробовал это с несколькими разными учетными записями, и проблема сохраняется со всеми из них, и я убедился, что приложение авторизовано для доступа к почтовому ящику exchange. Любые предложения или помощь будут высоко оценены.

Комментарии:

1. Проверьте, не истек ли тайм-аут. Попробуйте один поиск, а затем подождите 15 минут и попробуйте второй.

2. @jdweng В настоящее время тайм-аут установлен на 5 минут, и его ошибка произойдет раньше

3. Тогда проблема в таймауте, а не в количестве пакетов? Я пытаюсь определить, является ли сбой таймаутом количества отправляемых сообщений. Ваша публикация подразумевает, что сбой произошел из-за того, что было отправлено около 15 сообщений. Я думаю, что это тайм-аут.

4. @jdweng выяснил, что срок действия токена истекает через первый час. На данный момент я получаю ошибку invalid_lifetime и должен выяснить, как обновить токен.

Ответ №1:

После дальнейшего отслеживания ошибки 401 возникла проблема с тем, что срок службы токена истек на 1 час. Это связано с тем, что исходный токен OAuth имеет начальный срок службы 1 час. Однако это удалось исправить, настроив код для автоматического обновления токена при необходимости. Вот код для решения этой проблемы для всех, кто столкнется с этой проблемой.

Менеджер аутентификации:

 class AuthenticationManager
{ 
    protected IPublicClientApplication App { get; set; }

    public AuthenticationManager(IPublicClientApplication app)
    {
        App = app;
    }

    public async Task<AuthenticationResult> AcquireATokenFromCacheOrUsernamePasswordAsync(IEnumerable<String> scopes, string username, SecureString password)
    {
        AuthenticationResult result = null;
        var accounts = await App.GetAccountsAsync();

        if (accounts.Any())
        {
            try
            {
                result = await (App as PublicClientApplication).AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync();
            }
            catch (MsalUiRequiredException)
            { }
        }

        if (result == null)
        {
            result = await (App as PublicClientApplication).AcquireTokenByUsernamePassword(scopes, username, password).ExecuteAsync();
        }

        return resu<
    }
}
 

Я использую прямую аутентификацию по имени пользователя и паролю, но строка кода также может быть переключена на аутентификацию пользователя с помощью интерактивных методов. Код по существу создает новый экземпляр диспетчера аутентификации с приложением Publicclient, используемым для его инициализации, в котором содержатся идентификатор приложения и идентификатор арендатора. После инициализации вы можете вызвать функцию AquireATokenFromCacheOrUsernamePasswordAsync, которая попытается узнать, есть ли учетная запись, против которой можно получить токен. Затем он попытается получить ранее кэшированный токен или обновить токен, если срок его действия истекает менее чем через 5 минут. Если есть доступный токен, он вернет его в основное приложение. Если токен недоступен, он получит новый токен, используя имя пользователя и пароль, указанные в сообщении. Реализация этого кода выглядит примерно так,

 class ExchangeServices
{
     AuthenticationManager Manager = null;

     public ExchangeServices(String AppId, String TenantID)
     {
          var pcaOptions = new PublicClientApplicationOptions
          {
               ClientId = AppID,
               TenantId = TenantID,
          };
          var pca = PublicClientApplicationBuilder.CreateWithApplicationOptions(pcaOptions).Build();
          Manager = new AuthenticationManager(pca);
     }

     public static async Task<ExchangeService> GetExchangeService()
     {
          var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" }
          var securePassword = new SecureString();
          foreach(char c in Password)
               securePassword.AppendChar(c);

          var authResult = await Manager.AquireATokenFromCacheOrUsernamePasswordAsync(ewsScopes,           Username, securePassword);

          ExchangeService exchangeService = new ExchangeService()
          {
               Credentials = new OAuthCredentials(authResult.AccessToken),
               Url = new Uri("https://outlook.office365.com/ews/exchange.asmx");
          };
          return exchangeService;
     }
}
 

Приведенный выше код содержит все, что необходимо для создания нового диспетчера аутентификации и использования его для получения и обновления новых токенов при использовании служб EWS через OAuth. Это решение, которое я нашел, чтобы устранить проблему, описанную выше.

Я дам вам кусок кода, который я сделал, который работает. У меня также была проблема с 401 Auth, которая была в то время, и я не помню, что я точно изменил, чтобы заставить ее работать, но теперь это происходит. Конечно, вам не нужен весь код, но он дает вам представление.

public class Outlook
{
    /// <summary>
    /// Returns the selected node Index which equals selected mail index.
    /// </summary>
    public static Int32 selectedMailindex
    {
        get
        {
            return (Int32)Form1.GlobalAccess.Invoke((Func<int>)delegate
            {
                return Form1.GlobalAccess.mailTree.SelectedNode.Index;
            }); 
        }
    }

    static PullSubscription subscriptionInbox;
    public static ExchangeService runningService;

    /// <summary>
    /// Used to save EmailMessage retrieved from user inbox.
    /// </summary>
    public static List<EmailMessage> mailMessages = new List<EmailMessage>();

    /// <summary>
    /// We start the procedure to grab Emails.
    /// </summary>
    public static void StartupLoadMails()
    {
        ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
        service.TraceEnabled = true;
        service.Credentials = new WebCredentials("username", "password"); //Modify this
        service.Url = new Uri("Exchange.asmx URL"); //Modify this
        GetBinding(service);
        runningService = service;
        GetMailItems(runningService);
    }

    static ExchangeService GetBinding(ExchangeService service)
    {
        //Subscribe to Inbox newmail/modified events.
        subscriptionInbox = service.SubscribeToPullNotifications(
        new FolderId[] { WellKnownFolderName.Inbox }, //Inbox
        5, //TimeOut
        null,
        EventType.NewMail, EventType.Modified); //NewMail or Modified
        // Display the service URL.
        return service;
    }

    /*static bool RedirectionUrlValidationCallback(String redirectionUrl)
    {
        return true;
    }*/

    public static void GetMailItems(ExchangeService service)
    {
        Form1.GlobalAccess.Invoke(new Action(() =>
        {
            Form1.GlobalAccess.mailTree.Nodes.Clear();                
        }));
        mailMessages.Clear();
        //SearchFilter to get unreaded messages only.
        SearchFilter sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
        //Create new Item view with the last 9 unreaded items.
        ItemView view = new ItemView(9);
        //Execute the query
        FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.Inbox, sf, view);
        //We use Parallel for faster initial app loading, reducing ~5/10 secs.
        Parallel.ForEach(findResults, item =>
            {
                try
                {
                    EmailMessage message = EmailMessage.Bind(service, item.Id);
                    mailMessages.Add(message);
                }
                catch
                {
                    MessageBox.Show("ERROR");
                }
            });

        //Since we used parallel we need to sort the emails in our EmailMessage LIST by date, so they show cronologicaly over the treeview.
        mailMessages.Sort(delegate(EmailMessage m1, EmailMessage m2) { return m2.DateTimeReceived.Date.CompareTo(m1.DateTimeReceived.Date); });
        foreach (EmailMessage m in mailMessages)
        {
            Form1.GlobalAccess.Invoke(new Action(() =>
            {
                Form1.GlobalAccess.mailTree.Nodes.Add(m.Subject + " : " + m.Sender.Name);
            }));
        }
    }

    /// <summary>
    /// Calls threaded MarkReaded function to mark an Email as readed localy and remotly.
    /// </summary>
    public static void MarkReaded()
    {
        Thread thread = new Thread(CallThreadedReadFunction);
        thread.Priority = ThreadPriority.AboveNormal;
        thread.Start();
    }

    static void CallThreadedReadFunction()
    {
        ReadFunction(Outlook.mailMessages[selectedMailindex],selectedMailindex,runningService);
    }

    /// <summary>
    /// Mark as readed
    /// </summary>
    /// <param name="message">E-mail Message</param>
    /// <param name="index">Index Message Possition</param>
    /// <param name="service">EWS Service</param>
    static void ReadFunction(EmailMessage message, Int32 index, ExchangeService service)
    {
        Form1.GlobalAccess.Invoke(new Action(() =>
        {
            Form1.GlobalAccess.mailTree.Nodes[index].SelectedImageIndex = 3;
            Form1.GlobalAccess.mailTree.Nodes[index].ImageIndex = 3;
        }));
        EmailMessage msg = EmailMessage.Bind(service, message.Id);
        msg.IsRead = true;
        msg.Update(ConflictResolutionMode.AutoResolve);
    }

    /// <summary>
    /// Calls threaded Delete function to delete an Email localy and remotly.
    /// </summary>
    public static void Delete()
    {
        var deleteWorker = new BackgroundWorker();

        deleteWorker.DoWork += (sender, args) =>
        {
            DeleteFunction(Outlook.mailMessages[selectedMailindex], selectedMailindex, runningService);
        };

        deleteWorker.RunWorkerCompleted += (sender, args) =>
        {
            if (args.Error != null)
                MessageBox.Show(args.Error.ToString());

            Form1.GlobalAccess.Invoke(new Action(() =>
            {
                Form1.GlobalAccess.mailrefreshIcon.Visible = true;
            }));
        };

        deleteWorker.RunWorkerAsync();
    }

    /// <summary>
    /// Delete Emails choosen by the user.
    /// </summary>
    /// <param name="message">E-mail Message</param>
    /// <param name="index">Index Message Possition</param>
    /// <param name="service">EWS Service</param>
    public static void DeleteFunction(EmailMessage message, Int32 index, ExchangeService service)
    {
        try
        {
            Form1.GlobalAccess.Invoke(new Action(() =>
            {
                Form1.GlobalAccess.mailTree.Nodes.RemoveAt(index);
                mailMessages.RemoveAt(index);
                Form1.GlobalAccess.mailTree.SelectedNode = null;
                Form1.GlobalAccess.mailBrowser.DocumentText = null;
            }));

            EmailMessage msg = EmailMessage.Bind(service, message.Id);
            msg.Delete(DeleteMode.MoveToDeletedItems);
            msg.Update(ConflictResolutionMode.AlwaysOverwrite);
        }
        catch
        {
            //En ocaciones, si se borran muchos emails consecutivamente y se da refresh al inbox rapidamente, causa un crash porque el servidor encuentra el email que ordenamos borrar y mientras esta
            //Iterando es borrado, causando un crash. Lo mejor es unicamente ignorar la excepción, el programa seguira trabajando bien.
        }
    }

    /// <summary>
    /// Listens for new mails in the user inbox folder, if found, notifies the user and update mail list.
    /// </summary>
    /// <param name="service">EWS Service</param>
    public static void GetLatests(ExchangeService service)
    {
        GetEventsResults eventsInbox = subscriptionInbox.GetEvents();
        EmailMessage message;
        // Loop through all item-related events.
        foreach (ItemEvent itemEvent in eventsInbox.ItemEvents)
        {
            switch (itemEvent.EventType)
            {
                case EventType.NewMail:
                    try
                    {
                        Item item = Item.Bind(service, itemEvent.ItemId);
                        if (item.ItemClass.ToLower() == "IPM.Note".ToLower())
                        {
                            message = EmailMessage.Bind(service, itemEvent.ItemId);
                                Form1.GlobalAccess.Invoke(new Action(() =>
                                {
                                if (Form1.GlobalAccess.mailTree.Nodes.Count == 8)
                                {
                                    try
                                    {
                                        Form1.GlobalAccess.mailTree.Nodes.RemoveAt(8);
                                        mailMessages.RemoveAt(8);
                                    }
                                    catch
                                    {
                                    }
                                }
                                Form1.GlobalAccess.mailTree.Nodes.Insert(0, message.Subject);
                                mailMessages.Insert(0, message);
                                }));
                            }               
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
            }
        }
    }

    /// <summary>
    /// Reply to a single user. This method gets called from ReplyMail winform.
    /// </summary>
    /// <param name="message">Email Message OBJECT</param>
    /// <param name="index">Email position in the tree</param>
    /// <param name="service">EWS Current Service</param>
    /// <param name="bodyText">Message Text</param>
    /// <param name="subject">Message Original Subject</param>
    public static void Reply(EmailMessage message, Int32 index, ExchangeService service, String bodyText, String subject)
    {
        var replyWorker = new BackgroundWorker();

        replyWorker.DoWork += (sender, args) =>
        {
            bool replyToAll = false;
            ResponseMessage responseMessage = message.CreateReply(replyToAll);
            responseMessage.BodyPrefix = bodyText;
            responseMessage.Subject = subject;
            responseMessage.SendAndSaveCopy();
        };

        replyWorker.RunWorkerCompleted += (sender, args) =>
        {
            if (args.Error != null)
                MessageBox.Show(args.Error.ToString());

            ReplyMail.GlobalAccess.Invoke(new Action(() =>
            {
                ReplyMail.GlobalAccess.mailSentPic.Visible = true;
                ReplyMail.GlobalAccess.MailSentTxt.Visible = true;
                ReplyMail.GlobalAccess.button2.Visible = true;
            }));
        };
        replyWorker.RunWorkerAsync();

    }

    /// <summary>
    /// Forward an E-mail, lets you introduce recipients.
    /// </summary>
    /// <param name="message">EMAILMESSAGE</param>
    /// <param name="index">Mail position on the treeview</param>
    /// <param name="service">EWS Service</param>
    /// <param name="addresses">List with the mail adresses that the user is forwarding to</param>
    public static void Forward(EmailMessage message, Int32 index, ExchangeService service, List<EmailAddress> addresses)
    {
        var forwardWorker = new BackgroundWorker();

        forwardWorker.DoWork += (sender, args) =>
        {
            message.Forward(message.Body.Text, addresses);
        };

        forwardWorker.RunWorkerCompleted += (sender, args) =>
        {
            if (args.Error != null)
                MessageBox.Show(args.Error.ToString());

            ReplyMail.GlobalAccess.Invoke(new Action(() =>
            {
                ReplyMail.GlobalAccess.mailSentPic.Visible = true;
                ReplyMail.GlobalAccess.MailSentTxt.Visible = true;
                ReplyMail.GlobalAccess.button2.Visible = true;
            }));
        };
        forwardWorker.RunWorkerAsync();            
    }

    /// <summary>
    /// Send a single E-Mail
    /// </summary>
    /// <param name="service">EWS Service</param>
    /// <param name="subject">EMAILMESSAGE subject</param>
    /// <param name="bodyText">EMAILMESSAGE body.text</param>
    /// <param name="destination">EMAILADDRESS from receiver</param>
    public static void Send(ExchangeService service, String subject, String bodyText, String destination)
    {
        var sendWorker = new BackgroundWorker();

        sendWorker.DoWork += (sender, args) =>
        {
            EmailMessage message = new EmailMessage(service);
            message.Subject = subject;
            message.Body = bodyText;
            message.ToRecipients.Add(destination);
            message.SendAndSaveCopy();
        };

        sendWorker.RunWorkerCompleted += (sender, args) =>
        {
            if (args.Error != null)
                MessageBox.Show(args.Error.ToString());

            ReplyMail.GlobalAccess.Invoke(new Action(() =>
            {
                ReplyMail.GlobalAccess.mailSentPic.Visible = true;
                ReplyMail.GlobalAccess.MailSentTxt.Visible = true;
                ReplyMail.GlobalAccess.button2.Visible = true;
            }));
        };
        sendWorker.RunWorkerAsync();           
    }
}

}

Hey SW community,

I know that there have been a few topics discussing this issue already, but none of the solutions fixed it so far for me. I have the feeling it’s probably an easy fix but I can’t see it. I’ve been trying for a week to fix it.

We’ve published our Exchange 2016 directories (on Windows Server 2016) through nginx (on Ubuntu 20.04.1 LTS). We’re using Exchange Version 15.1 ‎(Build 2044.4)‎, should be the newest CU, iirc. OWA is accessible from external locations and works fine.
Autodiscover doesn’t want to work tho. Internal autodiscover works fine again, but the external autodiscover shows the following error (MS connectivity analyzer):

Attempting to send an Autodiscover POST request to potential Autodiscover URLs.
Autodiscover settings weren’t obtained when the Autodiscover POST request was sent.
An HTTP 401 Unauthorized response was received from the remote Unknown server. This is usually the result of an incorrect username or password. If you are attempting to log onto an Office 365 service, ensure you are using your full User Principal Name (UPN).

All other test steps succeed:

— Attempting to resolve the host name
— Testing TCP port 443 on host
— Testing the SSL certificate
— Checking the IIS configuration

I tried to play with the authentication settings of the autodiscover virtual directory but that broke the internal outlook connection (constant password prompts). Also deleted and recreated my autodiscover vd. Test-OutlookWebServices works.

I’ve also looked at the following threads:

— https://community.spiceworks.com/topic/2198860-exchange-2016-autodiscover-failure-401-unauthorized-s… (Created BackConnectionHostNames but that didn’t really do anything besides unbinding my back end certifiate for exchange)
— https://social.technet.microsoft.com/Forums/ie/en-US/f1c6b257-6d8c-4701-87c8-d332cb17cbc7/exchange-2… Opens a new window — (Kernel mode was already disabled)
— https://www.reddit.com/r/exchangeserver/comments/75p99q/autodiscover_401_issues/ Opens a new window (Reset virtual directory)

I receive the expected error 600 response when opening the autodiscover URL directly (internal). When opening the URL from an external connection, the site will ask for a password over and over again, but won’t proceed to the XML.
The same behaviour occurs in Microsoft Outlook 2016 and when opening the EWS URL directly, it’ll ask for a password constantly but won’t connect.

It just screams authentication settings into my face but I don’t see what I can and can’t change to make this work.

Does anyone here maybe have an idea?

Cheers!
Max

PS, this is the full MS protocol:

Attempting to send an Autodiscover POST request to potential Autodiscover URLs.
Autodiscover settings weren’t obtained when the Autodiscover POST request was sent.

The Microsoft Connectivity Analyzer is attempting to retrieve an XML Autodiscover response from URL https://autodiscover.domain.tld:443/Autodiscover/Autodiscover.xml Opens a new window for user user@domain.tld.
The Microsoft Connectivity Analyzer failed to obtain an Autodiscover XML response.

An HTTP 401 Unauthorized response was received from the remote Unknown server. This is usually the result of an incorrect username or password. If you are attempting to log onto an Office 365 service, ensure you are using your full User Principal Name (UPN).
HTTP Response Headers:
Connection: keep-alive
request-id: b930db52-7615-44cd-9ea3-7d7da35540af
Content-Length: 0
Server: Microsoft-IIS/10.0
WWW-Authenticate: Basic realm=»autodiscover.domain.tld»
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
X-FEServer: SVEXC001
Date: Wed, 12 Aug 2020 05:58:10 GMT

  • Remove From My Forums
  • Question

  • Hello,

    I am trying to create appointment from Exchange Web Service, If I am trying to login to exchange server from URL then its working and allowing me to login to my exchange server but if I am trying to connect from my C# application then I am getting this error «The
    request failed. The remote server returned an error: (401) Unauthorized.»

    My current code is as below:

    ExchangeService service = new ExchangeService();
    service.Credentials = new WebCredentials("XXXusername", "pwd", "exchange.domain.com");
    
    service.Url = new Uri("https://exchange.domain.com/EWS/Exchange.asmx"); 
    Appointment appointment = new Appointment(service);
    appointment.Subject = "Status Meeting";
    appointment.Body = "The purpose of this meeting is to discuss status.";
    appointment.Start = new DateTime(2017, 1, 30, 9, 0, 0);
    appointment.End = appointment.Start.AddHours(2);
    appointment.Location = "Conf Room";
    appointment.RequiredAttendees.Add("xyz@domain.com");
    appointment.Save(SendInvitationsMode.SendToNone);

    If I am trying to login from url and its also working fine.
    exchange.domain.com
    username — XXXusername
    pwd — «pwd»

    Also I have tried to login web service from URL and its work for me.

    Please anyone suggest what’s the wrong with C# code and how I can make working it.

    Thanks.

Answers

  • You credentials aren’t specified correctly you should use either downlevel format which is domainusername which would be

    service.Credentials = new WebCredentials("XXXusername", "pwd");

    (Note you don’t specify the domain as you have already included it in the downlevel format)

    or use

    service.Credentials = new WebCredentials("username", "pwd","XXX");

    (Where XXX is the NetBIOS domain name not the FQDN like you have used) this is recommended method because of potential issue with UPN suffixes

    https://support.microsoft.com/en-us/help/2933452/-401-unauthorized-error-when-you-use-an-ews-application-to-impersonate-a-user-in-office365-dedicated-itar

    or use the Active Directory User Principal name which includes the domain name eg

    service.Credentials = new WebCredentials("username@upndomain.com", "pwd");

    Cheers
    Glen

    • Marked as answer by

      Tuesday, January 31, 2017 5:49 PM

Понравилась статья? Поделить с друзьями:
  • Exchange ecp 500 непредвиденная ошибка
  • Exchange 2016 ошибка сервера в приложении ecp
  • Exchange 2016 ecp ошибка 500
  • Exchange 2013 ecp ошибка 500
  • Exception while verifying signature ошибка