POST command missing parameters in SSL (https:) but working with http - ssl

I have an ASMX web service for login into the website.
I call the web service by using an ajax request.
this works as intended on non-SSL (HTTP). However, when running in SSL (https)
I got an error message as below.
{"Message": "Invalid web service call, missing value for parameter: 'username'.",
"StackTrace": "
at System.Web.Script.Services.WebServiceMethodData.CallMethod(Object target, IDictionary`2 parameters)
at System.Web.Script.Services.WebServiceMethodData.CallMethodFromRawParams(Object target, IDictionary`2 parameters)
at System.Web.Script.Services.RestHandler.InvokeMethod(HttpContext context, WebServiceMethodData methodData, IDictionary`2 rawParams)
at System.Web.Script.Services.RestHandler.ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)","ExceptionType": "System.InvalidOperationException"
}
Here is my ASMX web service.
using System.Web.Security;
using System.Web.Services;
namespace MyWebSite
{
[WebService(Namespace = "https://mywebsite.net")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class AuthService : System.Web.Services.WebService
{
[WebMethod]
public bool Login(string username, string password)
{
if (Membership.ValidateUser(username, password)){
FormsAuthentication.SetAuthCookie(username, true);
return true;
}
else {
return false;
}
}
}
}
And this is the ajax for call the web service.
function login(event) {
if ($('#form1').validate().form()) {
$.ajax({
type: 'POST',
url: 'Services/AuthService.asmx/Login',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: JSON.stringify({
username: $('#inputUsername').val(),
password: $('#inputPassword').val()
}),
success: function (response) {
$('#btn-login').removeAttr('disabled');
//
if (response.d) {
window.location.href = 'default.aspx';
}
},
error: function (response) {
var err = $.parseJSON(response.responseText);
console.log(err.message);
}
});
}
}
Please help me. Thanks in advance!

Related

ASP.NET Core 3.1 is getting an empty body

The code below, which is summarized for better understanding, works perfectly with LOCALHOST, however, when I put it in IIS, the body of the request always arrives EMPTY. Can someone help me?
Client application code:
login(userName: string, password: string): Observable<User> {
const headers = new HttpHeaders({
'Content-Type': 'application/json'
});
return this.http.post(`${environment.API_URL}/profiles/login`,
{ userName, password }, { headers }
).pipe(
tap((currentUser: User) => {
this.updateUser(currentUser)
.then(
() => {
console.log('currentUser login stored: ', AppSettings.currentUser);
},
error => console.error('Error storing currentUser login', error)
);
return AppSettings.currentUser;
}),
);
}
ASP.NET Core 3.1 application code on the server:
[Route("api/[controller]")]
[ApiController]
public class ProfilesController : ControllerBase
{
[HttpPost("login")]
public async Task<ActionResult> Login(LoginRequest request)
{
try
{
using (var Reader = new StreamReader(Request.Body, Encoding.UTF8))
{
var sb = new StringBuilder();
sb.AppendFormat("ContentType: {0}\n", Request.ContentType);
sb.AppendFormat("Request: {0}\n", Request.ToString());
sb.AppendFormat("ContentLength: {0}\n", Request.ContentLength.ToString());
if (Request.IsHttps)
sb.AppendFormat("{0}\n", "HTTPS!");
var headers = String.Empty;
foreach (var key in Request.Headers)
headers += key.Key + "=" + key.Value + Environment.NewLine;
sb.AppendFormat("Headers: \n{0}\n", headers);
sb.AppendFormat("QueryString: {0}\n", Request.QueryString);
var text = await Reader.ReadToEndAsync();
sb.AppendFormat("Body: {0}\n", text);
return Ok(sb.ToString());
}
return Ok("OK");
}
catch (System.Exception ex)
{
return Unauthorized($"{ex.Message}: {ex.StackTrace}");
}
}
}
Request result:
ContentType: application/json
Request: Microsoft.AspNetCore.Http.DefaultHttpRequest
ContentLength: 79
Headers:
Accept=*/*
Accept-Encoding=gzip, deflate, br
Cache-Control=no-cache
Connection=keep-alive
Content-Length=79
Content-Type=application/json
Host=loja.online
User-Agent=PostmanRuntime/7.22.0
Postman-Token=121f1927-c340-492f-a98b-0d6586ff32d8
QueryString:
Body:
Using POSTMAN the same thing happens!
Try Specifying the source:
public async Task<ActionResult> Login([FromBody] LoginRequest request) //Added [FromBody]
Just for Further Details

webflux: cross Domain + basic authorization is not working?

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");
}
};
}
}

Issue Application Id for each kind of Client Application

I'm using Microsoft Owin and ASP.NET WebApi for authentication and authorization process for my client application. Also the authentication server is secured by HTTPS. I've read a few articles about using Microsoft Owin, one of them which I've chosen to implement is:
Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
There are some differences between my project and that implementation:
I need to identify my client in case the request is sent by my application on the mobile phone, not any other devices or tools like Fiddler. I think one the options could be sending an application id by each request from the mobile application. But I don't know how and where should I verify the requests in authentication server application. This is really important for registering users:
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel userModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await _repo.RegisterUser(userModel);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
I don't want to let unreliable devices, i.e. the clients except the mobile application, to call this method.
I need to let anonymous users to buy some products from the website, but I don't know what is the best practice to issue token for anonymous users without doing authentication.
If you want to identify your client and authorize it you can override the method ValidateClientAuthentication.
In Taiseer's example you have linked you will find some code:
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
and a note which says:
As you notice this class inherits from class
“OAuthAuthorizationServerProvider”, we’ve overridden two methods
“ValidateClientAuthentication” and “GrantResourceOwnerCredentials”.
The first method is responsible for validating the “Client”, in our
case we have only one client so we’ll always return that its validated
successfully.
If you want to validate the client you have to put some logic in there.
Normally you would pass a clientId and a clientSecret in the header of your http request, so that you can validate the client's request with some database parameters, for example.
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
context.SetError("invalid_client", "Client credentials could not be retrieved through the Authorization header.");
context.Rejected();
return;
}
try
{
// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
context.Validated(clientId);
}
else
{
// Client could not be validated.
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
}
}
catch (Exception ex)
{
string errorMessage = ex.Message;
context.SetError("server_error");
context.Rejected();
}
return;
}
In the sample above you will try to extract the client credentials sent in the header of your request:
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
and validated them:
// You're going to check the client's credentials on a database.
if (clientId == "MyApp" && clientSecret == "MySecret")
{
context.Validated(clientId);
}
if the client is sending a wrong request header you need to reject the request:
context.SetError("invalid_client", "Client credentials are invalid.");
context.Rejected();
The method ValidateClientAuthentication is processed before GrantResourceOwnerCredentials. This way you can extend it and pass GrantResourceOwnerCredentials some extra information you might need there.
In one of my applications I've created a class:
class ApplicationClient
{
public string Id { get; set; }
public string Name { get; set; }
public string ClientSecretHash { get; set; }
public OAuthGrant AllowedGrant { get; set; }
public DateTimeOffset CreatedOn { get; set; }
}
which I use in ValidateClientAuthentication right after I've checked the clientId and the secret are ok:
if (clientId == "MyApp" && clientSecret == "MySecret")
{
ApplicationClient client = new ApplicationClient();
client.Id = clientId;
client.AllowedGrant = OAuthGrant.ResourceOwner;
client.ClientSecretHash = new PasswordHasher().HashPassword("MySecret");
client.Name = "My App";
client.CreatedOn = DateTimeOffset.UtcNow;
context.OwinContext.Set<ApplicationClient>("oauth:client", client);
context.Validated(clientId);
}
As you can see here
context.OwinContext.Set<ApplicationClient>("oauth:client", client);
I am setting a Owin variable which I can read later on. In your GrantResourceOwnerCredentials now you can read that variable in case you need it:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
ApplicationClient client = context.OwinContext.Get<ApplicationClient>("oauth:client");
...
}
Now, if you want to fetch the bearer token - which you're going to use for all the secure API calls - you need to encode your clientId and clientSecret (base64) and pass it in the header of the request:
An ajax request with jquery would look something like this:
var clientId = "MyApp";
var clientSecret = "MySecret";
var authorizationBasic = $.base64.btoa(clientId + ':' + clientSecret);
$.ajax({
type: 'POST',
url: '<your API token validator>',
data: { username: 'John', password: 'Smith', grant_type: 'password' },
dataType: "json",
contentType: 'application/x-www-form-urlencoded; charset=utf-8',
xhrFields: {
withCredentials: true
},
headers: {
'Authorization': 'Basic ' + authorizationBasic
},
beforeSend: function (xhr) {
},
success: function (result) {
var token = result.access_token;
},
error: function (req, status, error) {
alert(error);
}
});
As you can see I've also added the username and password - with the grant type - in the body of the request:
data: { username: 'John', password: 'Smith', grant_type: 'password' }
so that the server will be able to validate the client (clientId + clientSecret) and the user (username + password).
If the request is successful you should get back a valid token:
oAuth.Token = result.access_token;
which you can store somewhere for the following requests.
Now you can use this token for all the requests to the api:
$.ajax({
type: 'GET',
url: 'myapi/fetchCustomer/001',
data: { },
dataType: "json",
headers: {
'Authorization': 'Bearer ' + oAuth.Token
},
success: function (result) {
// your customer is in the result.
},
error: function (req, status, error) {
alert(error);
}
});
Another thing you might want to add to your API during the startup is SuppressDefaultHostAuthentication:
config.SuppressDefaultHostAuthentication();
this is an extension method of HttpConfiguration. Since you're using bearer tokens you want to suppress the standard cookie-based authentication mechanism.
Taiseer has written another series of articles which are worth reading where he explains all these things.
I have created a github repo where you can see how it works.
The Web API is self-hosted and there are two clients: jQuery and Console Application.

Sending data to Restful WCF service using ExtJS

This is how my WCF service is defined to authenticate a user:
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, ResponseFormat = WebMessageFormat.Json)]
string validateUser(string username, string password);
It simply returns "Validated" or "NotValidated" as result.
And my ExtJS code is:
function loginclick(btn) {
var form = mainPanel.getForm();
if (form.isValid()) {
var userget = Ext.getCmp('txtuser').value;
var passget = Ext.getCmp('txtpass').value;
var myparams = { 'username': userget, 'password': passget };
Ext.Ajax.request({
url: 'http://localhost:52984/ExtJsRestfulService.svc/validateUser',
params: Ext.encode(myparams),
method: 'POST',
headers: this.header || { 'Content-Type': 'application/json;charset=utf-8' },
success: function (response, options) {
var s = response.responseText;
Ext.MessageBox.alert('Success', s);
},
failure: function (response, options) {
Ext.MessageBox.alert('Failed', 'Unable to get');
}
});
}
}
When i click on the login button, I get bad request(400) error. My service is not in the same solution. When I post the data to an aspx form in the same solution it works. What am I doing wrong?
What about:
Ext.Ajax.request({
url: 'http://yourdomain:52984/ExtJsRestfulService.svc/validateUser?username='+userget+'&password='+passget,
params: Ext.encode(myparams),
method: 'GET',
headers: this.header || { 'Content-Type': 'application/json;charset=utf-8' },
success: function (response, options) {
var s = response.responseText;
Ext.MessageBox.alert('Success', s);
},
failure: function (response, options) {
Ext.MessageBox.alert('Failed', 'Unable to get');
}
});
Can you fetch the params from the url?
Have you tried without encoding?
try
params: {
'username': userget,
'password': passget
}
Why do you have two times the params config?
You shouldn't use Ext.getCmp instead use Ext.ComponentQuery.query('myXtype[itemId=myItemId]')[0]; http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.ComponentQuery
or
define the service in a manner that the call to it would be fullfilled like
string validateUser();
then inside the service do something similar to:
String pass = request.getParameter("password");
Like in java

401 error when using [Authenticate] with BasicAuthProvider

I'm having some trouble with authenticating with ServiceStack using the BasicAuthProvider. All works well when I authenticate using the provider route 'auth/myauth' but when I go to one of my other service DTOS that use the [Authenticate] attribute e.g. /hello, I always get a 401 Unauthorized error even when I always supply the basic authentication details in the 'Authorization' header using beforeSend with jQuery.
Basically, I'm building an API for a mobile app that involves credential authentication on the first time(or if a supplied token isn't expired), then subsequently basic authentication of supplied token for other requests. I'm trying to authenticate every request, as described here. Also here. Here's my code:
Custom Provider
public class MyAuthProvider : BasicAuthProvider
{
public new static string Name = "MyAuth";
public new static string Realm = "/auth/myauth";
public MyAuthProvider()
{
this.Provider = Name;
this.AuthRealm = Realm;
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
var httpReq = authService.RequestContext.Get<IHttpRequest>();
var basicAuth = httpReq.GetBasicAuthUserAndPassword();
if (basicAuth == null)
throw HttpError.Unauthorized("Invalid BasicAuth credentials");
var us = basicAuth.Value.Key;
var ps = basicAuth.Value.Value;
if (ps == "password")
{
return true;
}
return false;
}
}
Service
public class HelloService : Service
{
//handle OPTIONS in preflight - http://joeriks.com/2013/01/12/cors-basicauth-on-servicestack-with-custom-authentication/
public object Options(Hello request) { return true; }
[Authenticate("MyAuth")]
public object Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
[Authenticate("MyAuth")]
public object Get(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Configure Method
public override void Configure(Container container)
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
new MyAuthProvider()
}));
//register any dependencies your services use, e.g:
container.Register<ICacheClient>(new MemoryCacheClient() { FlushOnDispose = false });
//set endpoint information
SetConfig(new EndpointHostConfig
{
GlobalResponseHeaders =
{
{"Access-Control-Allow-Origin","http://localhost"},
{"Access-Control-Allow-Methods","GET, POST, PUT, DELETE, OPTIONS"},
{"Access-Control-Allow-Headers", "Content-Type, Authorization, Accept, Origin" }
},
});
}
This works
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return "Basic " + hash;
}
////
$.ajax({
url: 'http://localhost:61750/auth/myauth?format=json',
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", make_base_auth("id#email.com","password"));
}
}).done(function (data) {
if( console && console.log ) {
console.log("Sample of data:", data);
}
});
But this doesn't
$.ajax({
url: 'http://localhost:61750/hello?format=json',
data: { Name:"Foo" },
type: 'POST',
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", make_base_auth("id#email","password"));
}
}).done(function (data) {
if( console && console.log ) {
console.log("Sample of data:", data);
}
});
Thanks for your help.
I had to create a custom authenticate attribute with guidance from this gist -> https://gist.github.com/joeriks/4518393
In the AuthenticateIfBasicAuth method, I set provider to use MyAuthProvider.Name
Then,
[CustomAuthenticate]
public object Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}