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

integreting with authentication plugin #79

Closed
waspinator opened this issue Apr 17, 2019 · 14 comments
Closed

integreting with authentication plugin #79

waspinator opened this issue Apr 17, 2019 · 14 comments

Comments

@waspinator
Copy link
Contributor

I'm trying to add authentication+authorization to an app using this and its related authentication plugin.

I first followed the guide for using the Authentication plugin, and things seemed to be working okay. After adding Authorization, I'm getting an Call to undefined method Authentication\Identity::setAuthorization() error after logging in.

My User entity implements the identity interface class User extends Entity implements IdentityInterface and has a setAuthorization function, as described in https://book.cakephp.org/authorization/1.1/en/middleware.html#identity-decorator

    public function setAuthorization(AuthorizationService $service)
    {
        $this->authorization = $service;

        return $this;
    }

In the a Application middleware() function I added the two new middlewares.

    $authentication = new AuthenticationMiddleware($this, [
        'unauthenticatedRedirect' => Router::url('/users/login')
    ]);

    $authorization = new AuthorizationMiddleware($this, [
        'identityDecorator' => function (AuthorizationService $authorization, Identity $user) {
            return $user->setAuthorization($authorization);
        }
    ]);

    $middlewareQueue->add($authentication);
    $middlewareQueue->add($authorization);

    return $middlewareQueue;

If I try to return the user directly from the identityDecorator I get a Invalid identity returned by decorator. 'Authentication\Identity' does not implement 'Authorization\IdentityInterface'. runtime exception .

Which configuration options am I missing to get things to work together correctly?

@markstory
Copy link
Member

What is the type of user in your identity decorator callback? I suspect that you are getting an authentication decorator and not your user class.

@markstory
Copy link
Member

This doc on authentication identity shows how you can implement the authentication interface and have your user class used by both plugins.

@waspinator
Copy link
Contributor Author

I didn't realize that I didn't have to make my own user class? Does the plugin do user class stuff on its own?

