Problem/Motivation

Currently in TFA we provide no method to actually authenticate through REST endpoints,

Some authentication methods are more complex than others to protect.

HTTP Digest authentication for example has no standard provisions for providing an OTP code. We could use a secondary REST call or we could use the classic 'append the token to the password' method to provide protection for this service. Both of these might dicate opening a SESSION to keep track of the authentication OR that we provide an authentication endpoint that than provides an API key to be used in all requests after that.

Ultimately our decisions will impact API clients, and we need to figure out what works best for API clients.

Steps to reproduce

N/A

Proposed resolution

Provide a method where session based login can be protected by TFA
Evaluate how we handle other authentication methods.

Remaining tasks

TBD

User interface changes

TBD

API changes

TBD

Data model changes

TBD

Issue fork tfa-3374260

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

cmlara created an issue. See original summary.

mingsong’s picture

In case anyone interested in how to make TFA working with REST API at this stage, I would suggest following login process instead your own custom REST API login form/page.

  1. Redirect your custom login URL to the Drupal login URL(/user/login) or Drupal login form.
  2. Inject your business logic into the Drupal login form via Drupal form alert hook function if you need to. Such as redirecting the user to a API landing page after logging in by the following codes.
    // URL string.
    $form_state->setRedirect('route', $args, $options);
    // Or a Drupal URL object.
    $form_state->setRedirectUrl($url);
    

Once the user has signed in with TFA, then the browser will remember the user session via cookies. After this point, the user will be able to access any API or non-API pages as long as the user has the permission to do so.

This is a temporary solution as a workaround without compromising security (bypassing TFA).
Personally, I would vote for fixing(implementing) this feature in the Drupal core to make Drupal as a API First CMS.

mingsong’s picture

Is the TFA Web Services sub module created for this purpose?

The /Drupal/services_tfa/Plugin/ServiceDefinition::processRequest() function looks like a solution for it, right?

https://git.drupalcode.org/project/tfa/-/blob/2.x/services_tfa/src/Plugi...

cmlara’s picture

Is the TFA Web Services sub module created for this purpose?

To be honest I question if tfa_services was ever fully designed and implemented. It appears to have been brought in during Week 7 of the 2016 GSOC with the description of "Add Web Serivces To TFA, Add the missing reset form class". I assume this was a result of a direct conversation with the GSOC mentor as I've been unable to find anything related to it otherwise.
See: https://github.com/d8-contrib-modules/tfa/pull/57
See: https://github.com/therealssj/GSoC2016-Final-Report

A key point is at the moment it only validates a token is valid and provides an answer to the client without using the token to validate or control authentication.

I've pushed up some code that I was originally working on as a possible parallel solution for SA-CONTIRB-2023-030. I've rebased it to the 2.x branch, though I've not yet tested it on 2.x. The code is very much a work in progress and shouldn't have an MR opened yet. I recall the code had some edge cases that I needed to clean up, though I can't remember what they were at the moment. The code also absolutely needs tests written.

The main design of the code is that it intercepts the user.authentication.cookie service, ensuring that authenticate() only approves sessions that have provided a token, otherwise they cant receive 'authentication' status. In theory by being at the authentication provider we actually should be able to lock down security going forward, issues like #3323579: User login via route user.login.http bypasses TFA in theory wouldn't be possible for session based authentication anywhere, as if any route provides session authentication it wouldn't get approval from the TFA cookie service.

I'll note that this only covers SESSION based authentication, we would have to discuss how to handle other methods such as basic_auth and other random authentication (may require that we maintain a whitelist and de-register unknown method, or make it very clear that TFA is not protecting those methods.)

There are other methods for session as well, instead of using the tfa_services module we could extend/replace the existing user.login.http route and either use the 'password+token" method, there are multiple formats or we could add our own field onto the existing route and decode it for the token.

This part is really more a "whats the cleanest API"

#3041437: Increase security with single failure for login, password, and tfa comes into play as well regarding design on if we choose to use 'multiple requests' or 'single request'. I'm honestly more partial to single request as that prevents knowing if the password or the token was invalid.

cmlara’s picture

Opened #3378072: Decorate the user.auth service as another possible solution. Discussing in its own issue as it touches on a few more areas than just the REST, however it would allow us to protect known password based authentications without significant overhauls and without interfering with other formats such as API access keys that are not password based.

A user would authenticate using the ldap/radius style format for the password where the password field becomes a combination of password and token. There are several styles of this such as:

$password . $token
$password . '+' . $token // '+' may be any character or even multiple characters in theory)
// The above two but reversed order.
$token . $password
$token .  '+' .  $password
cmlara’s picture

Looking at #3378072: Decorate the user.auth service I believe that is a cleaner method of implementing the authentication validation for REST than what I have submitted into this issue so far.

I would suggest we adopt #3378072: Decorate the user.auth service and just use this issue to determine if we want to make any additional changes to REST API, such as adding explicit fields or routes. We may also want to remove tfa_services if it provides no actual security features.

jcnventura’s picture

And in a related path, we should deprecate and remove the services_tfa module: #3395250: Deprecate the services_tfa module

cmlara’s picture

Status: Active » Closed (outdated)