What I am trying to do is something functionally like this (of course, the code below is nonsense):
#UseGuards( AuthGuard || AdminAuthGuard )
I want my guards to be false only if both return false. Basically, I'd like my auth guards to be like an OR statements because the endpoint can be activated by multiple independent sources and each has its own authentication system.
Of course, I could combine those two auths in the same function. The problem is that the code would become messy as there are instance where I need to use each of them individually and I do not want an overlap with one another.
Anyone has any idea how to implement a behavior like that?
PS: Also, as additional complexity, I am using RoleGuards with one of the two login system. So RoleGuards would be applied, for example, if AdminAuth is true, but not apply is normal Auth is true.
SOLUTION: Here's how I solved this problem(I still don't know if there's a simple nestjs way of doing it. If you you, I want to know ;)).
In my original guards, I exported the validateRequest function. I created an auth guard that is called adminOrNormalAuthGuard. In there I import the functions and implement the logic I want.
As for the roleGuards, all that I'm doing is importing those validate functions. If login is normal=> return true. If special login=> result depends on guards logic.
As this seems related to authentication, you can follow the recomended behaviour in the doc seen here: https://docs.nestjs.com/security/authentication#extending-guards
Basically what you can do is extend an AuthGuard composed with multiple strategies:
export class MyAuthGuard extends AuthGuard(['strategy_1', 'strategy_2', '...']) { ... }
Then you just have to set your new "MyAuthGuard" guard to your routes.
If the first strategy fail, the next one is used and so on until all strategy fails or one is successful.
As this seems related to authentication, you can follow the recomended behaviour in the doc seen here: https://docs.nestjs.com/security/authentication#extending-guards
Basically what you can do is extend an AuthGuard composed with multiple strategies:
export class MyAuthGuard extends AuthGuard(['strategy_1', 'strategy_2', '...']) { ... }
Then you just have to set your new "MyAuthGuard" guard to your routes.
If the first strategy fail, the next one is used and so on until all strategy fails or one is successful.
But in the docs, the description is:
In addition to extending the default error handling and authentication logic, we can allow authentication to go through a chain of strategies. The first strategy to succeed, redirect, or error will halt the chain. Authentication failures will proceed through each strategy in series, ultimately failing if all strategies fail.
Related
Background
A data structure in my database consists of "sections"; lists of custom objects. The number of sections may expand in the future, so to keep my code as DRY as possible, I wanted to determine the section to add/update/delete an item from to be defined dynamically as a parameter.
I quickly realised that doing something like #Body() section: SectionA | SectionB | SectionC... disables validation so I needed a single DTO Section that could encompass all sections. To do that I need to define dynamically which validators to apply as I have several #IsNotEmpty constraints.
So I came across this post whose selected answer recommends the usage of groups.
This posed the following challenges:
I now have to write a custom validation pipe. Relied heavily on this
I want to override the global validator pipe that I already had running and use my custom one for just that method. Outcome: didn't work, had to start defining the pipe on every controller method, a tradeoff I am willing to accept. Looks like there is no simple alternative.
However, I'm now faced with the final problem: how to use the parameters in the request to define these groups in the validator; another brick wall. No simple solution.
Solution
This question has been asked here but no satisfactory solution was actually given.
Option one recommended redefining the scope of the pipe to "request" level but didn't explain how, and solutions found online didn't work.
The second solution, using a custom decorator to perform the validation instead, did work, very well in fact here is a simplified version of the code:
export const ProfileSectionData = createParamDecorator(
async (data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
let object = plainToInstance(SectionDto, request.body); // I don't need to access the metatype from the request because I know what type I need but I'm sure I could if need-be.
const groups = [request.params.profileSection];
let validatorOptions = { groups, ...defaultOptions };
const errors = await validate(object, validatorOptions);
if (errors.length > 0) {
throw new BadRequestException();
}
return request.body;
},
);
Implications?
Here's my question. When Jay McDoniel recommended using a custom decorator, they warn: "Do note, that this could impact how the ValidationPipe is functioning if that is bound globally, at the class, or method level."
What does this mean?
Are there any vulnerabilities or performance drawbacks associated with this solution?
Obviously, one drawback is that you are using validation outside a validation pipe which is not ideal from a point of view of order and single-responsibility but I can't think of tangible inconveniences beyond aesthetics and maintainability.
Knowing the background, would you have approached the problem in a completely different way?
I'm pretty new to typescript and I faced the issue with extending the Request type. I actually found a solution, but it doesn't feel right to me, and seems like there might be a better way.
First, the structure. I have a middleware checkAuth that checks the Bearer token, finds the user in the database (or creates one, in case it's the first time) and adds user to the req variable.
Most people recommend modifying the Express' Request type with custom declarations. I don't like this idea, because that will put user object into all requests, even before I actually save user into req.
Another solution is what I use now:
interface ReqWithUser extends Request {
user?: {
...
}
}
This allows me to declare req: ReqWithUser. But there's a flaw. When I try to access req.user, typescript is telling me that it's possibly undefined. Well, that's exactly what I declared with user?:. If I don't put a question mark there, then typescript is mad in router, saying Property 'user' is missing in type Request. That's fair. One of the solution is to use req!.user. Solves all problems, but it still feels imperfect to me. I know the req.user is there, it's not optional, otherwise checkAuth would fail and return 401. If it didn't, the user is there. What feels like the right solution is somehow modify the req type after the checkAuth middleware. Because that's when user is added into the req. Is it possible?
The issue has nothing to do with your types, but it's the fact that Express will always emit Request and not RequestWithUser.
So in your middleware function, you will always get a Request. The only reason RequestWithUser is acceptable is because you made it optional.
Truth is the middleware / decorator pattern doesn't work that well with express. You have a few options (some of which you mentioned)
Use declaration merging to 'patch' the built-in Request. You already mentioned you don't like this though, because it's a bit of a hack.
Don't use Express' middleware system and instead write something that understands how types change with decorators. I don't know exactly what this looks like, or if this has been done before.
Whenever you want to use RequestWithUser in your controllers/middlewares start off with an assertion function to ensure that user exists (allowing you to make it non-optional).
Cast to RequestWithUser, when you need it.
All these options have drawbacks:
You don't like for the reasons mentioned. It's not always 'accurate' so you are sort of lying to Typescript for convenience.
Sounds hard to do
Requires the use of an assertion function, which means you need to do a little extra work every time you want to use the user property. This work is not needed because you as developer know.
You probably don't like casting for the same reason you don't like to use declaration merging.
By far I think 2 is the best solution, because it gives you the advantage of typing without any hacks and without having to do the extra work of an assertion function (which happens during runtime). But it means abandoning Express' Middleware system. I wouldn't know exactly how to write this code, but I am curious how if it can be done...
FYI I opened another stack overflow question, because I am curious:
Typescript typing for decorator middleware patterns
I read the doc, been there, done that. Still no clue how to write a decorator I need in a way that makes common sense.
In brief: Got an interceptor that executes before the validation layer. That simply means that invalid data can get in the interceptor and break the app. To avoid that I would like to use a decorator on some methods, and to be more accurate on parameters of such methods.
public async getUserById(#IsIntNumber() userId: number): Promise<UserEntity>
{
// method logic
}
Here #IsIntNumber() is a custom decorator that validates the userId parameter.
As a matter of fact I'd like to have a little library of mine in the application holding a bunch a that kinds of validation decorators that I could apply to different parameters.
Is there some legal method to do this without shedding too much blood and too many tears?
I know it's a difficult question.
In the docs they sort of say:
The #required decorator adds a metadata entry that marks the parameter
as required. The #validate decorator then wraps the existing greet
method in a function that validates the arguments before invoking the
original method.
Meaning I've got to pack all my validation logic into that validate function or what? Really?
Does it mean that we don't have adequate parameter decorators in TS? Cos if I understand this right, these ones are absolutely, totally unusable.
I would like to add method such as view and json to the context object passed to my controllers. I do this in a middleware that runs before everything else:
async function(ctx, next){
ctx.view = view.bind(ctx);
ctx.json = json.bind(ctx);
await next()
ctx.renderer.render();
}
these methods set some conventional configuration object (Renderer) that the middleware interprets and then renders out the actual response by setting the correct ctx.body. That allows me to switch template language easily and have an easier time combining API and Template requests.
Except it doesn't work because after await next() the ctx.renderer is the default one, not the one set by controllers. I suspect it's a namespacing issue, but I am not sure where it comes from.
What's the best practice to attach functions to the context that can reference context without it being passed to them?
Ok it's here in the docs I just missed it, the docs are inside a repo and are not hosted, which makes them hard to navigate.
TL;DR: use app.context to access the context prototype. Adding functions there attaches them to the context object and allows you to use this from within to access it.
I'm implementing an oAuth server and need to store refresh tokens, to do this I have (at the moment) chosen to serialize the tokens into JSON.
While I can see that the JSON includes everything that would be needed to rehydrate, when I de-serialize with token.FromJson() the embedded claims are not being reconstructed correctly.
So far I've considered inheriting from JsonConverter to create a claims converter but don't see a way of adjusting the global JsConfig to utilise it :(
Can any one point me in a good direction?
So...
Walking away from the code and returning did the trick!
Instead of using a JsonConverter you need to utilise a generic version of JsConfig when changing/overriding the behaviour of ServiceStack on a specific class, just stick the following in your services start-up code for example.
JsConfig<Claim>.SerializeFn = claim => string.Format("{0}|{1}", claim.Type, claim.Value);
JsConfig<Claim>.DeSerializeFn = claimDetails =>
{
var values = claimDetails.Split('|');
return new Claim(values[0], values[1]);
};