Mule application blocked all of a sudden due to CORS - node.js

I have been playing with React, Node and Mule in order to make requests go through Mule. I managed to get it all working but today for some reason I get an ERROR:
Access to XMLHttpRequest at 'http://localhost:88/api/courses/pizza' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This ERROR was not there before, everything was perfectly working and it was returning JSON as it is supposed to. I reverted all changes I made but to no avail. I even used allow CORS extension for chrome but then it seemed like requests didn't even reach the Mule application executing in Anypoint Studio, since it returned no message. My Mule is on PORT 88, React 3000 and Node on 1234. I tried to use CORS interceptor in Anypoint Studio too according to this tutorial: https://dzone.com/articles/enable-cors-into-mule-4-at-application-level but that didn't help either. Please leave some advices as to what else I can try.
React request:
export default function APIRequest(url, method, body) {
return fetch(url, {
method: method,
body: body,
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
}
Node:
router.get("/:name", async (req, res) => {
console.log(req.params.name);
const courses = await Course.find();
course = courses.filter((course) => course.category === req.params.name);
res.send(course);
});
Mule:
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core" xmlns:wsc="http://www.mulesoft.org/schema/mule/wsc"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/wsc http://www.mulesoft.org/schema/mule/wsc/current/mule-wsc.xsd
http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd">
<http:listener-config name="HTTP_Listener_configuration" doc:name="HTTP_Listener_configuration" doc:id="70a95691-a977-48ff-8439-79a5d2fc7bb7" >
<http:listener-connection host="0.0.0.0" port="88" />
<http:listener-interceptors >
<http:cors-interceptor >
<http:origins>
</http:origins>
</http:cors-interceptor>
</http:listener-interceptors>
</http:listener-config>
<wsc:config name="Web_Service_Consumer_Config" doc:name="Web Service Consumer Config" doc:id="0a403287-6d01-45e1-bb5f-47ffcc7a5f71" >
<wsc:connection wsdlLocation="https://www.dataaccess.com/webservicesserver/NumberConversion.wso?wsdl" service="NumberConversion" port="NumberConversionSoap" address="https://www.dataaccess.com/webservicesserver/NumberConversion.wso">
<wsc:web-service-security actor="http://schemas.xmlsoap.org/soap/actor/next" />
</wsc:connection>
</wsc:config>
<http:request-config name="First_REST_API" doc:name="HTTP Request configuration" doc:id="84f17cc0-eb86-4c2a-b122-7d5f22781423" basePath="https://random-data-api.com/api/number/random_number?size=1" >
<http:request-connection protocol="HTTPS" />
</http:request-config>
<http:request-config name="HTTP_Request_configuration" doc:name="HTTP Request configuration" doc:id="93a99571-d4d7-43c3-9505-26f2a56e97b3">
<http:request-connection protocol="HTTPS" />
</http:request-config>
<http:request-config name="HTTP_Request_configuration1" doc:name="HTTP Request configuration" doc:id="282ae316-cee6-4305-ae58-283858258ec6">
<http:request-connection protocol="HTTPS" host="random-data-api.com"/>
</http:request-config>
<http:request-config name="Local_REST_API_Config" doc:name="HTTP Request configuration" doc:id="6cf97fb4-f770-44fb-b450-4ccccf29db1a" >
<http:request-connection host="localhost" port="1234" />
</http:request-config>
<http:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config" doc:id="0a9a912a-50df-4963-b17a-f61f53aaa656" >
<http:listener-connection host="0.0.0.0" port="3001" readTimeout="3000"/>
</http:listener-config>
<flow name="GET:\courses\id-find_course_by_id" doc:id="e4042d03-603f-41c5-91a1-e12c2992d75e" >
<http:listener doc:name="Listener with URI ID" doc:id="b1ecf442-8975-4385-b11b-cac1677613ef" config-ref="HTTP_Listener_configuration" path="/api/courses/find/{id}" />
<set-payload value="#[attributes.uriParams.id]" doc:name="Set payload with URI ID" doc:id="8499152a-c601-488c-8cbd-2a081e38b20f" />
<http:request method="GET" doc:name="REST API request for Course with ID" doc:id="d17382dd-e848-44fc-b4e4-495991b61325" config-ref="Local_REST_API_Config" path="/api/courses/find/{id}" outputMimeType="application/json">
<http:body><![CDATA[{}]]></http:body>
<http:uri-params><![CDATA[#[output application/java
---
{
id: payload
}]]]></http:uri-params>
</http:request>
<set-variable value='#[output application/json
---
{
isPublished: payload.isPublished,
tags: payload.tags map ( tag , indexOfTag ) -> tag,
"_id": payload."_id",
dishName: payload.dishName,
category: payload.category,
author: payload.author,
ingredients: payload.ingredients map ( ingredient , indexOfIngredient ) -> {
"_id": ingredient."_id",
quantity: ingredient.quantity,
unit: ingredient.unit,
description: ingredient.description
},
cookingTime: payload.cookingTime,
sourceUrl: payload.sourceUrl,
imageUrl: payload.imageUrl,
price: payload.price,
date: payload.date,
"__v": payload."__v"
}]' doc:name="Set variable with response JSON" doc:id="f712c7d0-65a8-48ab-a7a4-088ba23c9956" variableName="results" mimeType="application/json" />
<http:request method="GET" doc:name="Call REST API for random number" doc:id="90a7f6f5-aeba-4b28-a5af-43dcd4b87e16" config-ref="HTTP_Request_configuration1" path="/api/number/random_number?size=1" />
<set-variable value="#[output application/java
---
payload.decimal]" doc:name="Set Variable with REST API response " doc:id="5e409a00-0968-4cc7-a24e-da4efb9b2e03" variableName="price_number"/>
<ee:transform doc:name="Transform response to XML" doc:id="3a0f5690-a8ca-4e4d-9a8a-71645cf0940f" >
<ee:message >
<ee:set-payload ><![CDATA[output application/xml
---
{
NumberToDollars #(xmlns: "http://www.dataaccess.com/webservicesserver/"): {
dNum: payload.decimal
}
}]]></ee:set-payload>
</ee:message>
</ee:transform>
<wsc:consume operation="NumberToDollars" doc:name="Call SOAP API with payload for string version of number" doc:id="0a8622d2-98ab-4114-8902-57bcdc2f58ba" config-ref="Web_Service_Consumer_Config" />
<set-variable value="#[output application/java
ns ns0 http://www.dataaccess.com/webservicesserver/
---
payload.body.ns0#NumberToDollarsResponse.ns0#NumberToDollarsResult]" doc:name="Set Variable with SOAP API response" doc:id="25495ffe-b5ef-49de-a9c0-d4db668a58a6" variableName="price_string"/>
<ee:transform doc:name="Transform all responses into one JSON" doc:id="10c1c874-782b-4c4c-84d4-bb14bf3e50a7" >
<ee:message >
<ee:set-payload ><![CDATA[%dw 2.0
output application/json
---
{
isPublished: vars.results.isPublished,
tags: vars.results.tags map ( tag , indexOfTag ) -> tag,
"_id": vars.results."_id",
dishName: vars.results.dishName,
category: vars.results.category,
author: vars.results.author,
ingredients: vars.results.ingredients map ( ingredient , indexOfIngredient ) -> {
"_id": ingredient."_id",
quantity: ingredient.quantity,
unit: ingredient.unit,
description: ingredient.description
},
cookingTime: vars.results.cookingTime,
sourceUrl: vars.results.sourceUrl,
imageUrl: vars.results.imageUrl,
price: vars.price_number[0],
priceText: vars.price_string,
date: vars.results.date,
"__v": vars.results."__v"
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>
<flow name="GET:\courses\name-find_course_by_name" doc:id="11a73a6e-f58f-4c7d-b79a-a310f4e4dc8d" >
<http:listener doc:name="Listener with URI name" doc:id="ee5e9716-60c1-41ae-9c12-dcd1b44cbb96" config-ref="HTTP_Listener_configuration" path="/api/courses/{name}" />
<set-payload value="#[attributes.uriParams.name]" doc:name="Set payload with URI name" doc:id="bac678be-6661-42e7-ac54-db88dac44383" />
<http:request method="GET" doc:name="REST API request for Course with name" doc:id="9010ac49-c81f-4961-93f0-f0539494edb2" config-ref="Local_REST_API_Config" path="/api/courses/{name}" outputMimeType="application/json" >
<http:body ><![CDATA[{}]]></http:body>
<http:uri-params ><![CDATA[#[output application/java
---
{
name: payload
}]]]></http:uri-params>
</http:request>
</flow>
<flow name="GET:\courses-get_all_courses" doc:id="686d41ad-7d8b-40c6-9bb5-3e5ea53efd88" >
<http:listener doc:name="Listener" doc:id="3dc9bfa3-eff1-4cfd-b931-8d47eeeb6695" config-ref="HTTP_Listener_configuration" path="/api/courses/" />
<http:request method="GET" doc:name="REST API request for all courses" doc:id="c7ed66df-bb09-4ecd-9406-151dac1c1f30" config-ref="Local_REST_API_Config" path="/api/courses/" outputMimeType="application/json" >
<http:body ><![CDATA[{}]]></http:body>
</http:request>
</flow>
<flow name="POST:\courses\post-create_course" doc:id="c1fd8373-a7e3-4b7d-bb5b-f3c8214c488b" >
<http:listener doc:name="Listener" doc:id="18b0ed4d-5b6a-4d60-ba96-ed37d1ca10be" config-ref="HTTP_Listener_configuration" path="/api/courses/add/" />
<http:request method="POST" doc:name="REST API request for new course" doc:id="7b565a3e-8eef-4d3e-b3ab-113acca530ae" config-ref="Local_REST_API_Config" path="/api/courses/" outputMimeType="application/json" >
</http:request>
</flow>
<flow name="DELETE:\courses\delete\id-delete_course" doc:id="3c8d98ce-89a7-4437-b9f4-3db38582e596" >
<http:listener doc:name="Listener with URI ID" doc:id="77cfb541-b6e1-4d5d-a6e4-e66381b36af0" config-ref="HTTP_Listener_configuration" path="/api/courses/delete/{id}" />
<set-payload value="#[attributes.uriParams.id]" doc:name="Set payload with URI ID" doc:id="663d6350-4921-40b2-b923-71a24bd46f2c" />
<http:request method="DELETE" doc:name="REST API request for deleting course with ID" doc:id="6db59045-4d70-4901-b696-4f1068d7ef91" config-ref="Local_REST_API_Config" path="/api/courses/delete/{id}" outputMimeType="application/json" >
<http:body ><![CDATA[{}]]></http:body>
<http:uri-params ><![CDATA[#[output application/java
---
{
id: payload
}]]]></http:uri-params>
</http:request>
</flow>
<flow name="PUT:\courses\update\id-put_course" doc:id="42dc3b31-370b-4051-9799-0e957f2b1841" >
<http:listener doc:name="Listener with URI ID" doc:id="9ca7c395-64c4-4c4c-a3b7-3e70323be279" config-ref="HTTP_Listener_configuration" path="/api/courses/update/{id}" />
<set-variable value="#[attributes.uriParams.id]" doc:name="Set Variable with URI ID" doc:id="6677b155-ce84-445b-b608-bcd1f83c06b5" variableName="id" />
<logger level="INFO" doc:name="Logger" doc:id="526b8602-25ae-4c47-949a-dd9bd1fe7c43" message="#[vars.id]"/>
<http:request method="PUT" doc:name="REST API request for updating course with ID" doc:id="95024c7e-99df-4d2c-8ccd-5f2c9b09ca6a" config-ref="Local_REST_API_Config" path="/api/courses/update/{id}" outputMimeType="application/json" >
<http:uri-params ><![CDATA[#[output application/java
---
{
id: vars.id
}]]]></http:uri-params>
</http:request>
</flow>
</mule>

