ferrerojosh / nest-keycloak-connect

keycloak-nodejs-connect module for Nest
MIT License
314 stars 123 forks source link

ResourceGuard not checking URI #123

Open KlausNie opened 2 years ago

KlausNie commented 2 years ago

I've set up a server, with a controller like this

@Resource("server")
@Controller()
export class ServerController {
    @InjectRepository(Server)
    private serverRepository: Repository<Server>;

    @Scopes("Get")
    @Get('/') // full url would be http://localhost:3002/api/v1/server
    async getServerInfo() {
        return this.serverRepository.findOne({});
    }
}

My keycloak connection options look like this:

{
            authServerUrl: this.configService.get<string>('KEYCLOAK_URL'),
            clientId: this.configService.get<string>('KEYCLOAK_CLIENT_ID'),
            multiTenant: {
                realmResolver: this.realmResolver.resolve,
                realmSecretResolver: this.realmSecretResolver.resolve,
            },
            secret: 'fallback-secret-should-never-be-needed',
            "public-client": false,

            logLevels: ['error', 'warn'],
            useNestLogger: this.configService.get<boolean>(
                'KEYCLOAK_LOGGING',
                true
            ),
            policyEnforcement: PolicyEnforcementMode.ENFORCING,
            tokenValidation: TokenValidation.ONLINE,
};

In keycloak I have Authorization enabled, and I've created the following resource, scope, policy and permission:

image image image image

Now everything works fine... except that it should not. In the first image, you see that I did not put an URI there, actually I tried to put anything there, no matter what, the permission evaluation always goes through successfully and the user gets access to that resource/URI combination. Now I'm not 100% sure, but shouldn't the URI, that the user tries to access over my server be checked? And if that URI is not contained in the resource, the access be denied? Am I doing something wrong on the server side?

Or is the URI in a keycloak resource not supposed to be an access check?

ferrerojosh commented 2 years ago

The URI in a keycloak resource wouldn't do anything without a permission and policy. Everything revolves around it, most of the time it is metadata.

KlausNie commented 2 years ago

So basically, specific endpoints, like /api/v1/servers/1 where 1 is a parameter cannot be guarded with keycloak And the URIs do not matter at all... all that matters is the resource name and how you use it in your resource server(to which URL you apply it), not the actual URL you are mentioning in the resource declaration...

Is that what you mean?

ferrerojosh commented 2 years ago

If I understood how Keycloak works then yes, you can cross check by their documentation here: https://www.keycloak.org/docs/latest/authorization_services/

Based on that document, everything revolves around permissions and policies, I don't know how uris are used but I'm guessing its for user-resource ownership metadata.

KlausNie commented 2 years ago

The only thing they mention is:

image

Nothing regarding checking it or using it to authorize by URI.

The thing that bugs me is, I found source code in keycloak where they check the URI and have tests for it: https://github.com/keycloak/keycloak/pull/7947/files But they do this with the help of an AuthZ client. Which seems to be the java client for authorization... looking further I found this lib https://www.npmjs.com/package/keycloak-authz (https://github.com/witcom-gmbh/keycloak-js-bower ) which claims to be the same for JS...

But the documentation ( http://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter ) for this says it is only an adapter for a public client and that again defeats the whole purpose...

The only idea that I have left is: I'll try my own custom ResourceGuard, see if I can get the URIs out of the UMA ticket response and then authorize by comparing the original URL with the one provided for that resource

bonnzer commented 2 years ago

@ferrerojosh @KlausNie Let's say we have following controller and AdminOrResourceOwnerPolicy. We have a lot of resources like <userId>MessagesResource containing respective URIs like /users/<userId>/messages/*. So the question is - how to protect this controller with current Resources guard implementation? I think this guard should be:

What do you think guys? May be my approach is not correct at all?

ferrerojosh commented 2 years ago

The issue is that Keycloak is not checking or using authorization for those URIs at all and is beyond my control since this is just an adapter for their official nodejs library.

I should be closing this issue for now since it might bring confusion to a lot of people.

bonnzer commented 2 years ago

Hello @ferrerojosh @KlausNie I've spent some time investigating how it all works (and should work) and here are my conclusions:

That's it. That's one of the possible ways to evaluate resource access permissions by URI which could be quite useful for some cases. 2nd useful case is actually in #131

So basing on written above :arrow_up: I believe Resource decorator should:

  1. be dynamic (or handle Nest route params somehow at least)
  2. allow to pass URI value
IRuFFeYI commented 1 year ago

so I understand it correctly that at the moment it is not possible to secure an exact resource.

In my scenario, users can upload files and access them via the rest api. I want that when a file is uploaded, it is only accessible by a certain group.

The endpoint is accessible under {{baseurl}}/files/{id}

Thus, it is very important that I have a keycloak resource for just that url. In keycloak I also already create a resource when uploading a file with the name file_{{id}}

With the evaluation tool in Keycloak everything works already. I create a group-policy and associate with the permission an exact resource, the group and the corresponding scopes.

My thought was that I specify in Keycloak the URI: {baseurl}}/files/{id} and when this URL is called in the backend, it is mapped to the exact Keycloak-resource.

If that doesn't work. How else would it be possible to secure single resources?

ferrerojosh commented 1 year ago

I'll have to find a way to pass the resource uri to the enforcer for this to work. Owned-resources will usually be dynamically created. I hope Nest has a way to pass url params to another decorator. If it is possible, It'll be as easy as specifying a url for that certain handler.