office365 autodiscover 401 via POX API but successful over testexchangeconnectivity.com - objective-c

I have a situation where I am trying to handle auto discover in an iOS app (GOD I HATE auto discover with a passion). I'm at this point where I have followed through several sequences of redirects and email aliases, and the auto discover is now pointing me to an Office 365 URL (this user has been migrated to Office365).
I'm seeing a HTTP redirect to https://autodiscover-s.outlook.com, and I'm POST-ing the standard POX to https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml with the user's credentials. I'm getting a 401 in return. I'm also getting a 401 when I curl this manually.
My POST is to https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml. This is authenticated using the users email and password (I'm using AFNetworking 2.0, so I'm using the HTTPRequestSerilizer authentication mechanism)
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request>
<EMailAddress>user#Service.domain.com</EMailAddress>
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
</Request>
</Autodiscover>
The response that I get is:
Domain=AFNetworkingErrorDomain Code=-1011 "Request failed: unauthorized (401)" UserInfo=0x8be9280 {
NSErrorFailingURLKey=https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml,
AFNetworkingOperationFailingURLResponseErrorKey=
{
URL: https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml
}
{ status code: 401,
headers {
"Cache-Control" = private;
"Content-Length" = 0;
Date = "Thu, 06 Mar 2014 05:00:49 GMT";
RequestId = "ee723790-60db-4008-b800-7d44d9884498";
Server = "Microsoft-IIS/7.5";
"Www-Authenticate" = "Basic Realm=\"\"";
"X-AspNet-Version" = "2.0.50727";
"X-DiagInfo" = BLUPRD0810CA020;
"X-Powered-By" = "ASP.NET";
} }, NSLocalizedDescription=Request failed: unauthorized (401)}
I get the same error when I try to CURL the same post data with the same user and password.
However, when I go to testexchangeconnectivity.com, I see the following, which I don't receive through the POX APIs:
Test Steps
The Microsoft Connectivity Analyzer is attempting to retrieve an XML Autodiscover response from URL https://autodiscover-s.outlook.com/Autodiscover/Autodiscover.xml for user user#Service.domain.com.
The Autodiscover XML response was successfully retrieved.
Additional Details
An HTTPS redirect was received in response to the Autodiscover request. The redirect URL is https://pod51008.outlook.com/Autodiscover/Autodiscover.xml.
Is there something wrong that I'm doing? I think the credentials work, because the initial auto discover to the user's on premise domain resolves fine and sends me online. The fact that curl also doesn't work leads me to think that there is some special office365 authentication that I'm not doing properly.
Any ideas?

