Skip to content

Instantly share code, notes, and snippets.

@igorbenic
Last active September 5, 2019 09:10
Show Gist options
  • Save igorbenic/12c7c4f1fc7ec8895660e7f7a02f1a95 to your computer and use it in GitHub Desktop.
Save igorbenic/12c7c4f1fc7ec8895660e7f7a02f1a95 to your computer and use it in GitHub Desktop.
Headless WordPress: Logging with Google and JWT | https://www.ibenic.com/headless-wordpress-google-jwt
<?php
namespace Headless\REST_API;
use \Firebase\JWT\JWT;
class Login extends REST_API {
// ...
/**
* Login with Google.
*
* @param \WP_REST_Request $request
*/
public function login_google( $request ) {
header( 'Access-Control-Allow-Origin: *' );
$params = $request->get_params();
$token = isset( $params['token'] ) ? $params['token'] : false;
if ( ! $token ) {
return new \WP_Error( 'no-token', __( 'No token received from Google', 'vivant' ) );
}
if ( ! class_exists( 'Google_Client' ) ) {
include_once plugin_dir_path( HEADLESS_FILE ) . '/vendor/google/apiclient/src/Google/Client.php'; // change path as needed
}
$client = new \Google_Client( [ 'client_id' => $this->googleClient ] ); // Specify the CLIENT_ID of the app that accesses the backend
try {
$payload = $client->verifyIdToken( $token );
if ( $payload ) {
$google_user_id = $payload['sub'];
// If request specified a G Suite domain:
//$domain = $payload['hd'];
$email = $payload['email'];
$name = $payload['name'];
$user = $this->find_user_by( 'google_id', $google_user_id );
if ( is_a( $user, 'WP_User' ) ) {
update_user_meta( $user->ID, '_google_token', $token );
return $this->create_token( $user );
} else {
$user = $this->find_user_by( 'email', $email );
if ( ! $user ) {
$username = str_replace( ' ', '', $name );
$user_id = $this->create_user( $username, $email );
$first_name = isset( $payload['given_name'] ) ? $payload['given_name'] : $name;
$last_name = isset( $payload['family_name'] ) ? $payload['family_name'] : '';
if ( is_wp_error( $user_id ) ) {
return $user_id;
}
$user = new \WP_User( $user_id );
wp_update_user( array( 'ID' => $user_id,
'display_name' => $name,
'first_name' => $first_name,
'last_name' => $last_name
) );
}
add_user_meta( $user->ID, '_google_user_id', $google_user_id );
add_user_meta( $user->ID, '_google_token', $token );
return $this->create_token( $user );
}
} else {
return new \WP_Error( 'invalid-token', __( 'Token is not valid', 'vivant' ) );
}
} catch ( \Exception $e ) {
return new \WP_Error( $e->getCode(), $e->getMessage() );
}
}
}
<?php
namespace Headless\REST_API;
use \Firebase\JWT\JWT;
class Login extends REST_API {
/**
* Client
*
* @var string
*/
protected $googleClient = 'YOUR_GOOGLE_CLIENT_ID';
/**
* @var string
*/
protected $path = 'login';
/**
* Experience constructor.
*/
public function __construct() {
$this->prepare_google();
$this->add_route( 'google', array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array( $this, 'login_google' ),
));
}
/**
* @param $key
* @param $value
*
* @return false|\WP_User
*/
protected function find_user_by( $key, $value ) {
$user = false;
switch ( $key ) {
case 'google_id':
$user_query = new \WP_User_Query(
array(
'meta_key' => '_google_user_id',
'meta_value' => $value
)
);
// Get the results from the query, returning the first user
$users = $user_query->get_results();
if ( is_array( $users ) && $users ) {
$user = $users[0];
$user = new \WP_User( $user->ID );
}
break;
default:
$user = \get_user_by( $key, $value );
break;
}
return $user;
}
/**
* Prepare Google Data.
*/
protected function prepare_google() {
if ( ! $this->googleClient && defined( 'HEADLESS_GOOGLE_CLIENT_ID' ) ) {
$this->googleClient = HEADLESS_GOOGLE_CLIENT_ID;
}
}
/**
* Create the user.
*
* @param string $username
* @param string $email
*
* @return integer User ID.
*/
protected function create_user( $username, $email, $password = '' ) {
if ( username_exists( $username ) ) {
$username .= date( 'YmdHis');
}
if ( ! $password ) {
$password = wp_generate_password();
}
$user_id = wp_create_user( $username, $password, $email );
return $user_id;
}
/**
* Create the token for the User.
*
* @param \WP_User $user User object.
*
* @return mixed|void|\WP_Error
*/
protected function create_token( $user ) {
$secret_key = defined('JWT_AUTH_SECRET_KEY') ? JWT_AUTH_SECRET_KEY : false;
/** First thing, check the secret key if not exist return a error*/
if (!$secret_key) {
return new \WP_Error(
'jwt_auth_bad_config',
__('JWT is not configurated properly, please contact the admin', 'wp-api-jwt-auth'),
array(
'status' => 403,
)
);
}
/** Valid credentials, the user exists create the according Token */
$issuedAt = time();
$notBefore = apply_filters('jwt_auth_not_before', $issuedAt, $issuedAt);
$expire = apply_filters('jwt_auth_expire', $issuedAt + (DAY_IN_SECONDS * 7), $issuedAt);
$token = array(
'iss' => get_bloginfo('url'),
'iat' => $issuedAt,
'nbf' => $notBefore,
'exp' => $expire,
'data' => array(
'user' => array(
'id' => $user->ID,
),
),
);
/** Let the user modify the token data before the sign. */
$token = JWT::encode(apply_filters('jwt_auth_token_before_sign', $token, $user), $secret_key);
$data = array(
'token' => $token,
'user_email' => $user->user_email,
'user_nicename' => $user->user_nicename,
'user_display_name' => $user->display_name,
);
/** Let the user modify the data before send it back */
return apply_filters('jwt_auth_token_before_dispatch', $data, $user);
}
/**
* Login with Google.
*
* @param \WP_REST_Request $request
*/
public function login_google( $request ) {}
}
<?php
namespace Headless\REST_API;
/**
* Class REST_API
*
* @package Vivant
*/
class REST_API {
/**
* Version of the REST API.
*/
protected $version = 'v1';
/**
* @var string
*/
protected $path = '';
/**
* @var array
*/
protected $routes = array();
/**
* @return array
*/
public function get_routes() {
return $this->routes;
}
/**
* @param $route
* @param $config
*/
public function add_route( $route, $config ) {
$this->routes[] = array(
'route' => $this->version . '/' . $this->path . '/' . $route,
'config' => $config
);
}
}
<?php
/**
* Handling global REST API requests.
*/
namespace Headless;
use \Headless\REST_API\Login;
/**
* Class REST_API
*
* @package Vivant
*/
class REST_APIS {
private $routes = array();
public function __construct() {
include_once 'rest-api/class-login.php';
}
/**
* Register all hooks.
*/
public function register_hooks() {
add_action( 'rest_api_init', array( $this, 'prepare_route' ) );
}
/**
* Prepare the routes
*/
public function prepare_route() {
$login = new Login();
$this->routes = array_merge( $this->routes, $login->get_routes() );
foreach ( $this->routes as $route ) {
\register_rest_route( 'headless', $route['route'], $route['config'] );
}
}
}
<?php
/**
* Plugin Name: Headless WordPress
* Description: Plugin to allow certain headless functions
*/
namespace Headless;
if ( ! defined( 'ABSPATH' ) ) {
return;
}
define( 'HEADLESS_FILE', __FILE__ );
/**
* Headless
*/
class Headless {
public function __construct() {
$this->includes();
$this->run();
}
public function includes() {
include_once 'vendor/autoload.php';
include_once 'includes/abstracts/class-rest-api.php';
include_once 'includes/class-rest-apis.php';
}
public function run() {
$rest_apis = new REST_APIS();
$rest_apis->register_hooks();
// Used in the previous tutorial on DropZone.
if ( function_exists( 'register_meta' ) ) {
register_meta( 'user', 'avatar', [ 'show_in_rest' => true ]);
register_meta( 'user', 'avatar_id', [ 'show_in_rest' => true ]);
}
}
}
new Headless();
import React from 'react';
import { TextControl, Button, Notice } from '@wordpress/components';
import LoginGoogle from './login/LoginGoogle'; //Adding Google Component
const axios = require('axios');
class Login extends React.Component {
constructor( props ) {
super( props );
// ... previous bindings are here.
this.setLogin = this.setLogin.bind(this);
}
setLogin( token ) {
localStorage.setItem( 'login', token );
this.props.setLogin( token ); //setLogin is passed in App.js.
}
render() {
return (
<form className="login" method="post">
// Other fields are here.
<Button isPrimary onClick={this.handleSubmit}>Login</Button>
Or
<LoginGoogle url={this.props.url} setLogin={this.setLogin} />
</form>
);
}
}
export default Login;
import React from 'react';
import GoogleLogin from 'react-google-login';
const axios = require('axios');
class LoginGoogle extends React.Component {
constructor( props ){
super( props );
this.loginWithGoogle = this.loginWithGoogle.bind(this);
}
loginWithGoogle( response ) {
var self = this;
var id_token = response.tokenId;
axios.post( this.props.url + '/wp-json/headless/v1/login/google', { token: id_token } ).then( ( resp ) => {
self.props.setLogin( resp.data.token );
});
}
errorWithGoogle( error ) {
console.error( error );
}
render() {
return (
<GoogleLogin
clientId="YOUR_GOOGLE_CLIENT_ID"
buttonText="Google"
onSuccess={this.loginWithGoogle}
onFailure={this.errorWithGoogle}
/>
)
}
}
export default LoginGoogle;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment