HTTP post to ASP MVC 3 from a client app - rest

I have an ASP MVC 3 site that allows a user to submit some helpdesk information. I also have a client app (WPF) that users should be able to submit helpdesk information from. I would like to just do a HTTP POST to the same form that the user would use via the web site.
Is this possible? I've tried using RestSharp but I keep getting a 401.2 from my site.
Thoughts?
My MVC controller action is something like:
//
// POST: helpdesk/submit
[HttpPost]
public ActionResult Submit(HelpDeskRequest helpDeskRequest)
{
}

You could use a WebClient to POST values to your controller action:
using (var client = new WebClient())
{
var values = new NameValueCollection
{
{ "Id", "123" },
{ "Name", "abc" },
};
var result = client.UploadValues(
"http://www.domain.com/helpdesk/submit",
values
);
}
and inside the action you will get:
[HttpPost]
public ActionResult Submit(HelpDeskRequest helpDeskRequest)
{
// helpDeskRequest.Id = 123;
// helpDeskRequest.Name = "abc";
...
}

Related

.NETCORE Middleware Read the Body value from the POST entered by a user in Postman AND Validate a particular value (To:) before sent

I am using a .NET CORE API application with Middleware but I need the Post to read the individual values from the Body in Postman AND have the Middleware Validate a particular value in the Body, for example, the (To:) of an email; and, if it meets whatever criteria I would like to (POST) send the email on to the intended recipient (To:) without a database.
This is what my code looks like so far
using (StreamReader streamReader = new StreamReader(httpContext.Request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: bufferSize, leaveOpen: true))
{
await streamReader.ReadToEndAsync();
//Do something
////From test
if (evaluate.IsSenderFromConfiguration() == true)
{
message.From = From;
}
message.To = new List<MailboxAddress> { new MailboxAddress("mail#mail.com")};
message.Subject = "Message From Create Middleware";
message.HtmlBody = "This is just a test of Middleware ";
await SendEmailAsync(message);
httpContext.Request.Body.Position = 0;
}
await httpContext.Response.WriteAsync(body);
await _next.Invoke(httpContext);
}
Here is the Post from the Controller
[HttpPost]
public IActionResult Post([FromBody] Email email)
{
var message = new Email(_message.From, _message.To, _message.Subject, _message.HtmlBody);
if (message == null)
{
return NotFound();
}
return Ok(message);
}
Right now I just have what I did in the Get to send the email. How can I have it to where the properties message.To, message.From, etc. can be set by the User/Me in Postman and if the value in message.To is valid send the email?
I think there is a way to use the httpContext.Request.Body somehow, but I don't know how in .NET Core to set each individual value separately outside of the Middleware class.
To clarify: My Post Controller DOES NOT WORK FOR FORM VALIDATION The Middleware should be doing the work and then passing it to the controller once validated. That is where I need the assistance for the Middleware class.
use FluentValidation
also use in middleware like below:
services.AddMvc(setup => {
//...mvc setup...
}).AddFluentValidation();

.net core 2.0 external login - extra profile information

