Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW add fields to prepare to add the bearer authentication for API #33149

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

hregis
Copy link
Contributor

@hregis hregis commented Feb 19, 2025

No description provided.

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@eldy I have a doubt about this PR because I think that we should be able to define a different API key and password key per external site... maybe add a "llx_user_api" table in order to define different keys and tokens?

@hregis hregis marked this pull request as draft February 19, 2025 21:12
@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@eldy maybe add a table "llx_user_api_authentication" with fields :

rowid, entity, fk_user, type, url, api_key, api_pass, api_token, api_token_renewal, api_token_ttl, status

field "type" is to have the possibility to defined an API type (REST) for to have the possibility to use another API type...
field "url" (or ip) to have the origin of request of authentication
field "api_token_ttl" is to have a personalized TTL for to renew "api_token" by user (otherwise a global TTL)

and maybe fields with dates of creation, validity, etc...

what do you think about it?

@rycks
Copy link
Contributor

rycks commented Feb 19, 2025

@hregis if you want to do it right please have a look at https://laravel.com/docs/11.x/passport ... or at least https://laravel.com/docs/11.x/sanctum but please don't try to reinvent the wheel ...

for sanctum (easyer than passport) here is the database https://github.com/laravel/sanctum/blob/4.x/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php, that is an other table to be able to have 1..n links with users table (a user can have "tons" of keys).

and ... if i could, please have a look at that if "my" work because we are working on it since a long time ... https://inligit.fr/cap-rel/dolibarr/plugin-smartauth/-/blob/master/sql/llx_smartauth_auth.sql?ref_type=heads

as it's an external module we can work on it and wait until it will be as robust and as bullet proof before make PR on dolibarr core ... that's the way i imagine our contribution

for example we imagine that a user can have a token ... but not only ... it could be strange but there is tons of situations were we could imagine auth links with societe_account table entries for example

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@rycks ok, but dolibarr don't use laravel!

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@rycks authentication it's just a key to compare with the destination... laravel, passport, sanctum, oauth, etc... its the same problem !

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@rycks l'authentification n'est qu'une comparaison de clé, cette clé peut-être unique ou aléatoire dans un temps défini mais il y aura toujours cette comparaison... le tout c'est de faire en sorte que cette clé ne soit pas interceptée !

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@rycks le top c'est d'avoir plusieurs clés... c'est le but du TOTP ou de des clés USB... mais ici dans l'authentification d'une API le token avec une TLL et un renewal token tous les deux stockés en JWT me parait une alternative sécurisée pour le moment ! Et il faut faire notre propre mécanisme et ne pas dépendre de laravel ou de je ne sais quelle classe au risque de ne pas contrôler les éventuelles bugs !

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@rycks au fil du temps je suis devenu allergique aux frameworks... ;-) Ils complexifient et rendent le code chiant à déboguer, et quid si ce framework n'est plus maintenu ? Dolibarr va finir en un Joomla ou un Prestashop de merde ? 😄

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@rycks pour en revenir à notre sujet, je ne vois pas grand chose dans ta table qui peut gérer une authentification forte !

les seuls champs qui me semblent pouvoir avoir un impact sont "appuid", "salt", 'auth_elemen", "ip"
rien qui permet d'avoir une key unique à un moment donné

CREATE TABLE llx_smartauth_auth(
	rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL,
	appuid integer, --from module->numero
    salt varchar(32),
	date_creation datetime NOT NULL,
	date_lastused datetime,
	date_eol datetime,
	tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
	fk_user_creat integer NOT NULL, -- id of created user
	fk_user_modif integer,
	fk_authid integer NOT NULL, -- id of authenticated user or element
	auth_element varchar(128) NOT NULL, -- may be user or societe_account or ...
	ip varchar(50) DEFAULT '',
	status integer NOT NULL,
	entity integer DEFAULT 1 NOT NULL
) ENGINE=innodb;

@rycks
Copy link
Contributor

rycks commented Feb 19, 2025

@rycks pour en revenir à notre sujet, je ne vois pas grand chose dans ta table qui peut gérer une authentification forte !

les seuls champs qui me semblent pouvoir avoir un impact sont "appuid", "salt", 'auth_elemen", "ip" rien qui permet d'avoir une key unique à un moment donné