CORS is a restriction on the browser, not on the Mule application. It looks like in the CORS interceptor configuration you didn't add either an authorized origin (looks to be 'http://localhost:3000' based on the error), or configure the resource as public. The first option is more secure because restricts to a limited lists of origins allowed to do the request. Those options are explained in the article you linked.

Related

How configure Azure API Management for CORS

I have create an Azure API Management Service and connected my APIs. I added CORS policies to them.
I checked the Calculate effective policy and the result is this policy
<policies>
<inbound>
<!-- base: Begin Product scope -->
<!-- base: Begin Global scope -->
<cors allow-credentials="true">
<allowed-origins>
<origin>https://developer.mydomain.com</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
<!-- base: End Global scope -->
<!-- base: End Product scope -->
<cors>
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods>
<method>GET</method>
<method>POST</method>
</allowed-methods>
</cors>
</inbound>
<backend>
<!-- base: Begin Product scope -->
<!-- base: Begin Global scope -->
<forward-request />
<!-- base: End Global scope -->
<!-- base: End Product scope -->
</backend>
<outbound />
<on-error />
</policies>
If I call the API with C#, it is working. Then, I created a simple JavaScript to call the API but there is no way to avoid CORS. I tried different JavaScript
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
<script>
var url = '';
var headerKey = 'subscription-Key';
var headerValue = 'e1e21';
$.ajax({
url: url,
beforeSend: function(xhrObj){
// Request headers
xhrObj.setRequestHeader("Origin","https://www.puresourcecode.com/");
xhrObj.setRequestHeader("Access-Control-Allow-Origin","https://www.puresourcecode.com/");
xhrObj.setRequestHeader("Access-Control-Request-Method","GET");
xhrObj.setRequestHeader("Access-Control-Allow-Credentials","true");
xhrObj.setRequestHeader("Access-Control-Request-Headers","X-Custom-Header");
xhrObj.setRequestHeader("Access-Control-Allow-Headers","Origin, Content-Type, Accept, Authorization, X-Request-With");
xhrObj.setRequestHeader(headerKey,headerValue);
},
type: "GET",
contentType: "application/json; charset=utf-8",
})
.done(function(data) {
alert("success");
})
.fail(function() {
alert("error");
});
// jQuery preflight request
$.ajax({
type: "GET",
headers: {headerKey: headerValue},
url: url
}).done(function (data) {
console.log(data);
});
// XMLHttpRequest preflight request
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.setRequestHeader(headerKey, headerValue);
xhr.onload = function () {
console.log(xhr.responseText);
};
xhr.send();
// Fetch preflight request
var myHeaders = new Headers();
myHeaders.append(headerKey, headerValue);
fetch(url, {
headers: myHeaders
}).then(function (response) {
return response.json();
}).then(function (json) {
console.log(json);
});
</script>
</body>
</html>
All of them are failing because of CORS. I tried to add preflight-result-max-age="300" and also specified the allowed-headers like the one I use to pass the subscription key without success. I also tried to copy this file in my server with the same error.
As origin I set * because I thought in this way every request for every URL is accepted but obviously not.
What is the correct settings to apply in the Azure API Management to avoid CORS?
I think to issue happens when you enable the Developer portal. Then, in the configuration for the portal, you enable CORS. At this point, Azure automatically adds a global policy to the API Management Service but you don't know that.
<policies>
<inbound>
<!-- base: Begin Global scope -->
<cors allow-credentials="true">
<allowed-origins>
<origin>https://developer.mydomain.com</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>*</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
<!-- base: End Global scope -->
</inbound>
</policies>
Happily, you configure CORS in your API and when you try to call them from JavaScript, you are facing the CORS error.
If you open the CORS configuration in the Azure portal you can see something like that:
<policies>
<inbound>
<base />
<cors>
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods preflight-result-max-age="300">
<method>GET</method>
<method>POST</method>
</allowed-methods>
<allowed-headers>
<header>*</header>
<header />
</allowed-headers>
<expose-headers>
<header>*</header>
</expose-headers>
</cors>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
My understanding is the portal combine the base configuration with yours. So, like in Active Directory the more restricted policy is applying. That means in my example, only the Developer Portal can call the APIs without CORS issue.
Solution
Delete <base /> under <inbound> and save.
Yes, updating the CORS in the global setting solved our problem. We spent a lot of time troubleshooting by overriding the policy for a specific service to identify the root cause. It turned out to be that we have to apply the CORS policy at the global level.API Management Service -> APIS -> All APIS -> Inbound processing settings
UI Screenshot
I was running into the same issue on every API Management I set up. I keep forgetting what I did to fix it, so this is also a reminder to my future self.
Add a CORS policy,
Allowed headers *
Enable Allow credentials
Include Slash in URL

