Sign-up procedure design using Feathersjs - feathersjs

I have a question regarding the design of the sign-up procedure using FeatherJS for the back-end of my application.
The problem I'm facing is the following:
I want a user to be able to sign-up his organisation/company using his/her email and a password. This user can be considered as the administrator of the organisation.
When that admin user has been created I want that same user to be able to create additional users that are linked to his/her organisation. To ensure that only the admin user can create additional users I've set up feathers-permission to check for 'admin' rights in the "before" hook of the "Users" service
module.exports = {
before: {
all: [ ],
find: [ authenticate('jwt') ],
get: [ authenticate('jwt') ],
create: [ authenticate('jwt'), checkPermissions({
roles: [ 'admin' ]
}), hashPassword ],
update: [ authenticate('jwt'), checkPermissions({
roles: [ 'admin' ]
}), hashPassword ],
patch: [ authenticate('jwt'), checkPermissions({
roles: [ 'admin' ]
}), hashPassword ],
remove: [ authenticate('jwt'), checkPermissions({
roles: [ 'admin' ]
}) ]
},
The issue that I am encountering now is that I cannot sign-up the initial user because the before hook is requesting an authenticated user that has admin rights which I don't have because that user hasn't been created yet.
I could remove the authentication and permissions check but then I leave my User service "unprotected" and anyone would be able to create a user using the right POST call to the server.
The question I have is: How should I setup my service and/or hooks so that I can sign-up a new user in my application and that only that authenticated admin user can create new users in the application?
Many thanks in advance!

There's a few different ways you could go about this.
One would be to make your checkPermissions hook conditional on whether or not there already exists a user, using iff from https://github.com/feathersjs-ecosystem/feathers-hooks-common
Another would be a different endpoint for setting up the initial user - it would have different hooks (e.g. confirm there are no users in the system instead of the permissions check).

As #Joe said, there's a few ways you can implement this.
In my current project I am building a multi-tenant application. I differentiate between registration for organizations and users.
The first step is to register an organization and it's default user. The org's default user is set as 'super_user' and must verify email/phone(sms) before becoming active.
Once the default user is verified, I automatically create a record in the user table, set the user as 'admin' with a generic admin#[org name].com email address. The client-side verification UI redirects to an interface the requests a second password. Now, the org's default user can log in as super_admin to change org settings and can sign-in as an admin that provides access to all of the sites features including inviting users.
User invitations are set via email. The link within the email contains a special token that says that the user has been invited and can register under the specified organization. New users that register are setup as guests until the admin upgrades them to a higher role. I use hooks on user.create to verify all of this and return errors if things don't match up.
I also use feathers-authentication-management to wire up sending emails and managing tokens.
So, instead of forcing new user's to be admin's before creating them, I use tokens to ensure that have been invited. These tokens are associated to a specific org. Similar to your code example, I have a checkInvitation() function as a guard to my route rather than using a role.

Related

chrome.identity.getAuthToken requires re-authorisation from the user, even though the user has already given permission to my app

The chrome.identity API seems to require re-authorisation from user even though the user has given permission to my app in the past.
These are the steps being followed:
Get user authorisation for required scopes using
chrome.identity.getAuthToken(
{ account: { id: googleAccountId }, interactive: true },
(token) => { console.log("Access token", token); })
This brings up a dialog that prompts the user to authorize my app to access certain sensitive scopes. Once the user accepts it, I get the access token and use that to get user information.
Whenever I need an access token I call the
chrome.identity.getAuthToken. I understand chrome caches it internally, so I do not need to worry about calling it multiple times.
Every once in a while, it seems that the user will need to re-authorize my app. i.e. calling the chrome.identity.getAuthToken non interactively does not work. The user will need to be prompted again to authorize my app. This is the issue.
Expected behavior is:
Once the user has authorized my application and given permission to certain sensitive scopes unless there is a change in password / there is a need to access new scopes, the chrome.identity API should never ask the user to re-authorize my app.
This is an extremely poor user experience.
Notes
When the user was prompted to re-authorize my app again, I checked the permissions for my app for that user under accounts.google.com. My app already had the required permissions.
My understanding is that once a user has given permission to my app - it should never bring up the dialog again (for that user).
(My understanding of the chrome.identity API may be incorrect, if so please let me know).
Any input is much appreciated.

IBM Watson Assistant integration with zendesk

I followed all steps listed here to integrate IBM Watson Assistant to Zendesk
https://cloud.ibm.com/docs/assistant?topic=assistant-deploy-zendesk#deploy-zendesk-routing
But I'm struggling to find exactly how am I suppose to pass values to Zendesk. The information provided is not clear enough, and if I request an agent, there's no information about the user, it shows in zendesk like this:
The documentation says:
Specify the information by using the following syntax. Use the exact names (name and email) for the two name and value pairs.
{
user_payload : {
name: '#{customerName}',
email: '#{customerEmail}'
}
}
And after that:
Decide whether to allow unidentified users to access Zendesk.
The web chat integration allows anonymous users to initiate chats. However, as soon as you enable visitor authentication, Zendesk requires that the name and email of each user be provided. If you try to connect without passing the required information, the connection will be refused.
If you want to allow anonymous users to connect to Zendesk, you can provide fictitious name and email data. Write a function to populate the two fields with fictitious name and email values.
For example, your function must check whether you know the name and email of the current user, and if not, add canned values for them:
const userPayload = {
"name" : "Jane Doe1",
"email" : "jdoe1#example.com",
}
In my case, since users will access the assistant through a webview in an Android app, I have both name and email, and I don't need a whole authentication steps, all I want is to pass to zendesk those values, but can't exactly figure it out how. In the example, it just sets this constant but I don't know what to do with it, is there any code examples for it?
Thanks in advance
if you just want to pass the email and username to Zendesk so the agent will see them, those can be passed if you set the context of the conversation as follows
context.integrations.zendesk.pre_chat = { username: "John", email: "j#j.com"}
This should appear as tags in your agent's zendesk workspace.
If you want to use Zendesk security and have the chat authenticated, then you have to set security on for the web chat and embed a user_payload inside your JWT. (as described in the docs you have linked)

Box Api : Adding User Group as Collaborator return 403 Forbidden

I am trying to add collaborators through Box API, but when I am adding a user group, the api request return status 403: Forbidden.
Here is the request attributes i'm passing:
{"item": { "id": "3907701373", "type": "folder"}, "accessible_by": {"id": "400235","type":"group"}, "role": "viewer"}
And it returns me this error:
Bearer realm="Service", error="insufficient_scope", error_description="The request requires higher privileges than provided by the access token."
The documentation for Create Collaboration states that:
Errors may occur if the IDs are invalid or if the user does not have permissions to create a collaboration.
Are you able to create a collaboration with a regular user on that folder?
Follow-Up
I think the problem could be due to one, or possibly both, of the following:
Box recently introduced the Group Admin, which has permissions to "add existing users to their groups, create new users that will be assigned to their groups, and assign folder access to their groups." You may need to have those permissions in order to manage group collaborations.
Your may need to enable the Manage an Enterprise scope for your application, under OAuth2 Parameters > Scopes. If (1) above is true then your app most likely requires elevated, enterprise privileges.

Cannot add Enterprise Admins to a Group via Box.com API

It seems I'm not allowed to add an Enterprise Admin or Co-Admin to a group via the API. I can add all other types of users to groups, and can create/edit/delete groups just fine, but I'm not allowed to add a user to a group if that user is an Enterprise Admin or Co-Admin.
Is this expected behavior, or is my application user missing some sort of entitlement?
POST /group_memberships
{ "user": { "id": "12345"}, "group": { "id": "12345" } }
{
"type":"error",
"status":403,
"code":"access_denied_insufficient_permissions",
"help_url":"http:\/\/developers.box.com\/docs\/#errors",
"message":"Access denied - insufficient permission",
"request_id":"xxxxxxx"
}
This is the box a limitation/feature. You can not add an Enterprise Admin or Co-Admin to the group of another Administrator.
For example:
User A and B are enterprise admins. Each sing up to box separately.
When user A create a group, he can create a new user and add the created user to the group.
But user A can not add the user B to the group.
Likewise, when user B create a group, he can not add the user A.
Because they are from different organizations.
You will clear when you try to add an Enterprise Admin to the group via https://app.box.com website. If you do so, the pop up will show with the message "The supplied email address already has a Box account that belongs to a different organization."

Granting user access

We're developing a web app using the Zend framework and Mysql.
Currently, accounts are unique by email address. We want to be able to allow the admin of an account to grant access to the admin of another account. This person would then be a "user" of the linked account. The account holder would then log into their admin account and then select which linked account they want to access.
Please note: the access should only be one way. Account 1, who grants access to Account 2, should not be able to access account 2. Only account 2 can access account 1. If Account 1 wanted access to account 2, account 2 would then have to grant access to account 1.
What is the best method of going about this?
I think trying to tie permissions to accounts is your problem, you need to add a second 'layer'. Let's stick with Google Analytics as the example:
Let's say Joe Bloggs wants to use Google Analytics. He first has to create a Google account (assuming he doesn't already have one). He then creates a Google Analytics account for his site. Say Joe then wants to give access to Jane Smith, let's assume she already has a Google account. To give her access all he is doing is giving her Google account access to his site, he's not giving her access to his Google account.
Zend_Acl is role based so let's try and apply ZF concepts to this example. The user management screens in GA allow you to give users either "View reports only" access, or "Account administrator". So you'd define a role in Zend_Acl for each of these access levels:
$acl = new Zend_Acl();
$acl->addRole(new Zend_Acl_Role('guest'));
$acl->addRole(new Zend_Acl_Role('admin'), 'guest');
the second parameter on addRole means the role should inherit all permissions from the other role specified. So what I've done above is define two roles: guest and admin; and said admin should inherit all permissions that guest has.
You then have your 'resources', which are the things that can be accessed. So we'll define one for reports, and one for user management:
$acl->add(new Zend_Acl_Resource('reports'));
$acl->add(new Zend_Acl_Resource('users'));
we'll then give 'guest' access to reports, and 'admin' access to users:
$acl->allow('guest', 'reports');
$acl->allow('admin', 'users');
then in the relevant controllers (or plugin, or wherever) you can check permissions:
public function reportsAction()
{
[...]
// assume $role contains the role of the currently logged in user
if (!$acl->isAllowed($role, 'reports')) {
// show a permissions error
}
}
public function usersAction()
{
[...]
if (!$acl->isAllowed($role, 'users')) {
// permissions error
}
}
As far as storing this in MySQL goes, you just need a lookup table that links users, sites (in this example) and roles:
userID | siteID | role
1 1 admin
2 1 guest