CREATE TABLE llx_smartauth_auth(
	rowid integer AUTO_INCREMENT PRIMARY KEY NOT NULL,
	appuid integer, --from module->numero
    salt varchar(32),
	date_creation datetime NOT NULL,
	date_lastused datetime,
	date_eol datetime,
	tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
	fk_user_creat integer NOT NULL, -- id of created user
	fk_user_modif integer,
	fk_authid integer NOT NULL, -- id of authenticated user or element
	auth_element varchar(128) NOT NULL, -- may be user or societe_account or ...
	ip varchar(50) DEFAULT '',
	status integer NOT NULL,
	entity integer DEFAULT 1 NOT NULL
) ENGINE=innodb;

ho chouette on en arrive à du concret ... peux tu venir contribuer sur le dépôt de smartauth plutôt ? j'aurais le plaisir de t'expliquer tout ça ...

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@eldy Laurent je ne sais pas toi, mais moi mon firewall me renvoi des tentatives d'intrusion toutes les heures... il me semble qu'ils nous faut une authentification plus forte sur l'API même si on peut restreindre l'accès à des adresses IP ! cette restriction ne veut plus rien dire de nos jours !

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@rycks tu auras beau travailler pendant toute ta vie sur un sujet, l'authentification n'est autre qu'une comparaison de clé vers un accès... tu veux vraiment être sécurisé ? ne soit pas sur internet ! ;-) tu auras beau rendre ton code plus compliqué le résultat sera toujours le même ! 😄

@hregis
Copy link
Contributor Author

hregis commented Feb 19, 2025

@rycks notre société est en régression mentale et internet suit le mouvement... L'IA va ramollir nos cerveaux, les ordinateurs quantiques vont renforcer l'IA et détruire toutes les protections...
il va falloir revenir à des ordinateurs autonomes ! L'âge de la disquette ! Mais l'âge où on ne se souciait que des bugs et pas des hackers !

@eldy
Copy link
Member

eldy commented Feb 20, 2025

It seems there is a confusion on how the API works and how we want them to work.
You make a mix between the OAUTh2 authentication good practices (Access+Refresh token, JWT) and the practice for full machine to machine needs (Bearer token mechanism).

OAUth2, that is using an Access token + a Refresh Token mecanism, is designed when there is a possible authentication with an end user interaction. The client A is the browser (used by end user), the server with the resource to server is R (so the API server) and the authentication server is a third component O (Like Auth0, Google OAuth, Microsoft OAuth...).

This need of access in a user interaction context is covered by OAuth2 authentication and in this protocol, there is a refresh token because once A has asked a token to O, he can access to resources of S without asking and validation anymore the validity of token. Even if permission change on O, the token is still valid to read resources on S so to avoid to have a token valid too long, the token has a short delay (generally 1h) and you need to use the refresh token between A and O to get a new Token that is valid for a new 1 h.
But on Dolibarr (like any API service), the check of permission on a ressource is done at EACH call, so if you change permission on an API, effect is immediate, no need to wait expiration like for OAuth2, so no need for a refresh token.

