Authenticate API with Username and Password

How can I authenticate a user with a username and password via the API?

In the previous version you could (in config/public/api/auth/php) do

return $this->invoke('Cockpit\\Controller\\RestApi', 'authUser');

But apparently this doesn’t work anymore.

Thanks in advance!

2 Likes

I’m also looking for this feature in V2.
I know this isn’t easy to implement in V2.
I wish @artur answer us how to implement it or the right way to get API key with username and password. Thanks

Found a way for that.

  1. Generate the api key of the user from the admin panel.

  2. Edit app.user.disguise event in /modules/App/bootstrap.php and comment or delete the $user['apiKey'] item.

$this->on('app.user.disguise', function(array &$user) {
    unset($user['password'], /* $user['apiKey'], */ $user['_reset_token']);
});
  1. Edit /modules/App/Controller/Auth.php and add this new function:
public function apiLogin() {

    $auth = $this->param('auth');
  
    if (!$auth || !isset($auth['email'], $auth['password']) || !\is_string($auth['email']) || !\is_string($auth['password'])) {
        return $this->stop(412);
    }
  
    $user = $this->helper('auth')->authenticate($auth, true);
  
    if ($user) {
        return ['success' => true, 'user' => $user];
    }
  
    return ['success' => false];
}
  1. Edit modules\App\Helper\Auth.php and modify the authenticate function:
public function authenticate(array $data, $forApi = false): mixed {

    $data = array_merge([
        'user'     => '',
        'email'    => '',
        'password' => ''
    ], $data);

    if (!$data['password']) return false;

    $filter = ['active' => true];

    if ($data['email']) {
        $filter['email'] = $data['email'];
    } else {
        $filter['user'] = $data['user'];
    }

    $user = $this->app->dataStorage->findOne('system/users', $filter);

    if ($user && (password_verify($data['password'], $user['password']))){

        $user = array_merge($data, (array)$user);

        unset($user['password']);

        if (!$forApi) {
            $this->app->trigger('app.user.authenticate', [&$user]);
        }

        return $user;
    }

    return false;
}
  1. Now you can get the whole user info and its api key from /api/auth/apiLogin endpoint with the request body below:
{ auth: { email: "xyz@example.com", password: "Abc123@" } }

I don’t sure exposing the disguised api key is a good idea, but it works.

1 Like

Thanks @ronaldaug, yes this is a safer approach. But I think it would be better to return users’ own api key instead of sending a fixed one for everyone. It will cause that every cruds are made by the same user.

In Cockpit you can create (file based) custom api end points pretty easy within the config folder.

Create a file (with folder path) in the root folder:

config/api/user/auth.php

with the following contents:


<?php

$user = $this->param('user');
$password = $this->param('password');

if (!$user || !$password) {
    return $this->stop(['error' => 'User and password are required'], 412);
}

$user = $this->helper('auth')->authenticate(compact('user', 'password'));

if (!$user) {
  return $this->stop(['error' => 'Authentication failed!'], 412);
}

return ['apiKey' => $user['apiKey']];


Now you can run requests against /api/user/auth

2 Likes

@artur This solution worked perfectly for my frontend, Thanks. :heart:

one question, previously we had /api/cockpit/saveUser & /api/cockpit/listUsers for saving and listing user from cockpit v1. how do we accomplish the same in latest cockit 2.3 v2? help will be highly appreciated, stuck over this problem form sometime now,without success :sweat_smile:

1 Like

This is my current status:

<?php

$user = "tesqtme";
$name= "tesqtme";
$email= "testqme@gmail.com";
$password = "123456";
$role= "admin";
$created = time();





 $app = Cockpit::instance();
    
    $userExist = $this->app->dataStorage->findOne('system/users', ['user' => $user]);
    $emailExist = $this->app->dataStorage->findOne('system/users', ['email' => $email]);
    
   // echo $userExist;
   // echo $emailExist;
  //  echo "done";
     
        $user = [
        'active' => true,
        'user' => $user,
        'name' => $name,
        'email' => $email,
        'password' => $app->hash('$password'),
        'i18n' => 'en',
        'role' => 'admin',
        'theme' => 'auto',
        '_modified' => $created,
        '_created' => $created
         ];

        $app->dataStorage->save('system/users', $user);
      //  return [ $userExist =>  $emailExist]; 
    
        
       //if (!$userExist && !$emailExist ){
       
       
?>

Plz let me know how to proceed “properly” from this point.

Still trying to figure a way to list all users though, plus only admin or “editor” should be able to do that, currently i am able to count (though that path is public)

<?php 
 $app = Cockpit::instance();
if ($app->dataStorage->getCollection('system/users')->count()) {
            $users= $app->dataStorage->getCollection('system/users')->count();
            echo $users ;
            exit;
    }
    
    ?>
2 Likes

Hello. If i want to have a list of the users and not de number of users. What I need to change in this code ?
Thank’s

Hello Artur.
Thank’s for your help.
But, in the config/api/user/list.php i have this code

<?php 
 $app = Cockpit::instance();
if ($app->dataStorage->getCollection('system/users')->count()) {
            $users= $app->dataStorage->getCollection('system/users')->count();
            $usersv= $app->dataStorage->getCollection('system/users')->findOne();
            $usersv["nb"] = $users;
            echo json_encode($usersv);
            //echo $users ;
            exit;
    }

I want to see the users list but de find function don’t worls and the findOne function return me the first user.

Have You one idea ?

Thank’s

1 Like