Nestjs: AuthGuard validation per request? - authentication

I have a nestjs graphql backend and I’d like to do the token authentication once per http request.
The Problem:
For one http request my global defined guard app.useGlobalGuards(new GqlAuthGuard()) is validating the user 7 times if I have the following graphql query:
{
article(id:1)
{
id
name
}
categories {
id
name
}
topics {
id
name
}
lawmakers {
id
name
}
articleTypes {
id
name
}
articleStatuses {
id
name
}
countries {
id
name
}
}
It looks that the AuthGuard is called for each Module that is affected by the query.
How can I get rid of this multiple validation?

You could probably end up attaching some sort of validated or authenticated property to the request context so that on each new guard the request goes through it could be shorted with that field. The problem is that with your query, you are essentially making 7 http calls in one, and Nest will run the guard against each query and mutation being requested because that's how a global guard is bound. The only real way around it is just to add some sort of short circuit that makes the complex logic not happen.

Related

Restricting options method in javalin

We have a kotlin code like the following, I am trying to disable the options method for the API's as follows using Javalin(3.12.0), but it is resulting in blocking all the other methods like get and post as well. What is that I am missing here?
val app = Javalin.create {
it.defaultContentType = "application/json"
it.enableWebjars()
it.addStaticFiles("", Location.CLASSPATH)
it.enableCorsForAllOrigins()
it.dynamicGzip = true
}
app.options("/*") {ctx -> ctx.status(405)}
app.routes {
path("/auth") {
post("/login") {
Auth.doLogin(it)
}
get("/metrics") {
val results = getData()
it.json(results)
}
}
Also there are 2 questions
1.want to implement the ratelimit for the get APi's for 20 request for an hour using the below code
app.get("/") { ctx ->
RateLimit(ctx).requestPerTimeUnit(5, TimeUnit.MINUTES) // throws if rate limit is exceeded
ctx.status("Hello, rate-limited World!")
}
How to achieve it?
How to restrict the jetty server version to display when the API call is made?
For Jetty...
There is only 1 Rate Limit concept in Jetty, and that's the org.eclipse.jetty.server.AcceptRateLimit, added as a Jetty Container LifeCycle bean to the ServerConnector, it cannot adjust rates for specific request endpoints, only for the entire connector.
If you want specific endpoint rates, then the org.eclipse.jetty.servlets.QoSFilter is the way that's done with Jetty.
The org.eclipse.jetty.server.HttpConfiguration for the org.eclipse.jetty.server.ServerConnector contains the controls to enable/disable the server announcement.
See
HttpConfiguration.setSendServerVersion(boolean)
HttpConfiguration.setSendXPoweredBy(boolean)
HttpConfiguration.setSendDateHeader(boolean)

How to check if an Authenticated User has a specific claim AND the a Route segment is the same as the UserId?

I'm trying to see if it's possible in an ASP.NET-Core 2 web app, that if a User is authenticated in a request, we can also check in some Filter/ActionMethod Attribute:
They have a specific claim
The route has an string id segment (e.g. HttpPut[("{id}")] ) and that id segment needs to match the Auth'd User's Id.
Request includes a JWT header with the bearer token in it, which is used to 'create' the Authenticated Identity (which works 100% fine).
e.g.
HTTP PUT /accounts/PureKrome | User Id:PureKrome | Claim: Irrelivant. => Can continue. [You are updating yourself. Don't need any special claim when updating yourself].
HTTP PUT /accounts/PureKrome | User is Anonymous or Id:SomethingElse | Claim: irrelivant => Failure (Forbidden response) [Someone else is trying to update you and doesn't have the correct overriding claim. So fail]
HTTP PUT /accounts/SomeoneElse | User is Id:PureKrome | Claim: correct claim. => Can continue [Trying to update a different user BUT you have a claim that allows you to do that]
Right now, I do this in my ActionMethod code ... one of the first things. So I was just curious to see if this could be achieved using an Attribute that decorates the ActionMethod, instead.
That isn’t actually too complicated. All you need to do is have an authorization filter that looks at the route values and then checks it with the current user.
Something simple like this should already work fine:
public class ValidateUserIdRouteAttribute : Attribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
var requestedUserId = context.RouteData.Values["id"] as string;
var currentUserId = user.FindFirstValue(ClaimTypes.NameIdentifier);
if (requestedUserId != currentUserId &&
!user.HasClaim(c => c.Type == "may-edit" && c.Value == requestedUserId))
{
context.Result = new UnauthorizedResult();
}
}
}
And used on a route it would look like this:
[ValidateUserIdRoute]
[HttpGet("/account/update/{id}")]
public IActionResult UpdateAccount(string id)
{
// …
}
That’s all. If you have authentication set up properly, the Bearer token will be used to authenticate the user which may or may not set up the claims properly, and then you just check against those claims to see if accessing the route is allowed or not.
Of course, you can expand on this idea and add some more functionality to it, e.g. support different route data keys or something like that.

JSON API design - express

I want to write a JSON API.
My problem is, that sometimes I want to query for an ID, sometimes for a String.
One option would be to add a querystring, for example:
example.com/user/RandomName
example.com/user/1234556778898?id=true
and use it like:
api.get('user/:input', function(req, res) {
if(req.query.id) {
User.find({ '_id': req.params.input }, cb);
} else {
User.find({ 'name': req.params.input }, cb);
}
};
But this seems like bad practice to me, since it leads to a bunch of conditional expressions.
Are there more elegant ways?
I would suggest handling two endpoints. One for getting ALL the users and one for getting a SPECIFC user by ID.
example.com/users
example.com/users/:id
The second endpoint can be used to find a specific user by id.
The first endpoint can be used to find all users, but filters can be applied to this endpoint.
For example: example.com/users?name=RandomName
By doing this, you can very easily create a query in your Node service based on the parameters that are in the URL.
api.get('/users', function(req, res) {
// generate the query object based on URL parameters
var queryObject = {};
for (var key in req.query) {
queryObject[key] = req.query[key];
}
// find the users with the filter applied.
User.find(queryObject, cb);
};
By constructing your endpoints this way, you are following a RESTful API standard which will make it very easy for others to understand your code and your API. In addition, you are constructing an adaptable API as you can now filter your users by any field by adding the field as a parameter to the URL.
See this response for more information on when to use path parameters vs URL parameters.

Having trouble making a OAuth 1.0a signed request to the Tumblr API using HelloJS

I'm trying to interface with the Tumblr API to pull a list of followers. I'm brand new the whole OAuth thing, so I was trying to model my calls off the demos at https://adodson.com/hello.js/demos/tumblr.html . Unfortunately, the example they give only requires the API key for identification (https://www.tumblr.com/docs/en/api/v2#posts) where as getting the followers needs a signed OAuth request (https://www.tumblr.com/docs/en/api/v2#followers).
The call I'm using is:
function getFollowers(blog){
hello('tumblr').api('blog/'+blog+'/followers/').then(function(r){
console.log("r", r);
//Bellow here not really relevant
var a = r.data.map(function(item){
return "<h2>"+item.title+"</h2>"+item.body_abstract;
});
document.getElementById('blogs').innerHTML = a.join('');
});
}
This generates the request url from the proxy:
https://auth-server.herokuapp.com/proxy?path=https%3A%2F%2Fapi.tumblr.com%2Fv2%2Fblog%2Fnmlapp.tumblr.com%2Ffollowers%2F%3Fapi_key%3DREDACTED08u%26callback%3D_hellojs_9kvqxi31&access_token=&then=redirect&method=get&suppress_response_codes=truee
and Tumblr's API returns
_hellojs_9kvqxi31({"meta":{"status":401,"msg":"Not Authorized"},"response":[]});
I can see that the login call has all of the OAuth info in the Query String Parameters field, and the one I'm trying to make does not, but I'm not sure what the right way to include that through helloJS is.
Got it, the function had to be wrapped in the login method. This was shown in the other example, but the way that it called parameters from the api object had me confused.
function doTheThing(network){
hello( network ).login({force:false}).then( function(r){
hello('tumblr').api('followers').then(function(r){
console.log("r", r);
var a = r.data.map(function(item){
return "<h2>"+item.title+"</h2>"+item.body_abstract;
});
document.getElementById('blogs').innerHTML = a.join('');
});
});
}
//...
tumblr:{
get: {
//...
//This next part needs to be generated dynamically, but you get the idea
'followers': 'blog/BLOGNAME.tumblr.com/followers',
}
callback(p.path);
}
},
post: {
//...
'followers': function(p, callback) {
p.path = 'followers';
query(p, callback);
}
},

Laravel Authentication with condition

I am using Laravel 5.1 and Laravel's default authentication system.
In database (MySQL) I add a new column named 'role'. The value will be 1 for admin and 2 for members.
Now I want to give login permission only for admin, means where the value is 1. How can I do that?
Actually I solved it. I just add these code in postLogin() method of AthenticatesUsers.php method.
// If role is equal to 1, user allowed to login
// You can change $admin value anytime according to database Design
// Example: In role column the value for admin is 2 or A. You just need to change the value of $admin.
$userData = User::select('role')->where('email',$request['email'])->first();
$admin = 1;
$role = $userData->role;
if($role == $admin){
$request['role'] = $role;
}
I feel that there are better ways to achieve what you're after, such as middleware, however given what you're after this would be one way to do it.
Upon logging in a user us sent to 'home', unless you specify otherwise in the AuthController.
Inside your routes.php, if you just set up a GET route to point to a HomeController (or whatever you name it) then you could use a function to run the tests you're after.
routes.php
Route::get('home', 'HomeController#index');
HomeController
public function index()
{
//If they are yet to log in then return your normal homepage
if (Auth::guest())
{
return View::make('home');
}
else
{
//Run your tests here to check their role and direct appropriately
//Given you have added the role column to the users table, you can access it like so:
//Auth::user()->role
}
}