Also, even when using the OAUth2 protocol to get a token , once you got the token (for example a JWT token), this token is a "Bearer" token (called Access Token, or OIDC token if OAUTH2 was used with scope openid). It means once you own/know the token, you have access to resource. It is the same when using Dolibarr API than when using the SMTP or the IMAP, the OVH API, Amazon, Scaleway API, etc... the token is provided in the header of the protocal (IMAP, SMTP, OVH, Amazon, Scaleway API like it is in Dolibarr API (inside the header of the protocol).

The "Bearer" authentication is necessary when you are doing a full machine to machine interaction, with no possibility to have end user interaction (because it is machine to machine).
There is never password on a bearer authentication because the Bearer token is already similare to a string that contains a login+password+other data but hashed.

For this reason any service using bearer authentication recommend to add more protection than the simple Bearer autentication, like this ones:

  • Adding a limit of time of a token if we now that access must be revoked at a certain day. It is a "TTL" but without renewal mecanism (Note; the term is "lifespan" instead of TTL but we understand in both cases). In most cases it is difficult to have a lifespan because there is no way to renew a token on a machine to machine protocol because there is no human interaction to reenter a non stored password. But some services provide this feature, like OVH API where you can choose 1 day, 1week or permanent, Google does not provide at all such mecanism for its Bearer token sayong that is has no sense for machine to machine. Dolibarr provides such a feature by setting the "end date of validity" of the virtual API user of the token.
  • Adding a test/filter on the HTTP referer (for API access). Not really efficient but still provided by some API providers.
  • Adding a restriction on IPs (also available on Dolibarr into global setup of API module but not yet available on a user basis)
  • Limit permission a token can be used for. This is called "RBAC - Role-Based Access Control". This is is also possible on Dolibarr by setting the permission of the API user.
  • Adding a mitigation system (for example fail2ban if you host yourself the API). Some API providers provide a limit max of call per IP. Dolibarr already offer such a protection providing fail2ban example profiles to use.
  • Forcing use of HTTPS
  • Encrypt the storage of the token
  • ...

For example, Google refuse to use some of its api you don't activate at least one of the additional protection mecanism.

Providing a coupe login + pass to a user A for an API call to use the API is useless. The user A will need to store the 2 information in a reversable way if he want to use them in a machine to machine context, and if someone has hacked A, he will be able to get both the login and password (because both are stored in the A system in a reversable way). This is the reason why all Bearer authentications are using only 1 key, instead of 2 (login + pass).

Note that a Login / pass can be used with Oauth2 to get the token, and then the token must be stored in user system to be used to call the API (this is how OVH API or Chorus API, works for example). But Dolibarr has already a backoffice with possibility to generate a token so no need to OAuth2. If we really want a login/pass interaction in Dolibarr before using an API, it is possible, but it means Dolibarr must be modified to be an OAUTH2 provider/server (currenyly only oauth2 client) or must be integrated with an OAUTH2 server. But in such a case it will be working for API that can be initiate from Web by a user. In most cases, we want full machine to machine. This is why a lot of API providers like Amazon, Google, OVH, Scaleway, etc... provide a solution based on a Bearer token (like Dolibarr do), enhanced by the other possible protections seen previously (like Dolibarr do) for some of their API.

Conclusion

The only stronger solution that goes beyond what dolibarr already does is

  • rotation of the Bearer token based on an algorithm known to the client and the server (very little implemented in the industry because it complicates the client's life)
  • add an HMAC type signature (it is a little better in terms of security than the Bearer token because the key is a signature that depends on the content and a timer. For each different message content, it is therefore a different key that transits (a signature of the content made with the key that no longer transits). But it is still necessary to share a secret between the 2 machines (the signature key) so it remains very close. This also offers the guarantee that the message cannot be replayed after a certain time thanks to the included timestamp).
  • or private key - public key authentication. Dolibarr does not yet manage this.
  • integrate or be full server oauth2 (very complex to reimplement)

It is one of these last 3 (or all 3) that can be interesting to add..

@Dolibarr Dolibarr deleted a comment from hregis Feb 20, 2025
@eldy eldy added the Discussion Some questions or discussions are opened and wait answers of author or other people to be processed label Feb 20, 2025
@Dolibarr Dolibarr deleted a comment from hregis Feb 20, 2025
@Dolibarr Dolibarr deleted a comment from hregis Feb 20, 2025
@Dolibarr Dolibarr deleted a comment from hregis Feb 20, 2025
@Dolibarr Dolibarr deleted a comment from hregis Feb 20, 2025
@hregis
Copy link
Contributor Author

hregis commented Feb 20, 2025

for my part I think that using the user's login/password to have a static API key is not secure. It would be entirely possible to add a public interface to authenticate in order to retrieve a TTL token and a refresh token. And we could also define an API key and API password different from the user's login/password to authenticate to the API and retrieve a TTL token and a refresh token. Everything is possible and more secure than what currently exists.

@eldy
Copy link
Member

eldy commented Feb 21, 2025

for my part I think that using the user's login/password to have a static API key is not secure. It would be entirely possible to add a public interface to authenticate in order to retrieve a TTL token and a refresh token. And we could also define an API key and API password different from the user's login/password to authenticate to the API and retrieve a TTL token and a refresh token. Everything is possible and more secure than what currently exists.

Yes, you should never use login/pass to get the api key. The api key should be generated from the back office.
The api to generate api key is for very non common use and must be avoid as much as possible.

@rycks
Copy link
Contributor

rycks commented Feb 21, 2025

Yes, you should never use login/pass to get the api key. The api key should be generated from the back office.

hmmmmmm and how are you authenticated to the backoffice ? most of the time with the use of a login/pass no ? :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Discussion Some questions or discussions are opened and wait answers of author or other people to be processed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants