I'm building a GraphQL API for a mobile app backend. I'm building it on top of Laravel using rebing/graphql-laravel, and I'm having a bit of trouble.
Most of the queries and mutations require that the user be logged in, so they use authentication middleware to limit access (though at some point I plan to replace this with JWT, which is what will be used in production). However, there are two mutations that should be accessible to users who are not logged in - one to pass through the login details and get a JWT, the other to register the user and get a JWT.
The only way I could find to do this was to move these mutations to a separate auth schema. However, despite rooting through the documentation I can't for the life of me find how to specify a schema other than the default.
The mutation for creating a user looks like this:
mutation {
createUser (
email: "jeff#example.co.uk",
name: "Jeff",
password: "password"
) {
token
}
}
It works if I move the mutation to the default schema, but of course that's no use for my use case.
How do I need to adapt this to use the auth schema instead of the default schema? It sounds like it should be straightforward, but I can't for the life of me find it via Google.
Turns out this is straightforward enough, just seemed hard to track down.
The URL for both the GraphQL endpoint and the GraphiQL interface has an optional parameter for the schema name field, as show in the relevant lines from the output of php artisan routes:list. This includes the following routes:
graphql/{default}
graphql/{auth}
graphiql/{default}
graphiql/{auth}
If you navigate to the graphiql/auth in the browser, you can therefore use the queries and mutations registered to the auth schema there. Similarly, you can query graphql/auth as usual. In both cases, you'll have the middleware set for the auth schema applied.
If you don't specify the second URL parameter, then you'll be using the default schema.
Related
I have a keycloak server setup. I am using the token endpoint: http://localhost:8080/auth/realms/demo/protocol/openid-connect/token to authenticate a user and generate a token. This token I understand can be used in subsequent calls to verify if it is a valid user.
However, I am not sure how do I use this to authorize the user? ie verify if this user has the roles to access a resource.
I see that it is possible to configure a resource URI under the client section. But once that is done, I want to be able to read the token and verify if this user has the roles to access this resource.
Right now, this is what I am doing:
I have used spring boot here.
doSomething(String token)
{
1. get token info using: http://localhost:8080/auth/realms/demo/protocol/openid-connect/userinfo
2. from this get the roles the user has
3. Manually check the roles required for the above function. (Right now, this is set in a simple switch statement)
4. If the role obtained from step 2 matches what we get in step 3, go ahead. Else return failure.
}
I want to know if step 3 above can be done in a better way. I know taht you can set a resource in clients from the keycloak console. What I was hoping is we could replace the 4 steps above with something like:
keycloakAPIToAuthorizeToken(token,resource)
which would tell me whether this user has the roles (obtained from token) to access this resource.
Please suggest if this is doable.
Thanks in advance.
Om
There are MANY ways to do this. One of them is to use Keycloak's roles, and assign those roles to users. That way, in your server/api you can check if the user has that role and proceed or reject the call.
Example, in my api project, I have some endpoints that are exclusive for system administrators, so I have a role SystemAdministrators:
and then, if you go to Users, Role Mappings, you can add that role to the users to set as admins:
Then, any api call should include a bearer token (obtained from the Keycloak login), in your code, you can decode this jwt (it will depend what language you are using, I use python so its pretty simple), it will have an element "realm_access" and inside it, a "role" element, example:
"realm_access": {
"roles": [
"offline_access",
"uma_authorization",
"SystemAdministrators"
]
},
If that element contains the role SystemAdministrators, you know is a system administrator.
I found this is the simplest way to do it. You can get fancy and use role attributes to determine individual options inside screens, etc (attributes are key/value pairs so the way I implemented this is, the key is an option name and the value is the permission level, for example: "screen1": "read", "screen2": "write", and so on. For this, you would need to use the keycloak admin api: https://www.keycloak.org/docs-api/6.0/rest-api/index.html#_roles_resource which has many endpoints that can help you.
Leveraging Laravel Passport ... is there a way to protect one API route with two grant types but as an OR statement and not that both need to pass?
I have been looking for some sort of solution for this but can only think of creating two separate routes - one for the "client credential grant" and the other route for the "password grant" ... which ideally I would like to just have one API route and depending on the grant type you end up using the JSON we spit back changes...
As an example if you look at the docs for the Uber API you can see they protect certain routes with both a server token and OAuth token: Server token or OAuth 2.0 user access token with any valid scope. link: https://developer.uber.com/docs/riders/references/api/v1.2/products-get
I would like to set up an IT solution based on the Front / Back principle.
Front side I would use a technology like React, Angular and Back side I would use a technology like java spring boot to implement controller Rest.
The front will make Rest requests on the back to retrieve data.
I would like to add a security concept to the solution by implementing the JWT standard on the back. Thus the client, knowing the secret, could request a token back and could make requests by specifying the token via the header of the request.
I found several tutorials explaining how to set up this type of solutions. In particular: https://medium.com/#nydiarra/secure-...n-e57a25806c50
In this tutorial, we assume that we define somewhere (here in a H2 database) the different users of the app and their role (admin or standard).
So the front could ask a token but it would have to indicate the user and his password and the secret defined. The back looks in the database and gives a token relative to the role defined for this user.
My question is simple. Do we have to define users and roles if we want to use JWT?
What I would have liked to do is not to inform and not to store potential users and their roles.
Simply the front requests a token with the secret without giving user and the back gives a token. Which will be used later in the header of the requests.
In Laravel it's very useful to access Auth Facade after authentication in order to get user data like:
Auth::user()
What If during session and users interaction I'd like to enrich the object returned by the above call? Maybe assign some attributes to a user after he performed some action on my webapp?
E.g. A user performs a fast registration, and after while completes some other profile data. I'd like them to be available directly in Auth::user() instead of perform subsequent DB queries...
IMPORTANT
I'm integrating Auth0 into Laravel authentication. So the default driver/provider behind Auth is not Eloquent but Auth0.
Auth0 gives back a Json object containing all authenticated data.
Auth0User->userInfo
What I'm trying to achieve is to edit the Auth data after Auth0 Authentication by adding custom data to Session Object.
Basically I want to use another service to manage account related data, and use Auth0 only for managing user/password grant.
You can add a custom attribute to the User model. For example:
public function getTestAttribute()
{
return session()->get('test', 'defaultValue');
}
It is possible to set the session key somewhere else in your application:
session()->put('test', 'otherValue');
I'm new to the whole client-side SPA world. I'm using the above technologies, which seem quite promising. However, one huge snag that I can't get over easily is the lack of built-in security. I had to manually roll out the user authorization, which IMHO should be part of the framework.
Now that I have that sorted, I'm getting major headaches with vertical security: where one user is logged in but can easily access other users' info by changing a few parameters in the browser console. I could pass the userId with every call and then compare it with the one on the server, but I was hoping that there would be an overarching solution that doesn't pollute the breeze data calls with user ids.
For example, let's say there's a call from the data service like this:
function getItems(){
var query = breeze.EntityQuery.from('Items').expand("Person");
return manager.executeQuery(query);
}
this will get all the items, not good. So let's limit by userId:
function getItems(userId){
var query = breeze.EntityQuery.from('Items').where("userId", "==", authentication.userId).expand("Person");
return manager.executeQuery(query);
}
in the second example, we get the userId from the authentication service, which stored the userId when the user was logged in. However, a malicious user can easily go the browser console and change that value.
Of course, I could pass the userId using withParameters(...) and compare it with the current one on the server, but I'd have to do that for every call, which doesn't seem right. Is there a better way to secure calls with the trusted user id?
#Ali - I understand your pain and your concern. You are right to fear any form of so-called security that relies on information passed in the URL. Fortunately there are excellent answers to your concerns and Breeze apps work well with them.
For example, have you studied the ASP.NET Breeze/Knockout Template? It uses Forms Auth for authentication and guards the Web API controller with an [Authorize] attribute. Only logged-in users can access any of the controller methods.
That authentication also sets the IPrincipal which the Web API controller makes available through its User property. You'll see User passed to the constructor of the TodoRepository. In that repository you'll find guard logic to restrict query and saves to just the Todo information belonging to the requesting user.
Look at the network traffic. You won't find any user identifying information in the URL or the request/response bodies. You will see an encrypted authentication cookie in a header.
An obvious flaw in the example is that the client/server traffic takes place in the clear. You must add transport level security (HTTPS) before you go to production. But this is a demo after all.
Why not just do this in the controller?
If the Web Api is secured with [Authorize] then you can get the users ID in the controller and make sure that the data returned is for the currently logged in user.