REST API and Identity Server 4 testing with Postman - api

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.

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.

404s when interacting with Google Sheets REST API, 200s with Google API Explorer

I'm attempting to interact with the Google Sheets API and running into an inexplicable problem that I'm finally reaching out to see how anyone else may have tackled it. Put simply, I can use the in-page API Explorer tool with only the https://www.googleapis.com/auth/spreadsheets.readonly OAuth2 scope at https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/get to query my spreadsheet (just the spreadsheet ID, leaving all other fields to empty defaults) and I'll see the 200 with the response in the bottom as expected.
Of course, I can't re-use the same access token that tool uses, but if provision an access code for the same user for my own app (same scope), and make the same GET request to https://sheets.googleapis.com/v4/spreadsheets/<spreadsheetId> in Postman (again, no other fields populated), substituting the access token into the Authentication header with Bearer <accessToken>, I get a 404.
I know the file is there - I've triple checked that I'm using the same spreadsheet ID across either request and I'm consistently getting a 404 (not a 401 or 403) indicating that my access token does authenticate.
I've tried broadening my OAuth2 scopes to include the full range listed on the API Explorer:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/spreadsheets.readonly
Of course, I don't want to have to use all those scopes for my purposes - I'd like to use the most narrow scope possible, but I also wanted to rule out that it wasn't failing to work for some scoping scenario. No difference - still a 404 every time I make the request in Postman. I've tried issuing multiple access tokens now, using accounts.google.com to invalidate the tokens for my app between re-issuances, but to no avail.
To be clear, the Google Sheets API has been enabled for my app.
In hopes that someone else has experienced the same inability to query Google's v4 REST API despite using valid access tokens, could you share how you managed to do it?
I appreciate it!
Update:
So I've been playing around with the OAuth 2.0 Playground shared in the comments and found that the authorization endpoint I was using was identical, but the token endpoint differed. This doesn't seem to matter since I used the custom option to use the alternate endpoint and the Playground was still able to work without issue just like the API Explorer.
Using the custom entries, I also entered my own app's client ID and client secret (after registering the playground redirect URI), minimizing the differences between what I'm doing in Postman and in the various Google tools. Again, my GET request to the spreadsheet works without issue.
Just to be clear, here's what I've been doing in the Playground:
In Step 1, I've specified the https://www.googleapis.com/auth/spreadsheets.readonly scope to authorize. I click the Authorize APIs button and log in with the user account.
It returns with the authorization code, so I exchange that code for the tokens via a POST to the token endpoint.
I then make a GET request to https://sheets.googleapis.com/v4/spreadsheets/<spreadsheetId> with no additional headers and it works without issue - 200 OK and all the data I'm expecting to see.
Here my approach in Postman:
Make a GET request to:
https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&response_type=code&redirect_uri=https://<myDomain>/oauthResp&client_id=<appClientId>&scope=https://www.googleapis.com/auth/spreadsheets.readonly&state=abc123
Receive a response similar to the following in my browser on the redirect (since the domain intentionally 404s).
https:///oauthResp?state=abc123&code=zyx098&scope=https://www.googleapis.com/auth/spreadsheets.readonly
Make a POST request to: https://www.googleapis.com/oauth2/v4/token with a body of:
client_id=<appClientId>
client_secret=<appClientSecret>
redirect_uri=https://<myDomain>/oauthResp
grant_type=authorization_code
Receive a response similar to:
{
"access_token": "abc123",
"expires_in": 3599,
"refresh_token": "zyx098",
"scope": "https://www.googleapis.com/auth/spreadsheets.readonly",
"token_type": "Bearer"
}
Make a GET request to https://sheets.googleapis.com/v4/spreadsheets/<spreadsheetId> with a 'Content-Type' header of application/json and an 'Authorization' header of Bearer abc123 (per the access token above).
Unlike the API Explorer and the OAuth 2.0 Playground, this yields a 404 - exactly the issue I've been experiencing for no obvious reason.
Further, if I simply take the fresh access token from the Playground and drop that into Postman, I get the same 404.
Any other ideas?

Getting back 401 from Dynamics 365 despite being issued valid token

I am attempting to rewrite a client app that currently connects to Dynamics 365 using JavaScript
let URL = "https://<company-name>.operations.dynamics.com/data/FinancialDimensionValues?cross-company=true";
let body = '';
var headers = {'Content-Type':'application/json'};
let response = ai.https.authorizedRequest(URL, 'GET', body, headers);
Currently this JavaScript application works and gets back JSON data. I am attempting to rewrite this application using C#. I am first starting with Postman to make sure I have all the authentication steps in place before moving on the writing the C# code.
Using Postman I am able to successfully obtain a JWT token using the "Client Credentials" flow where I pass the Client ID and the Client Secret to the Access Token Request URL. However, when trying to access an API endpoint within Dynamics 365 I receive back an HTTP 401 even though I am passing the JWT access token properly.
Here is the Access Token Request URL:
https://login.microsoftonline.com/722b0db7-9629-4304-92a0-dfb4a1debe62/oauth2/token?resource=https://<company-domain-here>.dynamics.com
I am thinking that I must be authenticating properly or I would not get back a valid access token. Also since the JavaScript application already in place works without issue I am assuming that Dynamics 365 is provisioned properly to allow API access.
What I am trying to figure out is what I might be doing wrong within Postman that results in my receiving a 401? What could be different between the working JavaScript request and what I am sending via Postman?
Issue is finally solved.
As mentioned follow the documentation mentioned.
If you face 401 Error, here is the last trick.
In postman Under Authorization--> Add authorization data to--> select Request Headers.
Now fire the query you shall have the 200 ok.
Ref Article which helped me figure it out.

Getting a 401 http response (sometimes) while request is proper

i've been facing a problem lately and i don't know how to handle it, or even what the problem might be coming from.
Tools:
-MYSQL(for data base)
-JAVA EE web application(to service as back-end)
-Jersey 2.26 dependency (to service as API on top of web application /CORS filter implemented)
-Apache Tomcat/7.0.69 ( as web server where web app sets )
-Angular 4 ( as website and client to the API)
-CentOS Linux release 7.3 (Core) (Server operating system )
Problem:
once the request is sent to the API ( to a specific secured Endpoint ) lets call it customers, Sometimes the response is 401 Unauthorized, and in other times it works just fine, from the angular website for instance, if press f12 and follow the requests, i send the same request again ( for the one i got 401 for ) but this time it works, the JWT is valid and it works just fine, and its not that there is too many request going to the server, even if its few sometimes i get 401, but most of the time it works great.
the only thing in the code that can return 401 is the authentication filter, which checks the validity of token, but it looks fine to me.
i checked this article that talks about 401 response, but still, its hard for me to grasp the idea why it works good sometimes and not work other times for the same request. i get the 401 while checking with the website and also with checking with postman. so i guess it's not a cache or cookies thing, but i might be wrong to assume this.
any ideas on what might be wrong ? or where should i be checking for bugs or errors ?
Thanks allot
Set tomcat security to false .

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

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!