Azure API Management Rate Limit By Json Body

hello i am trying applying rate limiting on azure api management by json body value i have rule like that
<rate-limit-by-key calls="6" renewal-period="180" counter-key="#(context.Request.Body.As<JObject>()["phoneNumber"].ToString())" increment-condition="#(context.Response.StatusCode >= 200 && context.Response.StatusCode < 300)" />
but rate limiting not working.
My test was succesful.
Request-Body:
{
"phoneNumber": "+1234"
}
Policy:
<inbound>
<base />
<rate-limit-by-key calls="6" renewal-period="180" counter-key="#(context.Request.Body.As<JObject>()["phoneNumber"].ToString())" increment-condition="#(context.Response.StatusCode >= 200 && context.Response.StatusCode < 300)" />
<return-response>
<set-status code="200" reason="OK" />
<set-body />
</return-response>
</inbound>
return-response is just used for testing.
Response-Body after a few tries:
{
"statusCode": 429,
"message": "Rate limit is exceeded. Try again in 174 seconds."
}
How is this different from your implementation?
Do you have a different Request-Body?

how to make http request from xml soap?

I am implementing one of the payment gateway(Advance cash) in my application.Here is the link :
Adv documentaion
According to its documentation I have to made request for transaction. But the request mention in it in xml format.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsm="http://wsm.advcash/">
<soapenv:Header/>
<soapenv:Body>
<wsm:sendMoney>
<arg0>
<apiName>api_name</apiName>
<authenticationToken>token</authenticationToken>
<accountEmail>name#example.com</accountEmail>
</arg0>
<arg1>
<amount>1.00</amount>
<currency>USD</currency>
<email>name#example.com</email>
<note>Some note</note>
<savePaymentTemplate>false</savePaymentTemplate>
</arg1>
</wsm:sendMoney>
</soapenv:Body>
</soapenv:Envelope>
I think it is kind of soap request,I want to know how to request this and what are the parameters I have to send using my node js application using "request" npm module.Please help.
You should use node-soap package. There is a request option which override the request module.
I made a sample, take a look
service.ts:
const service = {
UserService: {
ServicePort: {
getUserById(args) {
const user = { id: args.id, name: faker.name.findName(), email: faker.internet.email() };
return user;
}
}
}
};
service.wsdl:
<definitions name="Service" targetNamespace="http://localhost:8001/get-started/service.wsdl"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://localhost:8001/get-started/service.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<message name="GetUserByIdInput">
<part name="id" type="xsd:string"></part>
</message>
<message name="GetUserByIdOutput">
<part name="id" type="xsd:string"></part>
<part name="name" type="xsd:string"></part>
<part name="email" type="xsd:string"></part>
</message>
<portType name="ServicePortType">
<operation name="getUserById">
<input message="tns:GetUserByIdInput"/>
<output message="tns:GetUserByIdOutput"></output>
</operation>
</portType>
<binding name="ServiceSoapBinding" type="tns:ServicePortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="getUserById">
<soap:operation soapAction="getUserById"/>
<input message="tns:GetUserByIdInput">
<soap:body parts="id" use="literal"/>
</input>
<output message="tns:GetUserByIdOutput">
<soap:body parts="id" use="literal"/>
<soap:body parts="name" use="literal"/>
<soap:body parts="email" use="literal"/>
</output>
</operation>
</binding>
<service name="UserService">
<documentation>Get started service</documentation>
<port name="ServicePort" binding="tns:ServiceSoapBinding">
<soap:address location="http://localhost:8001/get-started"/>
</port>
</service>
</definitions>
request.xml:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tns="http://localhost:8001/get-started/service.wsdl">
<soap:Body>
<tns:getUserById>
<id>8104d3c3-de13-432f-b4a0-a62f84f6206a</id>
</tns:getUserById>
</soap:Body>
</soap:Envelope>
server.spec.ts:
it('should get correct result using http request and static xml', async (done: jest.DoneCallback) => {
const xml = fs.readFileSync(path.resolve(__dirname, './static-xml.xml'), 'utf8');
const uuid = '8104d3c3-de13-432f-b4a0-a62f84f6206a';
const options = {
url,
method: 'POST',
body: xml,
headers: {
'Content-Type': 'text/xml;charset=utf-8',
'Accept-Encoding': 'gzip,deflate',
'Content-Length': xml.length
}
};
const rawXml = await request(options);
parser.parseString(rawXml, (err, actualValue) => {
if (err) {
done(err);
}
console.log('actualValue: ', JSON.stringify(actualValue));
expect(actualValue['soap:Envelope']['soap:Body']['tns:getUserByIdResponse']['tns:id']).toBe(uuid);
done();
});
});
The result:
actualValue: {"soap:Envelope":{"$":{"xmlns:soap":"http://schemas.xmlsoap.org/soap/envelope/","xmlns:tns":"http://localhost:8001/get-started/service.wsdl"},"soap:Body":{"tns:getUserByIdResponse":{"tns:id":"bf0f6172-2f53-4b33-94c8-9ff9ed8fd431","tns:name":"Theo Leannon","tns:email":"Brent.Berge#hotmail.com"}}}}
Here is the demo: https://github.com/mrdulin/nodejs-soap/tree/master/src/get-started

