Search code examples
phpcodeignitergoogle-oauthcodeigniter-4

CodeIgniter 4 with Shield and Google Oauth2


So I just want to add login with google feature on my working authentication web app (with Codeigniter Shield package). I've already create a login_google function on Login controller that extends LoginController from shield package like this :

LoginController

<?php

namespace App\Controllers;

use App\Controllers\BaseController;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\Shield\Controllers\LoginController;

class Login extends LoginController
{
    function __construct()
    {
        require_once __DIR__ . '/../../vendor/autoload.php';
        $this->userModel = new \App\Models\UserModel();
        $this->google_client = new \Google_Client();
        $this->google_client->setClientId(getenv('OAuth2.clientID'));
        $this->google_client->setClientSecret(getenv('OAuth2.clientSecret'));
        $this->google_client->setRedirectUri('http://localhost:8080/login_google');
        $this->google_client->addScope('email');
        $this->google_client->addScope('profile');           
    }
    public function loginView()
    {
        if (auth()->loggedIn()) {
            return redirect()->to(config('Auth')->loginRedirect());
        }

        /** @var Session $authenticator */
        $authenticator = auth('session')->getAuthenticator();

        // If an action has been defined, start it up.
        if ($authenticator->hasAction()) {
            return redirect()->route('auth-action-show');
        }

        $data['google_button'] = "<a href='".$this->google_client->createAuthUrl()."'><img src='https://developers.google.com/identity/images/btn_google_signin_dark_normal_web.png' /></a>";
        return view('login', $data);
    }

    public function loginAction(): RedirectResponse
    {
        // Validate here first, since some things,
        // like the password, can only be validated properly here.
        $rules = $this->getValidationRules();

        if (! $this->validate($rules)) {
            return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
        }

        $credentials             = $this->request->getPost(setting('Auth.validFields'));
        $credentials             = array_filter($credentials);
        $credentials['password'] = $this->request->getPost('password');
        $remember                = (bool) $this->request->getPost('remember');

        /** @var Session $authenticator */
        $authenticator = auth('session')->getAuthenticator();

        // Attempt to login
        $result = $authenticator->remember($remember)->attempt($credentials);
        if (! $result->isOK()) {
            return redirect()->route('login')->withInput()->with('error', $result->reason());
        }

        /** @var Session $authenticator */
        $authenticator = auth('session')->getAuthenticator();

        // If an action has been defined for login, start it up.
        if ($authenticator->hasAction()) {
            return redirect()->route('auth-action-show')->withCookies();
        }

        return redirect()->to(config('Auth')->loginRedirect())->withCookies();
    }

    public function login_google() {
        $token = $this->google_client->fetchAccessTokenWithAuthCode($this->request->getVar('code'));
        if (!isset($token['error'])) {
            $this->google_client->setAccessToken($token['access_token']);
            $this->session->set('access_token', $token['access_token']);

            $google_service = new \Google\Service\Oauth2($this->google_client);
            $data = $google_service->userinfo->get();
            
            $userdata = array();
            if ($this->userModel->isAlreadyRegister($data['id'])) {
                $userdata = [
                    'first_name' => $data['givenName'],
                    'last_name' => $data['familyName'],
                    'email' => $data['email'],
                    'avatar' => $data['picture'],
                ];
                $this->userModel->updateUserData($userdata, $data['id']);
            } else {
                $userdata = [
                    'first_name' => $data['givenName'],
                    'last_name' => $data['familyName'],
                    'email' => $data['email'],
                    'avatar' => $data['picture'],
                    'oauth_id' => $data['id'],
                ];
                $this->userModel->insertUserData($userdata);
            }
            $this->session->set('LoggedUserData', $userdata);
        } else {
            $this->session->set("error", $token['error']);
            return redirect('/register');
        }

        return redirect()->to('/profile');
    }
}

UserModel like this :

UserMode

<?php

namespace App\Models;

use CodeIgniter\Model;
use CodeIgniter\Shield\Models\UserModel as ModelsUserModel;

class UserModel extends ModelsUserModel
{
    protected $allowedFields = [
        'username',
        'status',
        'status_message',
        'active',
        'last_active',
        'deleted_at',
        'gender',
        'first_name',
        'last_name',
        'avatar',
        'phone_number',
        'full_address',
        'oauth_id',
    ];

    function isAlreadyRegister($authid){
        return $this->db->table('users')->getWhere(['id'=>$authid])->getRowArray()>0?true:false;
    }
    function updateUserData($userdata, $authid){
        $this->db->table("users")->where(['id'=>$authid])->update($userdata);
    }
    function insertUserData($userdata){
        $this->db->table("users")->insert($userdata);
    }
}

But everytime I clicked sign in with google button, it won't work (the interface for choosing google account to authenticate is worked) and always return to login page

am I missing something when combining CodeIgniter Shield with Google Oauth ? Anyone can help ? TIA


Solution

  • A new package has been created for OAuth with Shield package: https://github.com/datamweb/shield-oauth

    You can use it instead of your own one.