I'm used to the Auth component, so I just baked a user as usual, and I thought I added everything I needed. It seems the user class is in the 'data' property, but I'm not sure how to access it. $user->data is null?

        $authorization = new AuthorizationMiddleware($this, [
            'identityDecorator' => function (AuthorizationService $authorization, Identity $user) {
                debug($user);die;
                return $user->setAuthorization($authorization);
            },

returns

object(Authentication\Identity) {

	'config' => [
		'fieldMap' => [
			'id' => 'id'
		]
	],
	'data' => object(App\Model\Entity\User) {

		'id' => (int) 1,
		'username' => 'admin',
                //...
		'[new]' => false,
		'[accessible]' => [
			'username' => true,
			'password' => true,
			//...
		],
		'[dirty]' => [],
		'[original]' => [],
		'[virtual]' => [
			(int) 0 => 'full_name',
			(int) 1 => 'avatar'
		],
		'[hasErrors]' => false,
		'[errors]' => [],
		'[invalid]' => [],
		'[repository]' => 'Users'
	
	}

}

my user entity is as follows

<?php
namespace App\Model\Entity;

use Authentication\PasswordHasher\DefaultPasswordHasher;
use Authorization\AuthorizationService;
use Authorization\IdentityInterface;
use Cake\Filesystem\File;
use Cake\ORM\Entity;

class User extends Entity implements IdentityInterface
{
    protected $_accessible = [
        'username' => true,
        'password' => true,
        //...
    ];

    protected $_hidden = [
        'password',
        //...
    ];

   //...

    /**
     * Authentication\IdentityInterface method
     */
    public function getIdentifier()
    {
        return $this->id;
    }

    /**
     * Authentication\IdentityInterface method
     */
    public function getOriginalData()
    {
        return $this;
    }

    /**
     * Authorization\IdentityInterface method
     */
    public function can($action, $resource)
    {
        return $this->authorization->can($this, $action, $resource);
    }

    /**
     * Authorization\IdentityInterface method
     */
    public function applyScope($action, $resource)
    {
        return $this->authorization->applyScope($this, $action, $resource);
    }

    /**
     * Setter to be used by the middleware.
     */
    public function setAuthorization(AuthorizationService $service)
    {
        $this->authorization = $service;

        return $this;
    }

}

@waspinator
Copy link
Contributor Author

I created an example project with authentication and authorization to better describe the issue.

https://github.com/waspinator/cakephp-quickstart/tree/authorization

@markstory
Copy link
Member

I didn't realize that I didn't have to make my own user class? Does the plugin do user class stuff on its own?

You should still make your own user class as it will be easier to add application specific logic. Your user entity also needs to implement the IdentityInterface in the authentication plugin. Then you can follow the directions in the link I gave earlier to have the authentication plugin skip decorating your User object.

@waspinator
Copy link
Contributor Author

I tried to implement the IdentityInterface, but maybe I missed something?

https://github.com/waspinator/cakephp-quickstart/blob/authorization/app/src/Model/Entity/User.php

Authorization can't find the setAuthorization function though.

@markstory
Copy link
Member

markstory commented Apr 24, 2019

Each plugin has its own identity interface. Your class needs to look like:

namespace App\Model\Entity;
use Authentication\PasswordHasher\DefaultPasswordHasher;
use Authorization\AuthorizationService;
use Authorization\IdentityInterface as AuthorizationIdentity;
use Authentication\IdentityInterface as AuthenticationIdentity;
use Cake\ORM\Entity;

class User extends Entity implements AuthorizationIdentity, AuthenticationIdentity
{
...
}

And you'd also need to replace the identityDecorator callback in the authentication middleware like in this page.

@waspinator
Copy link
Contributor Author

I'm sorry Mark, can you spell out what I need to change? I updated the Entity implements, but I'm not sure what to do with the identityDecorator This is what I have:

Application.php

class Application extends BaseApplication implements AuthenticationServiceProviderInterface, AuthorizationServiceProviderInterface
{

    public function getAuthenticationService(ServerRequestInterface $request, ResponseInterface $response)
    {
        $service = new AuthenticationService([
            'identityClass' => $identityResolver // what should $identityResolver be?
        ]);

        $fields = [
            'username' => 'email',
            'password' => 'password'
        ];

        // Load identifiers
        $service->loadIdentifier('Authentication.Password', compact('fields'));

        // Load the authenticators, you want session first
        $service->loadAuthenticator('Authentication.Session');
        $service->loadAuthenticator('Authentication.Form', [
            'fields' => $fields,
            'loginUrl' => '/users/login'
        ]);

        return $service;
    }

    public function getAuthorizationService(ServerRequestInterface $request, ResponseInterface $response)
    {
        $resolver = new OrmResolver();

        return new AuthorizationService($resolver);
    }

...

@markstory
Copy link
Member

If your user class implements both IdentityInterfaces, you don't need to use an identityResolver, the authentication plugin will detect that your user object implements the Authentication\IdentityInterface and not apply any decorators.

@waspinator
Copy link
Contributor Author

waspinator commented May 2, 2019

okay, I think I got it. I was using the wrong type for the $identity parameter.

$authorization = new AuthorizationMiddleware($this, [
    'identityDecorator' => function (AuthorizationServiceInterface $authorization, ArrayAccess $identity) {
        return $identity->setAuthorization($authorization);

    },
    'requireAuthorizationCheck' => true,
    'unauthorizedHandler' => [
        'className' => 'Authorization.Redirect',
        'url' => '/users/login',
        'queryParam' => 'redirectUrl',
        'exceptions' => [
            MissingIdentityException::class,
            OtherException::class
        ]
    ]
]);

@dgnovo
Copy link

dgnovo commented Nov 21, 2020

My cakePHP app works with authorization plugin.

On phpunit, if i test login with

$this->session(['Auth' => $user]); $this->enableCsrfToken(); $this->enableSecurityToken(); $this->get('Users/login');

'identityDecorator' => function (AuthorizationService $auth, ArrayAccess $user) { debug($user); return $user->setAuthorization($auth); }

My User on test is

`
object(Authentication\Identity) {

'config' => [
	'fieldMap' => [
		'id' => 'id'
	]
],
'data' => object(App\Model\Entity\User) {...}`

and i retrieve this error:

Call to undefined method Authentication\Identity::setAuthorization()

Any ideas?

@markstory
Copy link
Member

I think you have partially setup using a custom identity object. Your user class should also implement the Authentication\IdentityInterface as well.

@dgnovo
Copy link

dgnovo commented Nov 21, 2020

I think you have partially setup using a custom identity object. Your user class should also implement the Authentication\IdentityInterface as well.

I didn't setup Custom Identity.

@othercorey
Copy link
Member

Please open a new issue if something isn't working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants