Netflix Zuul 2 API gateway authentication/request validation approach (no Spring) - authentication

I would like to put Netflix Zuul 2 (https://github.com/Netflix/zuul) in front of my services and do some authentication/validation of the incoming requests.
Each request that goes through the API gateway, e.g. /api/service1/value would have an auth token and I would like to validate that token before routing the request to the appropriate backend service.
I have several questions and was hoping someone could point me in the right direction.
An inbound filter seems like the right place to put that logic.
At the moment, my understanding is that it could be done like this:
class RequestAuthenticationFilter extends HttpInboundSyncFilter {
#Override
HttpRequestMessage apply(HttpRequestMessage request) {
// extract header with token and call auth service to validate it
if (ifTokenNotValid) {
// returns a 401
request.getContext()
.setEndpoint(UnauthorizedRequestEndpoint.class.getCanonicalName())
}
// continue filter chain
return request
}
}
What I'm not sure about is how to make that call to the auth service and get the response in the inbound filter so that the response can be checked and appropriate action can be taken - either return a 401 or continue routing the /api/service1/value request to a downstream backend service.
Is there a standard approach for making that external call from the inbound filter to a service?
Would I need to import another library with a HttpClient, etc. to achieve this?
Is there a different/better approach to handle this flow?
I understand that for normal routing from an inbound filter to an endpoint a ProxyEndpoint can be used:
inbound filter config:
...
if (path in request matches) {
context.setEndpoint(ZuulEndPointRunner.PROXY_ENDPOINT_FILTER_NAME)
context.setRouteVIP("service1")
}
return request
Either Eureka config:
service1.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
service1.ribbon.DeploymentContextBasedVipAddresses=authservice
or a list of servers configured manually:
service1.ribbon.listOfServers=localhost:8080
service1.ribbon.client.NIWSServerListClassName=com.netflix.loadbalancer.ConfigurationBasedServerList
Is there a way to configure the auth service that way and plug it into the flow, i.e.
request >> validate auth token (auth service request, response) >> continue original request?

Related

Cloudflare Worker redirect stripping auth headers

I set up a Cloudflare worker to redirect to our API gateway since we don't have control of the DNS and can't just set up a CNAME. The redirect works and it passes along the body and all the headers except Authorization. It receives it, and when I look at the worker console it lists it as redacted. It also redacts the user_key param I'm passing but it passes that through.
const base = 'https://myurl.com'
const statusCode = 308;
addEventListener("fetch", event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url);
const { pathname, search } = url;
const destinationURL = base + pathname + search;
return Response.redirect(destinationURL, statusCode);
}
First, note that the redactions you are seeing are purely for display in the workers console. This is a feature to protect sensitive secrets from being logged, but it doesn't affect the content of any live request.
Now, with regard to what your Worker is actually doing:
This worker returns a 308 redirect response back to the client. It is then up to the client to follow the redirect, sending the same request to the new URL.
It is the client, then, that decides whether to send the Authorization header to the new location -- the behavior is NOT controlled by Cloudflare Workers. As it turns out, many clients intentionally drop the Authorization header when following redirects to a different domain name. For example, the Go HTTP client library does this, and node-fetch recently started doing this as well. (I happen to disagree with this change, for reasons I explained in a comment.)
If the client is a web browser, then the behavior is complicated. If the Authorization header was added to the request as part of HTTP basic auth (i.e. the user was prompted by the browser for a username and password), then the header will be removed when following the redirect. However, if the Authorization header was provided by client-side JavaScript code when it called fetch(), then the header will be kept through the redirect.
Probably the best way to solve this is: Don't use a 3xx redirect. Instead, have the Worker directly forward the request to the new URL. That is, instead of this:
return Response.redirect(destinationURL, statusCode);
Try this:
return fetch(destinationURL, request);
With this code, the client will not receive a redirect. Instead, the Worker will directly forward the request to the new URL, and then forward the response back to the client. The Worker acts as a middleman proxy in this case. From the client's point of view, no forwarding took place, the original URL simply handled the request.

Authentication in GraphQL servers

How to properly handle authentication in GraphQL servers?
Is it ok to pass a JWT token at the Authorization header of query/mutation requests?
Should I use something from GraphQL specification?
Stateless solutions is preferable.
Thanks.
A while ago I was wondering the same thing for sometime,
but apparently authentication is out of the scope of what GraphQL is trying to accomplish (see the conversations on Github).
But there are solutions such as this which handles it with sessions.
Assuming you use express-graphql, here is what you can do.
import graphQLHTTP from 'express-graphql'
app.use(`/graphql`, [aValidationFunction, graphQLHTTP(options)])
function aValidationFunction(req, res, next) {
const { authorization } = req.headers
// Do your validation here by using redis or whatever
if (validUser) {
return next()
} else {
return res.status(403)
}
}
It depends on whether your GraphQL consumer is a webapp or mobileapp.
If it is a webapp, then I would recommend sticking with session-cookie-based authentication since most popular web frameworks support this, and you also get CSRF protection.
If it is a mobileapp, then you will want JWT. You can try manually getting a cookie header from login response, and put stuff this "cookie" in your next request, but I had problem that some proxy servers strip off this "cookie", leaving your request unauthenticated. So as you said, including JWT in every authenticated request (GraphQL request) is the way to go.

