I was trying to implement oauth authentication in my WCF service. I am doing the service call from jQuery ajax. I have tried the following code in CORS enabled service with POST verb. But here I am getting pa["oauth_consumer_key"] as always null. Please see the code and help me to find out the issue.
Using POST and CORS
jQuery ajax call:-
function logClick() {
var sEmail = $('#username').val();
var sPassword = $('#password').val();
var key = "test";
var oauth_signature = "xxxxxxx";
var timestamp = (new Date()).getTime();
var nonce = Math.random();
var auth_header = 'OAuth oauth_nonce="' + nonce + '"' +
', oauth_signature_method="HMAC-SHA1"' +
', oauth_timestamp="' + timestamp + '"' +
', oauth_consumer_key="' + key + '"' +
', oauth_signature="' + oauth_signature + '"' +
', oauth_version="1.0"';
var userData = '{"email":"' + sEmail + '","password":"' + sPassword + '"}';
$.support.cors = true;
$.ajax({
data: userData,
type: "POST",
dataType: "json",
contentType: "application/json;charset=utf-8",
url: "http://mydomain/MyAppService.svc/UserValidation",
beforeSend : function(xhr, settings) {
$.extend(settings, { headers : { "Authorization": auth_header } });
},
success: function (msg) {
alert("success");
},
error: function () {
alert("Network error");
}
});
}
WCF service code
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped, Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "UserValidation")]
int UserValidation(string email,string password);
public int UserValidation(string email, string password)
{
if (Authenticate(WebOperationContext.Current.IncomingRequest))
{
//my code
return 1;
}
else
{
return 0;
}
}
private static bool Authenticate(IncomingWebRequestContext context)
{
bool Authenticated = false;
string normalizedUrl;
string normalizedRequestParameters;
NameValueCollection pa = context.Headers;
//NameValueCollection pa = context.UriTemplateMatch.QueryParameters;// tried this also
if (pa != null && pa["oauth_consumer_key"] != null) // pa["oauth_consumer_key"] is always null
{
// to get uri without oauth parameters
string uri = context.UriTemplateMatch.RequestUri.OriginalString.Replace
(context.UriTemplateMatch.RequestUri.Query, "");
string consumersecret = "suryabhai";
OAuthBase oauth = new OAuthBase();
string hash = oauth.GenerateSignature(
new Uri(uri),
pa["oauth_consumer_key"],
consumersecret,
null, // totken
null, //token secret
"GET",
pa["oauth_timestamp"],
pa["oauth_nonce"],
out normalizedUrl,
out normalizedRequestParameters
);
Authenticated = pa["oauth_signature"] == hash;
}
return Authenticated;
}
I did the same aouth authentication in GET and JSONP . Following is the code. Here the authentication is working, but I am not getting the result even though the service return data. ( entering to error block in jQuery ajax call)
GET and JSONP
jQuery ajax call:-
function getData() {
$.ajax({
url: "http://mydomain/MyAppService.svc/GetData/328?oauth_consumer_key=test&oauth_nonce=10a33ed37b549301644b23b93fc1f1c5&oauth_signature=AMTsweMaWeN7kGnSwoAW44WKUuM=&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1289976718&oauth_version=1.0?callback=?",
type: "GET",
crossDomain: true,
contentType: "application/json; charset=utf-8",
dataType: "jsonp",
processdata: true,
success: function (msg) {
alert("success");
},
error: function error(response) {
alert(" Network Error"); // always entering to this block
}
});
WCF service :-
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "GetData/{ParentID}")]
List<Parent> GetData(string ParentID);
public List<Parent> GetData(string ParentID)
{
List<Parent> ParentList = new List<Parent>();
if (Authenticate(WebOperationContext.Current.IncomingRequest)) // it is working
{
//my code
return ParentList ; // result is getting, but on client it is going to error block of jQUery ajax call
}
else
{
return ParentList ;
}
}
private static bool Authenticate(IncomingWebRequestContext context)
{
bool Authenticated = false;
string normalizedUrl;
string normalizedRequestParameters;
NameValueCollection pa = context.UriTemplateMatch.QueryParameters;
if (pa != null && pa["oauth_consumer_key"] != null)
{
// to get uri without oauth parameters
string uri = context.UriTemplateMatch.RequestUri.OriginalString.Replace
(context.UriTemplateMatch.RequestUri.Query, "");
string consumersecret = "suryabhai";
OAuthBase oauth = new OAuthBase();
string hash = oauth.GenerateSignature(
new Uri(uri),
pa["oauth_consumer_key"],
consumersecret,
null, // totken
null, //token secret
"GET",
pa["oauth_timestamp"],
pa["oauth_nonce"],
out normalizedUrl,
out normalizedRequestParameters
);
Authenticated = pa["oauth_signature"] == hash;
}
return Authenticated;
}
Web.config:-
<?xml version="1.0"?>
<configuration>
<system.web>
<authentication mode="None" />
<httpRuntime maxRequestLength="2147483647"/>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/>
<services>
<service name="DataAppAppService.MyAppService">
<endpoint address="" behaviorConfiguration="webHttpBehavior" binding="webHttpBinding" bindingConfiguration="WebHttpBindingWithJsonP" contract=DataAppAppService.IMyAppService" />
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="WebHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true" maxReceivedMessageSize="2147483647"
maxBufferSize="2147483647" transferMode="Streamed"
>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="30" maxConcurrentInstances="30" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
<directoryBrowse enabled="true" />
</system.webServer>
</configuration>
I was able to solve the "Using POST and CORS" issue. I have added the Authorization header into "Access-Control-Allow-Headers" and it solved the issue.
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept");
Is there any way to generate oauth_signature from javascript. Now I am hard coding the value, but as the timestamp and oauth_nonce changing each time, I am getting different signature. SO I need to pass the correct signature through the ajax request rather than giving hard coding value. Please give a suggestion.
But still I have issue with Get and JSONP and oAuth. Any thoughts?
Thanks.
"But still I have issue with Get and JSONP and oAuth. Any thoughts?" --> I could solve this issue using GET method with CORS. Here is my code.
$.support.cors = true;
$.ajax({
type: "GET",
dataType: "json",
contentType: "application/json;charset=utf-8",
url: "http://mydomain:89/MyAppAppService.svc/GetFolders",
beforeSend: function (xhr) {
var username = "test";
var password = "testpwd";
xhr.setRequestHeader("Authorization", "Basic " + $.base64('encode', username + ':' + password));
},
success: function (msg) {
alert("Success");
},
error: function (jqXHR, status, message) {
alert(jqXHR.responseText);
alert(status + " " + message);
}
});
Thanks.
Related
I have a local ASP.Net FrameWork WebAPI server with the following controller:
public class ValuesController : ApiController
{
// GET api/values
[AuthorizationFilter]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
}
I created an AuthorizationFilter attribute to handle authorization (only for GET with no id action):
public class AuthorizationFilter : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext ctx)
{
if(ctx.Request.Headers.Authorization == null)
{
ctx.Response = ctx.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized);
} else
{
string authenticationString = ctx.Request.Headers.Authorization.Parameter;
string decodedAuthString = Encoding.UTF8.GetString(Convert.FromBase64String(authenticationString));
string username = decodedAuthString.Split(':')[0];
string password = decodedAuthString.Split(':')[1];
// assume that I have checked credentials from DB
if (username=="admin" && password=="root")
{
// authorized...
} else
{
ctx.Response = actionContext.Request.CreateResponse(System.Net.HttpStatusCode.Unauthorized);
}
}
}
}
Also, I modified Web.config to allow CORS:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
...
</system.webServer>
I ran the server and tried to get /api/values/1 from the browser, and it worked.
I then tried to access the action that requires authorization: /api/values :
I used Insomnia to send requests and test CORS. First I tried the action that doesn't require authorization:
Then I tried the action with authorization:
Then I tried the same action but after adding the authentication username and password, and that worked out fine:
After this point, I knew my webapi is configured correctly, so I tried to pull data from a React app using axios:
const api = axios.create({
baseURL: "http://localhost:50511/api",
});
const response = await api.get("/values/1");
console.log(response.data); // works fine: I get "value" as expected
And now, the final step, to configure axios to request the action that requires authentication:
const api2 = axios.create({
baseURL: "http://localhost:50511/api",
auth : {
username: "admin",
password: "root"
}
});
const response = await api2.get("/values"); // causes a network exception
The reported error is strange, since it talks about CORS. I don't get it. If there shall be an error, it can imagine it being an error related to authorization. Not CORS. Not after being able to use axios to pull data from the action that has no authentication filter.
I examined the request header to make sure that it was configured with the correct Authorization parameter:
I also tried to use axios in different ways:
const response1 = await axios.get("http://localhost:50511/api/values",{
auth: {
username: "admin",
password: "root"
}
});
const response2 = await axios.get("http://localhost:50511/api/values",{
headers: {
Authorization: "Basic " + btoa("admin:root"),
}
});
Both of these attempts did not work.
Then I tried again, but this time passing an empty object as the second parameter to the axios call:
const response3 = await axios.get("http://localhost:50511/api/values", {}, {
auth: {
username: "admin",
password: "root"
}
});
const response4 = await axios.get("http://localhost:50511/api/values", {}, {
headers: {
Authorization: "Basic " + btoa("admin:root"),
}
});
Again, none of these attempts worked. What am I don't wrong?
i used Spring 2.0.1, here is my SecurityWebFilterChain
#Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http
// Demonstrate that method security works
// Best practice to use both for defense in depth
.authorizeExchange()
.anyExchange().permitAll()
.and()
.httpBasic().and()
.build();
Here is Cros config
#Configuration
#EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
final String ALLOWED_HEADERS = "x-requested-with, authorization,
Content-Type, Authorization, credential, X-XSRF-TOKEN";
final String ALLOWED_METHODS = "GET, PUT, POST, DELETE, OPTIONS";
final String ALLOWED_ORIGIN = "http://192.168.18.124:8888";
final long MAX_AGE = 3600;
registry.addMapping("/report/**")
.allowedOrigins(ALLOWED_ORIGIN)
.allowedMethods("PUT", "GET")
.allowedHeaders("x-requested-with", "authorization",
"Content-Type", "Authorization", "credential", "X-XSRF-TOKEN")
.allowCredentials(true).maxAge(3600);
}
}
My ajax code
var data = {};
$.ajax({
type: 'GET',
async: false,
url: 'http://192.168.18.135:8765/report/summaries/date/2017-06-12',
dataType: 'json',
data: data,
crossDomain: true,
crossOrigin: true,
beforeSend: function (xhr) {
xhr.withCredentials = true;
xhr.setRequestHeader('Authorization', 'Basic ' + "xxxxx");
},
success: function (responseData) {
console.log('-----------------response-------------------');
console.log(responseData);
console.log('-----------------response-------------------');
response = responseData;
},
error: function (responseData) {
response.error = responseData;
}
});
return response;
});
the error responded from server:
http://192.168.18.135:8765/report/summaries/date/2017-06-12. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.168.18.124:8888' is therefore not allowed access. The response had HTTP status code 500.
if i remove
xhr.setRequestHeader('Authorization', 'Basic ' + "xxxxx");
it will return 401 authorization.
Is is possible cros domain + basic authorization?
This is my CORS configuration. Create a new Class WebConfig and declare a Bean like this:
#Configuration
public class WebConfig {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
};
}
}
I used ajax method to call the controller and fetch the data and convert it to json or list and set a jquery DataTable. With 1000 records it's working fine, but when I fetch more than 5000 records, ajax method gives me:
500 Internal server error
Here is my code:
$('#btnAllData').click(function () {
$.ajax({
url: 'PartMaster/GridLoad',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (Result) {
debugger;
var pageload = Result.split('|');
var status = (pageload[0])
if (status == "ERROR") {
Error(pageload[1]);
}
else {
var Partdetails = (pageload[0]);
//var LocDetails = JSON.parse(pageload[2]);
}
//gridDetails(status1);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
});
check how much time your database is taking to returning data.
Set length of web response ( You can adjust the JSON response size in the web.config with ).
<configuration>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="1000000" />
</webServices>
</scripting>
</system.web.extensions>
</configuration>
i create a wcf service and hosted it on windows azure. The wcf service is a https one. When ever i call the service the client needs a certificate to verify its authenticity.
When i type the service url on broswer it asks for a verifying certificate and the serivce runs.
So far so good.
Now i need to access the same service in an MVC 4 application. So i made a simple ajax call.
<script>
$(document).ready(function () {
$("#GetAdjustedSalary").click(function () {
var salary = parseFloat($("#salary").val());
var infalation = parseFloat($("#inflation").val());
$.ajax({
url: "https://newtonsheikh.cloudapp.net/SalaryService.svc/adjustedsalary?a=" + salary + "&b=" + infalation,
type: "GET",
dataType: "JSON",
contentType: "application/json",
success: function (data) {
alert(data);
}
});
});
});
</script>
But i dont get the result. Instead i always get abort error 403.
Do i need to write something on the web.config file in the MVC application? I am stuck and really need help out here.
Thanks
Got a solution:
In the ajax call i made a call to the controller
<script>
$(document).ready(function () {
$("#GetAdjustedSalary").click(function () {
var salary = parseFloat($("#salary").val());
var infalation = parseFloat($("#inflation").val());
var object = {
salary: salary,
infalation: infalation
}
var data = JSON.stringify(object);
$.ajax({
url: "Home/GetData/",
type: "POST",
data: data,
dataType: "JSON",
contentType: "application/json",
success: function (data) {
$("#answer").html(data);
}
});
});
});
Then in the controller:
[HttpPost]
public ActionResult GetData(string salary, string infalation)
{
string output = "";
try
{
X509Certificate Cert = X509Certificate.CreateFromCertFile("d://Cert//newton2.cer");
ServicePointManager.CertificatePolicy = new CertPolicy();
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://newtonsheikh.cloudapp.net/SalaryService.svc/adjustedsalary?a="+salary+" &b="+infalation+"");
Request.ClientCertificates.Add(Cert);
Request.UserAgent = "Client Cert Sample";
Request.Method = "GET";
HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
Console.WriteLine("{0}" + Response.Headers);
Console.WriteLine();
StreamReader sr = new StreamReader(Response.GetResponseStream(), Encoding.Default);
int count;
char[] ReadBuf = new char[1024];
do
{
count = sr.Read(ReadBuf, 0, 1024);
if (0 != count)
{
output += new string(ReadBuf);
}
} while (count > 0);
}
catch (Exception ex)
{
//Throw the exception...lol :P
}
output = output.Replace("\0", "");
string jsonString = JsonConvert.SerializeObject(output, Newtonsoft.Json.Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
return Json(jsonString, JsonRequestBehavior.AllowGet);
}
The CertPolicy Class:
class CertPolicy : ICertificatePolicy
{
public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem)
{
// You can do your own certificate checking.
// You can obtain the error values from WinError.h.
// Return true so that any certificate will work with this sample.
return true;
}
}
I have a wcf service that it's working well if i am using get but
when i use post it's showing
System.ServiceModel.ServiceActivationException status 500
any ideas whats the error??
here is the code
function Login(e) {
debugger;
e.preventDefault();
//getting mail
var loginmail =
$("[id$='txtLoginEmail']").val();
//getting password
var loginpassword =
$("[id$='txtLoginPassword']").val();
//getting remember me checkbox
var checked =
($("[id$='chkRememberme']").is(':checked'));
var proxy = new _AppProxy('MemberService/Login', '', 'xml', 'POST', '', { mail: loginmail, password:
loginpassword, rememberMe: checked });
proxy.Invoke();
};
[OperationContract]
[WebInvoke(ResponseFormat =
WebMessageFormat.Xml,Method="POST")]
public string Login(string mail , string password ,
bool rememberMe)
{
}
<system.serviceModel>
<standardEndpoints>
<webHttpEndpoint>
<standardEndpoint
name=""
helpEnabled="true"
automaticFormatSelectionEnabled="true" />
</webHttpEndpoint>
</standardEndpoints>
serviceHostingEnvironment
aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" >
</serviceHostingEnvironment>
</system.serviceModel>
Can be that you miss the .svc ending in your URI to the WebService. If MemberService is the name of the service and Login is the name of the Method,
MemberService.svc/Login