[keycloak-user] Policy Enforcing for nodejs REST Api
Pedro Igor Silva
psilva at redhat.com
Tue Jun 4 08:06:30 EDT 2019
Differently than other adapters, the NodeJS adapter does not fetch
resources from the server, so you need to use the enforcer on each route:
app.get('/api/users', keycloak.enforcer(['users'])
app.get('/api/devices', keycloak.enforcer(['devices'])
Please, create an RFE if you the current behavior is not enough for you.
On Tue, Jun 4, 2019 at 5:23 AM Lasse Jahn <lasse.jahn at student.hpi.de> wrote:
> Hey Pedro,
>
> sorry for the really late reply. There've been some other issues I had to
> fix first before I could come back to authorization. But now I try to get
> this done.
>
> Unfortunately I don't really get the thing with the resources and the
> regarding URIs.
>
> I want the keycloak enforcer middleware only called at the one point, like
> I explained. Based on the called route (e.g. /api/users or api/devices) I
> would like to have only the permissions of the resource evaluated.
>
> I guess somehow I just miss a thing and it should be easy possible.
>
>
> What I did:
> 1. Created 2 resources (users, devices with URIs "/api/users" or
> "/api/devices"
> 2. Created 2 permission and policy (users grant always, devices deny
> always)
> 3. Added the keycloak enforcer middleware before the router.
>
> ****
> app.use('/api', keycloak.enforcer(['users', 'devices']), routes);
> ****
>
> Unfortunately when I access /api/devices this is allowed.
>
> I would like to create resources on the client and have one policy per
> each to decide. Is it possible, that the enforcer checks which resource is
> requested and uses only that one.
>
> If not what is the URI of a resource for?
>
>
> Regards Lasse
>
>
> On 15.05.19 19:55, Pedro Igor Silva wrote:
>
>
>
> On Wed, May 15, 2019 at 8:52 AM Lasse Jahn <lasse.jahn at student.hpi.de>
> wrote:
>
>> Hi Pedro,
>>
>> thanks for the quick reply. So I got it working now, that the resource I
>> created is enforcing the one policy. For a single resource this is great.
>>
>> Later on I would like to have an multi tenant solution, short explanation
>> what I mean:
>>
>> Different companies have the same functionality but maybe want to change
>> the restriction for there self. But still with only one backend application
>> running. So each company should get one realm with the backend application
>> registered as a client. When they call the api the backend should enforce
>> the policies of the company specific client.
>>
>> Therefore I have 2 questions:
>>
>> 1. Is it possible to configure the enforcer to enforce all policies for
>> all resources only depending on the requested on the path. So I only to
>> have to add the middleware once before the express router. So for example
>> we have a route /api/devices and /api/users (GET,POST,DELETE each). Both
>> are represented by a resource in the keycloak admin console.
>>
>> I would like to have something like this:
>>
>> router.js
>>
>> ****
>>
>> const express = require('express');
>> const router = express.Router();
>> const users = require('../controllers/users.controller');
>> const devices = require('../controllers/devices.controller');
>>
>> router.post('/users/', users.create);
>> router.delete('/users/', users.deleteAll);
>> router.get('/users/', users.findAll);
>>
>> router.post('/devices/', devies.create);
>> router.delete('/devices/', devicese.deleteAll);
>> router.get('/devices/', devices.findAll);
>>
>> module.exports = router;
>>
>> ****
>>
>>
>> app.js
>>
>> ****
>>
>> .... //all from before
>>
>> app.use('/api', keycloak.enforcer(__SOME_CONFIG__), routes);
>>
>> ****
>>
>>
>> I dont want to write keycloak.enforcer(...) to each line of users or
>> devices...
>>
>> Maybe this can done by the claims and the context information? But if yes
>> I don't get how.
>>
> Yes, by using claims you are allowed to use them in your policies. Here is
> an example:
> https://github.com/keycloak/keycloak-quickstarts/blob/latest/app-authz-rest-employee/config/quickstart-realm.json#L90.
> More details here
> https://www.keycloak.org/docs/latest/authorization_services/index.html#examples
> .
>
> Your keycloak.enforce would be similar to
> https://github.com/keycloak/keycloak-nodejs-connect/blob/master/test/fixtures/node-console/index.js#L177
> .
>
>>
>> 2. For now the solution is only single tenant, but If I want to have it
>> multi tenant and have realm per company with similar clients (only
>> different in policies and permissions). Do you have an idea how I can solve
>> the problem that the keycloak object is configured realm specific?
>> I would probably build a middleware which checks for a custom HTTP header
>> or looks for a subdomain for referencing the company. Depending on the
>> company I would set the keycloak object.
>> Do you think this can work ? Or do you have a better idea?
>>
> AFAIK, this is how you do it. So that accordingly with the request you
> build a new Keycloak object using a specific realm.
>
>
>>
>> Some suggestions for your documentation:
>>
>> - Could you somewhere describe what the middleware option protected is
>> doing? The Logout, ... options are explained, but the protected I couldn't
>> find.
>> - Your default resource is called 'Default Resource' but in the example
>> the resource is renamed to 'resource' the rest is untouched and default
>> config. Maybe a comment or adjustment of the example might be helpful.
>>
> Thanks for the feedback. Feel free to create a JIRA so that we can track
> and plan the improvements you are proposing.
>
>>
>> Regards,
>> Lasse
>> On 14.05.19 20:33, Pedro Igor Silva wrote:
>>
>> Hi,
>>
>> We've added more docs to NodeJS PEP recently [1]. They should be
>> available in the next release. Please, let me know if that is enough or if
>> we need to add more information.
>>
>> In your case, this code:
>>
>> app.use('/api', keycloak.enforcer({WHAT_COMES_HERE}), routes);
>>
>> Would be:
>>
>> app.use('/api', keycloak.enforcer('{resource_name}:{resource_scope}'),
>> routes);
>>
>> If you have a resource in Keycloak called "foo" and a scope associated
>> with this resource called "bar", the code would be:
>>
>> app.use('/api', keycloak.enforcer('foo:bar'), routes);
>>
>> Hope it helps.
>>
>> [1] https://github.com/keycloak/keycloak-documentation/pull/654
>>
>> On Tue, May 14, 2019 at 1:25 PM Jahn, Lasse wrote:
>>
>>> Hello,
>>>
>>> It's the first time writing to keycloak mailing list (I hope this is the
>>> correct one?) so excuse if I forget to provide some information or any
>>> other mistakes ..
>>> Sorry for the text wall.
>>>
>>> Shortly what I try to do (maybe I got something completely wrong):
>>> I create a backend (node.js Bearer Only) which shall offer an REST api.
>>> Partially it is used via a frontend (keycloak-clients) or directly by some
>>> devices.
>>> In general I try to create an application with a lot of CRUD. User
>>> Management is done in keycloak and only I forward these requests to the
>>> admin REST Api. Other stuff like the devices ... I store in a separate
>>> database.
>>> So the backend is the abstraction layer for frontend and other use-cases.
>>>
>>> So far so good, but for the beginning it was enough to check weather the
>>> request comes from an authenticated person or not, so all handled via
>>> keycloak.protect() The Token from the authenticated person was passed
>>> But now I'd want to offer different authorization level (can differ due
>>> to reasons of multitenancy, why I want to solve this via policies and co in
>>> admin-console inside the client configuariton) because the normal user
>>> shall have access to only some routes and the management shall have full
>>> access to the api, but of course don't need the keycloak admin access.
>>> So I enabled the service account for my backend client and gave this one
>>> the realm-admin role so the client has access to everything and I can
>>> handle the authorization inside the backend client it self (using policies,
>>> permissions, .. inside the admin-console).
>>> (Just in case no one gets what I'm talking about. Fixing [1] should help
>>> me fixing my issue I guess)
>>>
>>> Setup
>>> - node.js application using express
>>> - registered as single client in keycloak admin-console (confidential,
>>> but config inside the code is bearer-only)
>>> - Keycloak is running in a docker-container (version 4.5)
>>> - all services are running in a docker-compose network and are behind a
>>> reverse proxy for common uri
>>> - enabled Authorization in client and changed the default policy to
>>> Negative to always deny => to see if it is enforced)
>>>
>>> My Problem
>>> I don't understand how to use the policies, permissions and Co I created
>>> in the admin-console inside the backend it self. How do I enforce that
>>> these are used?
>>> I tried to check different examples and documentation, but could get it
>>> working.
>>> The last thing I found was that the entitlement api was removed, but a
>>> policy-enforcer was added to the nodejs adapter. In the documentation for
>>> the policy-enforcer [2] I couldn't find a documentation of the middleware
>>> (keycloak.enforcer({}) [3][4]).
>>>
>>> My Code
>>>
>>> *****
>>> app.js
>>>
>>> const express = require('express');
>>> const app = express();
>>> const Keycloak = require('keycloak-connect');
>>> const session = require('express-session');
>>> const routes = require('./routes/index');
>>>
>>>
>>> const kcConfig = {
>>> 'realm': 'master',
>>> 'bearer-only': true,
>>> 'auth-server-url': `https://DOMAIN/auth<https://domain/auth>`,
>>> 'ssl-required': 'all',
>>> 'resource': 'fm-backend',
>>> 'credentials': {
>>> secret: 'SOME_SECRET',
>>> },
>>> 'confidential-port': 0,
>>> 'policy-enforcer': { //tried with an
>>> without this, changed nothing
>>> 'enforcement-mode': 'ENFORCING',
>>> },
>>> };
>>>
>>> const memoryStore = new session.MemoryStore();
>>> const keycloak = new Keycloak({ memoryStore }, kcConfig);
>>>
>>> app.use(keycloak.middleware({ logout: '/api/logout', protected:
>>> '/api/gates' }));
>>>
>>> // used before, worked for well for authentication
>>> app.use('/api', keycloak.protect(), routes);
>>>
>>> // now unfortunately I don't understand how to use keycloak.enforcer()
>>> middleware
>>> app.use('/api', keycloak.enforcer({WHAT_COMES_HERE}), routes);
>>>
>>> module.exports = app;
>>>
>>> *****
>>>
>>> [1]
>>> https://stackoverflow.com/questions/53722033/how-to-enable-policy-enforcing-in-keycloak-for-node-js-application
>>> [2]
>>> https://keycloak-docs.github.io/deploy-docs/dev/master/authorization_services/index.html#_enforcer_overview
>>> [3]
>>> https://github.com/keycloak/keycloak-documentation/blob/master/securing_apps/topics/oidc/nodejs-adapter.adoc
>>> [4]
>>> https://github.com/keycloak/keycloak-nodejs-connect/blob/master/example/index.js
>>>
>>>
>>> Any Help is appreciated :)
>>>
>>>
>>> With kind regards
>>> Lasse
>>> _______________________________________________
>>> keycloak-user mailing list
>>> keycloak-user at lists.jboss.org
>>> https://lists.jboss.org/mailman/listinfo/keycloak-user
>>>
>>
More information about the keycloak-user
mailing list