Get email address from OpenID provider using Janrain OpenID library - php

When I sign in to StackOverflow using Google, I get the following message:
Stackoverflow.com is asking for some information from your Google Account example#gmail.com
• Email address: example#gmail.com
However, on my own site, when I log in with OpenID I can't ask for the e-mail address. Instead, I get this message:
You are signing in to example.com with your Google Account example#gmail.com
I am also finding it difficult to understand at what step I need to request the e-mail address. Here is the code that I think the step should be built into:
/**
* Authenticates the given OpenId identity.
* Defined by Zend_Auth_Adapter_Interface.
*
* #throws Zend_Auth_Adapter_Exception If answering the authentication query is impossible
* #return Zend_Auth_Result
*/
public function authenticate() {
$id = $this->_id;
$consumer = new Auth_OpenID_Consumer($this->_storage);
if (!empty($id)) {
$authRequest = $consumer->begin($id);
if (is_null($authRequest)) {
return new Zend_Auth_Result(
Zend_Auth_Result::FAILURE,
$id,
array("Authentication failed", 'Unknown error')
);
}
if (Auth_OpenID::isFailure($authRequest)) {
return new Zend_Auth_Result(
Zend_Auth_Result::FAILURE,
$id,
array("Authentication failed", "Could not redirect to server: " . $authRequest->message)
);
}
$redirectUrl = $authRequest->redirectUrl($this->_root, $this->_returnTo);
if (Auth_OpenID::isFailure($redirectUrl)) {
return new Zend_Auth_Result(
Zend_Auth_Result::FAILURE,
$id,
array("Authentication failed", $redirectUrl->message)
);
}
Zend_OpenId::redirect($redirectUrl);
} else {
$response = $consumer->complete(Zend_OpenId::selfUrl());
switch($response->status) {
case Auth_OpenID_CANCEL:
case Auth_OpenID_FAILURE:
return new Zend_Auth_Result(
Zend_Auth_Result::FAILURE,
null,
array("Authentication failed. " . #$response->message)
);
break;
case Auth_OpenID_SUCCESS:
return $this->_constructSuccessfulResult($response);
break;
}
}
}
This seems like it should be so obvious... but I'm having a hard time Googling it and combing through the code to figure it out. Any help would be greatly appreciated!

You can ask for an email address using Zend Simple Registration Extension
$sreg = new Zend_OpenId_Extension_Sreg(array(
'nickname'=>true,
'email'=>false,
'fullname'=>false), null, 1.1);
$consumer = new Zend_OpenId_Consumer();
if (!$consumer->login($_POST['openid_identifier'],
'example-6_3.php',
null,
$sreg)) {
die("OpenID login failed.");
}
If you want to use Janrain library, you can add extensions to the request like this:
$sreg_request = Auth_OpenID_SRegRequest::build(
// Required
array('nickname'),
// Optional
array('email'));
if ($sreg_request) {
$authRequest->addExtension($sreg_request);
}
Have a look at the consumer example: https://github.com/openid/php-openid/blob/master/examples/consumer/

Related

Create my own JWT server auth or use a library like auth0

I am just learning about JWT and Authentication in general, its my first real look inside this world so Its all abit overwhelming.
I wanted to add login/general sessions for my new site (Angular 2 SPA), and after abit of research JWT seemed to be the best approach, so I got php-jwt and setup a super basic authentication with my basic database shown here:
class userAuth {
// create an empty id variable to hold the user id
private $id;
private $email;
private $key = "16FD8C979FC40CCB97457F4AD79B32A73758771B4D1943C379FB3266EECE0C3E";
// Checks if the user exists in the database
private function validUser($email, $password) {
$conn = new mysqli(DBSERVER, DBUSERNAME, DBPASSWORD, DBNAME);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$truepassword = hash('sha256', $password); // password hashing using SHA256
$query = $conn->query("select * from users where ( username='$email' OR email = '$email') and password='$truepassword'");
$count = mysqli_num_rows($query);
if($count == 1) {
$row = mysqli_fetch_array($query);
$this->id = $row['id'];
$this->email = $row['email'];
return true;
}else{
return false;
}
}
private function genJWT() {
// Make an array for the JWT Payload
$payload = array(
"id" => $this->id,
"email" => $this->email,
"exp" => time() + (60 * 60)
);
// encode the payload using our secretkey and return the token
return JWT::encode($payload, $this->key);
}
public function checkUser($email, $password) {
// check if the user exists
if ($this->validUser($email, $password)) {
// generate JSON web token and store as variable
$token = $this->genJWT();
$resultJSON = array(
'email' => $this->email,
'token' => $token
);
return json_encode($resultJSON);
} else {
return 'We Couldn\'t Find You In Our Database. Maybe Wrong Email/Password Combination';
}
}
private function validJWT($token) {
$res = array(false, '');
// using a try and catch to verify
try {
//$decoded = JWT::decode($token, $this->key, array('HS256'));
$decoded = JWT::decode($token, $this->key, array('HS256'));
} catch (Exception $e) {
return $res;
}
$res['0'] = true;
$res['1'] = (array) $decoded;
return $res;
}
public function validLogin($token) {
// checks if an email is valid
$tokenVal = $this->validJWT($token);
// check if the first array value is true
if ($tokenVal['0']) {
// create user session and all that good stuff
return "Everything went well, time to serve you what you need.";
} else {
return "There was an error validating your email. Send another link";
}
}
}
And this was good to get my head around the idea of JWT auth, I managed (after hours mind you) to collect the token and save it to local storage on a successful login. but I tried to use a jwt library to properly manage getting info with my token etc I couldnt get it to work, beccause I imagine my setup doesnt provide what the lib expects the token to be formatted or something along those lines
Would it be a good idea to try and just continue and learn as I go in this manner, because at the moment I havnt found many resources or tutorials on building my own JWT backend, or how I would talk to said backend correctly.
I have now setup a basic auth0 account and im looking into/testing that usage, would I be better off learning on my own, or using a lib (happy to be referred to, to other JWT API's that work with angular 2, or would be appropriate for it).
I also dont like the idea of my server backend being someone else's if that makes sense.

Google always returns false verifying id token

I have the next code, got directly from google reference (https://developers.google.com/identity/sign-in/web/backend-auth)
public function verifyFromAndroid($idToken=null) {
if(empty($idToken)) {
$idToken = self::SAMPLE_ID_TOKEN;
}
$client = new Google_Client(['client_id' => self::CLIENT_ID]);
$payload = $client->verifyIdToken($idToken);
if ($payload) {
print_r($payload);
$userid = $payload['sub'];
// If request specified a G Suite domain:
//$domain = $payload['hd'];
} else {
var_dump($payload);
$this->lastError = "Invalid ID token";
return false;
}
}
But this method always returns false, even using a valid id token that is created and working using the oauthplayground online tool.
The next code works fine, using directly the GoogleAccessToken_Verify class. Can someone tell me why the official Google code doesn't work and yes my own code using the official Google-clien-php sdk?
try {
$verify = new Google_AccessToken_Verify();
$result = $verify->verifyIdToken($this->idToken);
if($result) {
print_r($result);
$friendlyData = $this->translateData($result, true);
if(!$friendlyData) {
return false;
}
return $friendlyData;
}
else {
$this->lastError = "Invalid token verification, no error code";
return false;
}
}
catch(UnexpectedValueException $ex) {
$this->lastError = "UnVaEx (Code {$ex->getCode()}): {$ex->getMessage()}";
return false;
}
try adding complete client ID
xxxxxxxxxxxxxx-xxxxx-yy-zz.apps.googleusercontent.com
while initiating the
$client = new Google_Client(['client_id' => self::CLIENT_ID]);
It should work i was also facing the same issue ...
Had a similar issue.Deleted my android app on firebase console and created a fresh app wirh debug key sha1.Then downloaded and replaced my google.json into my app.This fixed my issue.This has happened to me twice now. At times you just need to recreate the android app on firebase console.
Before you begin register your backend URL at https://developers.google.com/identity/sign-in/web/sign-in with Configure your project button and don't use any credidentials or api key in your code. After doing them your code should look like to this.
public function verifyFromAndroid($idToken=null) {
if(empty($idToken)) {
$idToken = self::SAMPLE_ID_TOKEN;
}
//As you notice we don't use any key as a parameters in Google_Client() API function
$client = new Google_Client();
$payload = $client->verifyIdToken($idToken);
if ($payload) {
print_r($payload);
$userid = $payload['sub'];
// If request specified a G Suite domain:
//$domain = $payload['hd'];
} else {
var_dump($payload);
$this->lastError = "Invalid ID token";
return false;
}
}
I hope it helps.
I faced the same issue. After checking different PHP versions, I found that the google client library is working in PHP7.4 but not with PHP8.0.
Please try the below code after downgrading the version of PHP to 7.4
require_once 'vendor/autoload.php';
$id_token = $_POST['credential'];
$client = new Google_Client(['client_id' => $CLIENT_ID]); // Specify the CLIENT_ID of the app that accesses the backend
$payload = $client->verifyIdToken($id_token);
if ($payload) {
$userid = $payload['sub'];
// If request specified a G Suite domain:
//$domain = $payload['hd'];
} else {
// Invalid ID token
}
Or For development and debugging, you can call google oauth2 tokeninfo validation endpoint.
https://oauth2.googleapis.com/tokeninfo?id_token=$id_token

parse php sdk and error code 251 Twitter credential verification failed

I use parse-php-sdk.Now I need my customers login with twitter then add them as parse users.Because there is no function I can use as twitter logining in ParseUser.php, so I write one:
public static function logInWithTwitter($id, $access_token, $expiration_date = null)
{
if (!$id) {
throw new ParseException("Cannot log in twitter user without an id.");
}
if (!$access_token) {
throw new ParseException(
"Cannot log in twitter user without an access token."
);
}
if (!$expiration_date) {
$expiration_date = new \DateTime();
$expiration_date->setTimestamp(time() + 86400 * 60);
}
$data = json_encode(['authData' =>
["twitter" => [
"id" => $id, "access_token" => $access_token,
"expiration_date" => ParseClient::getProperDateFormat($expiration_date)
]]
]);
$result = ParseClient::_request("POST", "/1/users", "", $data);
$user = new ParseUser();
$user->_mergeAfterFetch($result);
$user->handleSaveResult(true);
ParseClient::getStorage()->set("user", $user);
return $user;
}
But when I finished login with twitter, error occurs: Code : 251 Message:Twitter credential verification failed.. I try to look for solutions, but all I found are things about apps platform problems, and they can fix if by setting consumerKey and consumerSecret in TWSignedRequest. But I don't know whether I need to set twitter consumerKey and consumerSecret, if I need to do this ,where should I set?

Using PHP to access Gmail using Service Account

I am trying to list emails from a standard Gmail account (not a Google Apps account) using OAuth2 and a Service Account (actually, I want to send emails – but that can wait).
I have created the Project, created the service account, downloaded the private key, and enabled the Gmail API (and the Calendar API). I can successfully access my calendar using code very similar to the code below. However, I am receiving the following error when attempting to list mail messages.
Error refreshing the OAuth2 token, message: error_description "Unauthorized client or scope in request."
(For info. If I comment out the $credential->sub = … line, I get the following error: “(500) Backend Error” and I can see that this is logged in the Usage tab of the Developers API page).
Can anyone confirm that what I am trying to do is possible? And if so, any ideas where to go next?
Here is my code:
class mail
{
private $service;
public function __construct($clientid, $keyfile, $account_name, $app_name)
{
$client = new Google_Client();
$client->setApplicationName($app_name);
$this->service = new Google_Service_Gmail($client);
// Load the key in PKCS 12 format
$key = file_get_contents($keyfile);
$client->setClientId($clientid);
$credentials = new Google_Auth_AssertionCredentials(
$account_name, array('https://mail.google.com/'),$key);
$credentials->sub = 'myemailaddress...#gmail.com';
$client->setAssertionCredentials($credentials);
}
public function list_mail()
{
try
{
$result = $this->service->users_messages->listUsersMessages("me");
}
catch (Exception $e)
{
throw new Exception("Gmail API error: ".$e->getMessage()."<br />");
}
return $result;
}
}
$mail_obj = new mail(CLIENT_ID, KEY_FILE, SERVICE_ACCOUNT_NAME, APP_NAME);
$result = $mail_obj->list_mail();

How to use Service Accounts? Can i use my existing account at google as a service account?

So, i have two accounts at google, one is for personal use and one for company use. At the company account i have bought drive quota and it is at 200gb (i think), so im using it as a file storage cloud-server. My idea is to implement some of the files to the company website using google drive php api. As long as i know i can Use Application-Owned Accounts which sounds great, BUT i have to create new account it seems in order to use it with a regular account and if i want to use it with a server-side i will be not be able to use the company files at the regular account. So, im stuck at this situation!? Please, give me some advice. This is all new to me, so i need your help.
EDIT:
What it says from the link i posted above is this:
You may create a regular Google account like any user would, by going through the Google account sign-up flow or by creating an account on your Google Apps domain. Make sure it is then never used by an actual person but only by your application.
OK, but my account it is not new and it HAVE been used before. That mean that i will not be able to use my company account and if that is true, how can i achieve my goal?
i finally did it after days of researching how i can do this, here is a very simple code for how to obtain the access token and after you have it how to take the refresh token which you will need in order to access the user when he is in offline. I still have to understand how can i know when i store those values in the databse, how can i know that this user with the google id is the same user from the database and put the refresh token in the php, so the user dont have to authenticate again and he can do this only once (service account). So this simple code is using SESSIONS in order to store the access token and also the refresh token. It's not using database for the storage, but if you want when i figure out how this is done i can post the code here as well. So, here is the code:
<?php
session_start();
// Set error reporting
error_reporting(E_ALL | E_STRICT);
// Display errors
ini_set("display_errors", 1);
// require pages, you have to change it if your pages are somewhere else!
require_once 'src/Google_Client.php';
require_once "src/contrib/Google_Oauth2Service.php";
require_once "src/contrib/Google_DriveService.php";
/**
* Retrieved stored credentials for the provided user ID.
*
* #param String $userId User's ID.
* #return String Json representation of the OAuth 2.0 credentials.
*/
function getStoredCredentials($userId) {
if (!empty($_SESSION['userid'])) {
return $_SESSION['userid'];
}
}
/**
* Store OAuth 2.0 credentials in the application's database.
*
* #param String $userId User's ID.
* #param String $credentials Json representation of the OAuth 2.0 credentials to store.
*/
function storeCredentials($userId, $credentials) {
$_SERVER['userid'] = $userId;
}
/**
* Build a Drive service object.
*
* #param String credentials Json representation of the OAuth 2.0 credentials.
* #return Google_DriveService service object.
*/
function buildService($credentials) {
$apiClient = new Google_Client();
$apiClient->setUseObjects(true);
$apiClient->setAccessToken($credentials);
return new Google_DriveService($apiClient);
}
/**
* Send a request to the UserInfo API to retrieve the user's information.
*
* #param String credentials OAuth 2.0 credentials to authorize the request.
* #return Userinfo User's information.
* #throws NoUserIdException An error occurred.
*/
function getUserInfo($credentials) {
$apiClient = new Google_Client();
$apiClient->setUseObjects(true);
$apiClient->setAccessToken($credentials);
$userInfoService = new Google_Oauth2Service($apiClient);
$userInfo = null;
try {
$userInfo = $userInfoService->userinfo->get();
} catch (Google_Exception $e) {
print 'An error occurred: ' . $e->getMessage();
}
if ($userInfo != null && $userInfo->getId() != null) {
return $userInfo;
} else {
throw new NoUserIdException();
}
}
function retrieveAllFiles($service) {
$result = array();
$pageToken = NULL;
do {
try {
$parameters = array();
if ($pageToken) {
$parameters['pageToken'] = $pageToken;
}
$files = $service->files->listFiles($parameters);
$result = array_merge($result, $files->getItems());
$pageToken = $files->getNextPageToken();
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
$pageToken = NULL;
}
} while ($pageToken);
return $result;
}
function printFile($service, $fileId) {
try {
$file = $service->files->get($fileId);
print "Title: " . $file->getTitle();
print "Description: " . $file->getDescription();
print "MIME type: " . $file->getMimeType();
} catch (apiException $e) {
print "An error occurred: " . $e->getMessage();
}
}
// fill your details from the google console:
$client = new Google_Client();
$client->setApplicationName('***************');
$client->setScopes(array(
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile'));
$client->setClientId('***************');
$client->setClientSecret('***************');
$client->setRedirectUri('***************/google-drive-api-php-client/serverside.php');
$client->setApprovalPrompt('force');
$client->setAccessType('offline');
$client->setDeveloperKey('***************');
// a simple code to check if the user have already login to the site and authenticate the site and if he does the site will not ask the user again for authentification and it will use the refresh token to "log" the user in
if (empty($_GET['code'])) {
// if the user visit the website for the first time he need to authentificate (redirecting the website to google)!
if (empty($_SESSION['access_token']) && !isset($_SESSION['refresh_token'])) {
header('Location: ' . $client->createAuthUrl());
// if the user have already visited the site, but the access token have expired use this code
} elseif (empty($_SESSION['access_token']) && isset($_SESSION['refresh_token'])) {
echo "refresh token1" . "<br>";
$google_token = json_decode($_SESSION['refresh_token'], true);
$client->refreshToken($google_token['refresh_token']);
$_SESSION['access_token']= $client->getAccessToken();
}
} elseif (!empty($_GET['code']) && empty($_SESSION['access_token'])) {
// if the user is visiting the website for the first time and dont have refresh token:
if (!isset($_SESSION['refresh_token'])) {
echo "access token" . "<br>";
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $_SESSION['access_token'];
// this will never execute, but i put it anyway :) if the user have already visited the site, but the access token have expired use this code (its the same as the above)
} elseif (isset($_SESSION['refresh_token'])) {
echo "refresh token2" . "<br>";
$google_token = json_decode($_SESSION['refresh_token'], true);
$client->refreshToken($google_token['refresh_token']);
$_SESSION['access_token']= $client->getAccessToken();
}
}
// if the access token have expired use the refresh token to gain access instead:
if ($client->isAccessTokenExpired()) {
$google_token = json_decode($_SESSION['refresh_token'], true);
$client->refreshToken($google_token['refresh_token']);
$_SESSION['access_token']= $client->getAccessToken();
}
// unset the sessions for testing:
// unset($_SESSION['access_token']);
// unset($_SESSION['refresh_token']);
// get some info from the user Google API like the file info
if (!empty($_SESSION['access_token'])) {
// create the service in this case Google Drive
$service = buildService($_SESSION['access_token']);
// mark the file ID
$fileid = "*******************";
// print the access token
echo "<pre>";
print_r(getUserInfo($_SESSION['access_token']));
echo "</pre>";
// print file metadata from google drive
// echo "<pre>";
// print_r(printFile($service, $fileid));
// echo "</pre>";
}
// printing the session for testing...
echo "<pre>";
print_r($_SESSION);
echo "</pre>";
// print the refresh token for testing
print_r($_SESSION['refresh_token']);
// print echo to see if the code is executing till the end or there is a fatal error someone in the code :)
echo "string";
?>

Categories