I have a .net core 2.0 app and am implementing external login providers like google, twitter, and facebook. I have the requirement to get the user's display name and profile picture, and can't find any documentaion of how to achieve this in .net core 2.0.
I add the authentication like this post: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/
Here are my twitter login and callback functions...
[HttpGet]
[Route("/api/security/login/type/socialmedia/twitter")]
public IActionResult GetTwitterLogin(string redirect_uri)
{
ClientCallback = redirect_uri;
string redirectUrl = "/api/security/login/type/socialmedia/twittercallback";
var properties = SignInManager.ConfigureExternalAuthenticationProperties("Twitter", redirectUrl);
return Challenge(properties, "Twitter");
}
[HttpGet]
[Route("/api/security/login/type/socialmedia/twittercallback")]
public async Task<HttpResponseMessage> GetTwitterCallBackAsync()
{
var info = await SignInManager.GetExternalLoginInfoAsync();
var result = await SignInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
}
else
{
}
Response.StatusCode = (int)HttpStatusCode.OK;
return null;
}
It looks like you can get some items from info.Principal.Claims, but nothing for the user's display name or profile picture.
How do you get the display name or profile picture for the various login providers?
I finally figured this out...you need to add claims when you configure the authentication. These claims look at the resulting json response and pulls items from it. The pertinent lines are the ClaimActions items.
services.AddAuthentication()
.AddTwitter(twitterOptions =>
{
twitterOptions.ConsumerKey = cfg.SystemConfig["TwitterConsumerKey"];
twitterOptions.ConsumerSecret = cfg.SystemConfig["TwitterConsumerSecret"];
twitterOptions.SaveTokens = true;
twitterOptions.RetrieveUserDetails = true;
twitterOptions.ClaimActions.MapJsonKey("display-name", "name");
twitterOptions.ClaimActions.MapJsonKey("profile-image-url", "profile_image_url_https");
})
.AddFacebook(facebookOptions =>
{
facebookOptions.AppId = cfg.SystemConfig["FacebookClientId"];
facebookOptions.AppSecret = cfg.SystemConfig["FacebookClientSecret"];
facebookOptions.SaveTokens = true;
facebookOptions.ClaimActions.MapJsonKey("display-name", "name");
})
.AddGoogle(googleOptions =>
{
googleOptions.ClientId = cfg.SystemConfig["GoogleClientId"];
googleOptions.ClientSecret = cfg.SystemConfig["GoogleClientSecret"];
googleOptions.SaveTokens = true;
googleOptions.ClaimActions.MapJsonSubKey("profile-image-url", "image", "url");
googleOptions.ClaimActions.MapJsonKey("display-name", "displayName" );
});
After getting the login information in your callback using
var info = await SignInManager.GetExternalLoginInfoAsync();
If populated successfully you can query the claims and find the values
var profileImageClaim = info.Principal.Claims.Where(x => x.Type == "profile-image-url").FirstOrDefault();
Facebook images are different from google and twitter and can be found using...
var claim = info.Principal.Claims.Where(x => x.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").FirstOrDefault();
var url = "http://graph.facebook.com/" + claim.Value + "/picture";
In ASP.NET Core 2.0, FacebookOptions uses extension methods on ClaimActions to map the profile data returned by UserInformationEndpoint.
ClaimActions.MapJsonKey(ClaimTypes.DateOfBirth, "birthday");
In the mapping above, "birthday" is a top-level property in the Facebook Graph API response that's mapped to the value represented by the claim ClaimTypes.DateOfBirth.
To grab the profile picture you would do the same thing, but since the picture in the Graph API response is a nested JSON object, you would have to use MapCustomJson()
services.AddAuthentication()
.AddFacebook(options =>
{
// ...other options omitted
options.Fields.Add("picture");
options.ClaimActions.MapCustomJson("urn:facebook:picture",
claim => (string)claim.SelectToken("picture.data.url"));
})
Here, claim is a NewtonSoft JObject that uses JPath syntax to select the nested property value and cast it to a string.
The profile picture URL will now appear in your Claims list.

Implement Custom Authentication In Windows Azure Mobile Services

Windows Azure Mobile Services currently doesn't have an option for custom authentication and looking at the feature request
http://feedback.azure.com/forums/216254-mobile-services/suggestions/3313778-custom-user-auth
It isn't coming anytime soon.
With a .NET backend and a .NET application how do you implement custom authentication, so that you don't have to use Facebook, Google or any of their other current providers?
There are plenty of partially completed tutorials on how this this is done with a JS backend and iOS and Android but where are the .NET examples?
I finally worked through the solution, with some help of the articles listed below, some intellisense and some trial and error.
How WAMS Works
First I wanted to describe what WAMS is in a very simple form as this part confused me for a while until it finally clicked. WAMS is just a collection of pre-existing technologies packaged up for rapid deployment. What you need to know for this scenario is:
As you can see WAMS is really just a container for a WebAPI and other things, which I won't go into detail here. When you create a new Mobile Service in Azure you get to download a project that contains the WebAPI. The example they use is the TodoItem, so you will see code for this scenario through the project.
Below is where you download this example from (I was just doing a Windows Phone 8 app)
I could go on further about this but this tutorial will get you started:
http://azure.microsoft.com/en-us/documentation/articles/mobile-services-dotnet-backend-windows-store-dotnet-get-started/
Setup WAMS Project
You will need your MasterKey and ApplicationKey. You can get them from the Azure Portal, clicking on your Mobile Services App and pressing Manage Keys at the bottom
The project you just downloaded, in the Controllers folder I just created a new controller called AccountController.cs and inside I put
public HttpResponseMessage GetLogin(String username, String password)
{
String masterKey = "[enter your master key here]";
bool isValidated = true;
if (isValidated)
return new HttpResponseMessage() { StatusCode = HttpStatusCode.OK, Content = new StringContent("{ 'UserId' : 'F907F58C-09FE-4F25-A26B-3248CD30F835', 'token' : '" + GetSecurityToken(new TimeSpan(1,0, 0), String.Empty, "F907F58C-09FE-4F25-A26B-3248CD30F835", masterKey) + "' }") };
else
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Username and password are incorrect");
}
private static string GetSecurityToken(TimeSpan periodBeforeExpires, string aud, string userId, string masterKey)
{
var now = DateTime.UtcNow;
var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var payload = new
{
exp = (int)now.Add(periodBeforeExpires).Subtract(utc0).TotalSeconds,
iss = "urn:microsoft:windows-azure:zumo",
ver = 2,
aud = "urn:microsoft:windows-azure:zumo",
uid = userId
};
var keyBytes = Encoding.UTF8.GetBytes(masterKey + "JWTSig");
var segments = new List<string>();
//kid changed to a string
var header = new { alg = "HS256", typ = "JWT", kid = "0" };
byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));
segments.Add(Base64UrlEncode(headerBytes));
segments.Add(Base64UrlEncode(payloadBytes));
var stringToSign = string.Join(".", segments.ToArray());
var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);
SHA256Managed hash = new SHA256Managed();
byte[] signingBytes = hash.ComputeHash(keyBytes);
var sha = new HMACSHA256(signingBytes);
byte[] signature = sha.ComputeHash(bytesToSign);
segments.Add(Base64UrlEncode(signature));
return string.Join(".", segments.ToArray());
}
// from JWT spec
private static string Base64UrlEncode(byte[] input)
{
var output = Convert.ToBase64String(input);
output = output.Split('=')[0]; // Remove any trailing '='s
output = output.Replace('+', '-'); // 62nd char of encoding
output = output.Replace('/', '_'); // 63rd char of encoding
return output;
}
You can replace what is in GetLogin, with your own validation code. Once validated, it will return a security token (JWT) that is needed.
If you are testing on you localhost, remember to go into your web.config file and fill in the following keys
<add key="MS_MasterKey" value="Overridden by portal settings" />
<add key="MS_ApplicationKey" value="Overridden by portal settings" />
You need to enter in your Master and Application Keys here. They will be overridden when you upload them but they need to be entered if you are running everything locally.
At the top of the TodoItemController add the AuthorizeLevel attribute as shown below
[AuthorizeLevel(AuthorizationLevel.User)]
public class TodoItemController : TableController<TodoItem>
You will need to modify most of the functions in your TodoItemController but here is an example of the Get All function.
public IQueryable<TodoItem> GetAllTodoItems()
{
var currentUser = User as ServiceUser;
Guid id = new Guid(currentUser.Id);
return Query().Where(todo => todo.UserId == id);
}
Just a side note I am using UserId as Guid (uniqueidentifier) and you need to add this to the todo model definition. You can make the UserId as any type you want, e.g. Int32
Windows Phone/Store App
Please note that this is just an example and you should clean the code up in your main application once you have it working.
On your Client App
Install NuGet Package: Windows Azure Mobile Services
Go into App.xaml.cs and add this to the top
public static MobileServiceClient MobileService = new MobileServiceClient(
"http://localhost:50527/",
"[enter application key here]"
);
In the MainPage.xaml.cs I created
public class Token
{
public Guid UserId { get; set; }
public String token { get; set; }
}
In the main class add an Authenticate function
private bool Authenticate(String username, String password)
{
HttpClient client = new HttpClient();
// Enter your own localhost settings here
client.BaseAddress = new Uri("http://localhost:50527/");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync(String.Format("api/Account/Login?username={0}&password={1}", username, password)).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var token = Newtonsoft.Json.JsonConvert.DeserializeObject<Token>(response.Content.ReadAsStringAsync().Result);
App.MobileService.CurrentUser = new MobileServiceUser(token.UserId.ToString());
App.MobileService.CurrentUser.MobileServiceAuthenticationToken = token.token;
return true;
}
else
{
//Something has gone wrong, handle it here
return false;
}
}
Then in the Main_Loaded function
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
Authenticate("test", "test");
RefreshTodoItems();
}
If you have break points in the WebAPI, you will see it come in, get the token, then come back to the ToDoItemController and the currentUser will be filled with the UserId and token.
You will need to create your own login page as with this method you can't use the automatically created one with the other identity providers. However I much prefer creating my own login screen anyway.
Any other questions let me know in the comments and I will help if I can.
Security Note
Remember to use SSL.
References
[] http://www.thejoyofcode.com/Exploring_custom_identity_in_Mobile_Services_Day_12_.aspx
[] http://www.contentmaster.com/azure/creating-a-jwt-token-to-access-windows-azure-mobile-services/
[] http://chrisrisner.com/Custom-Authentication-with-Azure-Mobile-Services-and-LensRocket
This is exactly how you do it. This man needs 10 stars and a 5 crates of beer!
One thing, I used the mobile Service LoginResult for login like:
var token = Newtonsoft.Json.JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
Hope to get this into Android now!