I discovered the problem. I received an email address change from Autodiscover indicating that I should go from user#domain.com to user#service.domain.com. I then re-built the auto discover urls using user#service.domain.com. However, I was authenticating with user#service.domain.com, not user#domain.com. This is why I received 401.
I hate auto discover. None of this is documented anywhere (http://msdn.microsoft.com/en-us/library/jj900154(v=exchg.150).aspx).

I can't figure out what is going on without seeing every request and response. What is the structure of the Office365 URL?
Did you try an un-authenticated GET request to "http://autodiscover." + domain + "/autodiscover/autodiscover.xml"? In Autodiscover for Exchange, there is a Phase 3 sub section that describes this. This article captures much of the requirements for implementing auto discover for a client.

I think Michael meant to link to Handling Autodiscover error messages. However, it doesn't explicitly note that you should not change your user name in your credentials when you get a redirect to another email address. You only change it in the EMailAddress element. Thanks for the feedback, and sorry for the confusion!

Related

Continue when HTTP authentication fails

I have created an app (backend and frontend) that is mainly used on a Windows intranet. I'm using Kerberos authentication to do SSO so that anyone logged in to Windows domain is automatically authenticated to the server. To do this I set up Kerberos SPN for server and configured browsers etc and is all working fine in the normal scenario. My problem is that I need to continue if the user is not authenticated (ie connects from outside the Windows domain or does not have their browser configured correctly).
In summary there are two scenarios:
if authenticated OK continue with authorization granted for their ID [currently works]
if not authenticated continue with no (public) authorization [does not work]
In the first case the HTTP requests/responses are:
a. frontend: initial HTTP request
b. backend: no auth found so return 401 unauthorized with WWW-Authenticate:Negotiate header
c. frontend: re-sends request with Authorization header -> decoded to get the login ID
In the 2nd case:
a. frontend: initial HTTP request
b. backend: no auth found so return 401 with WWW-Authenticate:Negotiate (and error text in the body)
c. frontend: browser stops (displaying the body of the response as text to the user)
This is the crux of the problem I need to somehow avoid the browser just completely bombing (as at step c above).
Solutions I have tried:
display a message to the user about how to adjust browser settings to allow SSO to work in the body of the 401 response message. This is pretty ugly looking and does not work for connections from outisde the domain
Tried a 301 redirect in stead of 401 unauthorized response, but the browser does not like this.
Tried a redirect using javascript in the 401 response body, but it is not executed.
Have the backend send 401 but with WWW-Authenticate:Negotiate,Basic. But this display an unneeded login/password dialog and still fails if they don't login.
What I really need is an None option, ie: WWW-Authenticate:Negotiate,None then continue with no auth if the subsequent frontend request indicate "None" was used.
Of course, there isn't a "None" option. :(
It seems that this should be a fairly typical scenario but I have been researching this to no avail for 3 days now. Any advice would be greatly appreciated.
If the browser is connecting from outside the intranet then just continue. That is do not send the 401 response at all (no auth). You should be able to tell from the IP address where they connect from.
Another option is to redirect using JS in a page in the 401 body. As mentioned above I think you need to include Content-type: text/html or Content-type: text/javascript.

REST API and Identity Server 4 testing with Postman

I have a solution that has my web application, my REST API, and my Identity Server 4. All of which are now on .net 5. Locally everything works fine, but once I load everything up to the server, I get an error on Postman.
Setup - The API, and the IDP server are on separate sites.
What I Know - I know the IDP server works because I can get a token in Postman. I also know that the actual API works because when I remove the [Authorize] attribute from the controller I have, the call from Postman works fine.
The Problem - The problem that I have now is that when I put the [Authorize] attribute back in, I always get a 401 Unauthorized error for the API call. Below is the Startup file portion that sets up the authentication:
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "https://bob.com/API-IDP/";
options.ApiName = "BOBSAPI";
options.ApiSecret = "bobssecret";
});
I also know that the Configure portion, that the order of the Use***** is correct. I've also tried tweaking with the AppPool settings, in terms of "Load Profile", all based on things I've found while searching. I've gone to the Identity 4 website and followed those examples as best as I can. Oh, one more thing. The IDP database has a table for PersistedGrants. I do see a few records in that table, which I think means the authentication worked? But if the authentication worked, then why did the API call return a 401? Is there something I need to do on the controller besides the [Authorization] attribute? I've spent 3 days on this and I'm pulling my hair out. Please help!
I would look at the response headers of the response from the API and see if this header gives any clues to why you are not authorized:
For example:
HTTP/1.1 401 Unauthorized
Date: Sun, 02 Aug 2020 11:19:06 GMT
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"
You should also make sure this flag is set to True in the AddJwtBearer config:
//True if token validation errors should be returned to the caller.
options.IncludeErrorDetails = true;
You can use a tool like Fiddler to do that.
Then I would look at the ASP.NET Core logfile to determine why it does not accept your token.
It took a bit, but with some help from Tore Nestenius, I was able to figure it out. Here's what I did with my final testing to get things working:
I used my Identity Server 4, hosted on our test server, as the authority. Then, I fired up my local API, so I could see the console. I was successful in getting the token, but when I went to request data through Postman, I got the same error about being unauthorized. I looked at the console and the error in the console basically said that the authority did not match what it was expecting. The difference turned out to be a forward slash. Once I made them match in my startup file, the API command worked.
The moral of the story here is make sure the authority you set up in your startup file is correct. If you are getting unauthorized issues, I would look there first.

How to access GitHub via Personal Access Token in URL

