Is your feature request related to a problem? Please describe.
Not related to a problem. It's a feature request.
authChecks are limited to an array / scope of roles. It would be great to also allow for callback functions so that we can pass contextual info.
Describe the solution you'd like
Can we pass a callback function to the @Authorized helper, for example:
Given the following authChecker:
const authChecker = ({ root, args, context, info }, roles) => {
return roles(root, args, context, info)
}
We can pass a function that returns a boolean in the decorator
@Authorized( { context } ) => {
// req is part of the apollo context
if(context.req.user.age > 18){
return true
}
return false;
} )
Describe alternatives you've considered
As of the moment, i'm having to execute context-based authorisation within the resolver itself.
authChecks are limited to an array / scope of roles
That's not true - it's a generic type, you can put everything you want inside it, even functions:
https://typegraphql.com/docs/authorization.html#how-to-use

We can pass a function that returns a boolean in the decorator
Please just replace @Authorized with @UseMiddleware and it's gonna work exactly the way you want - just throw the error instead of return false 馃槈
The goal of @Authorized is to provide built-in simple auth feature, not a over-complicated and customizable feature.
If your use case is more complicated than the basic roles, just use custom reusable middlewares that receives your function and to the magic of true-false-error inside:
https://typegraphql.com/docs/middlewares.html#reusable-middleware
I realize this issue is older but since it is currently the latest open issue I thought I'd tap in and provide a simple example of such an authorization middleware I recently wrote. It can easily be adapted to fit your purpose.
import { MiddlewareFn } from 'type-graphql';
import { SomeContext } from '[...]/@types/SomeContext';
interface IsNotAuthOptions {
silent?: boolean;
errorMsg?: string;
}
/**This middleware __throws__ when the issuer __is authenticated__.
* You can provide a custom error message as option _`errorMsg`_.
* The default error message is _"forbidden"_.
*
* If you would rather return `null` instead of throwing,
* set _`silent`_ to `true` in the options.
*/
export function isNotAuth(options?: IsNotAuthOptions): MiddlewareFn<SomeContext> {
return async ({ context: { req }, args, info, root }, next) => {
if (req.user) {
if (options?.silent) return null;
throw new Error(options?.errorMsg ? options.errorMsg : 'forbidden');
}
return next();
};
}
Just write @UseMiddleware(isNotAuth()) over any @Resolver() class method and it should do the trick.
_Note: In my example I use express' req-object with an added property called user that is assigned earlier in the middleware stack, but you can use anything that is provided in context or even args, info or root_
I realize this issue is older but since it is ~currently the latest~ open issue I thought I'd tap in and provide a simple example of such an authorization middleware I recently wrote. It can easily be adapted to fit your purpose.
import { MiddlewareFn } from 'type-graphql'; import { SomeContext } from '[...]/@types/SomeContext'; interface IsNotAuthOptions { silent?: boolean; errorMsg?: string; } /**This middleware __throws__ when the issuer __is authenticated__. * You can provide a custom error message as option _`errorMsg`_. * The default error message is _"forbidden"_. * * If you would rather return `null` instead of throwing, * set _`silent`_ to `true` in the options. */ export function isNotAuth(options?: IsNotAuthOptions): MiddlewareFn<SomeContext> { return async ({ context: { req }, args, info, root }, next) => { if (req.user) { if (options?.silent) return null; throw new Error(options?.errorMsg ? options.errorMsg : 'forbidden'); } return next(); }; }Just write
@UseMiddleware(isNotAuth())over any@Resolver()class method and it should do the trick._Note: In my example I use express'
req-object with an added property calleduserthat is assigned earlier in the middleware stack, but you can use anything that is provided incontextor evenargs,infoorroot_
thanks
I've revised the idea and I have to reject your proposal.
Basically, the idea of @Authorized decorator is to decouple the implementation details from the requirement.
The handler (query/mutation) should only declare the requirements, like the roles in examples:
@Authorized(Requirement.IsAdult)
myQuery() {
// ...
}
And then your authChecker implementation should translate the requirement into a proper implementation:
@Authorized<Requirement>(
{ root, args, context, info },
requirements,
) {
if (requirements.includes(Requirement.IsAdult)) {
return context.req.user.age > 18;
}
// ...
}
So in all I'm against exposing the resolver data into the callback of the decorator as it's clearly a leak of responsibility and it leads to code duplication 馃敀
Most helpful comment
I realize this issue is older but since it is
currently the latestopen issue I thought I'd tap in and provide a simple example of such an authorization middleware I recently wrote. It can easily be adapted to fit your purpose.Just write
@UseMiddleware(isNotAuth())over any@Resolver()class method and it should do the trick._Note: In my example I use express'
req-object with an added property calleduserthat is assigned earlier in the middleware stack, but you can use anything that is provided incontextor evenargs,infoorroot_