[SOLVED] CakePHP 3.8 Authentication Plugin: FAILURE_CREDENTIALS_MISSING

This is my first attempt at using the Authentication plugin in Cake 3.8. I’ve followed the examples outlined in https://book.cakephp.org/authentication/1/en/index.html.

Angular / Typescript code for sending credentials to Cake:

  /**
   * Login
   */
  public login( username : string,
                password : string,
                captchaResponse : string ) {

    return this.http.post<any>( 'http://api.mydomain.localhost/users/token.json',
                                {
                                  username,
                                  password,
                                  captchaResponse
                                },
                                {
                                  headers : new HttpHeaders()
                                    .set( 'X-Requested-With', 'XMLHttpRequest' )
                                }
    ).pipe( map( response => {
      // Store user details and jwt token in local storage
      // to keep user logged in between page refreshes
      this.user = response.data;
      this.setUser( this.user.profile );
      this.setToken( this.user.token );
      return this.user;
    } ) );
  }

In Application.php

public function bootstrap() {
    // Authentication
    $this->addPlugin( 'Authentication' );
}

/**
 * Returns a service provider instance.
 *
 * @param \Psr\Http\Message\ServerRequestInterface $request Request
 * @param \Psr\Http\Message\ResponseInterface $response Response
 * @return \Authentication\AuthenticationServiceInterface
 */
public function getAuthenticationService( ServerRequestInterface $request, ResponseInterface $response ) {
    $service = new AuthenticationService();

    // Load identifiers
    $service->loadIdentifier( 'Authentication.Password', [
        'fields'   => [
            'username' => 'username',
            'password' => 'password',
        ],
        'resolver' => [
            'className' => 'Authentication.Orm',
            'finder'    => 'active',
            'userModel' => 'Users',
        ],
    ] );
    // Load the authenticators
    $service->loadAuthenticator( 'Authentication.Form', [
        'fields' => [
            'username' => 'username',
            'password' => 'password',
        ],
        'loginUrl' => '/users/token.json'
    ] );
    $service->loadIdentifier( 'Authentication.JwtSubject' );

    // Configure the service. (see below for more details)
    return $service;
}

public function middleware( $middlewareQueue ) {
    $middlewareQueue
        // Authentication Middleware
        ->add( new AuthenticationMiddleware( $this ) );

    return $middlewareQueue;
}

In AppController.php

// Authentication Component
$this->loadComponent( 'Authentication.Authentication' );

I’m sending the credentials to Cake (acting as a REST API) from an Angular 7 app. The creds are sent via a post request embedded in the body of the request. I keep getting authentication result as invalid, when I check via $result->isValid().

In UsersController.php, when I try to trace the error:

$this->log( $this->request->getData(), 'debug' );
$result = $result = $this->Authentication->getResult();
$this->log( $result, 'debug' );

I get the following output:

2019-12-03 07:35:30 Debug: Array
(
    [username] => myuser
    [password] => mypassword
    [captchaResponse] => 
)

2019-12-03 07:35:30 Debug: Authentication\Authenticator\Result Object
(
    [_status:protected] => FAILURE_CREDENTIALS_MISSING
    [_data:protected] => 
    [_errors:protected] => Array
        (
            [0] => Login credentials not found
        )

)

I just can’t figure out why Cake is failing to detect the presence of the credentials in the post data. Anybody else faced the same issue and have a solution?

Thank you.

Found the issue, finally. Adding some debug steps inside FormAuthenticator::_getData() helped resolve the issue. What I found is that _getData() was passing a null array to the authenticator.

If you look at the Angular code above, I was directly including the username and password as part of a dynamically created object in the body:

return this.http.post<any>( 'http://api.mydomain.localhost/users/token.json',
                            // NOT THE RIGHT WAY OF DOING IT
                            {
                              username,
                              password,
                              captchaResponse
                            },
                            {
                              headers : new HttpHeaders()
                                .set( 'X-Requested-With', 'XMLHttpRequest' )
                            }
)

For some reason, the new FormAuthenticator / Authetication plugin isn’t able to parse this information - though this wasn’t an issue with the old AuthComponent.

Instead, I had to modify the Angular code to utilize the FormData() object for automated addition of the Content-Type (application/x-www-form-urlencoded or formdata) header and the content boundary. The modified code is shown below:

// Prepare form data
const formData = new FormData();
formData.append( 'username', username );
formData.append( 'password', password );
formData.append( 'captcha', captchaResponse );

return this.http.post<any>( 'http://api.mydomain.localhost/users/token.json',
                            formData,
                            {
                              headers : new HttpHeaders()
                                .set( 'X-Requested-With', 'XMLHttpRequest' )
                                .set( 'Accept', 'application/json' )
                                .set( 'Cache-Control', 'no-cache' )
                            }
)

Hope this helps anyone facing the same issue in future.