I maintain a private repository but want to make one file publicly available.
GitHub documentation states that the CURL command below can retrieve a file:
curl -u username:token https://api.github.com/user
But I would like to provide access through a URL. E.g.
https://username:token#raw.githubusercontent.com/me/repo/master/README.md
This always return a 404. Am I missing something?
From "How can I download a single raw file from a private github repo using the command line?", you wouldneed to use a PAT (Personnal Access Token) without the username:
curl -s https://$TOKEN#raw.githubusercontent.com/....
But I would not recommend making that token visible in any way: it would give access to that file and the rest of the repository.
Putting that file in a separate location (be it a separate public repository, or any other online text storage service) would be safer.
For those of you wondering the "why" on 404 vs 401, it's basically a security measure on GitHub's part to output 404 instead of 401: https://docs.github.com/en/github-ae#latest/rest/overview/other-authentication-methods#basic-authentication
For those wondering why we get a 404 in the browser while cURL gives us a success response, you might've assumed that providing the username and password in the URL like https://username:password#somesite.com would pass the credentials along in the initial request. That is not the case - the browser steps in and sees if the page you are requesting returns a WWW-Authenticate response header, and only then does it send your credentials. In the case of your GitHub url, the resource doesn't send back a WWW-Authenticate. If it did return WWW-Authenticate, then you obviously wouldn't run into this problem.
And then there's cURL. cURL assumes Basic Authentication by default and automatically sets the Authorization header to your username and password (either from the url like my previous example, or set through CLI options like in your example), and it sends it regardless of whether or not the server returns a WWW-Authenticate response header.
Unfortunately for us, there's no way to force the browser to send it with the initial request. As to why GitHub doesn't send a WWW-Authenticate response header, it's probably because they don't want to promote the least secure way of authentication - they no longer allow account passwords to be sent this way, after all. However, they do realize its ease of use and have mitigated some of its weaker points by allowing users to use oAuth access token, GitHub App installation access token, or Personal Access Token in its place that can limit its scope of access. So really, it's the browser that is following standards, and GitHub allowing a form of Basic Authentication with some alterations, and cURL immediately passing our credentials into the Authorization header. I believe the below is what's happening behind your requests:
cURL sends a request along with Authorization header → GitHub: "Well, I didn't ask, but yeah, your creds check out" → GitHub: Authorized and redirects to resource
Browser sends request and waits for WWW-Authenticate response before handing credentials → GitHub: "Umm, you don't have permission to access this resource but I can't let you know whether it actually exists") → GitHub: Returns 404 (instead of 401 with WWW-Authenticate header) stopping the browser short from receiving the WWW-Authenticate header response and sending out an Authorization header with the credentials on hand.

Skype For Business Online Authentication Error - 403 Permission Denied

Hello Microsoft/Azure/Skype experts,
I'm tasked with accessing presence data from Skype For Business Online accounts from my macOS app (native).
I'm unfortunately stuck and i always get a 403 error when i access the autodiscover request and never get the link to the applications resource
I have been following this documentation
https://learn.microsoft.com/en-us/skype-sdk/ucwa/authenticationusingazuread
STEP 1
We have registered the app in the Azure Management Portal using our Office 365 account credentials.
We have used custome redirect URL (http://localhost)
Allow Implicit Flow is set to true in manifest
We pre-configure the permissions needed for Skype for business
online
STEP 2
Issuing a GET as specified in the documentation to initiate sign in and authorization check.
GET https://login.microsoftonline.com/common/oauth2/authorize?response_type=token&client_id=c#####-4d41-485e-871f-0a22aa79e52b&redirect_uri=http://localhost
This returns a 200 OK.
STEP 3
We got the Auto discover URL as described in the documentation.
This is what i get - i use the domain marked in RED.
STEP 4
As per the documentation, they ask me to do this
Requesting an access token using implicit grant flow
So i issue a GET as described
https://login.microsoftonline.com/oauth2/authorize?
response_type=id_token &client_id=######-4d41-485e-871f-0a22aa79e52b
&redirect_uri=http://localhost
&state=8f0f4eff-360f-4c50-acf0-99cf8174a58b
&resource=https://webdirin1.online.lync.com
Now this shows the sign in page, i sign in and then it throws an error
AADSTS90014%3a+The+required+field+%27nonce%27+is+missing.
I researched and could not fix this error.
So after lots of research and looking at this Microsoft documentation LINK (https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#requesting-individual-user-consent) , apparently there is another way of getting the bearer token.
STEP 4 - SECOND TRY
I then Request individual user consent by sending the SCOPE parameter for Skype for Business.
I then issue a GET request to
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#######-4d41-485e-871f-0a22aa79e52b&response_type=code&redirect_uri=http://localhost&response_mode=query&scope=https://api.skypeforbusiness.com/User.ReadWrite&state=12345
This returns an access code which i use in next step to get the TOKEN
STEP 5 - Get the bearer TOKEN
Issue a POST to following URL
https://login.microsoftonline.com/common/oauth2/v2.0/token With the
following data in POST body
"grant_type": "authorization_code", "client_id":
"######-4d41-485e-871f-0a22aa79e52b", "scope":
"https://api.skypeforbusiness.com/User.ReadWrite", "code":
"OAQABAAIAAACEfexX.........", "redirect_uri": "https://localhost"
This returns the bearer token in the following response JSON
{
"access_token" = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1........w4b-- gnWG_iOGtQ";
"expires_in" = 3599;
"ext_expires_in" = 3599;
scope = "https://api.skypeforbusiness.com/User.ReadWrite";
"token_type" = Bearer;
}
STEP 6
Yay! Got the bearer token at laaast!
Now back to the main documentation
https://learn.microsoft.com/en-us/skype-sdk/ucwa/authenticationusingazuread
And where we do this - 'Resending an autodiscovery request with the bearer token'
We execute a GET request to
https://webdirin1.online.lync.com/Autodiscover/AutodiscoverService.svc/root/oauth/user
Now this, as per the documentation should return this JSON
{
"_links":{
"self":
{"href":"https://webdirX.online.lync.com/Autodiscover/AutodiscoverService.svc/root/user"},
"applications":
{"href":"https://webpoolXY.infra.lync.com/ucwa/oauth/v1/applications"}
}
}
BUT i GET A 403: PERMISSIONS denied error
<div class="content-container"><fieldset>
<h2>403 - Forbidden: Access is denied.</h2>
<h3>You do not have permission to view this directory or page
using the credentials that you supplied.</h3>
</fieldset></div>
So thus i have never got the applications url and I have checked the manifest, registration and i have no idea, why i get this error.
Any inputs would be appreciated.
For step 4, you need to specify nonce=somestring in the URL. Typically this should be a securely random value that is only used once. It can contain any value.
Also, you are only requesting an id token. Set response_type=id_token+token.

PUT/POST request in SOAPUI giving 403 forbidden, while same request working fine in rest client Postman

There is no authentication on server side so authentication should not be issue.
URL format: PUT
https://localhost/api/v1/protections?integrationKey=111&userKey=1111&group=111&category=foo
Payload:
{"action":"BLOCK"}
This is working fine in Postman.
In SOAP UI , I am giving input as under:
EndPoint: https://localhost
Resource: /api/v1/protections
Parameters:?integrationKey=111&userKey=1111&group=111&category=foo
in Media type, I am selecting "application/json"
and entering {"action": "BLOCK"} but getting "Wed Jan 20 16:25:27 PST 2016:DEBUG:Receiving response: HTTP/1.1 403 Forbidden
"
Is there any suggestion to get the output in SOAP UI.
Depending on the server where the rest is exposed service generates an HTTP 403, you should verify that server is and thus find the fastest response.
Also try making a GET request from the browser to see if you can answer correctly because problem lock your machine to the server.
As is https, it may be that you lack some certificate set SOAPUI. possibly Postman you use already has configured. Try to check this setting.
In my case, I missed the Header "User-Agent" and "accept". I put in Soap UI and Works.
In Postman, this headers it put automatically.