Exploring Authorization With GraphQL & GRANDstack
Options for adding authorization to GraphQL APIs.
A look at various options for adding authorization to GraphQL APIs.
Links And Resources#
- GraphQL Authorization And Middleware
- Blog post: Authorization In GraphQL Using Custom Schema Directives
- Hi folks, welcome to the Neo4j stream. My name is Will. And today we're going to be looking at GraphQL authorization. So, sorry for missing last week, I had quite a bit of construction going on outside of the house. They're still going, they're about a block away now. So hopefully it's a bit more manageable. So we've been working on building up this real estate search application over a few weeks, and we had a basic data model. We were able to import some data, view it on a map, query that through GraphQL. And I had a lot of questions asking about how do we protect our GraphQL API. So how do we add authentication and authorization to our API, and to our application as well. Right? So for the real estate search app, we'll need to think about this both on, the front end and the back end. And what I thought it would be helpful to do, in this session today, is take a step back from our real estate search application. And just talk a bit about GraphQL authorization specifically. So specifically today, we're gonna to focus on the options for graphical authorization. Look just mostly at the back end. And then what I'd like to do next week, is then take what we found exploring today, and then add that to our real estate search application, adding authentication and authorization to our search app. Cool, so, so I think what we'll do today is maybe just start a new GraphQL API, connect that to the Neo4j, and then look through a couple of different ways, that we can protect that API and add authentication and authorization to it. Well, mostly authorization. So with that, I think what we'll do is start a new project. So make this bit bigger. So I'm going to do, NPX create GRANDstack app. Let's call this GraphQL auth-demo. And this is going to pull down the latest version of the GRANDstack starter project, which will, we'll just use the API piece of that. We won't use the front end piece of it, but that's fine. Oh no, it looks like we have, have an error here. What does this say? We have exceeded rate limit. Wow, that's, let's try one more time. Looks like we've got a GitHub error there. So what the great GRANDstack app does, it's a CLI, that downloads the latest release of this project from GitHub, from looks like there's some issue, with the GitHub API. So let's see, well, let's see, why don't we just download the release itself. And we are gonna need this GraphQL auth-demo. And that's gonna be in downloads, graphical auth-demo. So I have to explore what was going on, with, get up there, but, you can also just clone this project directly from GitHub or download the release like we've done. Okay, so we're just interested in the API piece of this. So I'll go into the API directory, you know, mess up BS code. Sure, you can have access to my downloads directory, since that's where we saved it. Okay, so let's get a terminal. And the first thing we'll do is, NPM install to install our dependencies. And then while this is running. Let's switch over to near after desktop, and let's go ahead and create a new database. The latest version that I have downloaded, that should be fine. GraphQL auth-demo and a password, you'll need to know the password, so that our GraphQL API can make authenticated requests. So let's start this database. And then there's a .env file, in our GraphQL API project here. And we're going to make sure that we have the credentials for our database here. So it's a bit bigger there we go. So then to URI is bolt local host 7687. That's the default to connect to the local database, which we're doing a default user name is Neo4j. And for password is letmein. Which is exactly the password I used here. So looks like I don't need to make any changes there. Let's take a look at SOC, schema.GraphQL and I've to got syntax highlighting there. I have a new computer, so I don't have everything quite set up yet. So let's get the GraphQL extension for syntax highlighting. Let's see which one of these, does anyone have a preference for the best GraphQL. Nope. There's a visitor on my desk here. All right, stay off the keyboard please, Jenny Cat. Thank you. Oh, now she's biting. Okay. We'll have to put Jenny cat and timeout just a sec. Okay, Jenny cat is on timeout for biting my legs. Don't appreciate that. Jack says hello. Hi Jack. Thanks for joining. Okay, let's install GraphQL. VSCode extension this one, will give us syntax highlighting for GraphQL files, there we go. Okay, so here's our schema, we have users, businesses, reviews. We have some custom queries and custom mutations. Cool, it's perfect. So let's go ahead and start our GraphQL API. We'll do NPM runs start and you get an error, that there is no procedure, with the name apoc.schema.assert. So what's going on there? Well, if we take a look at index JS, what we're running here. So this is starting a GraphQL API powered by Neo4j graphical JS so that, this is a library that has the logic for making requests to the database, generating Cypher queries from GraphQL Neo4j driver to connect to Neo4j, using Apollo server for our graphQL server layer. Then we also have this initialized database that we're pulling in from initialize. And if we take a look at initialize, we can see that we're running here, a cypher query that says CALL apoc.schema.assert. And then it is giving a list of labels and fields. So what this is doing, this is a call to the OPAC standard library for Neo4j, basically to ensure, that we have set up some indexes and constraints before the database comes online. Before our GraphQL API comes online, but it uses the OPAC library. So that's what our error here is, it's saying that, here we have not installed the OPAC library. So let's go ahead and do that. And you update desktop. So we're going to click on manage our database, plugins and then install and restart OPAC. So that will add the OPAC procedure library to our database, and then restart our database to register that. Okay, so that is online. Let's open up Neo4j browser and we can just verify that we did indeed install OPAC. So now we can say call DBMS.procedures. And this will tell us what procedures we have installed in this database. And we can see, we have a lot of procedures, under the OPAC namespace. Cool, so let's try that again. Come here and run start. And GraphQL server ready at local host 4,001/GraphQL. So let's check out local host 4,001/GraphQL. Okay, so here is GraphQL playground. So we can click on docs, and you can see the entry points, into our API so we can query it for users, businesses and so on. So here's the query, let's search for users and get all their names. And of course we have no data that we get back, because we have no data in our database. So let's solve that problem. I'm gonna switch back to Neo4j browser, and run call on play GRANDStack. This command, this : play command. This is one that we can use, to load these browser guides in Neo4j browser. This one is really simple. It's mostly just linking a cypher script, for loading some data, but there's lots of others. For example, : play movies gives us, the movies data set. I think there's : play Northwind, which teaches us how to work with Northwind graph in cypher. So these browser guides are really handy. When you first log Neo4j browser, you see this : play start browser guide that runs, that links us to some other browser guides and so on. Anyways, so : play, GRANDStack links us to a browser guide that has this embedded cypher query. This is just loading some data, from a CSE file, and then creating some data in the database. And this just happens to match the graphical schema, that we have defined in the starter project. We could also load the same data sets. There's a CDB script, which runs these mutations, which basically it loads the same CSV file, but then runs these graphical mutations to create the data in the database rather than doing it in cypher. But it's the same dataset either way. Cool, so we can now verify we have data. If we say match (n) RETURN COUNT. And we can say, yep, we've created 36 nodes. So not a whole lot of data, but enough to play around with. Now, if we run this GraphQL query again, we can see that, yap. We have a handful of users. They have some user ID. They've also written some reviews of businesses and so on. Cool, okay. So now we have some data coming from Neo4j through our GraphQL API. And right now anyone can access this GraphQL API. It's just protected, It's just unprotected here anyone can just run a query against it. What we want is to add some authorization rules, to say, hey, no, you have to be an admin user, or you're only able to see your own data, things like that to start to protect the API. So let's take a look at the GRANDstack docs. And if I search for authorization, there's this page GraphQL authorization and middleware, which I will travel link to in the chat, since this is fairly important for what we're gonna focus on today. So authorization in GraphQL is fairly open-ended, GraphQL is very unopinionated about the best authorization technique and they rather, you know, sort of leave it up to whoever's implementing the API to use whatever authorization technique, they deem appropriate. It may be, you know, something that you're just sort of passing through. If maybe your GraphQL API is sitting on top of other APIs, maybe you're just taking an authorization token and passing it down further. But in our case, we actually want to build the authorization layer, at our GraphQL API layer. So there's lots of options, for how we can go about introducing, authorization to our GraphQL API. I think today we'll talk about a few of these, and then maybe we'll take a stab at maybe implementing maybe two different approaches here. So if we look at the docs here on this page, the first option is GraphQL authorizations schema directives. So our GraphQL schema, let's jump over to that schema GraphQL, where we're defining types. This option is to add directives to the schema. And there's three specifically here. So is authenticated has role and has scope. So with this approach, what we would do is in our GraphQL type definitions, we would sort of annotate a type, or a field to say that you have to be authenticated, whatever that means. We'll explore that in a second, in order to access any of the data. In this case, on the user type. We could also do that on the field and say, well, I don't know, to get the user ID, you need to be authenticated. Then we can also add the hasRole schema directive. And this directive takes an argument called roles, which is an array of possible roles, that the user must have. So it could be the case, maybe we want to say the user to access any of the user data, they need to be authenticated, but then to be able to access user ID, they need to have a specific role of admin, maybe something like that. Okay, but how do these schema directives work. Well, this specific schema directive package that's built in to Neo4j graphical JS is called GraphQL auth directives. Here we go, and this is a library that, a package that exports these three directives is authenticated has role and has scope. And they specifically work with JSON Web Tokens, which we'll talk about in a second. Okay, so that's sort of one option, is we sort of annotate our schema to define what the authorization rules are, who can have access to which bits of data. And then we somehow wire that up to a JSON Web Token. And somehow those authorization rules are enforced, thanks to this GraphQL off the directives package. Okay, so that's one option, another option is this one here, that says cypher parameters from context. So in this case, this works specifically with the cypher schema directive fields and types, so for example, here in our schema user we have average stars. So this is a computed field, define the logic with cypher, and in this case we're not using any arguments in the cypher query, but with this cypher parameters from context method, this gives us the ability to inject values, into this cypher query, that we've defined in the schema here. So for example, looking at the example in the docs here is a bit more relevant so here, we're defining a current user field on the query type where we're saying MATCH a node with the label user, where the ID is cypher params.current user ID, and then returning that user. So sort of a custom query field searching for a user. And then somehow this cypher params.current user ID is injected into the query as a cypher parameter. So where is that cypher params current user ID value coming from? Well, in this case, when we spin up the Apollo server instance, we have to define the context object and the context object can be defined, in this case with a function. So at query time, it's passed in the request object. So this is the HTP request object. And here in this example, we're injecting the driver. This is the Neo4j driver instance. But then if we also create this cypher params object, any values here will be injected, into our cypher query here. So in this case current user ID is the value of user.ID from the request object. So in this case, maybe we're using some sort of middleware to add a user objects to the request, before the graphical resolver runs. And in this case at query time, now in our generative cypher query, we know the value of the user ID. So we can only return the authenticated user. Okay, so that's another option, for adding authentication and authorization in the case where we're working with cypher directive fields, other more general options are things like inspecting the context object in the resolver itself. This one, this is useful, if we're implementing customers resolvers, rather than relying on the generated database queries from the particular GraphQL JS, we still can use this option, with utograph JS. Basically what we're doing here is implementing the resolver function. We have some sort of authorization check. This could be in this case, we're just looking for, to make sure that there's a user object attached to the request, but this could be maybe going out to some external system to verify that the user has access to this resource that they're requesting whatever sort of custom authorization logic we have. And then if we do pass that authorization check, then in this case, we're returning the Neo4j GraphQL function, which will then generate the database query and send the results back. So this is sort of, in the case where we still want to rely on that generated database query functionality, but we have maybe some more custom authorization rules that we need to implement. The additional labels directive, this is useful for maybe something like multitenancy, where we have an additional label, maybe a label representing the tenant or something like that that we want to add to the generated database query. So we can define rules here. Also leveraging the same cypher params object from the context object. So, in this case, we're maybe attaching a user ID, and we have a label, an additional label on each node in the graph representing the user ID that has access to that, to that node. Then we have middleware options, and this is, this is a wide ranging option here, that gives us a lot of flexibility. In the example here we're using some middleware in this case just to check for an air header. And if we find an air header, then we add that to the request object, which then throws us, throws on air when we tried to resolve the data. But there's lots of options for adding middleware. Sakina Finn says thanks for the stream. Thanks for joining exactly what I've been looking for. Great, cool. Thanks for joining today. Cool, so yeah, so that's kind of an overview of the different options and kind of refresh what we wanna do. We talked about some of the various options. Let's see if we can implement maybe a couple of these on the API that we just created and then figure out how those work. And then what I wanna do next week, is go back to our real estate search app and actually implement some authentication and authorization functionality, to our real estate search app, both on the front end and the back end. Cool, so let's try to get one of these options working. Let's go with the cypher parameters from context. So let's try this approach first. So let me remove these schema directives that I added, maybe that'll be our second approach looking at our schema directives. So is authenticated and has role, let's remove those. Cool. Okay, so let's see. So maybe let's add a new query field where we want to return the authenticated user. So the case here might be, you know, I have maybe like a profile or dashboard page and when I'm logged in, I wanna be able to see, you know, all of my user information, my user ID, and then maybe all of the reviews and things like that, that I've made. So let's add a new query fields. Let's call this current user. This will be kind of similar to the example over here. I think that's fine. Sakina Finn says, not sure if you answer questions on the stream. We do, of course. The other question about how one would go about using an existing Apollo server with authentication with Neo4j. Yeah, do you mean you have an existing authentication functionality in your existing GraphQL API, and you're looking at how to add in your data that, or is it more of the case where you have an existing, yes. Okay, yeah. Yeah, so we'll cover, we'll cover maybe some areas that are helpful to that today. Yeah, using generic JWTs, so when we get to our schema directives, we'll use just any JWT provider for that. And maybe that will help answer some of, some of your questions on that, but yeah, if you want, maybe let's check in once again, once we get to that second option, and see if that maybe gives you some ideas. Cool, so we're adding here our current user fields, that's going to return a user object and we're gonna define. Oops, headphones went out for a second. Can you hear me? Hope so, hope so, shout, if you can't hear me. Challenge of bluetooth headphones. Okay, so what we wanna do, is search for a user, where, I think we call it user ID instead of ID, user ID, is some values, so starting out here, let's, let's just hard-code something in. So if we go to, we have two browser, and let's just pick some user random, is ID one, so, where user ID is U1 and return user. Okay, so it's restarts API that change. Cool, so now, oops, what happened there? Some texts air, oh, double quotes. You can use a single quote in here, or we can escape it, but you just use a single quote. Okay, cool. So now if we take a look, we have a current user, query field. So you can say current user, name, you get back well, user ID, and maybe all of the reviews this user has written, for the businesses and so. Okay, cool. So that is always just gonna give us this user, 'cause we've just hard-coded that value in here. But instead, what we want is to return the actual user, that's actually authenticated into our application. I'm gonna cheat a little bit, and just add a header here. Let's call this user ID. So in the real world, instead of passing in, in the header, the user ID, this would be some sort of token like a JWT, a JSON Web Token, or we might have some middleware that adds this to the request object. That's okay, this is fine. We'll take a look at JWTs in a second. Okay, so we're passing in, in the request, header the user ID. So instead we can go back to the docs here. It's grabbing cypher params. currentuserid So cypher params, let's say user ID, call it that. Do we have, I think we have a watcher script, is it. Oh yeah, start, not start run and start, : dev. So I think this one, this will have a watcher. Oh great, and it's also in debug mode. So now, now when I make changes, yeah, there we go. Now, when we make changes to the schema file, those get picked up, and when we run a query, we get the debug outputs, which is the generated cypher query. Cool, so that was the one I wanted. Okay, so now when we run this query, it says air fill the invoke function, if it's column caused by parameter not found exception, expected parameters, cypher params. Yeah. So our cypher query here is saying, that we're going to pass in, a parameter called cypher params. And I want this user ID to come from my request header here user ID, but that's not being injected. Why is that? Well, if we look at the docs here, it says that when we spin up Apollo server, we need to specify this cypher params object in the context object. So let's jump to index JS and here where we're starting Apollo server, here's the context. We're just hard coding it, passing in the driver. And then this is in the case where we have, a non-default database specified. So Neo4j has the ability to have multiple databases and we may want to connect our GraphQL API to the non-default, so we pass in the Neo4j database value, but we don't need that, we're just using the default database, but instead of passing an object, we can also pass a function here, that is passed in the request object. And then our function, is going to return the driver objects that's defined up here. So this is our connection to Neo4j that we're injecting into the context object so that Neo4j graphical JS can make a request Neo4j, but then we also want to define, the cypher params object and called it user ID. So user ID, we're now gonna grab from the headers and we should have a header called, I'm gonna call it user ID. So let's give this a try, go back and run our query again. Okay, and this time we get Will, and I guess this is me, user ID and reviews, but let's change the text a bit bigger here. Let's change now the header to you two, and now we get Bob's information, and if we send in you three, we get Jenny and so on. So in this case, we're just grabbing the value from the request header, but, no, like we could, we could set this using any logic, so that we have some sort of middleware, that's running here, maybe something like passports, if you are familiar with passports, or if we have JSON Web Token, we can decode that and pull out some of the claims to add the user ID here and so on. It's a pretty flexible approach to be able to inject these values into cypher statements. Cool, so that's the first approach, which is cypher parameters from the context object. What I wanna look at next, is the graphQL authorization, schema directives approach. So that was the case where we're adding these rules by annotating our schema, with these schema directives. And these work with JSON Web Tokens. So this has worked with JSON Web Tokens and assumes JWT is included in the GraphQL request header. Okay, so that's going to be something kind of similar, to how we're sending this header here, but instead of the raw user ID, it's gonna be, I don't know, something like this token and then some gibberish, something like that. So the client then is gonna to be responsible for adding that header. So if we're thinking of this in the context of our real estate search app, where we have a react front end, the react front end is gonna be responsible, for adding that token to the graphical request header. And we'll talk about how to do that, with the Apollo clients next week, when we get to that step. Okay, so it assumes a JWT is included in the GraphQL request header, and then the claims contained, in the job roles, scopes, et cetera, are used to validate the graphical request, protecting resources in the following ways they get the claims contained in the job roles, scopes, et cetera. Okay, so let's talk a bit about JSON Web Tokens. There's this really nice site, that I like called Online JWT Builder. So, JSON Web Token, is basically a bunch of claims. So a JSON object here, that is then encoded into some token, we can encode this one right here. So create signed JWT. So we just get some, some token here that we can then use to express these, they are called claims, but that's basically the individual elements in this JSON object. So then when say we send this token in this case, from a GraphQL request, or as a header in a GraphQL request, when the API server gets that, they can take this token and they can then inspect the claims and say, oh, well, you are saying that you are user Johnny Rockets and that you have roles, manager and project administrator, great. So I know now to go apply my authorization rules and I can then give you the resources that you have access to. How though does the API server know that, this is actually a valid request, that you actually are Johnny Rockets? And that's where the key comes into, so when we are in coding, one of these JSON Web Tokens, when we're taking the claims and generating this token, we use a cryptographic key. And because of public encryption magic, if I have the private key for this, I can then on the server, I can then verify that someone with the correct key actually did in code these claims and that it is indeed Johnny Rockets, who is making this request. So I kind of insist, so the job would need to contain a role array for has role. Yep, exactly, so if we look at our example here, so theirs is authenticated, so is authenticated. All that is doing is on the server, it is using a key to decode the token that's passed in. So it's just verifying that this is a valid signed JWT, that it was encoded by someone who has a valid key. So therefore they are authenticated for this API. It's not inspecting any of the claims in the JWT. If we look at hasRole, hasRole takes an argument roles. And so we need to ensure that in this case, admin is one of those roles. Let's give it role and then user and admin. Cool, so if we copy this token, and if we go to, we can go jwt.io, which is another fun online web app for decoding JWTs. We can paste that in, and we can see the claims, and you can see that, yap, this user has role user and admin. Now this is not at this point, it's not verifying the JWT because we don't know what the signature is. Don't know what the key is, but we have that here and says, yep, okay. That is verified. That is a valid signed JSON Web Token. Signature verified. So in this case, we should give Johnny Rockets access to whatever a user and admin whatever a user with those roles has in our API. So let's add this to our API, starting off, let's just say is authenticated. So add this, is authenticated directive on the user type. So in order to access any of these fields on the user type, you need to add a valid signed JSON Web Token. And we get unknown directive is authenticated. So that's because we needed to declare this directive. We can't just go adding any random directives to our schema without declaring them. And so to do that, we need to update the configuration object, in our call to make augmented schema. So make augmented schema. This is how we use the autograph JS to generate database queries. Yep. And Soccer Nathan has exactly got it for me here. Yeah, so we can add a schema directives object, or we have this to the call to make augmented schema, but we can also add in the config object, this is kind of a helper for declaring these, where we just sort of enable these. So is authenticated, is true. So what Soccer Nathan and posted here something like adding a schema directives key, and then adding is authenticated. And is authenticated directive. And that's true, that would work to do that, we would have to do something like install this GraphQL directives library, and then it would be look something like this. But because these are bundled in, you have to graphical JS. We can just use this nice helper to just enable this is authenticated. Cool, so now our server restarted, and now if we go here, let's try to request all usernames and we get an air. Can it be property cookies of undefined. The error message is not really very helpful. What it's saying though, is that we don't have access to this resource because this is not an authenticated request. So there's a few more configuration things we need to do. So we needed to, clear the secret somewhere. So let's stop our server and we'll say export JWT secrets. And this is the key here, that we use to encode the token. And then when we make the request, we want to ensure that we're sending that in the headers. And if we look at the example, so we're adding an authorization header as a bearer token. So I think the format that we want is authorization for the key, and then the value is bearer. And then let's grab our token. Let's give this a try, fail to fetch. 'Cause I need to restart, our GraphQL API So we are ready, read property cookies of undefined. So, let's see. What did we miss here? We added this authenticated schema directive reset the JWT secret, environment variable. Enabled to auth directive by default we look at role, role role roles. Yap, that's fine. You don't need to override this or the value of auth directive role key, we don't need to do that. Did this? We're not yet working with auto-generated craze mutations. We're not doing custom. Yeah. So what's going on there? Can we be property cookies of undefined at verify and decode token, and our objectives. Interesting that it's talking about cookies. Let's take a look at the code here and see what's going on. So, source index. So here's the is authenticated directive and there's the declaration, the object. We first decode, verify and decode token, And then we call next, so you can see by the way, this is how we implement schema directives. So this is using the GraphQL tools, schema directive visitor package from the GraphQL tools package. And this makes a really nice way, to sort of build these custom schema directives. A schema directive is basically anything that is defining any custom logic on the server, right? So in this case, this package is saying, do something with a custom auth rules, but we can define any sort of custom logic in these directives. But anyway, let's go back to our area. So property is an undefined field that resolve. Is there anything working with cookies here? Nothing mentioning cookies. Nope, that's not the one I want, I want to this one. Yeah, so do check to see. So there's a few options for where we can pass. I think I realized what I forgot to do here, what I need to do is pass the request object, into the context. There we go. So important step missing in the documentation here is that we need to inject into the context object, the request object, or at least one of these headers. So if we look at the code, here, let's verify the code, that takes the context object. And there's a few options, that it's kind of searching for, to look for these headers. So is it a request object or is it a context? An object is something in headers, is in the cookies, is in auth header and so on. Anyway, none of that works, If we don't inject the request object into the context. Okay, so missing that step, that's an important one. Let's make a note on the docs page. Was this content helpful? No, it was missing. What was it missing? It was missing a step to inject request object into a context for GraphQL off directives. So I think that's not covered here at least I didn't see it anyway. Yeah. Not seeing it in that section. Okay, cool, so that is something that would be helpful to add to the docs. But anyway, looks like we got this working. So now, if I have this valid token, then I get access to this data. Let's add an explanation or something here just to change the value of our token. So now this token is not able to be decoded. So it says, hey, you are not authorized for this resource. Your token is invalid, can't be decoded with cue Okay, so that is authenticated. What if I want to look at the roles now. So let's try, okay, you have to be authenticated, to view the user object and maybe to view the user ID, what you need, the admin role. So we can apply these directives on the type or the field. On our air here, is saying that we don't know what the hasRole directive is. So let's add that to our config object, let's say hasRole, true. Well, you know, all right. So just like the example here, we need to, instead of just passing in a string, we need to declare a role, you know, admin or user I think are the two that we used and then, that shouldn't be admin. Okay, cool. So now let's give this a try so I can work at username. I add user ID. I get the user ID because I have the admin role. That, that makes sense. If I look at my JWT, yap. I've got admin here. Let's take that out, and create a new token. So now this token no longer has the role admin. So if I make a request with this token, I should get an air, that says you do not have access to this resource. And that air is added for each of these requests, but I can still, access user name because that texas, I just need to be authenticated. But if I don't send an authorization token at all, well then I don't have access to that resource because texas user, I need to be authenticated. I can still access other information like business and so on. But as soon as I get to, so I can access business reviews, reviews have text and each review has a user. But as soon as I request user information, then I don't have access to that resource. And that applies also. So if I now make a, oops, where did my token go, is this my token, I now make an authenticated requests So authorization, bare with my token, this is a valid query, because I can go all the way down to the username. But as soon as I add user ID, now I get the other, I don't have access to this resource because my token does not have the admin role. But if I add that, so anyway, the point I'm trying to make here, is that this, these queries work not just from the roots, not just for protecting the roots query fields, but anywhere in the GraphQL selection set, those authorization rules are applied. The cypher params is basically an alternative to solution you're implementing now at JWT. So you wouldn't need both. Yeah, so I guess there's, more flexibility perhaps with the cypher params approach because I'm using cypher and I can define my auth rules using cypher. So maybe I have, maybe I have a more complex query. That's like, here's the user. And then maybe it's like in the graph, I'm storing something, like has access to, I don't know, books or something. And wanna know what all the books, that this user has access to. Maybe there's some additional level of complexity beyond just sort of like scopes and roles that I can express in a DWT claim. So it could be the case where you wanna to do, where you wanna to use both approaches, simply because cypher params just gives more flexibility to express anything and insight there. But yeah, that's a good question. So Delt says the JWT Builder is very handy. Yeah, oh yeah, let me paste the link to that. Yeah, that's super, super useful. If you just google, JWT Builder, you'll find it. But yeah, that's super nice. So both can have it's use at the same time. Yeah, exactly. Yeah, so we can use both of these approaches. We just need to be careful that, you know, for example, here, we're using the cypher params, on the current user query field, to return a user object. So we need to make sure that we also are meeting any auth requirements, that we have defined with our schema directives as well. Yeah. Cool, so those are the two auth approaches, that I wanted to actually touch on because these are the two that I wanna use, or at least starting with our schema directives, and then also looking at the cypher params in our real estate search app. So, yeah. So to answer your question about using both at the same time, is yes. We will see next week, we'll see that hands on. When we add this functionality, to our real estate search application, we'll see how we can use this schema directives, to protect some resources based on role and scope. And then we'll start building out something like a user dashboard, where I need to see only, the authenticated users information. Cool, So I think, I think that is probably enough for today. Jenny Cat wants to come out of timeout. So I think we'll call it a day there, and tune in next time, where we'll dig into, with our real estate application a bit more. Oh, yeah, before we go, a couple more questions. Does this indication automatically add to mutations? Oh yeah, so that's one thing that we didn't cover. So we have these generated mutations, right? So for you look at our docs, things like create user, update user, delete user, how do we? Because those are generated. We can't like add a directive, to something that's not in our schema. Right? So, the way that works, is if we enable the has scope directive. So and there's a section, in the docs here attaching directives to auto-generated queries and mutations. So if we enable, I'll paste the link to this page, if we enable the has scope directive, then it's automatically attached, to all of the generated mutations, and you would then need, to have the correct scope for each operation. So for example, like create movie is added, it has scoped directive, but this convention movie create. So then there's, you'd also have the same, for movie update movie delete. So your JWT token would need to have, a scopes claim with movie creates, movie update, movie, delete, to give that user access to those mutation operations. Yeah, and so we'll definitely need to, to cover that in our real estate search app. So we'll definitely go into more detail on that one. Another question, quick confirmation, your earlier explanation about the cypher prams, means that one could then could use them, to achieve an authorization similar to the one in this graph gist. Let's take a look at the graph gist, entitlements and access control. Let's see, so, oh, yes. This is like where we're expressing, the like identity and access management rules in the graph. Let's find an example here. So administrator is a member of some group allowed, do not inherit company child. Yeah, so if you wanted to use this sort of approach, what you would do is, you would take sort of, this isn't the exact query that we want here, but this is the basic idea , is you would take this query, and we'd plop it in here. So some somewhere in our schema as a cypher directive, and then we would do, and in this case we're using name, but more likely it would be some sort of, like user ID or user token. And yet this would be like cypher params.name. So in this case we would inject Sarah and then this query is returning resources that Sarah has access to, where those rules are expressed in this more complex graph pattern. So, yeah, that's totally something that we can do. Maybe we'll try to add a more complex example, like that in our real estate search app, but yeah, that's a good one. Cool, okay, I'm gonna sign off for now. Thanks everyone for joining. And if you can tune in next time, do this every Thursday at 3:00 PM.
Subscribe To Will's Newsletter
Want to know when the next blog post or video is published? Subscribe now!