Rendr add custom header to fetch request (such as basic auth)

I'm building a Rendr app and I'd like to populate my user model by making a request to my API service's /login endpoint. That endpoint simply expects a basic auth header.
I see Rendr has an option to allow custom headers but the problem is the header is only added from the client to the Rendr node.js server. When the API proxy intercepts my requests and makes a call to http://my-api.com/login the basic auth header is not carried through to this request. I'm assuming the apiProxy ignores these headers- it has no way to know what header is custom.
How can I define a custom header client side and ensure the header is added to an API request?
this.app.fetcher.fetch({
user: {
model: 'User',
params: {}
}}, {
headers': {
"Authorization": "Basic abcdefg12345678"
}
}, function(err, user){
});
This example only adds a header from client -> Rendr node server, not NOT from Rendr node server --> my API service.
I put together an npm module for this: https://www.npmjs.com/package/rendr-api-proxy-optionable that should do what you need.
It allows pass throughs of request and response headers at the api proxy level.
Hope that helps!

"transparent" server side proxy for requests to ASP.NET Web API

Have an ASP.NET Web API endpoint that generates JSON responses. But due to two factors can't be consumed directly from a browser.
cross-domain issues
need to provide session ticket for the API that is known only server side
So I need a lightweight server side proxy for client(browser) requests to extend the request with session key. Do not want to impose an overhead deserializing client JSON requests or Web API JSON responses in the proxy code. Would like to pass the payload "as is" and deserialize client requests only Web API side and the Web API responses only client (browser) side. That is the proxy takes json from the browser and passes it directly to Web API. It also passes the JSON response from the Web API to the browser directly without deserialization. Just a dummy proxy that does not know anything about the data it transfers.
Please suggest is it feasible and what is the best way to implement it. The existing web application (the one that is used to generate the client pages) is implemented using ASP.NET MVC 4.
Thanks in advance.
update for 2021:
You should probably be looking at https://microsoft.github.io/reverse-proxy/ if you have found your way here
old answer:
I wrote one for a previous version of WebApi. The code should be fairly easy to update for your purposes.
The basic idea is that you create a WebApi DelegatingHandler that passes the request on to an HttpClient:
public class ForwardProxyMessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Add("X-Forwarded-For", request.GetClientIp());
if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Trace) request.Content = null;
request.RequestUri = new Uri(request.RequestUri.ToString().Replace(":3002", "")); //comes through with the port for the proxy, rewrite to port 80
request.Headers.AcceptEncoding.Clear();
var responseMessage = await new HttpClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
responseMessage.Headers.TransferEncodingChunked = null; //throws an error on calls to WebApi results
if (request.Method == HttpMethod.Head) responseMessage.Content = null;
return responseMessage;
}
}

how do you request a session from servicestack basic authentication, at /auth/basic?

I have set up a servicestack service with basic authentication using the first example, here:
https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorization
This automatically sets up a route: /auth/basic
However, I cannot find any information or examples on how to format a request to this URL (Variables/GET/POST/Auth Header, etc.).
I am able to access a simple service using the basic authentication credentials, so they are active and correct.
I have no custom authentication plugged in, just basic authentication.
I have tried:
Using a JsonServiceClient to send UserName and Password variables by GET or Json POST to /auth/basic, with and without an Auth header also containing the user & pass.
Using a browser to send GET requests with URL parameters of the user/pass, or as http://user:pass#localhost:123/auth/basic
I always just get "HTTP/1.1 401 Invalid BasicAuth credentials".
The only examples I can find involve some kind of custom authentication, and then /auth/credentials is accessed, but I want to use /auth/basic
I have looked at the code and it looks like it reads an Auth header, but the service does not accept one.
I am actually trying to get this working so I can then disable it and verify it is disabled (I want to require basic authentication for every request).
Questions are:
What is the correct way to call the /auth/basic service? I will take a servicestack client API example, specifications or even a raw http request!
How do you disable the /auth services altogether?
Many thanks.
What is the correct way to call the /auth/basic service? I will take a servicestack client API example, specifications or even a raw http request!
var client = new JsonServiceClient("http://localhost:56006/api");
var resp = client.Post(new Auth() { UserName = "TestUser", Password = "Password" });
This assumes you have also registered an ICacheClient and IAuthUserRepository (and added a user account)
The JSON format looks like this if you call into /auth/basic?format=json
{
"UserName": "admin",
"Password": "test"
"RememberMe": true
}
How do you disable the /auth services altogether?
Don't add the AuthFeature plugin to configuration.
You can also remove plugins
Plugins.RemoveAll(x => x is AuthFeature);
Putting the following in apphost config seems to do the trick.
//Disable most things, including SOAP support, /auth and /metadata routes
SetConfig(new EndpointHostConfig()
{
EnableFeatures = Feature.Json | Feature.Xml
});
I am a little suspicious about what this does to /auth however, because it returns an empty response, while most routes return 404.
So, would this truly disable the /auth functionality? As in, if someone formed a correct request to /auth/credentials, will it still return an empty response?