Directus: no clear way to register a user

Created on 20 Mar 2018  Â·  25Comments  Â·  Source: directus/directus

I can't seem to determine how to register users through the public group. I gave the public group access to add to the directus_users table. I am able to add users using my admin authorization token. Nowhere in the readme or docs say anything about registering users for the public. Is there some public api key in my config.php somewhere?
I guess I could try to create a anonymous user group with this permission, create a new user and assign it to this, then use this user to register new users. I also don't see any way to auto assign new users to a particular group such as Normal Users.

Version Info

  • Directus version and branch 6.4.7
  • PHP version: 7.1
  • MySQL version: 5.6
  • Web server: Apache

Expected Behavior

Be able to register a user from public using
`POST https://url/api/1.1/users HTTP/1.1
Content-Type: application/json

{
"email":"[email protected]",
"first_name": "test",
"last_name": "User",
"password": "123456"
}`

Actual Behavior

{ "success": false, "error": { "message": "Permission denied" }, "code": 0, "class": "Directus\\Exception\\Http\\ForbiddenException", "file": "directoryv/api/core/Directus/Database/TableSchema.php", "line": 183, "public": true }

Schema Dump, Logs, or Screenshots

dregisteruser

Most helpful comment

I think we should 100% honor the permissions system – so if you want to enable Create access for directus_users for the Public group... then that should work as expected.

Also, I don't know if this matters, but perhaps it would be nice to have a specific "Register User" endpoint? Would that just be an alias helper, or would it be useful to combine the password hashing into a dedicated endpoint?

Lastly, we _could_ discuss adding a KVP to Global Settings for a "default user group". We never thought about this since all User Registration was done explicitly... but it might be useful for registration (if we want to officially support that).

All 25 comments

@WellingGuzman @benhaynes What are your thoughts on this?

I think we should 100% honor the permissions system – so if you want to enable Create access for directus_users for the Public group... then that should work as expected.

Also, I don't know if this matters, but perhaps it would be nice to have a specific "Register User" endpoint? Would that just be an alias helper, or would it be useful to combine the password hashing into a dedicated endpoint?

Lastly, we _could_ discuss adding a KVP to Global Settings for a "default user group". We never thought about this since all User Registration was done explicitly... but it might be useful for registration (if we want to officially support that).

This would totally open up using directus for mobile apps. This seems mostly business focused now, but we need the ability to register users for customer facing apps.

I have been able to accomplish adding users (which should be registering) w/ a new public user group and a single user with only create users permissions. I was unable to assign a group to these new users & it's rather inconvenient that only admin's can generate user api keys for other users see #1543 . I may write an extension until directus has official support for registering users and generating api tokens for standard users.

@benhaynes we could / should write an Medium article on how to setup user management for your app using directus

I've got no problems to allow users to signup / register.
@lastlink Did you forget the rows at the end of the url?

https://url/api/1.1/users/rows

Have to say that i also allowed public users to view directus_users, don't know if this could be the problem, too.

Edit: Adding the user to a particular group can be done with a filter, dont mind the top part of the function. I have used it to avoid users having the same first_name, because I'm using that field as nickname.

'table.insert.directus_users:before' => function($payload) {
        // Check if first_name already existing
        $users = \Directus\Database\TableGatewayFactory::create('directus_users');
        $user_exists = $users->findOneBy('first_name', $payload->get('first_name'));
        if($user_exists) {
          $message = "Duplicate entry: " . $payload->get('first_name');
          throw new \Directus\Database\Exception\DuplicateEntryException($message);
        }

        $payload->set('group', 3); // user group
        $payload->set('status', 2); // inactive
        $payload->set('token', \Directus\Util\StringUtils::randomString(32));

        return $payload;
      },

@BennerGe why would I need user/row? I'm not trying to list (GET) the users's, but create a new user using (POST). And that url endpoint does work w/ a user api token or authorization bearer.

Because that's how you create items:
https://api.getdirectus.com/1.1/#Create_Item

And the correct url is https://url/api/1.1/tables/directus_users/rows

@lastlink does the user get created? on 6.x we have a issue with Directus that the user must have permission to read to get the data back, otherwise the user get created/updated but the API returns a non-clear error that the user don't have permission, but what actually means is that the user doesn't have permission to read from the table.

Another thing to mention is to no confuse directus users with the product/project users.

the user does get successfully created using that rest call, I need to experiment more w/ the groupid which does not get set, but is instead null.

@lastlink In your example you don't set any user group, which is set to null (the default value)

POST https://url/api/1.1/users HTTP/1.1
Content-Type: application/json

{
"email":"[email protected]",
"first_name": "test",
"last_name": "User",
"password": "123456"
}

I know I'm figuring that out, but being able to set it is rather danger on creation b/c you could give new user's admin abilities.

