I am still finding my way around in ASP.NET.
I am experimenting with getting Angular code talking to a Web API 2 end point, which will only be accessed from within the solution itself.
One thing I want to be able to do is to show or hide edit buttons based on whether the current user is logged in or browsing anonymously.
I could do this in an MVC view by checking User.Identity.IsAuthenticated but I'm curious how I could do it with a pure Angular page, with no .NET coded in it.
I thought I could just do something like this
public class AuthorizationController : ApiController
{
public HttpResponseMessage Get()
{
if (User.Identity.IsAuthenticated)
{
return Request.CreateResponse(HttpStatusCode.OK, "Ok");
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "You are not authorized");
}
};
}
And then call it like this
$http.get("../api/authorization")
.then(function (response)
{
if(response.status=="200")
{
// logged in
}
else
{
// not logged in
}
});
Unfortunately, this doesn't do what I was hoping. If the user is not logged in, the API code hits the line creating an error response, but what it actually returns to the Angular callback is
{"data":"<!DOCTYPE html>\r\n<html>\r\n<head>\r\n <meta charset=\"utf-8\" />\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>Log in - My ASP.NET Application</title>\r\n <link href=\"/Content/bootstrap.css\" rel=\"stylesheet\"/>\r\n<link href=\"/Content/site.css\" rel=\"stylesheet\"/>\r\n\r\n <script src=\"/Scripts/modernizr-2.6.2.js\"></script>\r\n\r\n\r\n</head>\r\n<body>\r\n <div class=\"navbar navbar-inverse navbar-fixed-top\">\r\n <div class=\"container\">\r\n <div class=\"navbar-header\">\r\n <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\".navbar-collapse\">\r\n <span class=\"icon-bar\"></span>\r\n <span class=\"icon-bar\"></span>\r\n <span class=\"icon-bar\"></span>\r\n </button>\r\n <a class=\"navbar-brand\" href=\"/\">Application name</a>\r\n </div>\r\n <div class=\"navbar-collapse collapse\">\r\n <ul class=\"nav navbar-nav\">\r\n <li>Home</li>\r\n <li>About</li>\r\n <li>Contact</li>\r\n </ul>\r\n <ul class=\"nav navbar-nav navbar-right\">\r\n <li>Register</li>\r\n <li>Log in</li>\r\n </ul>\r\n\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"container body-content\">\r\n \r\n\r\n\r\n<h2>Log in.</h2>\r\n<div class=\"row\">\r\n <div class=\"col-md-8\">\r\n <section id=\"loginForm\">\r\n<form action=\"/Account/Login?ReturnUrl=%2Fapi%2Fauthorization\" class=\"form-horizontal\" method=\"post\" role=\"form\"><input name=\"__RequestVerificationToken\" type=\"hidden\" value=\"5h-wFJ5pn4Vq8uI15BbzTvAwAFuudI1jaF_YsHfpAp9YFaeArEkO4P6i5bFMYgSs6OY6BXDEHzNLFpxYA-IvQJlr7zYY8Bgj9mErF1dgMQQ1\" /> <h4>Use a local account to log in.</h4>\r\n <hr />\r\n <div class=\"form-group\">\r\n <label class=\"col-md-2 control-label\" for=\"Email\">Email</label>\r\n <div class=\"col-md-10\">\r\n <input class=\"form-control\" data-val=\"true\" data-val-email=\"The Email field is not a valid e-mail address.\" data-val-required=\"The Email field is required.\" id=\"Email\" name=\"Email\" type=\"text\" value=\"\" />\r\n <span class=\"field-validation-valid text-danger\" data-valmsg-for=\"Email\" data-valmsg-replace=\"true\"></span>\r\n </div>\r\n </div>\r\n <div class=\"form-group\">\r\n <label class=\"col-md-2 control-label\" for=\"Password\">Password</label>\r\n <div class=\"col-md-10\">\r\n <input class=\"form-control\" data-val=\"true\" data-val-required=\"The Password field is required.\" id=\"Password\" name=\"Password\" type=\"password\" />\r\n <span class=\"field-validation-valid text-danger\" data-valmsg-for=\"Password\" data-valmsg-replace=\"true\"></span>\r\n </div>\r\n </div>\r\n <div class=\"form-group\">\r\n <div class=\"col-md-offset-2 col-md-10\">\r\n <div class=\"checkbox\">\r\n <input data-val=\"true\" data-val-required=\"The Remember me? field is required.\" id=\"RememberMe\" name=\"RememberMe\" type=\"checkbox\" value=\"true\" /><input name=\"RememberMe\" type=\"hidden\" value=\"false\" />\r\n <label for=\"RememberMe\">Remember me?</label>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"form-group\">\r\n <div class=\"col-md-offset-2 col-md-10\">\r\n <input type=\"submit\" value=\"Log in\" class=\"btn btn-default\" />\r\n </div>\r\n </div>\r\n <p>\r\n Register as a new user\r\n </p>\r\n</form> </section>\r\n </div>\r\n <div class=\"col-md-4\">\r\n <section id=\"socialLoginForm\">\r\n \r\n<h4>Use another service to log in.</h4>\r\n<hr />\r\n <div>\r\n <p>\r\n There are no external authentication services configured. See this article\r\n for details on setting up this ASP.NET application to support logging in via external services.\r\n </p>\r\n </div>\r\n\r\n\r\n </section>\r\n </div>\r\n</div>\r\n\r\n\r\n <hr />\r\n <footer>\r\n <p>© 2016 - My ASP.NET Application</p>\r\n </footer>\r\n </div>\r\n\r\n <script src=\"/Scripts/jquery-2.2.3.js\"></script>\r\n\r\n <script src=\"/Scripts/bootstrap.js\"></script>\r\n<script src=\"/Scripts/respond.js\"></script>\r\n\r\n \r\n <script src=\"/Scripts/jquery.validate.js\"></script>\r\n<script src=\"/Scripts/jquery.validate.unobtrusive.js\"></script>\r\n\r\n\r\n\r\n<!-- Visual Studio Browser Link -->\r\n<script type=\"application/json\" id=\"__browserLink_initializationData\">\r\n {\"appName\":\"Firefox\",\"requestId\":\"69b5785bb7f0400088c465aa19c19c8a\"}\r\n</script>\r\n<script type=\"text/javascript\" src=\"http://localhost:55784/9f4b9571f5a149d8a3ad956c641aff65/browserLink\" async=\"async\"></script>\r\n<!-- End Browser Link -->\r\n\r\n</body>\r\n</html>\r\n","status":200,"config":{"method":"GET","transformRequest":[null],"transformResponse":[null],"url":"../api/authorization","headers":{"Accept":"application/json, text/plain, */*"}},"statusText":"OK"}
So it seems to be returning a log in page. Can someone tell me what I am doing wrong? Or am I just completely barking up the wrong tree with this approach?
Presumably, you're using forms authentication. By default, forms authentication will detect an unauthenticated event in the pipeline (401) and convert it to a redirect (302) to the login page. If your client is automatically following that redirect (most do), then you'll ultimately get the content of the login page with HTTP success status code (200).
There are two reasonably straight-forward solutions to this:
Don't use forms authentication (OK, not entirely straight-forward if you don't have an alternative authentication middleware to hand).
Set the HttpResponse.SuppressFormsAuthenticationRedirect property to true.
Per documentation for HttpResponse.SuppressFormsAuthenticationRedirect:
By default, forms authentication converts HTTP 401 status codes to 302
in order to redirect to the login page. This isn't appropriate for
certain classes of errors, such as when authentication succeeds but
authorization fails, or when the current request is an AJAX or web
service request. This property provides a way to suppress the redirect
behavior and send the original status code to the client.
And regarding the value of the property:
true if forms authentication redirection should be suppressed;
otherwise, false.
For further reading, and full credit to Phil Haack as my first source of easy information on the topic, check out his blog post on the subject.
I know the question is regarding the server side web api. but If someone want to examine another approach, and it is possible to do it on the client side using javascript/typescript maybe this approach can help future readers:
I prefer to do it locally, using localStorage and save unnecessary calls to the server.this.serverResponse.expires_inis the expiration time in seconds.
extract the expiration date from the web api server:
var tokenexpiration: Date = new Date();
tokenexpiration.setSeconds(new Date().getSeconds() + parseInt(this.serverResponse.expires_in))
console.log(tokenexpiration);
than you can save it to localStorage:
localStorage.setItem('expirationdate',tokenexpiration)
and with simple condition you can check whenever you need if the token was expired.
You should have a look at the AuthorizeAttribute. You can decorate your controllers with it or some of its methods and then handle the UnAuthorized exception in AngularJS (status 401 I think).
.Net will decide if a request is authorized or not based on cookies most of the times, but this really depends on the authentication scheme you're using.
Related
Use case (Django Project):
I want to log in on my login page -> Therefore I use this form in html (login.html):
<form class="loginform" action="" method="POST">
{% csrf_token %}
<div class="loginform">
{{form.as_p}}
</div>
<br>
<input type="submit" class="btn btn-success loginbtn" value="Login">
</form>
In case of successful log in I will redirect with DJANGO to this URL (localhost/welcome/)-> welcome.html:
LOGIN_REDIRECT_URL = "welcome"
I am able to send javascript notifications with alertify, but only when using simple things such as
click on it or mouseover:
function notification(text) {
console.log(text)
alertify.success(text);
}
I tried it with onsubmit="javascript:notification("")" in the html form tag, however this will
be displayed only BEFORE the URL redirection.
So my question is:
How is it possible to activate /send the push notification AFTER the URL redirection and in
case of successfull POST/ Log in?
I really appreciate your help! Thank you a lot!!
Javascript is client-side. That means your alertify.success(text); will be executed client-side, once server already did his render process.
The authentication process is server-side. So you have to make your server make your alertify a part of the rendered content.
Knowing that, you just have to change your LOGIN_REDIRECT_URL content page to include in it a call to notification:
<!-- your html template comes here -->
<script type="text/javascript">
notification('You are now logged in');
</script>
Be remembered that the notification will then display each time the person visits this page. You may want either to redirect him again, or to introduce a variable in Django to avoid displaying it again.
So I a couple of forms in my application looking like this :
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
{!! Form::open(['action'=>['AdminController#update',$upload->id,0], 'method'=>'POST']) !!}
{{Form::hidden('_method','PUT')}}
<a class="dropdown-item" href="/manage">Approve</a>
{!! Form::close()!!}
{!! Form::open(['action'=>['AdminController#update',$upload->id,1], 'method'=>'POST']) !!}
{{Form::hidden('_method','PUT')}}
<a class="dropdown-item" href="/manage">Reject</a>
{!! Form::close()!!}
</div>
I am sending these forms using the following jQuery snippet:
$(".dropdown-item").click(function(e){
e.preventDefault();
$(this).closest("form").submit();
});
I noticed that when this snippet is not commented out, I am unable to log out from my Laravel app. The log out just doesn't work.
I am using standard Laravel authentication.
This is part of how I log out:
<a class="dropdown-item" href="#" onclick="window.location='{{ route('logout') }}'">Log out</a>
First, can someone explain why this is happening? I tried to investigate, but what I read didn't make much sense, since I am new to all of this.
And what would be the workaround here? How can I send these forms without the preventdefault method? Or how can I log out while using it? I'd prefer not to change my log out functionality.
So to log out the user, you need to hit route('logout') which is a predefined route when you scaffold laravel auth system.
The logout route its definded under /src/Illuminate/Routing/Router.php line 1125. It needs to be a post request in order to logout the user, there are no required parameters to be sent expect laravel csrf token, which is the token to make sure that the requests are comming from the app itself and not any external script.
If you go to logout function which is hidden under LoginController you will see a trait(simply to keep the class cleaner) AuthenticatesUser Trait it is responsible to logout the user.
In AuthenticatesUser trait you will find a method at line 151:
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
return redirect('/');
}
which in simple terms it logs user out of session guard (Guards define how users are authenticated for each request.) and then destroys the session and the user its logged out with a redir to homepage.
Now as said we need a post request with csrf token to log the user out:
<a href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
Logout
</a>
Form placed under the a href tag
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
The a href tag which has event.preventDefault() which in simple terms means prevent a href tag from acting how normally it acts. That being said a href tag will not redirect to the route, instead we have document.getElementById('logout-form').submit();"> which simply gets the form logout-form and submits it whenever the user clicks on that a href tag
Or simply change 'dropdown-item' class name for your logout form , since prolly that is being frozen when both forms are active.
I've got a table of data with clickable rows which will lead to another page based on the row that was selected. To get the row, I am using a form and post the form to another page to pass the variable to php. Now, the problem is once I refresh the page my info is gone...
How can I avoid that?
<?php
$id = $_SESSION["uid"];
$sql = "select * from std_cources join courses where std_cources.std_id = '$id' and courses.course_id = std_cources.course_id";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
echo '
<div class="col-md-4">
<span class="fa-stack fa-4x">
<i class="fa fa-circle fa-stack-2x text-primary"></i>
<i class="fa fa-shopping-cart fa-stack-1x fa-inverse"></i>
</span>
</br>
<p style="color:white;">'.$row["course_name"].'</p>
Check forthe exams
<form name="myform" id="'. $row["course_id"].'" action="checkMarks.php" method="post">
<input type= "text" value ="'. $row["course_id"].' " name="test" style="visibility: hidden;"></input>
</form>
</br>
</div>';
}
}
?>
<script >
function functionTest(form_id) {
document.getElementById(form_id).submit();
}
</script>
I am retrieving names of few courses from database and put them in a table. Then I can click on each one of them and the form submission will be triggered and sends info to the next page and in the next page I get the given info and retrieve info from database again. However, on the second page, if I refresh the page I get
When you refresh a page that has received data from an earlier POST request, the refreshed page won't have that data unless another POST request is made. This can be done in the window object's DOMContentLoaded or load event.
Or, you can make the initial request for the data via a GET request. This will send whatever data you are sending to the server as part of your request as a query string appended to the URL. If you refresh the page, that query string will persist. This will only work if the data you are attempting to get comes from the server-side processing of the current page and not some other URL entirely.
Lastly, POST requests are for requests that change the state of the server (insert, update and delete type of operations). GET should be used for read operations.
Without your code, there's really not much more to offer.
EDIT:
Now that you have posted your code, I would suggest spending some time and cleaning up the HTML string that is sent back from your .php file. There is no such tag as </input> and you should remove the inline HTML event attributes (onclick, etc.). Here's why. Don't use javascript:... either (for many of the same reasons as in the link.
Lastly, I would suggest you change this from a form submission to an AJAX GET request, which will allow you to stay on the same page and keep the currently loaded data.
I'm trying to read data from an external JSON file using AngularJS.
Here is my HTML
<div class="panel panel-default" ng-controller="MyAppController">
<div class="panel-heading">
<div class="input-group">
<input ng-model="query" type="text" placeholder="What file are you looking for?" class="form-control"><span ng-click="clearFilter()" ng-disabled="query.length == 0" class="input-group-addon"><i class="glyphicon glyphicon-remove"></i></span>
</div>
</div>
<div class="panel list-group">
<span data-ng-repeat="cat in cats | filter: query" class="list-group-item animate-repeat">{{cat.title}}
</span>
<div class="clearfix"></div>
</div>
</div>
It works fine when I use this in my JS file and data shows up in a list.
function MyAppController($scope, $http) {
var url = 'http://jobs.github.com/positions.json?callback=JSON_CALLBACK';
$http.jsonp(url).success(function(data) {
$scope.cats = data;
});
}
But when I change the URL to my personal site nothing shows up even though I literally just copied and pasted everything in the github JSON file to a local JSON file. (just to test it out)
function MyAppController($scope, $http) {
var url = 'http://ciagent.com/Website-files/positions.json?callback=JSON_CALLBACK';
$http.jsonp(url).success(function(data) {
$scope.cats = data;
});
}
http://ciagent.com/Website-files/positions.json?callback=JSON_CALLBACK &
http://jobs.github.com/positions.json?callback=JSON_CALLBACK have the same exact content but only the github one works with my angular app for some reason.
Any reasons as to why it's doing this?
Assuming you are using a static resource file you need to realize that the string 'JSON_CALLBACK' is a placeholder and gets modified within each $http.jsonp() request to something else.
You should be able to see this in the actual request URL in network tab of browser dev tools.
You can also open the github version in browser and change the value to see that it is not static on their server and will adjust to whatever value is sent.
If you want to use jsonp server side it needs to return dynamic value of the callback GET parameter value.
+1 to what #charlietfl said. Also, be sure to set Content-Type:application/javascript;charset=utf-8 in your response headers.
My jQuery script stopped working when I moved my clients site from the dev-site to their own site (which basicly has the same setup using a VPS).
jQuery(document).ready(function() {
jQuery('input[name=button]:button').click(function(){
jQuery("#show").html('<center><img src="http://xxxx/images/ajax-loader.gif"></center>');
jQuery.get("http://xxx/inc/usrreg_ajax.php",{
namn: jQuery("input[name='namn']").val(),
nyhetsbrev: jQuery("input[name='news']").val()},
function(data){
jQuery("#show").fadeIn("slow").html(data);
});
});
});
im using the html
<div id="registering" style="margin-top: 15px;">
<div style="float:left;">
<label for="namn">Namn:</label><br/>
<input type="text" name="namn"><br/>
<input type="checkbox" name="news" value="ja">Ja, jag vill ha nyhetsbrev! <br/>
<div style="clear:both;"></div>
<input type="button" name="button" value="Anmäl dig!">
<div id="show"></div>
</div>
I've not made any changes post move and the jQuery script works perfect on the dev-site.
Very gratefull for any input!
All the best,
Marten
Is xxx/inc/usrreg_ajax.php on the same domain as your new site?
You have to remember that there a same origin policy that, for security reasons, prevent you from perform ajax calls from other domain urls.
http://en.wikipedia.org/wiki/Same_origin_policy
Due to browser security restrictions,
most "Ajax" requests are subject to
the same origin policy; the request
can not successfully retrieve data
from a different domain, subdomain, or
protocol.
You can use jsonp for different domain calls, but you will have to use jQuery.ajax for that, and won't be able to get the html as simple as you are doing right now
I test it here http://jsfiddle.net/bingjie2680/D4NuF/ and it works(get the image to show up), there must be a problem with the link to jQuery library.