mutatingPostAuthentication Hook Configuration
The mutatingPostAuthentication
hook is called right after the user authentication flow is complete and before the user is saved as a secure cookie.
This means, it's still possible, intentionally so, to deny the user to be authenticated at all.
Even though they might complete the authentication flow with the external Identity Provider,
we can still stop the user from authenticating with our WunderGraph application.
This is essential, because we might be building an internal application which should only grant access to our own employees.
By implementing the mutatingPostAuthentication
hook,
it's possible to write arbitrary NodeJS (TypeScript) code to determine if the user should be allowed and.
Additionally, to just allowing the user in, we can also assign the user roles, like "admin", "guest" or "user". By assigning one or more roles to the user, we're able to use the @rbac directive to implement role based access controls for operations.
So, the mutatingPostAuthentication
hook is responsible for assigning roles to the user.
The @rbac
directive defines, what roles will grant or deny access to Operations.
On top of simply accessing the user object, it's also possible to use the access token and id token and enrich the user object with custom claims and attributes. These can later be used to implement attribute based access controls or evaluate based on custom claims if a user is allowed to make a certain operation.
Let's look at an annotated hooks configuration.
// wundergraph.hooks.ts// this is our in memory database of super adminsconst superAdmins = ["jens@wundergraph.com"]const wunderGraphHooks = configureWunderGraphHooks({authentication: {// implement the following hook to take ownership of the authentication flowmutatingPostAuthentication: async (user, accessToken, idToken) => {// OpenID Connect Identity Providers return a boolean value that indicated if the user has verified their email// In this scenario, we're denying the completion of the authentication flow if the user has not verified their emailif (user.email_verified !== true) {return {status: "deny",message: "email not verified"}}// We check our in memory database if if contains the users email// If the users email is found, we grant the roles "user" and "superadmin"if (superAdmins.find(s => s === user.email) !== undefined) {return {status: "ok",user: {...user,roles: ["user","superadmin"],custom_attributes: ["jens:read",],custom_claims: {realm_access: accessToken.realm_access,resource_access: accessToken.resource_access,jens: accessToken.jens,}}}}// otherwise, we just grant the role "user"return {status: "ok",user: {...user,roles: ["user"]}}}},});export default wunderGraphHooks;
This is a simple example to demonstrate how easy it is to assign roles to a user. Keep in mind that you're allowed to put any logic into the hooks. You can talk to a database or external system to determine the roles of a user.
Next Steps#
In order to implement role based access controls for Operations, you have to use the @rbac directive.
If you're looking to define your own custom roles, have a look at the authorization configuration.