[keycloak-user] Policy Enforcing for nodejs REST Api

Pedro Igor Silva psilva at redhat.com
Wed May 15 13:55:58 EDT 2019


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