Authenticate Web API session before body upload - iis

I have a simple Web API 2 controller that handles file uploads. It looks like this:
[MyCustomAuthenticationFilter]
[Authorize]
public class FileController : ApiController
{
private IFileRepository _FileRepository;
public FileController(IFileRepository fileRepository)
{
_FileRepository = fileRepository;
}
public async Task<FileInfo> Post()
{
var stream = await Request.Content.ReadAsStreamAsync();
var info = await _FileRepository.CreateFileAsync(stream);
return new FileInfo(info);
}
}
It takes a streamed upload from the client, hands it off to a repository object (which talks to an Azure Storage Blob container), and then returns some data about the uploaded file. Simple enough, and it works great.
Except, the authentication filter is not applied until the client has finished uploading the file. Authentication is a simple challenge/response system using a token, so this means a client could easily upload several hundred megabytes of data (potentially over a slow cellular data connection) before finding out that their token has expired and they need to refresh it and try again. I'd like to be able to examine the request header and validate (or reject) it as early as possible, but that doesn't seem to be doable with the standard filters. I also tried creating a simple IHttpModule and hooking into the BeginRequest event, but that apparently does not fire until after the upload completes, either.
How can I hook into the pipeline such that I can validate the Authorization headers for a request before the client upload completes?
Edit to add:
Obviously Authenticating early doesn't do me any good if I can't also Authorize, based on the route in Web API. My service has a few anonymous methods, so I can't just blanket reject un-authenticated users. Given the architecture of IIS and ASP.net, maybe that means this just isn't possible.

You'll most likely need to make this request into a 2 stage request. One authentication request prior to the file upload request. That's a lot of baggage and the end point can't process a request until it finishes receiving it. Try a separate authorization request before uploading the file. If your request passes then you can send the file upload. Is this an okay solution?

Related

Get the requesting URL in Callable Firebase Functions

I have a service that shares html to multiple client web sites. I need to know The URL of where the request is coming from.
The client will add a custom script to their website and the script will load Firebase SDK and call one of my callable firebase functions.
exports.testFunction = functions.https.onCall(async (data, context) => {
//How do you access the requesting URL?
console.log(context.rawRequest.originalUrl) "/"
console.log(context.rawRequest.url) "/"
})
Thank you,
HTTP requests to callable functions don't really come "from" a URL. They come from anywhere on the internet. It could be a web site, Android or iOS app, or someone who simply knows the protocol to call the function.
If you're building a web app and you want to pass along the URL of the page making the request, you'll have to add that data into the object that the client passes to the function, which shows up in data.

running function after res.send

