We have a legacy ASP.NET WebForms project that I am trying to modernize by using Web APIs instead of the traditional [WebMethod] statics on Code-Behind (because it is just so limited).
However, I wanted to be able to read the Session Cookie in the Web API as well. Before, I can read it in the Code-Behind by accessing the HttpContext.Current.Session["NAME_OF_COOKIE_HERE"] and then casting it to a Model - I don't know how to do this with Web APIs.
I'm using axios to talk to my Web API Controllers.
I've added the withCredentials: true to my axios config but how do I move forward from there? How exactly can you read the session cookie in Web API Controllers?
Here's a sample:
// Client for testing
axios.get(API_ENDPOINT_URL_HERE, {withCredentials: true}).then(res => res).catch(err => err);
// Web API Controller
[Route(API_ENDPOINT_URL_ACCESSIBLE_BY_THE_CLIENT_TESTING)]
[HttpGet]
public IHttpActionResult SOME_FUNCTION_NAME() {
var currentUser = // I don't know what to do from here on.
}
The answer is in this link: ASP.NET Web API session or something?
Specifically, adding the following in the Global.asax.cs
public override void Init()
{
this.PostAuthenticateRequest += MvcApplication_PostAuthenticateRequest;
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
System.Web.HttpContext.Current.SetSessionStateBehavior(
SessionStateBehavior.Required);
}
You can read cookie just like any header property in httpRequestMessage by HttpRequestMessage.Headers .Please follow the link with proper implementation
WebAPI: Getting Headers, QueryString and Cookie Values
Also please note that if you expect a cookie in the Request then use "Cookie" header key and if you are making an rest api call and trying to find cookie in response then use "Set-Cookie"
Related
I have a web app with spring boot that uses loginUrlAuthenticationEntryPoint and restful service next to it.The Application serves to a one web page that uses thymeleaf which authenticate using sessions and a mobile app that uses basic authentication.So there is a cart items screen (html table ) in a restaurant page , i want to make those items deleted by clicking on them asynchronously(from database).But that page's security (page url) is handled by login-url-auth-entry-point , not rest auth security config.
So when ı make a ajax call to delete cart item url it returns 302.My ajax call don't authenticate and tries to redirect me to login page. I tried many things.
I tried using xhrFields{withCredentials:true} in ajax call-didn't worked.
I tried using setting Authorization field with document.cookie - document.cookie returns empty.
There is the controller i am trying to send request:
#ResponseBody
#PreAuthorize("hasRole('RESTAURANT') OR hasRole('ADMIN')")
#GetMapping("/flushitem/{id}")
public ResponseEntity<String> freeCartItem(#PathVariable("id") Long id) {
try {
cartService.deleteCart(id);
}catch(Exception e) {
e.printStackTrace();
}
return new ResponseEntity<>(HttpStatus.OK);
}
I also tried to delete #ResponseBody annotation and make a request with thymeleaf's th:href with a html link tags.It tries to redirect to somewhere even if my controller returns void.
So long story short, my question is , how should i make a ajax call in a web page which makes authentication by sessions ? Or can i make the functionality that i want without using ajax and rest controller function ?
Im not triying to make any cors related operation.(I guess)
How can I create a login page using Xamarin.Forms?
I'm debugging on Android device and
on the page I try to query the given username and password from an MSSQL database in MSSQL
and if the login be successful, the page will navigate to a new page, else will show authentication failed.
How can I do this in Xamarin.Forms?
(Xamarin LoginFlow Example linked at bottom of answer)
You would have multiple Xamarin.Forms pages and use a NavigationPage to push or pop them from the (lifecycle) stack.
Ref: https://developer.xamarin.com/api/type/Xamarin.Forms.NavigationPage/
Think of each Page a complete self-contained mini application. So the Login page might handle the getting the UserID/Password interactively from the user, performing a authorization check via a server via a Rest api that performs your SQL query. If login is successful it pushes a new Forms-based page on the NavigationPage stack. i.e.
SplashScreen -> LoginPage -> MainPagePage
LoginFlow Xamarin example
This sample demonstrates how to manipulate the navigation stack in order to only display the main page of the application once the user has successfully logged in.
For more information about the sample see Hierarchical Navigation.
What you need is the following functionalities:
Web APIs (Web Service for Login - Hosted on internet or local network - Connected with your MSSql Server)
A Login Screen in Xamarin.Forms Application (You can design it on your own or use the above mentioned demo)
On Login Screen's 'Login' button click, you need to call the Login Web Service from Xamarin.Forms application.
This requires networking (Internet Permission on Android Project, through Manifest.xml). You can use Microsoft.Net.Http for Web Service Calls.
(Look at this example http://www.c-sharpcorner.com/UploadFile/nirmal.hota/consuming-restful-web-service-in-xamarin-forms-project-using/ )
If your server responses with JSON and you need to parse JSON then you can use Json.Net (Newtonsoft.Json).
Note: What I am trying to tell you is that you need to use Web Services that are connected to the database and hosted on a server. From Xamarin.Forms application, you have to call this web-service with appropriate parameters. This requires networking, so you can use Microsoft.Net.Http (or there are other plugins also available). To get response from the Web Service, if it is in Json then you can use Newtonsoft.Json to parse Json.
There is a full example of an app with login page: xamarin-forms-samples/LoginDemo/ provided by Xamarin.
So lets say you have a LoginController on asp.net web api (if you don't know how to create an asp.net web api project etc. this is not the place to learn it:) )
[RoutePrefix("v1/login")]
public class LoginController : ApiController
{
[Route("")]
[HttpPost] //I'm using post!!!!!! you may use get etc.
public IHttpActionResult Login(UserLoginData request)
{
var userData = CheckFromDb(request);
return Json(userData);
}
}
It checks your request from db (UserLoginData is a simple class lets say holds username and password), than if user exists you return another class (lets say it is UserData hat holds name,surname, birthday etc.). If it can not find login it may return null. It's up to you.
So it will be available on your host machine like
localhost:34252/v1/login
(34252 your port-may change for you)
So you have to call it from the device (xamarin.forms) code like this
public string Post(string url, UserLoginData userLoginData)
{
//url is login url as defined above and userLoginData holds your
//user interface (textbox) data :)
using (var client = new HttpClient())
{
client.Timeout = new TimeSpan(0, 0, 30);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
StringContent content;
content = new StringContent(JsonConvert.SerializeObject(userLoginData), Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(url, content).Result;
if (!response.IsSuccessStatusCode)
{
throw new Exception("call failed");
}
//this is your response as json
var responseString = response.Content.ReadAsStringAsync().Result;
//you can convert it and check to a specific class if you need
var userData = JsonConvert.DeserializeObject<UserData>(responseString);
}
}
So UserLoginData and UserData classes both must exists on both sides. You can use Newtonsoft.Json library for json parsing (jsonconvert is it's method). Hope this helps you
And android device simulator networking is a bit tricky. You may not connect to localhost directy. It may take an address like 10.0.2.* for androidemulator or 10.71.34.* for xamarin simulator. Please check your network cards for ip addresses and findthe correct one via "cmd.exe" and "ipconfig /all" command.
I am recording a visual studio web test for angularjs spa application. The application is getting data from web api application, passing oauth token for authentication. When I replay the recorded web test the oauth token is not being sent and the recorded tests are getting failed.
Please let me know how this can be fixed.
The solution was to implement a custom WebTestRequestPlugin to pass authorization token.
public override void PreRequest(object sender, PreRequestEventArgs e)
{
var token = GetOAuthToken(1500, 1);
var header = new WebTestRequestHeader("Authorization", token);
e.Request.Headers.Add(header);
}
I am using recipe 10-3 in the newly released book ASP.NET Web Api 2 Recipes to support basic authentication in my Web API. This recipe utilizes a 3rd party library from Thinktecture. As seen from the below code, I am authentication the user against my own account service.
using Thinktecture.IdentityModel.WebApi.Authentication.Handler;
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
var authenticationConfiguration = new AuthenticationConfiguration();
var accountService = ServiceLocator.Get<AccountService>();
authenticationConfiguration.AddBasicAuthentication((userName, password) => accountService.Authenticate(userName, password));
config.MessageHandlers.Add(new AuthenticationHandler(authenticationConfiguration));
...
}
}
Now I want to make role-based authorization in my controllers using the Authorize attribute:
[Authorize(Roles="administrator")]
public IHttpActionResult Get()
{
...
}
My account service obviously knows about the users and their assigned roles, but this information is not available to the Authorize attibute (the roles are not set on the principal).
How do I accomplish this? Can the Thinktecture authentication handler be configured to set the roles on the principal? Or should I make my own custom Authorize attribute (deriving from the Authorize attribute)? And if so, should I override the OnAuthorization method to create and set the principal using my account service? Or maybe override the IsAuthorized method directly? Or maybe something else...
The AuthenticationHandler only does authentication. You'd need to set the roles in a separate step (e.g. in a delegating handler).
If you are on Web API v2 - I'd rather recommend switching to the basic auth OWIN middleware
https://github.com/thinktecture/Thinktecture.IdentityModel/tree/master/source/Thinktecture.IdentityModel.Owin.BasicAuthentication
This gives you full control over the principal that gets created.
https://github.com/thinktecture/Thinktecture.IdentityModel/blob/master/samples/OWIN/AuthenticationTansformation/KatanaAuthentication/Startup.cs
There is also a nuget.
I found out that the AddBasicAutentication method actually has an overload that takes a delegate for providing the roles. This is exactly what I was looking for. So now the call to AddBasicAuthentication looks like this, and everything works like a charm:
authenticationConfiguration.AddBasicAuthentication((userName, password) => accountService.Authenticate(userName, password), (username) => accountService.GetRoles(username));
I'm implementing a RESTful web service using ASP.Net Web Api. I have concluded to use Basic authentication + SSL to do the authentication part. What is the best/correct way to implement that?
My first attempt was to do it manually, parsing the Authorization header, decoding and verifying the user against my database. It works, but I wonder if I am missing something.
I've seen some solutions using user roles and principals. While I'm not sure what these actually do, I'm almost sure I will not be needing these, since in my database I define my own users and their roles.
Also what I haven't yet completely understand, is if the consumers of the service must sent the credentials with each request or they are somehow cached. Should my service do something in order for this to happen, or it's completely up to the consumer to handle this?
And a last question about clients making requests with javascript. Would there be any "cross domain request" problems if they try to use the service?
Jamie Kurtze provides a good explanation of using Basic Authentication here ASP.NET Web API REST Security Basics
From my understanding, if you want your requests to be stateless then each request will require the Authentication field to be set
Jamie Kurtze wraps the necessary code in a class derived from DelegateHandler, while Rick Strahl checks if the call is valid using a Filter. You can read more at his blog post on this topic at A WebAPI Basic Authentication Authorization Filter
Use basic authentication for the initial (sign in) request by adding a [BasicHttpAuthorize] attribute to the appropriate controllers/methods. Specify the Users and Roles with the attribute if desired. Define BasicHttpAuthorizeAttribute as a specialized AuthorizeAttribute like this:
public class BasicHttpAuthorizeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (Thread.CurrentPrincipal.Identity.Name.Length == 0) { // If an identity has not already been established by other means:
AuthenticationHeaderValue auth = actionContext.Request.Headers.Authorization;
if (string.Compare(auth.Scheme, "Basic", StringComparison.OrdinalIgnoreCase) == 0) {
string credentials = UTF8Encoding.UTF8.GetString(Convert.FromBase64String(auth.Parameter));
int separatorIndex = credentials.IndexOf(':');
if (separatorIndex >= 0) {
string userName = credentials.Substring(0, separatorIndex);
string password = credentials.Substring(separatorIndex + 1);
if (Membership.ValidateUser(userName, password))
Thread.CurrentPrincipal = actionContext.ControllerContext.RequestContext.Principal = new GenericPrincipal(new GenericIdentity(userName, "Basic"), System.Web.Security.Roles.Provider.GetRolesForUser(userName));
}
}
}
return base.IsAuthorized(actionContext);
}
}
Have the initial response include an API key for the user. Use the API key for subsequent calls. That way, the client's authentication remains valid even if the user changes username or password. However, when changing password, give the user an option to "disconnect clients", which you implement by deleting the API key on the server.
Have a look here for a good basic authentication implementation
http://leastprivilege.com/2013/04/22/web-api-security-basic-authentication-with-thinktecture-identitymodel-authenticationhandler/
there is more to read about it at:
https://github.com/thinktecture/Thinktecture.IdentityModel.45/wiki