Calling secured drill URL from nodeJS using request - node.js

I have NodeJS service which is protected and exposed using a load balancer. Need to call the drill Query.JSON API which is projected using j_secuirty_check.
Existing logic:
First hit the j_security_check URL with the credentials and receive the cookie.
Hit the query.JSON URL with the received cookie in the above step.
Code Snippet:
var options1 = {
url: 'http://<HOSTNAME>:8047/query.json',
method: 'POST',
json: JSON.parse('{ "queryType":"SQL", "query": "show schemas"}'),
headers : {
cookie : setcookie
}
};
The above method is working without the load balancer.
Example:
backend service running on http://localhost:3000
Drill URL: http://<SOMEIP:PORT>/query.JSON
Issue:
The second request is redirecting to login page thought we set cookie received in the previous step.
Example:
backend service running on https://<LOADBALANCER_URL>
Drill URL: http://<SOMEIP:PORT>/query.JSON
Is this because of https & http protocols or diff domain?
Please share your suggestion.

In case of load balancer the request might be going to another Drillbit WebServer which doesn't issued the cookie in first step. In which case it will not work, your load balancer should have some concept of sticky session or send the request from a client to same WebServer every time.

Related

Can I enable non-interactive NLTM authentication on API calls for an endpoint on a different server?

I have a website hosted in IIS that uses NTLM authentication. I can access the site without explicitly providing my credentials. The site itself has functionality that makes ajax calls to API endpoints hosted in an HTTP.sys Windows Service.
The ajax calls are configured like this:
const ajaxGet = (url) => $.ajax({
cache: false,
type: "GET",
url: `${url}`,
xhrFields: {
withCredentials: true
}
});
Everything works, but the browser (Chrome) always asks me to enter my credentials at least once when it calls the endpoint for the first time.
Is there a way to automatically use the NTLM credentials provided by IIS authentication instead of having to manually enter them?

DocusignApi integration with angular

I am looking for Integration of DocuSignApi with Angular. I am following these steps.
Angular Application
Backend Server using .net core Web API to handle and DocuSign api using nuget.
Can I achieve this?
Option 1 - Angular application - will hit - login method of middleware api application - middleware will communicate - docusign - after successful it will share details of logged in users.
Option 2 - Angular application - directly hit to docusign methods for this When I am doing like this
var url = "https://account-d.docusign.com/oauth/auth?response_type=token&scope=signature&client_id=XXXXXXXXXXXXXXX-01caXXXXXXXX&state=a39fh23hnf23&redirect_uri=http://localhost:81/";
return this._dataService.getThirdParty1(url, header)
.pipe(map((response: Response) => {
return response;
}));
- public getThirdParty(url) {
return this._httpClient.get( url).pipe().pipe(map(this.handleResponse)).pipe(catchError(this.handleError.bind(this)));
}
I am getting error
Access to XMLHttpRequest at 'https://account-d.docusign.com/oauth/auth?response_type=token&scope=signature&client_id=XXXXXXXXXXXXXXX-01ca8f1b220&state=a39fh23hnf23&redirect_uri=http://localhost:81/' from origin 'http://localhost:81' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
account-d.docusign.com/oauth/auth?response_type=token&scope=signature&client_id=XXXXXXXXXX-411a-9bb9-01ca8f1b220&state=a39fh23hnf23&redirect_uri=http://localhost:81/:1 Failed to load resource: net::ERR_FAILED
Please provide a way to check these options.
First, your issue is that you are making client-side calls to DocuSign from a different domain which validated CORS policy which is a security concern.
(Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading of resources. CORS also relies on a mechanism by which browsers make a "preflight" request to the server hosting the cross-origin resource, in order to check that the server will permit the actual request. In that preflight, the browser sends headers that indicate the HTTP method and headers that will be used in the actual request.)
Larry wrote extensively on this topic and here are some of the resources that can help.
Here is a three part series on the topic - https://www.docusign.com/blog/dsdev-building-single-page-applications-with-docusign-and-cors-part-1
Here is his code in GitHub showing you how to create a CORS gateway - https://github.com/docusign/blog-create-a-CORS-gateway
One other useful resource - https://www.cdata.com/kb/tech/docusign-odata-angularjs.rst

API Gateway - ALB: Hostname/IP doesn't match certificate's altnames

My setup currently looks like:
API Gateway --- ALB --- ECS Cluster --- NodeJS Applications
|
-- Lambda
I also have a custom domain name set on API Gateway (UPDATE: I used the default API gateway link and got the same problem, I don't think this is a custom domain issue)
When 1 service in ECS cluster calls another service via API gateway, I get
Hostname/IP doesn't match certificate's altnames: "Host: someid.ap-southeast-1.elb.amazonaws.com. is not in the cert's altnames: DNS:*.execute-api.ap-southeast-1.amazonaws.com"
Why is this?
UPDATE
I notice when I start a local server that calls the API gateway I get a similar error:
{
"error": "Hostname/IP doesn't match certificate's altnames: \"Host: localhost. is not in the cert's altnames: DNS:*.execute-api.ap-southeast-1.amazonaws.com\""
}
And if I try to disable the HTTPS check:
const response = await axios({
method: req.method,
url,
baseURL,
params: req.params,
query: req.query,
data: body || req.body,
headers: req.headers,
httpsAgent: new https.Agent({
: false // <<=== HERE!
})
})
I get this instead ...
{
"message": "Forbidden"
}
When I call the underlying API gateway URL directly on Postman it works ... somehow it reminds me of CORS, where the server seems to be blocking my server either localhost or ECS/ELB from accessing my API gateway?
It maybe quite confusing so a summary of what I tried:
In the existing setup, services inside ECS may call another via API gateway. When that happens it fails because of the HTTPS error
To resolve it, I set rejectUnauthorized: false, but API gateway returns HTTP 403
When running on localhost, the error is similar
I tried calling ELB instead of API gateway, it works ...
There are various workarounds, which introduce security implications, instead of providing a proper solution. in order to fix it, you need to add a CNAME entry for someid.ap-southeast-1.elb.amazonaws.com. to the DNS (this entry might already exists) and also to one SSL certificate, alike it is being described in the AWS documentation for Adding an Alternate Domain Name. this can be done with the CloudFront console & ACM. the point is, that with the current certificate, that alternate (internal !!) host-name will never match the certificate, which only can cover a single IP - therefore it's much more of an infrastructural problem, than it would be a code problem.
When reviewing it once again... instead of extending the SSL certificate of the public-facing interface - a better solution might be to use a separate SSL certificate, for the communication in between the API Gateway and the ALB, according to this guide; even self-signed is possible in this case, because the certificate would never been accessed by any external client.
Concerning that HTTP403 the docs read:
You configured an AWS WAF web access control list (web ACL) to monitor requests to your Application Load Balancer and it blocked a request.
I hope this helps setting up end-to-end encryption, while only the one public-facing interface of the API gateway needs a CA certificate, for whatever internal communication, self-signed should suffice.
This article is about the difference in between ELB and ALB - while it might be worth a consideration, if indeed the most suitable load-balancer for the given scenario had been chosen. in case no content-based routing is required, cutting down on useless complexity might be helpful. this would eliminate the need to define the routing rules ...which you should also review once, in case sticking to ALB. I mean, the questions only shows the basic scenario and some code which fails, but not the routing rules.

Invalid state on azure, but working locally

I have an Azure Active Directory tenant that I wish to authenticate with from my Node.js application running on an Azure App Service instance. I'm using passportjs and passport-azure-ad to do this.
Locally everything works fine. I can authenticate with the Azure AD tenant and it returns back to my page correctly. However on Azure it fails with the error:
authentication failed due to: In collectInfoFromReq: invalid state received in the request
My configuration is exactly the same (apart from redirectUrl) as I'm using the same tenant for local testing as well as in Azure yet it still fails. I've set up the proper reply urls and the authentication returns back to my application.
Here is my config:
{
identityMetadata: `https://login.microsoftonline.com/${tenantId}/.well-known/openid-configuration`,
clientID: `${clientId}`,
responseType: 'id_token',
responseMode: 'form_post',
redirectUrl: 'https://localhost:3000/auth/oidc/return',
allowHttpForRedirectUrl: false,
scope: [ 'openid' ],
isB2C: false,
passReqToCallback: true,
loggingLevel: 'info'
}
I'm using the OIDCStrategy.
My authentication middleware:
passport.authenticate('azuread-openidconnect', {
response: res,
failureRedirect: '/auth/error',
customState: '/'
});
I've compared the encoded state on the authorizerequest vs the returned response and they differ in the same way locally as well as on Azure, yet Azure is the only one complaining. Examples of how the states differ:
Azure:
Request state: CUSTOMEwAuZcY7VypgbKQlwlUHwyO18lnzaYGt%20
Response state: CUSTOMEwAuZcY7VypgbKQlwlUHwyO18lnzaYGt
localhost:
Request state: CUSTOMTAYOz2pBQt332oKkJDGqRKs_wAo90Pny%2F
Response state: CUSTOMTAYOz2pBQt332oKkJDGqRKs_wAo90Pny/
I've also tried removing customState completely yet it still fails.
Anyone know what's going on here? Am I configuring it incorrectly?
Edit: It appears that this may not be an issue with passport-azure-ad. I'm not sure yet, but some debugging revealed that there is no set-cookie header on the login request to my app. The session is created, but no cookie is set thus the returning response is unable to look up the session info including the state and compare them. The result is that it reports invalid state since it's unable to retrieve data from the session.
Turns out the problem was that the session was never properly created thus there was no state for process-azure-ad to compare. The reason for this was that I had configured express-session to use secure session cookies under the assumption that since I was connecting through the https://...azurewebsites.net address the connection was secure. This is not technically the case though.
Azure runs a load balancer in front of the Web Application effectively proxying connections from the outside to my app. This proxy is where the secure connection is terminated and then traffic is routed unencrypted to my application.
Browser -(HTTPS)> Load balancer -(HTTP)> Application
The result is that node did not report the connection as secure unless a set the configuration option trust proxy:
app.set('trust proxy', true);
When this option is set express will check the X-Forwarded-Proto header for which protocol was used to connect to the proxy server (in this case the load balancer). This header contains either http or https depending on the connection protocol.
For Azure though this is still not sufficient. The Azure load balancer does not set the X-Forwarded-Proto header either. Instead it uses x-arr-ssl. This is not a big problem though as iisnode (the runtime I'm using to run node on IIS in Azure) has an option called enableXFF that will update the X-Forwarded-Proto header based on the external protocol of the connection. Setting both these options enables express-session to set the secure cookie keeping the session stored and allowing passport-azure-ad to store and compare state information on authentication.
PS: Big thanks to Scott Smiths blog + comments for providing the answer:
http://scottksmith.com/blog/2014/08/22/using-secure-cookies-in-node-on-azure/
This is a known encode issue with module passport-azure-ad. See:
"State" gets encoded and causes "collectInfoFromReq: invalid state received" #309
"invalid state received in the request" causing infinite loop on Login #247
You could upgrade the module version to v3.0.7 or a newer one to fix it.

Microsoft login from oauth2 issue

I have a React app using axios library for handling request. So following the next post:
How to login with username/password using OAuth2 and microsoft login and HTTP request
I could perform the action on Postman.
Then I set up the axios library to perform the POST
const dataForBody = `${'grant_type=password&' +
'username='}${encodeURI(userName)}&` +
`password=${encodeURI(userPassword)}&` +
`client_id=${encodeURI(clientID)}&` +
`resource=${encodeURI('https://graph.microsoft.com')}&` +
`client_secret=${encodeURI(clientSecret)}`;
const messageHeaders = {
'Content-Type': 'application/x-www-form-urlencoded'
};
axios({
method: 'post',
url: 'https://login.microsoftonline.com/{tenant}/oauth2/token',
headers: messageHeaders,
data: dataForBody,
})
.then((response) => {
});
but I get the following error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at
https://login.microsoftonline.com/{tenant}/oauth2/token.
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
I tried adding:
'Access-Control-Allow-Origin': 'https://login.microsoftonline.com',
to the headers, but it did not work.
So adding Allow-Control-Allow-Origin: *​​​ chrome extension fixed my problem.
The thing is, my app is to be published on azure, so I tested the request on other web browsers and it did not work. So I don't want my users to install the extension.
Is there something wrong with my request? Why postman can do it without setting up headers?
Is there any other approach to achieve it?
PS: I read about using adal.js but I dont want to use the login screen from microsoft, because I know user and pass for the app, and I want to avoid manual login.
The problem you face is due to you trying to call the token endpoint via AJAX, which it won't accept due to the CORS header missing. You can't add it, it's missing from the response from Azure AD.
What you need to do is instead of getting the access token from the token endpoint, you must use the OAuth Implicit Grant Flow. This flow allows you to get the tokens directly in the authorization stage, and is especially designed for JavaScript-based apps. More info here: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-dev-understanding-oauth2-implicit-grant.
What this means is that you can't use the Password Grant Flow as you are doing now, unless you make the calls from your backend instead of the frontend.

Resources