I'm trying to run this code
module.exports = async (req, res, next) => {
res.set('Content-Type', 'text/javascript');
const response = {};
res.status(200).render('/default.js', { response });
await fn(response);
};
fn is a function that calls an api to a service that will output to the client something. but its dependent on the default.js file to be loaded first. How can do something like
res.render('/default.js', { response }).then(async() => {
await fn(response);
};
tried it, but doesn't seem to like the then()
also, fn doesn't return data to the client, it calls an api service that is connected with the web sockets opened by the code from default.js that is rendered.
do i have to make an ajax request for the fn call and not call it internally?
any ideas?
Once you call res.render(), you can send no more data to the client, the http response has been sent and the http connection is done - you can't send any more to it. So, it does you no good to try to add something more to the response after you call res.render().
It sounds like you're trying to put some data INTO the script that you send to the browser. Your choices for that are to either:
Get the data you need to with let data = await fn() before you call res.render() and then pass that to res.render() so your template engine can put that data into the script file that you send the server (before you send it).
You will need to change the script file template to be able to do this so it has appropriate directives to insert data into the script file and you will have to be very careful to format the data as Javascript data structures.
Have a script in the page make an ajax call to get the desired data and then do your task in client-side Javascript after the page is already up and running.
It looks like it might be helpful for you to understand the exact sequence of things between browser and server.
Browser is displaying some web page.
User clicks on a link to a new web page.
Browser requests new web page from the server for a particular URL.
Server delivers HTML page for that URL.
Browser parses that HTML page and discovers some other resources required to render the page (script files, CSS files, images, fonts, etc...)
Browser requests each of those other resources from the server
Server gets a request for each separate resource and returns each one of them to the browser.
Browser incorporates those resources into the HTML page it previously downloaded and parsed.
Any client side scripts it retrieved for that page are then run.
So, the code you show appears to be a route for one of script files (in step 5 above). This is where it fits into the overall scheme of loading a page. Once you've returned the script file to the client with res.render(), it has been sent and that request is done. The browser isn't connected to your server anymore for that resource so you can't send anything else on that same request.

Best way to handle API calls from frontend

Okay, so atm i have a frontend application built with Nuxt JS using Axios to do requests to my REST API(separate).
If a user does a search on the website the API URL is visible in XMLHttprequests so everyone could use the API if they want to.
What is the best way of making it so that only users that search through my website gets access to the API and people that just directly to the URL gets denied. I suppose using some sort of token system, but what is the best way to do it? JWT? (Users never log in so there is no "authentication")
Thanks!
IMO, you CANNOT block other illegal clients accessing your
backend as you describe that the official client and other illegal have the same knowledge about your backend.
But you can make it harder for illegal clients to accessing your backend through some approach such as POST all requests, special keys in header, 30-minutes-changed token in header and server-side API throttling by client IP.
If the security of the search API is really important, authenticate it by login; if not, just let it go since it is not in your critical path. Let's focus on other important things.
I'm in the same "boat" and my current setup is actually in VueJs but before even come to StackOverflow I developed a way to actually, the frontend calls the server and then the server calls the API, so in the browser, you will only see calls to the server layer that, the only constraint is that the call must come from the same hostname.
backend is handled with expressJs and frontend with VueJs
// protect /api calls to only be originated from 'process.env.API_ALLOW_HOST'
app.use(api.allowOnlySameDomainRequests());
...
const allowHostname = process.env.API_ALLOW_HOST ||'localhost';
exports.api = {
...
allowOnlySameDomainRequests: (req, res, next) => {
if(req.url.startsWith('/api') && req.hostname === allowHostname) {
// an /api call, only if request is the same
return next();
} else if (!req.url.startsWith('/api')) {
// not an /api call
return next();
}
return res.redirect('/error?code=401');
},
...
};
In our case, we use Oauth2 (Google sign through passportJs) to log in the user, I always have a user id that was given by the OAuth2 successful redirect and that user id is passed to the API in a header, together with the apikey... in the server I check for that userid permissions and I allow or not the action to be executed.
But even I was trying to find something better. I've seen several javascript frontend apps using calls to their backend but they use Bearer tokens.
As a curious user, you would see the paths to all the API and how they are composed, but in my case, you only see calls to the expressJs backend, and only there I forward to the real API... I don't know if that's just "more work", but seemed a bit more "secure" to approach the problem this way.

How to authorize for Amazon's Alexa API?

I want to send a request to this Amazon Alexa API.
That page contains the last 50 activities I made with my Amazon Echo. The page returns JSON. Before you can request that page, you need to authorize your account, so the proper cookies are set in your browser.
If I do something simple as:
const rp = require("request-promise");
const options = {
method: "GET",
uri: "https://alexa.amazon.com/api/activities?startTime=&size=50&offset=-1",
json: true
};
rp(options).then(function(data) {
console.log(data);
}).catch(function(err) {
console.log(err);
});
I can send a GET request to that URL. This works fine, except Amazon has no idea it's me who's sending the request, because I haven't authorized my NodeJS application.
I've successfully copied ~10 cookies from my regular browser into an incognito tab and authorized that way, so I know copying the cookies will work. After adding them all using tough-cookie, it didn't work, unfortunately. I still got redirected to the signin page (according to the error response).
How do I authorize for this API, so I can send my requests?
I have been looking for a solution for this too. The best idea I have is to use account linking, but I haven't try it yet. Looks like ASK-CLI has interface for this also, but I can't figure it out how to use it (what is that URL?). For linking account to 3rd party server is not easy, but link it back to Amazon for the json API should not be that complicated.

How to get HttpRequestContext when I am using Self Hosted Owin?

How to get HttpRequestContext when I am using Self Hosted Owin?
My class is being called by ApplicationInsights and not by a controller, so I don't have access to any context.
Is there any way similar to what we do using HttpContext?
There is no static class to retrieve the request when using OWIN self hosted, because you are essentially in a console application, which is unaware that you wrote code that receives http requests.
Owin is a pipeline. The request is passed from middleware to middleware by calling the next middleware with a context parameter containing the request (and the response for that matter).
Each middleware registered in the pipeline can inspect the request on the way in, in their registration order, and call the next middleware before processing the response.
It is very easy to write your own middleware, register it in the pipeline, and inspect the request object for each request coming in.
Take a look here if you need a quick code example : http://benfoster.io/blog/how-to-write-owin-middleware-in-5-different-steps

Resources