Facebook c# sdk get users email

I have a site which is using facebook for auth. I want to gather some basic info when a user signs up including their email address.
The code i have for the login is standard:
public ActionResult Login(string returnUrl)
{
var oAuthClient = new FacebookOAuthClient();
oAuthClient.AppId = AppSettings.GetConfigurationString("appId");
oAuthClient.RedirectUri = new Uri(AppSettings.GetConfigurationString("redirectUrl"));
var loginUri = oAuthClient.GetLoginUrl(new Dictionary<string, object> { { "state", returnUrl } });
return Redirect(loginUri.AbsoluteUri);
}
How do i add the request to access permissions in that? Or do i do it another way?
You need to use the email permission (the full list is here: http://developers.facebook.com/docs/authentication/permissions/ )
The way to add permissions to the authorization is by appending a comma separated list to &scope= , e.g.:
https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&scope=email,read_stream
Update: As you marked, the parameters are passed to the GetLoginUrl() method, although in the codeplex forum they also used ExchangeCodeForAccessToken(), which you might want to take a look at also.
A couple of examples using the C# SDK:
http://blog.prabir.me/post/Facebook-CSharp-SDK-Writing-your-first-Facebook-Application.aspx
Facebook .NET SDK: How to authenticate with ASP.NET MVC 2
http://facebooksdk.codeplex.com/discussions/244568
A snoop at the sdk code and i came up wiht:
public ActionResult Login(string returnUrl)
{
var oAuthClient = new FacebookOAuthClient();
oAuthClient.AppId = AppSettings.GetConfigurationString("appId");
oAuthClient.RedirectUri = new Uri(AppSettings.GetConfigurationString("redirectUrl"));
var parameters = new Dictionary<string, object>();
parameters["state"] = returnUrl;
parameters["scope"] = "email";
var loginUri = oAuthClient.GetLoginUrl(parameters);
return Redirect(loginUri.AbsoluteUri);
}
not tested it yet and the missus is shouting at me for working late so will have to test tomoz :)

