I have created my own identity server, which issued/makes tokens based on username and code. It works locally between app service to app service, but when I try to validate the token on AZURE API management fails.
I think the error is in openid-config but can see what is wrong.
But gets this error:
IDX10511: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.RsaSecurityKey, KeyId: 'AanrD1WcPkqMpK3p2S0JQ7ixqWkYBAL8hRnU6Dciiew', InternalId: 'b7aZZOAAhueurq_c62cqJcTBXL69skl6hu1a1oHLu1w'. , KeyId: AanrD1WcPkqMpK3p2S0JQ7ixqWkYBAL8hRnU6Dciiew
'.
kid: 'AanrD1WcPkqMpK3p2S0JQ7ixqWkYBAL8hRnU6Dciiew'.
Exceptions caught:
''.
token: '{"alg":"RS256","kid":"AanrD1WcPkqMpK3p2S0JQ7ixqWkYBAL8hRnU6Dciiew","typ":"JWT"}.{"nbf":1624878880,"exp":1627470880,"iss":"https://login.zenbi.dk","aud":"You"}'.
Token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkFhbnJEMVdjUGtxTXBLM3AyUzBKUTdpeHFXa1lCQUw4aFJuVTZEY2lpZXciLCJ0eXAiOiJKV1QifQ.eyJuYmYiOjE2MjQ4Nzg4ODAsImV4cCI6MTYyNzQ3MDg4MCwiaXNzIjoiaHR0cHM6Ly9sb2dpbi56ZW5iaS5kayIsImF1ZCI6IllvdSJ9.Lm32InrGT5DfphZalI9oQPzm-jcNDsOTGGkhE0dpdhdL7xpcVuZ4go6-i1dDx_cri7Neh4cow9vv3JR_Q75qhmVEr9TVrbAXP1Spkz0uvJPa9pLsQIZxH6B5D1ICnC0ROjgr5PQFXbMJXAYPludai5GpJWtX7ufUvFjauW2p2l1ssuK1iB27YeuYw7IDpMbgQvzlgVvqD8E4dzFoWdq-kLF8ZP-A3qnAtEchXu5JVJg4d7o3gI--cqJ7RaF6ehzVvFHvgADw54j4Gniif-mjnLDCZU0CYDMfRGmt5kURSJSvJUXZtaJgKYa9eQ0jSib6At4LZUVGYlHxx_I5jtjd3w
<policies>
<inbound>
<base />
<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="#((string)context.LastError.Message)" require-scheme="Bearer" require-signed-tokens="true">
<openid-config url="https://zenbicertificates.blob.core.windows.net/jwt/openid-configuration.json" />
</validate-jwt>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
</policies>
How about writing validate logic by yourself ?
<set-variable name="pass" value="#{
bool isAud = false;
bool isIss = false;
string pass = "false";
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
if (authHeader?.Length > 0)
{
string[] authHeaderParts = authHeader.Split(' ');
if (authHeaderParts?.Length == 2 && authHeaderParts[0].Equals("Bearer", StringComparison.InvariantCultureIgnoreCase))
{
Jwt jwt;
if (authHeaderParts[1].TryParseJwt(out jwt))
{
string tempScp = jwt.Claims.GetValueOrDefault("scp", "null");
if(tempScp != "null"){
isAud = tempScp.Contains("YOU");
}
string tempIss = jwt.Claims.GetValueOrDefault("iss", "null");
if(tempIss != "null"){
isIss = tempIss.Contains("xxx");
}
}
}
}
if(isAud || isIss ){
pass = "true";
}
return pass;
}" />
<choose>
<when condition="#(context.Variables.GetValueOrDefault("pass") == "false")">
<return-response response-variable-name="existing response variable">
<set-status code="401" reason="Unauthorized hhh" />
</return-response>
</when>
<otherwise />
</choose>
Related
I have created a rest api web service but I am getting error on hitting the url
http://localhost:81/magento/api/rest/category/2
<magento_api>
<messages>
<error>
<data_item>
<code>404</code>
<message>Request does not match any route.</message>
</data_item>
</error>
</messages>
</magento_api>
My api2.xml is:-
<?xml version="1.0"?>
<config>
<api2>
<resource_groups>
<esoft_rescategories translate="title" module="Esoft_Restcategories">
<title>Esoft Restcategories API</title>
<sort_order>11</sort_order>
</esoft_rescategories>
</resource_groups>
<resources>
<esoft_restcategories translate="title" module="Esoft_Restcategories">
<group>esoft_rescategories</group>
<model>esoft_restcategories/api2_restapi</model>
<title>Categories</title>
<sort_order>11</sort_order>
<privileges>
<admin>
<create>1</create>
<!--<retrieve>1</retrieve>
<update>1</update>
<delete>1</delete>-->
</admin>
<guest>
<retrieve>1</retrieve>
<!--<create>1</create>
<update>1</update>
<delete>1</delete>-->
</guest>
</privileges>
<attributes>
<category_id>Category ID</category_id>
<name>Name</name>
<parent_id>Category Parent ID</parent_id>
<child_id>Category Child List</child_id>
<active>Active</active>
<level>Level</level>
<position>Position</position>
</attributes>
<routes>
<route_entity>
<route>/categories/:cat_id</route>
<action_type>entity</action_type>
</route_entity>
<route_collection>
<route>/categories</route>
<action_type>collection</action_type>
</route_collection>
</routes>
<versions>1</versions>
</esoft_restcategories>
</resources>
</api2>
</config>
My version file for guest is:-
<?php
class Esoft_Restcategories_Model_Api2_Restapi_Rest_Guest_V1 extends Esoft_Restapi_Model_Api2_Restapi {
/**
* Retrieve list of category list.
*
* #return array
*/
protected function _retrieveCollection()
{
$ruleId = $this->getRequest()->getParam('cat_id');
// $cat_mod = Mage::getModel('catalog/category')->load($ruleId)->toArray();
$cats = Mage::getModel('catalog/category')->load($ruleId);
$subcats = Mage::getModel('catalog/category')->load($ruleId)->getChildren();
$cur_category = array();
$node['category_id'] = $ruleId;
$node['name'] = $cats->getName();
$node['parent_id'] = $cats->getParentId();
$node['child_id'] = $subcats;
if($cats->getIsActive()){
$node['active'] = 1;
}else{
$node['active'] = 0;
}
$node['level'] = $cats->getLevel();
$node['position'] = $cats->getPosition();
$cur_category[] = $node;
// $subcats = Mage::getModel('catalog/category')->load($ruleId)->getAllChildren();
// $subcats = Mage::getModel('catalog/category')->load($ruleId)->getChildren();
if($subcats != '')
{
foreach(explode(',',$subcats) as $subCatid)
{
$_category = Mage::getModel('catalog/category')->load($subCatid);
$childcats = Mage::getModel('catalog/category')->load($subCatid)->getChildren();
$node['category_id'] = $subCatid;
$node['name'] = $_category->getName();
$node['parent_id'] = $_category->getParentId();
$node['child_id'] = $childcats;
if($_category->getIsActive()){
$node['active'] = 1;
}else{
$node['active'] = 0;
}
$node['level'] = $_category->getLevel();
$node['position'] = $_category->getPosition();
$cur_category[] = $node;
}
}
return $cur_category;
}
}
Please let me know how to fix this error.
Also let me know on what basis we define routes.
Simple answer for those searching for cause of this error:
Request does not match any route
Is that you are posting the wrong METHOD such as GET/POST/PUT/DELETE.
It could also be the path of the API url itself is wrong.
As per your description your url format is wrong (route miss matching in given url).
<route>/categories/:cat_id</route>
^^^^^^^^^
So you need to change the url like below
http://localhost:81/magento/api/rest/categories/2
The API sends a verification code via a phone call that the user then enters into a website... basically verifying that there phone number is valid.
But I'm having trouble signing the request. No matter what I try it returns "Invalid Signature"
The API documentation:
http://docs.telesign.com/rest/content/verify-call.html
The authentication documentation:
http://docs.telesign.com/rest/content/rest-auth.html
The authentication examples:
http://docs.telesign.com/rest/content/auth-examples.html
The code:
<cffunction name="encryptHmacSHA1" returntype="binary" access="public" output="false">
<cfargument name="base64Key" type="string" required="true">
<cfargument name="signMessage" type="string" required="true">
<cfargument name="encoding" type="string" default="UTF-8">
<cfset var messageBytes = JavaCast("string",arguments.signMessage).getBytes(arguments.encoding)>
<cfset var keyBytes = binaryDecode(arguments.base64Key, "base64")>
<cfset var key = createObject("java","javax.crypto.spec.SecretKeySpec")>
<cfset var mac = createObject("java","javax.crypto.Mac")>
<cfset key = key.init(keyBytes,"HmacSHA1")>
<cfset mac = mac.getInstance(key.getAlgorithm())>
<cfset mac.init(key)>
<cfset mac.update(messageBytes)>
<cfreturn mac.doFinal()>
</cffunction>
<cfscript>
// PHONE NUMBER TO CALL
phoneNumberToCall = "15554565555";
// KEYS
keys = structNew();
keys.customerID = "D561FCF4-BA8D-4DFC-86D1-1A46DF47A308";
keys.apiKey = "mDzGHsMOc2g/ivkuINEFVh6fn/v4kdjvlTvtgFVOShu7hVWXS0eV2nLSw1FXgEzDSuOjhlKLXvneiq+YFG1/Vg==";
// DATES
dates = structNew();
dates.timeZoneInfo = GetTimeZoneInfo();
dates.dateToUse = DateAdd("h",dates.timeZoneInfo.utcHourOffset,now());
dates.signingDate = DateFormat(dates.dateToUse,"ddd, dd mmm yyyy") & " " & TimeFormat(dates.dateToUse,"HH:mm:ss") & " +0000";
// HEADERS
headers = [
"POST",
"application/x-www-form-urlencoded",
"#dates.signingDate#",
"phone_number=#phoneNumberToCall#&ucid=OTHR",
"/v1/verify/call"
];
headerText = arrayToList(headers, chr(10)) & chr(10);
// CREATE SIGNATURE
stringToSign = binaryEncode( encryptHmacSHA1(keys.apiKey, headerText), "base64");
// AUTHORIZE HEADER
Authorization = "TSA" & " " & keys.customerID & ":" & stringToSign;
</cfscript>
<cfhttp method="POST" url="https://rest.telesign.com/v1/verify/call" port="443" charset="UTF-8" result="verifyPhoneCall">
<cfhttpparam type="header" name="authorization" value="#Authorization#">
<cfhttpparam type="header" name="content-type" value="application/x-www-form-urlencoded">
<cfhttpparam type="header" name="date" value="#dates.signingDate#">
<cfhttpparam name="phone_number" value="#phoneNumberToCall#" type="formfield">
<cfhttpparam name="ucid" value="OTHR" type="formfield">
</cfhttp>
<cfdump var="#verifyPhoneCall#">
The headers need to be included in the signature with "new lines". And the docs also say that they need to be in the same order that the http tag is sending them in. I don't think I have the order correct... or even how I'm supposed to set an order in the cfhttp call.
Any help is appreciated. And yes the keys are real. I'll generate new ones soon.
Thanks,
Brian
Looking over their documentation on Constructing the CanonicalizedPOSTVariables Element it mentions that the POST body must match the string used when constructing the signature (emphasis mine):
... when you construct the signature string, you must use the body of
the POST request exactly as it is delivered to the service.
By default, cfhttpparam url encodes any formField values. Since you are not encoding those values when you construct the signature, the content submitted by cfhttp does not match. Hence the error.
Either disable automatic encoding for all of the form fields in the signature:
<cfhttpparam name="phone_number"
value="#phoneNumberToCall#"
type="formfield"
encoded="false">
... or use type="body" instead. Then you have complete control over the post content:
<cfhttpparam type="body"
value="phone_number=15554565555&ucid=OTHR">
Update:
Also, get rid of the final new line ie chr(10) in the "headerText". Otherwise your cfhttp content still will not match the signature. ie Use this:
headerText = arrayToList(headers, chr(10));
.. instead of:
headerText = arrayToList(headers, chr(10)) & chr(10);
I am developing REST based webservices in alfresco for data transfer & want to know what is the maximum amount of data i can send/get thru REST protocol?
Any reference would be very helpful.
Regards.
The max amount of data is as large as 2147000000. That's why if your data is large enough it is advisable to stream it to post to your REST service. Here's an example.
Sender/ Uploader Application or Client
var sb = new StringBuilder();
sb.Append("Just test data. You can send large one also");
var postData = sb.ToString();
var url = "REST Post method example http://localhost:2520/DataServices/TestPost";
var memoryStream = new MemoryStream();
var dataContractSerializer = new DataContractSerializer(typeof(string));
dataContractSerializer.WriteObject(memoryStream, postData);
var xmlData = Encoding.UTF8.GetString(memoryStream.ToArray(), 0, (int)memoryStream.Length);
var client = new WebClient();
client.UploadStringAsync(new Uri(url), "POST", "");
client.Headers["Content-Type"] = "text/xml";
client.UploadStringCompleted += (s, ea) =>
{
if (ea.Error != null) Console.WriteLine("An error has occured while processing your request");
var doc = XDocument.Parse(ea.Result);
if (doc.Root != null) Console.WriteLine(doc.Root.Value);
if (doc.Root != null && doc.Root.Value.Contains("1"))
{
string test = "test";
}
};
REST Service method
[WebInvoke(UriTemplate = "TestPost", Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Xml)]
public string Publish(string market,string sportId, Stream streamdata)
{
var reader = new StreamReader(streamdata);
var res = reader.ReadToEnd();
reader.Close();
reader.Dispose();
}
Don't forget to put the following configuration settings on your REST Service config file if you don't have this it will throw you an error
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime maxRequestLength="2147000000" maxQueryStringLength="2097151" maxUrlLength="2097151"/>
</system.web>
<system.webServer>
........
</system.webServer>
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true" maxReceivedMessageSize="2147000000" maxBufferPoolSize="2147000000" maxBufferSize="2147000000"/>
</webHttpEndpoint>
</standardEndpoints>
</system.serviceModel>
I'm trying to do server side facebook connect, but the sdk (get it here) provided gives the following error:
Invalid CFML construct found on line 523 at column 78.
ColdFusion was looking at the following text:
{
And it doesn't explain why it's throwing this error. I'm not good at cfscript, so I don't know whether the sdk uses the correct syntax, but it throws the error on this function, at the braces of the struct in the function arguments:
private String function getUrl(String path = "", Struct parameters = {})
{
var key = "";
var resultUrl = "https://www.facebook.com/" & arguments.path;
if (structCount(arguments.parameters)) {
resultUrl = resultUrl & "?" & serializeQueryString(arguments.parameters);
}
return resultUrl;
}
I had thought that using an sdk would be a no brainer, but apparently I'm missing something.
What am I doing wrong?
Part 2:
The code now comes to a halt at:
for (var propertyName in arguments.properties) {
httpService.addParam(type="formField", name=propertyName, value=arguments.properties[propertyName]);
}
Are you not allowed to use a for loop in cfscript?
Try structNew() or "#structNew()#" instead of {}
This should work for connecting to Facebook and getting an access token:
<cfset appID = ""/>
<cfset secret_key = ""/>
<cfset app_url = ""/>
<cfparam name="URL.Code" default="0">
<cfparam name="URL.state" default="0">
<cfparam name="SESSION.Redirect" default="0">
<cfset code_ = URL.Code>
<cfif code_ EQ "" OR code_ EQ 0>
<cfset SESSION.State = Hash(CreateUUID(),"MD5")>
<cfset dialog_url = "http://www.facebook.com/dialog/oauth?client_id=" & appID & "&redirect_uri=" & app_url & "&scope=email,user_photos,publish_stream" & "&state=" & SESSION.State>
<cflocation url="#dialog_url#" addtoken="no">
</cfif>
<cfif SESSION.State EQ URL.State>
<cfset token_url = "https://graph.facebook.com/oauth/access_token?client_id=" & appID & "&redirect_uri=" & app_url & "&client_secret=" & secret_key & "&code=" & code_>
<cfhttp url="#token_url#" result="AccessToken" method="GET">
<cfelse>
<p>The state does not match. You may be a victim of CSRF.</p>
</cfif>
I implemented a following code in Global.asax file of my web application.
void Application_BeginRequest()
{
string rule = ConfigurationManager.AppSettings.Get("WwwRule");
HttpContext context = HttpContext.Current;
if (context.Request.HttpMethod != "GET" || context.Request.IsLocal)
{
return;
}
if (context.Request.PhysicalPath.EndsWith(".aspx", StringComparison.OrdinalIgnoreCase))
{
string url = context.Request.Url.ToString();
if (!url.Contains("://www.") && rule == "add")
{
string url = context.Request.Url.ToString().Replace("://", "://www.");
context.Response.Redirect(url);
}
}
}
When I am running above code it works as follows
example.com redirects to www.example.com/default.aspx
www.example.com redirects to www.example.com
http://www.example.com/ redirects to http://www.example.com/
last two conditions works very well. But the first condition did'nt works well because its adding "default.aspx" in the URL which I am not intrested in.
Can anyone please tell me how to make it as below
example.com should redirects to http://www.example.com
Thanks
Most likely the Request.Url is appending default.aspx because that's the page actually being served at the time (IIS makes this transparent to you because it's one of the default pages).
When you make your new URL that you're going to redirect, add another .Replace("/default.aspx", "") to the end of it. So...
string url = context.Request.Url.ToString().Replace("://", "://www.").Replace("/default.aspx", "");
Actually, the /default.aspx is added before the request reaches the BeginRequest event. If you want to remove it, you have to actually remove it:
void Application_BeginRequest() {
string rule = ConfigurationManager.AppSettings.Get("WwwRule");
HttpContext context = HttpContext.Current;
if (context.Request.HttpMethod != "GET" || context.Request.IsLocal) {
return;
}
if (context.Request.PhysicalPath.EndsWith(".aspx", StringComparison.OrdinalIgnoreCase)) {
string url = context.Request.Url.ToString();
if (!url.Contains("://www.") && rule == "add") {
url = url.Replace("://", "://www.");
if (url.EndsWith("/default.aspx", StringComparison.OrdinalIgnoreCase) {
url = url.Substring(0, url.Length - 13);
}
context.Response.Redirect(url);
}
}
}
change your webconfig with below code:it solve my same problem.
<?xml version="1.0"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="default.aspx Redirect" stopProcessing="true">
<match url="^(.*\/)*default\.aspx$" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_METHOD}" negate="true" pattern="^POST$" />
</conditions>
<action type="Redirect" url="{R:1}" redirectType="Permanent"/>
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>