@rijkvanzanten should this become a feature label instead of question?

That's the reason why I'm overwriting the group in the table.insert.directus_users:before filter.
But you're right, might be not the best solution.

is this part of the core? how do you set filters?

So let me get this straight.

What we (ideally) want is the ability to create new users from the _public_ group. Eg everyone can register as a user. BUT, we want to be able to assign them to a specific group. We _don't_ want to allow the public user to set this group, as that would be a major security risk (the user can register themselves as admin and cause all sorts of trouble).

Does that sound about right @lastlink @BennerGe?

We never really saw the Directus User table as the table you'd use to manage users in the past, but with all these advancements in permissions, workflow, and auth, it makes total sense to me. Thoughts @benhaynes?

is this part of the core? how do you set filters?

Yeah! You can add filters to do whatever you want on certain events in configuration.php
@lastlink

ok maybe that would be easier than an extension, creating a new user create, add to default group & auto generate token. I may document my steps when I get a chance to work on it.
I'll look into using @BennerGe solution.
However, @BennerGe shouldn't you select the user by id & not firstname?

I see this is not a bug but rather a question on how to create Directus users. what you can do is create a hook as @BennerGe says, or set the group column default value to whatever group you want, and set a default user group, or create a custom endpoint and let them create them through it.

I will close this to as this is not an actual issue, we can continue the conversation on how to implement this.

The upper block in my code example is only a check, if an user with the same first_name already exists. Want to build a community based on directus and I'm using the first_name field for the nickname. And I don't want users to have the same nickname.

So in your case this is probably not necessary, you only need the last part of the function.

'table.insert.directus_users:before' => function($payload) {
        $payload->set('group', 3); // user group
        $payload->set('status', 2); // inactive
        $payload->set('token', \Directus\Util\StringUtils::randomString(32));

        return $payload;
      },

As a note for this I added a pull 36 request linking to this issue. I didn't give the full notes on setup as they are somewhat clear here. I also was having difficulty seeing the guidelines for our doc/api documentation since the summary.md has been so radically changed from 6.3.5 to 6.4.0.

And to reiterate I was able to use

POST https://url/api/1.1/users HTTP/1.1
Content-Type: application/json

{
"email":"[email protected]",
"first_name": "test",
"last_name": "User",
"password": "123456"
}

Without needing rows or directus_users.
I also used this as my filter

'filters' => [
        'table.insert.directus_users:before' => function ($payload) {
            $payload->set('group', 3); // normal user groupid
            $payload->set('status', 1); // active
            $payload->set('token', \Directus\Util\StringUtils::randomString(32));
            return $payload;
        },
    ],

This is somewhat secure b/c any user's created (not updated) from the rest api even if from admin will be overwritten with said groupid not allowing users to set create new users in the admin group.

I also had to create a publicuser, new publicuser group with permissions to add to directus_user table & not blacklist on writting to groups & tokens & use their api token for public registry.
publicregister

How can I do this in Directus 7?
I have a registration form for public users which writes the data to directus_users. Then i added a webhook filter in "item.create.directus_users:before" which writes the status 'invited':

'item.create.directus_users:before' => function (\Directus\Hook\Payload $payload) { $payload->set('status', 'invited'); return $payload; }

But how can i set the user role which is located in directus_user_roles?

I would probably do the role in a hook too, upon insertion, use the Id and intlser into junction. 😋

Hey @onlinelessons , I found a solution for Directus 7.

First, create a filter on config/api.php

'item.create.directus_user_roles:before' => function(\Directus\Hook\Payload $payload) {
    $payload->set('role', 3); // sets custom group
    return $payload;
}

Then, set role permissions like this

image

Finally, the client should send this request

POST https://url/project/users HTTP/1.1
Content-Type: application/json

{
    "email": "[email protected]",
    "first_name": "John",
    "last_name": "Doe",
    "locale": "en-US",
    "password": "password",
    "status": "active",
    "timezone": "America/Mexico_City",
    "roles": [{
        "role": 1
    }]
}

Even if the end user sends Administrator role (ID 1), the filter will change it by the right one.

Nice one @mariorojas!
I also wrote this custom endpoint a while ago if anyone's interested.

https://gist.github.com/shealavington/b79ba8111a224a8fac55c85351aa4868

Hi @shealavington!

Based on your code, here is a useful class for custom endpoints https://gist.github.com/mariorojas/27e3328f3e19c70c3ef6bc337498b43e

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Jonesus picture Jonesus  Â·  3Comments

dimitrov-adrian picture dimitrov-adrian  Â·  3Comments

christiaangoossens picture christiaangoossens  Â·  4Comments

benhaynes picture benhaynes  Â·  4Comments

vormatt picture vormatt  Â·  4Comments