I am down to a choice between angularjs and dojo as my front end MV* framework. One particular functionality which in my mind is critical is the ability to do unobtrusive and transparent authentication. I was extremely impressed with angular's capability in this regard using http interceptors. See http://www.espeo.pl/2012/02/26/authentication-in-angularjs-application for a good sample implementation/explanation.
It allows one to queue up any and all 401 responses while waiting for successful login and then re-transmit them once login is successful by using a combination of broadcast events, event handling, and http interceptor (plus a request queue). The overhead on each request appears to be minimal.
I wanted to compare the ability of dojo to do the same, but from my examination it does not appear to be possible. Examining the dojo documentation, it appears that this would probably have been possible in dojo 1.4 using the ioPipline, but that that is deprecated in favor of dojo/request/notify which does not appear to provide as low-level of an interception (specifically it doesn't appear to give access to the request object when an error is received). it is also not clear if it is possible to modify or replace the response from within the notified function...
Can anyone point me to a clear example of this having been accomplished using dojo's request/notify service or an alternate methodology using dojo? Critical requirements are that the authentication be transparent to the protected functions/controllers/objects (they should simply receive a promise which is eventually fulfilled), that the authentication code be able to be centralized (it should not require modification to every object/event/etc that makes a request), and that it should be secure (in the sense that both authentication/authorization should happen on the server side and no javascript manipulation should be able to bypass that requirement)...
You can register handlers within dojo/request/registry to intercept and handle requests made using dojo/request any way you want. Here’s a modified (untested, probably slightly defective) example based on the reference guide:
require([ "dojo/request/registry", "dojo/request/xhr" ], function (registry, xhr) {
request.register(function (url, options) {
return xhr(url, options).response.then(function (response) {
if (response.status === 401) {
// retry later
}
return response;
});
}, true);
});
With the above example, any request made with dojo/request will be intercepted and the method provided will be called. If you wanted to conditionally only match certain requests with this handler, you can pass another tester function as the first argument. This is detailed in the reference guide linked above.
All your handler function then needs to do is return a promise that will eventually resolve to the expected data.
Related
In some Azure Samples on GitHub like this one we have an example of using ADAL to access a protected Web API resource. The effort is protected by a try/catch looking for an AdalException
I'll summarize the code as thus:
try
{
//pseudo code... configure a client to use an access token..
var token = ADAL.AcquireTokenAsync...
var httpClient = new HttpClient(baseUri, token);
// use the token for querying some protected API
var result = //await HttpClient to return data...
return View(result);
}
catch (AdalException)
{
// do something important with the exception,
// e.g. return an error View w/login link
}
So, as I start to flesh out my MVC controllers to use ADAL-access_token'd requests, do I really want all this try/catch business in every controller?
Does it make sense to create an ActionFilter? This snippet was inspired by code I saw at this Azure Sample
public class AdalErrorAttribute : FilterAttribute, IExceptionFilter
{
void IExceptionFilter.OnException(ExceptionContext filterContext)
{
if(filterContext.Exception is AdalException)
{
if (filterContext.RequestContext.HttpContext.Request.QueryString["reauth"] == "True")
{
//
// Send an OpenID Connect sign-in request to get a new set of tokens.
// If the user still has a valid session with Azure AD, they will not be prompted for their credentials.
// The OpenID Connect middleware will return to this controller after the sign-in response has been handled.
//
filterContext.RequestContext.HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties(),
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
}
My Context:
I'm taking a rather homogenous set of scaffolded MVC controllers that were EntityFramework-centric at the time they were generated.. but now need to be re-tooled to access my Web API (via my new AutoRest client)
#vibronet made a good point - don't do that for your WebAPI endpoints. They're called with bearer auth, and shouldn't be automatically redirected to a sign-in process. Give back a 401 indicating the credentials are invalid and let it go.
But for an MVC app the user is using interactively, this is a reasonable idea to follow, subject to a couple of constraints.
Make sure your filter is very tight about what it matches. Ie. make sure it's only handling exceptions you can reasonably-certainly draw to an authentication issue (vs. being an access-denied issue). Perhaps a custom exception that gets thrown by your wrapping logic, or some extra conditions beyond it being an ADALException to make sure it's an issue solveable by logging in again.
Unless you really want every request have this handling, look at attaching it at the controller or action layer instead of globally.
Look out for potential "loop" issues, where you get an error, tell the user to log in again, get the error, make them log in again, etc. Maybe set some data in the session when triggering the login, or in the login process, or something like that. Better to give an error message than get the user's browser stuck in an infinite-redirect loop. (of course, this applies to the manually-handle-the-exception case too)
Hope that helps.
There are two issues with the approach.
the sample code you mention and the filter implementation are for web APPs, not web API. That's an important difference. Web APPs can rely on the browser to interact with the user for authentication; web APIs don't know what UX the client has (or even if there's any UX at all) hence the error behavior is radically different
a controller might call multiple APIs, requiring multiple token requests for different resources- which can fail or succeed independently, and connect to different providers. Different controllers might call different resources. The reaction to the error might vary. A filter such as the one you prototyped would only help with a very narrow set of cases.
Hence I would say that unless your controllers are all expected to perform very homogeneous logic, a filter such as the above would not give you the flexibility you need.
I'm developing a Yii2 REST API, with AngularJS for the frontend to consume.
I need a way to implement real time approach, e.g. for a chat, or to make some real time notifications.
Is this possible, how to achieve? I been reading about Ratchet, Socket.io and some other things, but I couldn't figure out how to make them fit together for REST or if this is the way to go.
Any advice would be appreciate.
You have a few options here.
Short / Long Polling (use setTimeout)
app.controller("MyController", function($scope, $timeout, $http) {
$scope.messages = [];
$timeout(callAtTimeout, 3000);
function callAtTimeout() {
console.log("Timeout occurred");
$http.get('PATH TO RESOURCE TO GET NEW MESSAGES').then(
function(res) { // update $scope.messages etc... },
function(err) { // handle error }
);
}
});
For both short and long polling on client side, you send request, wait to get a response back, then wait 3 seconds and fire again.
Short/Long polling works differently on the server side. Short polling will just return a response immediately - whether something has changed or not. Long polling, you hold the connection open and when there is a change, then you return the data. Beware of keeping too many connections open.
Socket.io (websockets)
I would recommend that you implement websockets using either something like node.js on your own web server or a hosted solution like Firebase.
The thing with Firebase is that from PHP, you can send a post request to a REST endpoint on the firebase server. Your javascript can connect to that endpoint and listen for changes and update the dom accordingly. It is possibly the simplest of all to implement.
I personally wouldnt use PHP for socket programming but it can be done.
To have real-time updates on your website, you can implement one of the following approaches depending on your requirement and how fast you need to update your frontend component.
Using a websocket
You can use a library like https://github.com/consik/yii2-websocket to implement it with Yii2. But this will act as a separate service from your REST API. Therefore you have to make sure that proper security practices are applied.
Running an Angular function with a timeout
You can continuously poll an endpoint of the REST API with a timeout of few milliseconds to receive updated values.
The second approach will create many requests to the API which will increase the server load. Therefore my preferred approach is using a websocket.
I am writing an AngularJs app to test an API we developed. The app uses the $http object to make requests to the API. One of the asks is that after the call it's possible to review the raw HTTP (headers and bodies) Request/Response, similarly to what's available in Fiddler via Raw tabs.
Is it something that $http provides out of the box?
If not, it appears that the only challenge is gaining access to the actual request http headers. It's easy to get the response headers and request/response bodies, but not sure how to get the actual request headers.
Thanks.
If you are using $http service to make your API calls, you can use Interceptors to achieve what you want.
Here is what docs tell us about them:
For purposes of global error handling, authentication, or any kind of synchronous or asynchronous pre-processing of request or postprocessing of responses, it is desirable to be able to intercept requests before they are handed to the server and responses before they are handed over to the application code that initiated these requests. The interceptors leverage the promise APIs to fulfill this need for both synchronous and asynchronous pre-processing.
You can find more in depth explanation in the official docs. For example, here.
Also, there are some questions about interceptors on this site. There are some examples of their usage for displaying loading screen in applications: here, here and, probably, somewhere else.
Hope, this helps.
Yes, AngularJs is wrapped around some JQuery or internally JQlite if JQuery is not present and written in Javascript and it provides some pre-defined services. A typical service looks like the following.
AngularJS docs: tutorial step 5
$ Prefix Naming Convention You can create your own services, and in
fact we will do exactly that in step 11. As a naming convention,
Angular's built-in services, Scope methods and a few other Angular
APIs have a $ prefix in front of the name.
The $ prefix is there to namespace Angular-provided services. To
prevent collisions it's best to avoid naming your services and models
anything that begins with a $.
If you inspect a Scope, you may also notice some properties that begin
with $$. These properties are considered private, and should not be
accessed or modified.
angular.module('myApp')
.factory('myService', function ($http, $injector) {
'use strict';
return $http.get('/endpoint')
.then(function () {
return $injector.get('endpoint');
}
.error(function () {
// handle error
}
};
})
Have a look a the image in AngularJS docs which shows a number of services with the $ prefix. Mostly, wrappers over service. It is reserved. More at FAQ.
I am currently building a dashboard page with multiple widgets. Those widgets retrieve their data with REST calls ($resource). A few widgets make similar calls and I don't want to DDOS our server so I am looking for a way to make a call only once and resolve all similar requests with the same response.
Since I am restricted to using POST requests only, I cannot use the cache option that $resource offers. This seems to be doing exactly what I want but only for GET requests.
I was thinking along the lines of using a http interceptor to queue similar POST requests, fire only one of them and resolving them all when the first one gets its response.
However, I cannot seem to put the pieces together so any help is appreciated. I am open to other options.
Kind regards,
Tim
Services in AngularJS are singletons, so a solution would be to store the response in the service, as a variable. Then next time you'll do the request, previously check if the variable is null, if it's not you wrap it in a promise and returned it. If it's null, then you do the request, and store the response for the next call.
You can also either use this in your request service or in your interceptor service.
I hope I helped !
Refactor your widgets to depend on a service (singleton).
This service should either poll the server via XHR, or get server push via websocket for updates.
If polling, look into server side caching and etags.
I'm trying to build some jasmine unit tests around my angular project. I've run into a situation I'm not sure how best to work around.
I use a response interceptor that's capable of retrying a request if it determines the error was a 401 error. It will make a call to renew the authorization token, and then reissue the request transparently.
(Orginal Call) -> 401 ? (recert and retry) : (return fail)
My problem lies with:
$httpBackend.whenPOST('/mymockedendpoint').respond(401, null);
This is the behavior I want the FIRST time it is queried. However, because it also controls all subsequent queries, my token renewal works, and then it reissues this request, but instead of returning a 200 like it would in production, it still returns the 401.
How can I extend the handling of that whenPOST so that I can control the behavior in a handler of some sort? Is that even possible?
Right now executing my test produces an infinite loop because the both reissues the request (since it successfully renewed the token) and catches again because the would-be-200 requests returns a 401).
Instead of using the "when" based functions of $httpBackend you can use the "expect" versions. This will let you assert that a particular request is made in a particular order. The documentation for $httpBackend describes the differences pretty well:
$httpBackend.expect - specifies a request expectation
$httpBackend.when - specifies a backend definition
Request Expectations vs Backend Definitions
Request expectations provide a way
to make assertions about requests made by the application and to
define responses for those requests. The test will fail if the
expected requests are not made or they are made in the wrong order.
Backend definitions allow you to define a fake backend for your
application which doesn't assert if a particular request was made or
not, it just returns a trained response if a request is made. The test
will pass whether or not the request gets made during testing.
Given that, try this in your test:
$httpBackend.expectPOST('/mymockedendpoint').respond(401, null);
$httpBackend.expectPOST('/mymockedendpoint').respond(200, { });
$httpBackend.flush();
Also, note the function $httpBackend.resetExpectations(), which could be useful in this type of scenario.