New Feature "Users"

Hi Sebastian,

Developing new feature “Users” on Squidex can make life easier in many projects.

Imagine we have a new feature called Users. This feature will use the current role system on the system. But provide to 2rd party app authorization/authentication.

“Users” of the App

  • Name
  • Surname (optional)
  • Email
  • Password (Hashed)
  • External Properties (Key, Value)
  • Role (Current System Roles)

When the user logs in with the client, he will be able to perform operations within his own authority. For this, a client such as frontend-client is needed.

Thus, a direct membership system can be developed on Squidex and members will be able to perform transactions within their own authority. Here, the “own” phrase we added in the roles / authorizations section will be very useful.

These users will not be able to login to Squidex Admin Panel. They will only be able to access the content according to their authorization through the client-credential type client created for the application.

Thus, client/user security and developability will be ensured in projects such as Angular/React.

I may have mistyped some topics as a english translation. If you need me to explain the architectural parts more, I can go into more detail.

In short;
Schemas
Contents
Assets
Users (New Feature)

  • Powered by system roles.
  • Not login to Squidex Panel Only 2rd party application auth.
  • Basic Authentication / Authorization
  • Provide to Angular / React etc. frontend application identity security
  • Safe & Simple content accessibility

A structure like Umbraco “Users” although not exactly the same…

Thank you.

@NeoMoritsiqu Our private discussion had no result so far, what do you think about it at moment? Can you summarize from the discussion what this feature should look like?

Hi Sebastian,

I especially want to provide a secure user management for my Angular or React applications. I must be able to assign these users the privileges I created from the role part or create a special authority pool.

My aim is to make the membership system usable in applications such as React and Angular, as well as to impose transaction restrictions by associating it with squidex roles.

Users should not be able to login to the squidex admin panel. It should only be able to login to the application I am developing. I don’t like the idea of creating a client for each user… A single client must be defined for these users and they must login through it.

I have finally time to work on this. But before i start I would like to clarify my point of view.

Lets start with authorization.

Authorization

Right now Squidex uses OpenId Connect. It is a well established standard and a proven implementation. We use it together with a client.

Clients

A client in OpenId Connect represents another application, that wants access. For example an mobile app, server application, website or Desktop application. A client can has several modes, called Grant Types.

  • Implicit: When a user goes to a login page, gets a token and so on. Only works with Users, that are registered in the OpenId Connect implementation. This is the default for every user in Squidex.

  • ClientCredentials: An application can get a token without a user, using the client id and secret.

When you create a new client for an app, this client is made available to the OpenID Connect implementation. It is not stored in the database, but an interface is implemented to support that.

This does not mean that it is the only option to have clients .The frontend is also a client for example and we could also create one client for each “end-user”.

Except clients there is another way to implement authorization:

API Keys

With API keys you share the key directly with each request. Therefore it is less secure. But for a Javascript frontend application where you have to make the client id and secret publicly available it does not really matter.

JWT Tokens

When you implement API Key authorization you have to check for each request if the API key is valid. You can solve this problem with JWT tokens (https://jwt.io/introduction), which are signed API keys.

The API key contains all information such as username and you can also store permissions there and with each request you only verify the signature of the JTW token.

Users

For me a user has several aspects and very often a user exists in more than one place.

  • Profile For example you wanna customize which profile information you collect.
  • Authorization to Squidex As described above there are several methods to manage that, e.g. clients, API keys and self contained API keys.
  • Authorization to a custom Frontend: For example when you want to store username and password somehow.

When we talk about the implementation, we have to think about what is the most easiest way in terms of implementation and new added features and what is the most easiest way for users and developers. We can implement something now that is a little bit tricky to use and then improve it later.

Suggestion

This is my suggestion:

  1. The Profile part is the hardest part to implement, but we already have that with schemas. It makes no sense to implement another dynamic content type in my opinion. Therefore I would leverage schemas for that.
  2. The Authorization to a custom Frontend is already possible and you can use scripting to hash the password.
  3. For Authorization to Squidex we have to store the API keys or clients somewhere. The current client structure is not sufficient because it is directly part of the app config, which is a single document (row) in the database. So you cannot have 10.000 clients. Therefore we need to create another structure for API keys or clients.
  4. When you create a user schema you wanna connect an API key to each user. You can also do this with scripting when point 3 is implemented. Not the best approach but it would work.

So how does it look like?

If we have implemented the client or API key list (point 3), we can write a very simple script:

// Create
if (ctx.data.password.iv) {
    ctx.data.password.iv = sha256(ctx.data.password.iv);
}

createAPIKey(function (result) {
    ctx.data.apiKey.iv = result;
});

replace();

// Delete
deleteApiKey(ctx.data.apiKey.iv);

Improvements

How can we improve that?

  1. Build a structure to cleanup fields for deleted contents.
  2. Write a custom field that provides an API key automatically.
  3. Provide an inbuilt way to hash fields without scripting.

Open questions

  • How do you want to connect to Squidex? Clients vs APIKeys. Clients are more secure but APIKeys are easier to use.
1 Like

This is the biggest thing that is making me consider alternative options for a headless CMS right now.

I could imagine doing it with OIDC, just like you would with Keycloak.

You would register your frontend app as a client of the app in Squidex, similar to how you would today in Squidex.

Then when you app needs to log in a user, it redirects to the Squidex login/register page associated to that app. Once the user authenticates Squidex re-directs to the front-end with a short-lived JWT.

Then you could do one of two things:

  1. Require using a refresh token or JWT to periodically get a fresh JWT so that your API access doesn’t expire.
  2. Allow you to use the short-lived JWT to create a session that is kept in memory on the Squidex server.
    • I like sessions because they let you log out to revoke your session token, while JWTs can’t be revoked and refreshing tokens isn’t super fun from an implementation perspective.

Is it currently possible to create a client for my app that I can use to let Squidex users like editors and writers login?

When I create a client it makes me specify a role for that client, but what if I want to create a client that can get tokens for the user who logged in, and I want the token to have the role of the user who logged in?


Hi @zicklag,

thanks for your input. Your solution sounds complicated. If you go the route with OIDC you have to make a lot of other features. For example it is necessary to provide customization features for the login page. Then your are not really headless anymore.

Only with self hosting and a little bit of code.

1 Like