Mule - JMS (ActiveMQ) Reconnection

This is my mule flow 1:
HTTP > Payload String > Logger > JMS /normalqueue
The first flow has an error handling:
File (Write a file per message handled)
Flow 2:
JMS /normalqueue > Logger
Recovery flow (Invoked with a groovy script):
File (Read file) > File to String > Flow reference (To First Flow again)
This is the XML from Mule:
<http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
<jms:activemq-connector name="Active_MQ" username="admin" password="admin" brokerURL="tcp://192.168.198.131:61616" validateConnections="true" doc:name="Active MQ" persistentDelivery="true">
<reconnect blocking="false" frequency="6000"/>
</jms:activemq-connector>
<file:connector name="File" writeToDirectory="C:\errors" autoDelete="true" streaming="true" validateConnections="true" doc:name="File"/>
<flow name="lab-file-catchFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<set-payload value="#[message.payloadAs(java.lang.String)]" doc:name="Set Payload"/>
<logger message="Started message: #[message.payloadAs(java.lang.String)]" level="INFO" doc:name="Logger"/>
<jms:outbound-endpoint queue="activemq" connector-ref="Active_MQ" doc:name="JMS">
<jms:transaction action="ALWAYS_BEGIN"/>
</jms:outbound-endpoint>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<file:outbound-endpoint path="C:\errors" connector-ref="File" responseTimeout="10000" doc:name="File"/>
</catch-exception-strategy>
</flow>
<flow name="flow-recovery" initialState="stopped" processingStrategy="synchronous">
<file:inbound-endpoint path="C:\errors" connector-ref="File" responseTimeout="10000" doc:name="File"/>
<file:file-to-string-transformer doc:name="File to String"/>
<logger message=" Recovery message: #[message.payloadAs(java.lang.String)]" level="ERROR" doc:name="Logger"/>
<flow-ref name="lab-file-catchFlow" doc:name="Flow Reference"/>
</flow>
<flow name="lab-file-catchFlow2" processingStrategy="synchronous">
<jms:inbound-endpoint queue="activemq" connector-ref="Active_MQ" doc:name="JMS"/>
<logger message="#[message.payloadAs(java.lang.String)]" level="INFO" doc:name="Logger"/>
</flow>
<flow name="lab-file-catchFlow1" >
<http:listener config-ref="HTTP_Listener_Configuration" path="/modify" doc:name="HTTP"/>
<scripting:component doc:name="Groovy">
<scripting:script engine="Groovy"><![CDATA[ if(muleContext.registry.lookupFlowConstruct('flow-recovery').isStopped())
{
muleContext.registry.lookupFlowConstruct('flow-recovery').start();
return 'Started';
} else
{
muleContext.registry.lookupFlowConstruct('flow-recovery').stop();
return 'Stopped';
}]]></scripting:script>
</scripting:component>
<set-payload value="#[message.payloadAs(java.lang.String)]" doc:name="Set Payload"/>
<logger message="#[message.payloadAs(java.lang.String)]" level="INFO" doc:name="Logger"/>
</flow>
I stop service from ActiveMQ, it store a file with the messages from the error handling and I receive the typical error:
Cannot process event as "Active_MQ" is stopped
Then, I run the ActiveMQ service again and start the recovery flow with a groovy script. That flow recover all messages, converts to string and return to the first flow to requeue.
The problem is that mule doesn't detect when service is running again, I need to restart the mule project to detect it.
Is there any way auto detect when the activeMQ is running again with Mule?
By <reconnect-forever/>, Mule will keep re-trying to connect to ActiveMQ
<jms:activemq-connector name="Active_MQ" username="admin" password="admin" brokerURL="tcp://192.168.198.131:61616" validateConnections="true" doc:name="Active MQ" persistentDelivery="true">
<reconnect-forever/>
</jms:activemq-connector>

How do I correctly digitally sign a SAML2.0 AuthnRequest?

I have a working SAML2.0 Single Sign On system built in coffeescript on express / nodejs.
I use SSOCircle to test my SSO and can successfully authenticate there with either HTTP-POST or HTTP-REDIRECT binding. So far so good.
Now I need to digitally sign my authentication request.
To do this I have used the nodejs xml-crytpo library. This results in SAML that looks like this:
<?xml version="1.0"?>
<samlp:AuthnRequest Version="2.0" ID="_1C8A2EFA-2644-4330-9A36-2B45547ECFAF" IssueInstant="2014-10-14T16:06:27.769Z" Destination="https://idp.ssocircle.com:443/sso/SSOPOST/metaAlias/ssocircle" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
<saml:Issuer>http://app.localhost</saml:Issuer>
<samlp:NameIDPolicy AllowCreate="true" />
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#_1C8A2EFA-2644-4330-9A36-2B45547ECFAF">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>m4qHiXM82TuxY31l6+QSECHEHc0=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>fps0I0Rp02qDK0BPTK7Lh+ ...</SignatureValue>
<KeyInfo>
<X509Data>MIICuDCCAaCgAwIBAgIQEVFtJk ...</X509Data>
</KeyInfo>
</Signature>
</samlp:AuthnRequest>
The certificate information contained in the digital signature has been removed for brevity but it comes from a pem file I generated locally (self-signed).
I also updated my Service Provider metadata supplied to SSOCircle to include the SAML attributes AuthnRequestsSigned and WantAssertionsSigned, like this:
<md:EntityDescriptor entityID="http://app.localhost" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
<md:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
...
</md:SPSSODescriptor>
</md:EntityDescriptor>
Sending my AuthnRequest without the digital signature works fine. When I include the digital signature it fails with a statuscode of 500 and a generic 'Invalid Request' message.
How do I correctly digitally sign my AuthnRequest so it works against SSOCircle?
Thanks.
Edit 1 - Add Cert Key to the SP Metadata
Following suggestions in the comments I have now added the appropriate KeyDescriptor attributes to my sp-metadata to include my self-signed certificate's public-key.
<md:EntityDescriptor entityID="http://app.localhost" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
<md:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509SubjectName>CN=app.localhost</ds:X509SubjectName>
<ds:X509Certificate>MIICu ... wg==</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>MIICu ... wg==</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
...
</md:EntityDescriptor>
I have included two KeyDescriptor elements, one specifying the use = signing and the other specifying the use = encryption, although I think the latter is redundant (as I am running over TLS) and will be ignored by the IdP.
This does not work and I get the same error:
Reason: The SAML Request is invalid.
Edit 2 - Self Signed Cerificates
Theory: I cannot use a self-signed certificate. Instead I must use a certificate that is known to and trusted by SSOCircle
Result: This was disproved. You can use a self-signed cert just as long as the correct public key is included in the sp-metadata.
Edit 3 - Added Correct Transform Algorithm
I have now updated my signature to specify the #enveloped-signature transformation algorithm.
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
Also I found that the correct structure for the included x509 certificate public key was the following (note the inner X509Certificate element):
<KeyInfo>
<X509Data>
<X509Certificate>MIID...MMxZ</X509Certificate>
</X509Data>
</KeyInfo>
I am now using the TestShib site to run against as this allows access to the IdP logs which provides more detailed and useful information.
Edit 4 - Swapping the SAML Element Order to Match Schema
According to answer given by #Hos (see below) the order of the SAML elements must match the Schema. Therefore the signature element must appear directly after the issuer element, like this:
<samlp:AuthnRequest Version="2.0" ID="_782F6F1E-1B80-4D7D-B26C-AC85030D9300" IssueInstant="2014-10-28T11:45:49.412Z" Destination="https://idp.ssocircle.com:443/sso/SSOPOST/metaAlias/ssocircle" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
<saml:Issuer>http://app.localhost9de83841</saml:Issuer>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#_782F6F1E-1B80-4D7D-B26C-AC85030D9300">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>pjXEtbAFMJA3hWhD/f6lxJTshTg=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>FY1...Qg==</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIID...MMxZ</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
<samlp:NameIDPolicy AllowCreate="true" />
</samlp:AuthnRequest>
This still fails SSOCircle with an opaque 500 error. However TestShib shows the following log:
11:02:37.292 - DEBUG [edu.internet2.middleware.shibboleth.common.security.MetadataPKIXValidationInformationResolver:631] - Write lock over cache acquired
11:02:37.292 - DEBUG [edu.internet2.middleware.shibboleth.common.security.MetadataPKIXValidationInformationResolver:634] - Added new PKIX info to entity cache with key: [http://app.localhost9de83841,{urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor,urn:oasis:names:tc:SAML:2.0:protocol,SIGNING]
11:02:37.292 - DEBUG [edu.internet2.middleware.shibboleth.common.security.MetadataPKIXValidationInformationResolver:637] - Write lock over cache released
11:02:37.293 - WARN [edu.internet2.middleware.shibboleth.idp.profile.saml2.SSOProfileHandler:406] - Message did not meet security requirements
org.opensaml.ws.security.SecurityPolicyException: **Validation of protocol message signature failed**
As I read this, the public key used to validate the signature has been extracted from the sp-metadata:
Added new PKIX info to entity cache with key: [http://app.localhost9de83841,{urn:oasis:names:tc:SAML:2.0:metadata}SPSSODescriptor,urn:oasis:names:tc:SAML:2.0:protocol,SIGNING]
However the actual validation has failed, presumably because the digest values do not match. Which means the digest being calculated by xmlcrypto is different from the digest calculated by ShibTest. This can surely only be for one of two reasons:
The algorithms used by xmlcrypto and TestShib to generate the digest are somehow different
The public keys do not match.
A minute examination of the keys show they are the same. So here is my code in case you can spot anything.
Note: This code relies on the nodejs xmlbuilder and xml-crytpo libraries)
getSamlRequest:(idpUrl, requestId, next)->
request = #xmlbuilder.create
'samlp:AuthnRequest':
'#xmlns:samlp':'urn:oasis:names:tc:SAML:2.0:protocol'
'#xmlns:saml': 'urn:oasis:names:tc:SAML:2.0:assertion'
'#Version': '2.0'
'#ID': requestId
'#IssueInstant': (new Date()).toISOString()
'#Destination': idpUrl
'saml:Issuer': '##spEntityId'
,null
,headless: true
request.comment 'insert-signature'
request.element 'samlp:NameIDPolicy':
'#AllowCreate': 'true'
saml = request.end()
#Your self-signed pem file that contains both public and private keys.
#The public key must also be included in your sp-metadata
certFilePath = "certs/sp-certificate.pem"
#fs.readFile certFilePath, (err, certificate)=>
signer = new #xmlcrypto.SignedXml()
signer.signingKey = certificate
signer.addReference "//*[local-name(.)='AuthnRequest']", ['http://www.w3.org/2000/09/xmldsig#enveloped-signature']
signer.keyInfoProvider = new =>
getKeyInfo: (key)=>
public_key = /-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----/g.exec(key)[1].replace /[\r\n|\n]/g, ''
"<X509Data><X509Certificate>#{public_key}</X509Certificate></X509Data>"
signer.computeSignature saml
signature = signer.getSignatureXml()
signed = saml.replace '<!-- insert-signature -->', signature
return next null, signed
Edit 5 - Trace of Requests
Below is a trace of the requests made during the SAML handshake with SSOCircle. The output was generated by the nodejs request module in conjunction with the request-debug module.
I have included some clarification notes below this trace output.
{ request:
{ debugId: 17,
uri: 'http://localhost:8082/security/sso/subscription/dev2/096b75a2-4a55-4eec-83c8-d2509e948b07',
method: 'GET',
headers: { host: 'localhost:8082' } } }
{ response:
{ debugId: 17,
headers:
{ 'transfer-encoding': 'chunked',
'content-type': 'application/json; charset=utf-8',
server: 'Microsoft-HTTPAPI/2.0',
'access-control-allow-origin': '*',
'access-control-allow-headers': 'origin, x-requested-with, accept, content-type',
'access-control-allow-methods': 'GET, POST, PATCH, PUT, DELETE',
date: 'Wed, 05 Nov 2014 16:55:49 GMT' },
statusCode: 200,
body: '{"RequestId":"_F7DDDD24-32C6-420E-A550-95872D30033B","SubscriptionName":"Develop-2","SubscriptionURL":"dev2","IdentityProviderCertificateFile":"ssocircle.cer","IdentityProviderDestinationUrl":"https://idp.ssocircle.com:443/sso/SSOPOST/metaAlias/ssocircle","SAMLBinding":"HttpPost"}' } }
{ request:
{ debugId: 18,
uri: 'https://idp.ssocircle.com:443/sso/SSOPOST/metaAlias/ssocircle',
method: 'POST',
headers:
{ host: 'idp.ssocircle.com:443',
'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
'content-length': 3492 },
body: 'SAMLRequest=PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBWZXJzaW9uPSIyLjAiIElEPSJfRjdEREREMjQtMzJDNi00MjBFLUE1NTAtOTU4NzJEMzAwMzNCIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMTEtMDVUMTY6NTU6NDkuODkwWiIgRGVzdGluYXRpb249Imh0dHBzOi8vaWRwLnNzb2NpcmNsZS5jb206NDQzL3Nzby9TU09QT1NUL21ldGFBbGlhcy9zc29jaXJjbGUiPjxzYW1sOklzc3Vlcj5odHRwOi8vYXBwLmxvY2FsaG9zdDlkZTgzODQxPC9zYW1sOklzc3Vlcj48U2lnbmF0dXJlIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj48U2lnbmVkSW5mbz48Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIgLz48U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIiAvPjxSZWZlcmVuY2UgVVJJPSIjX0Y3RERERDI0LTMyQzYtNDIwRS1BNTUwLTk1ODcyRDMwMDMzQiI%2BPFRyYW5zZm9ybXM%2BPFRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIiAvPjwvVHJhbnNmb3Jtcz48RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiIC8%2BPERpZ2VzdFZhbHVlPjBLdUtNVG1Zc29Wb1BlTXhtdDhuNml2M3RxZz08L0RpZ2VzdFZhbHVlPjwvUmVmZXJlbmNlPjwvU2lnbmVkSW5mbz48U2lnbmF0dXJlVmFsdWU%2BY1htVFJPWVBSWFZPNHpWSkxURXBBSWd0RTd1c2NsSG1QS0NFdTB0NXNzcmVYSjd1a0M4dWdGd2c4Zm1yWDZDenhib3FHNVl1enJkb2RWVVNsbU12bHZXMGpiaWtsQVBXMmtPNTcralB4TWV3UTdFdzJZdTJuRTZ2QkFhMUwxWGpsa0g0a1UrdlVWVEpFQnlsdXhOYjRQN0xoZnJTdnVrZDhWejJwbk5QTnJtY0tJZVp1LzFlZU8wWmRyRVZrdEY1REhVaFV0MEs5aFBhRXB5Z0xsYjVKYWhNZEttei9uQWk4OU04aTEyUTdrQ2hwb1UrcmhjYTQzaDBXdGk2SWI1N1lWRzgyMzE5MlNsdGR5UkZPdXl2bEJRL0FMSkdpN0hNYzRMRXVkQktOL3pxaUFpK1NZYm1ONVNuN0NucFJWbW13U3NJUElncWxCbmlrU2pIQzRQS1VBPT08L1NpZ25hdHVyZVZhbHVlPjxLZXlJbmZvPjxYNTA5RGF0YT48WDUwOUNlcnRpZmljYXRlPk1JSURnRENDQW1pZ0F3SUJBZ0lESUFTeE1BMEdDU3FHU0liM0RRRUJCUVVBTUM0eEN6QUpCZ05WQkFZVEFrUkZNUkl3RUFZRFZRUUtFd2xUVTA5RGFYSmpiR1V4Q3pBSkJnTlZCQU1UQWtOQk1CNFhEVEUwTVRBeE5qRTBOVFl3TlZvWERURTFNVEF4TmpFME5UWXdOVm93TmpFTE1Ba0dBMVVFQmhNQ1JFVXhFakFRQmdOVkJBb1RDVk5UVDBOcGNtTnNaVEVUTUJFR0ExVUVBeE1LWW1sdlpuSmhZM1JoYkRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS0Y0dDhzWVZNdHRVZkZ3eTZneFpQVjVWbWtCMDZwNXNqck5vUjUxUXZISmZFMkgyTnVTa0Nxa2paVmFwb1FRMStUVTNlelloMmxNVGNYSjU1Y0t2d0lUTlEvQWlLckxjaG4wdGxYZXFIRXhIdXBvazRGd1hqc20xSitpZTBvOUc1UDlRNTFXelRjRXYxSFRBV2Rkak9OK3Zsd3d0YndTaWRtNFBkN3hxZDdvQkhXTjJJSExQNlpGVHRPVWNpdzI1K0xtRk90V3dHdU41c1pNWDV6RDNUc216Y3ZNMFQwUzF0SVlHamhpaWNnM2UrbmhkWXhjSVNvZ1B1NjNlSWswKzM2OFU1TkhnYlJ5SVRnR2tPMFdtTU9PNDhpbFlFcWlPR1E3d3FxeTdrMzZzRGNxZXdiV1lvejFnQzBieGJBeXI1d3ZZUDdnaEVHYktSVXRWeDExZzdVQ0F3RUFBYU9CbmpDQm16QUpCZ05WSFJNRUFqQUFNQ3dHQ1dDR1NBR0crRUlCRFFRZkZoMVBjR1Z1VTFOTUlFZGxibVZ5WVhSbFpDQkRaWEowYVdacFkyRjBaVEFkQmdOVkhRNEVGZ1FVcTFyMnltMkRXb1NoYWgyRm11ekxJOWZjdDdZd1FRWURWUjBqQkRvd09LRXlwREF3TGpFTE1Ba0dBMVVFQmhNQ1JFVXhFakFRQmdOVkJBb1RDVk5UVDBOcGNtTnNaVEVMTUFrR0ExVUVBeE1DUTBHQ0FncVlNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0SUJBUUFIRW5xTlVKN2VaYkkvNmozZTNmK2tFY3BQQUs2L3dXS0hSL1k2TWt1TGZpZERPREJ4eDRPTThQL1MrNFV4QkZSYWtBNFRkWkRNbjdJT2dTZ2Z3elQ0RjFxS1cvTkJKMzRtcjdndi9Yc0gxTDdHMlBEZFdUdmdGN1N5aFpCck9rbVYyZy9KYWg2U2pBREdabGdPWGJuTTlHUjlNN1NpVDBUcmFkay90dU9Zc2pxd2NJaE40eTVwSU9MNnZlemJKQThIeWZUY1lpMGFZVXFyMGl4Qkw1WWh5VDA1Qk13SUhkdFFNNGhqNjdyRDV4ME9QcmVrcTg0MjRkL0RGWmV5QTRBNG04Z3BOL0VDTFF0NzN3ajlqMVRudjBLdmlLRGZOcWJ4NngvL1o3MnN6VVhpWldETWJrNnhvdHVOZTV6Yy9xcXdhSWxLZ2lnWDJvcGRvbU9nTU14WjwvWDUwOUNlcnRpZmljYXRlPjwvWDUwOURhdGE%2BPC9LZXlJbmZvPjwvU2lnbmF0dXJlPjxzYW1scDpOYW1lSURQb2xpY3kgQWxsb3dDcmVhdGU9InRydWUiLz48L3NhbWxwOkF1dGhuUmVxdWVzdD4%3D' } }
{ response:
{ debugId: 18,
headers:
{ server: '"SSOCircle Web Server"',
date: 'Wed, 05 Nov 2014 16:55:48 GMT',
'content-type': 'text/html;charset=UTF-8',
connection: 'close',
'set-cookie': [Object],
'transfer-encoding': 'chunked' },
statusCode: 500,
body: '\n\n\n\n<html><head><title>\n Error Page\n \n </title>\n <link href="/css/bx.css" rel="stylesheet" type="text/css" /></head>\n <body>\n <div id="myheader">\n <h1><img src="/logo.png" alt="IDPee - Put your LOGO here" height="65" width="180"> </h1>\n </div>\n <div id="mycontent">\n <div id="mynav">\n <ul>\n \n </ul>\n </div>\n <div id="mybox">\n <p>\n \n <h2>Error occurred</h2>\n<p>\nReason: Invalid signature in Request.\n</p>\n\n \n </p>\n </div>\n \n </div>\n <div id="myfooter">\n \n Copyright © SSOCircle/IDPee.com\n \n \n </div>\n </body>\n</html>\n\n\n\n' } }
error: message=Something went wrong whilst trying to automatically log you in. Please try again., name=SSOFailure, message=An error occurred whilst processing the SSO Request., name=SSORequestFailure, message=The IdP failed to successfully process the SAMLRequest and returned an statuscode of 500., name=SamlRequestInvalid, url=http://app.localhost/security/saml2/request
Request debugId= 17: This is the call made by my client into my nodejs proxy server that kicks of the SAML handshake.
ResponsedebugId= 17: A 200 OK response from the proxy server back to the client. The client now waits to be redirected once the SAML handshake concludes.
Request debugId= 18: The SAMLRequest is POSTed to the SSOCircle SAML endpoint
ResponsedebugId= 18: A 500 Internal Server Error response from SSOCircle plus an HTML error page payload.
Concerning Edit2: A self signed certificate is sufficient for SSOCircle as long as it was included into the service provider's metadata.
Concerning Edit3: Please move the signature element right after the issuer element. The request must comply to the schema:
<sequence>
<element ref="saml:Issuer" minOccurs="0"/>
<element ref="ds:Signature" minOccurs="0"/>
<element ref="samlp:Extensions" minOccurs="0"/>
</sequence>

Resources