How to get the complete request that calls my MVC2 controller?

Newbie question … sorry ;-)
I have to write and to integrate a new website in a complex web application.
My new (MVC2) website will be hosted on a separate server and only called when the user clicks on a link in the already existing, complex website.
Means I(!) define the URL which calls my(!) new website.
But “they” (the calling, already existing, complex web application/website) will add an attribute to the url. This attribute is the sessionID.
Ok, I think I understand already that this calls my (MVC2) controller.
But how can I get in my (MVC2) controller the “calling URL” (which include the added sessionID)?
Hopefully that someone understand what I ask ;-)
Thanks in advance!
I want just share my little parser - hopefully it helps someone. ;-)
Also requests like
(Request.Url.Query =) "?sessionID=12345678901234567890123456789012&argumentWithoutValue&x=1&y&z=3"
will be well parsed.
Here my code:
Hashtable attributes = new Hashtable();
string query = Request.Url.Query;
string[] arrPairs = query.Split('&'); // ...?x=1&y=2
if (arrPairs != null)
{
foreach(string s in arrPairs)
{
if (!String.IsNullOrEmpty(s))
{
string onePair = s.Replace("?", "").Replace("&", "");
if (onePair.Contains("="))
{
string[] arr = onePair.Split('=');
if (arr != null)
{
if (arr.Count() == 2)
{
attributes.Add(arr[0], arr[1]);
}
}
}
else
{
// onePair does not contain a pair!
attributes.Add(onePair, "");
}
}
}
You really should set your URL and Route to be more MVC-Like. The URL you are calling should be:
newapp/controller/action/sessionId
Then set your route up:
routes.MapRoute(
"sessionId",
"{controller}/{action}/{sessionId}",
new { controller = "controller", action = "action", sessionId = 0 });
Then in your controller:
public ActionResult Action(int sessionId)
{
}
In your controller you still have direct access to the Request object, so you can use Request.Url, etc.
Does that answer your question, or is it something else that you need?