How to populate `identifier` and `providers` in Firebase custom authentication? - php

I'm authenticating my users on my web service and then creating Firebase custom token via php-jwt:
// Requires: composer require firebase/php-jwt
use Firebase\JWT\JWT;
// Get your service account's email address and private key from the JSON key file
$service_account_email = ...;
$private_key = ...;
function create_custom_token($uid, $is_premium_account) {
global $service_account_email, $private_key;
$now_seconds = time();
$payload = array(
"iss" => $service_account_email,
"sub" => $service_account_email,
"aud" => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iat" => $now_seconds,
"exp" => $now_seconds+(60*60), // Maximum expiration time is one hour
"uid" => $uid,
"claims" => array(
"premium_account" => $is_premium_account
)
);
return JWT::encode($payload, $private_key, "RS256");
}
But the users that I authenticate this way, don't show the administrator-friendly "Identifier" and "Providers" fields in the "Authentication" panel in the Firebase Console:
The first two are users that I authenticated via this custom authentication process, and the last one is a user that I authenticated directly via Google.
How can I populate the "Identifier" and the "Providers" fields for users created via custom authentication?

The "Providers" column will only display an icon if the information attached to a user matches one or more of the the given providers in the "Sign-In Methods" section (https://console.firebase.google.com/project/_/authentication/providers).
Custom providers don't have a distinct icon, and Firebase wouldn't know what to display in the "Identifier" column (the UID is already in its own column at the end).
However, you do have some control for the display of the columns by creating them in advance (meaning: before signing them in for the first time), or by updating the user information after the user entry has been created.
I prepared an example showing which combination of fields leads to which display:
Please note:
The display name has no effect: if it is the only data provided, the user is considered anonymous.
Email + Password match the "Email/Password" Provider
Phone Numbers will alway match the "Phone" provider
The icons for a matched provider will be displayed in the column, even if a provider has been disabled.
Emails and Phone numbers have to be unique. If your application allows multiple users with the same email address/phone number, you will get into trouble, if you just want to see more information about the users of your Firebase project.
You can create and update users via the Firebase Auth REST API, but I would suggest to use one of the official Firebase Admin SDKs SDK to do it - in case you want to stick to PHP, I happen to know an unofficial one: kreait/firebase-php (Documentation) (Disclaimer: I'm the maintainer of the PHP SDK :) ).
On a non-technical note: I wouldn't bother too much with the user list in the Firebase Web Console: use the Firebase CLI tool or one of the official (or unofficial ;) ) Admin SDKs to create an overview that meets your needs.
You mentioned in the Bounty Annotation that you asked this in the Firebase Slack Community without an answer - you can find me and other PHP developers in the #php channel. I enabled notifications for the channel, so please feel free to join if you have further questions.
FYI, this is the code I wrote with the PHP SDK to create the data for the screenshot above:
<?php
declare(strict_types=1);
use Kreait\Firebase;
use Kreait\Firebase\Util\JSON;
require_once __DIR__.'/vendor/autoload.php';
$serviceAccount = Firebase\ServiceAccount::fromJsonFile(__DIR__.'/service_account.json');
$firebase = (new Firebase\Factory())
->withServiceAccount($serviceAccount)
->create();
$auth = $firebase->getAuth();
// Remove all users
foreach ($auth->listUsers() as $user) {
$auth->deleteUser($user->uid);
}
// Simulate custom auth
$ct = $auth->createCustomToken('a-custom-auth');
$r = $auth->getApiClient()->exchangeCustomTokenForIdAndRefreshToken($ct);
echo JSON::prettyPrint($auth->getUser('a-custom-auth'));
echo JSON::prettyPrint($auth->createUser([
'uid' => 'displayname-only',
'displayName' => 'Jérôme Gamez',
]));
echo JSON::prettyPrint($auth->createUser([
'uid' => 'email-only',
'email' => 'jerome#example.org',
]));
echo JSON::prettyPrint($auth->createUser([
'uid' => 'email-and-password',
'email' => 'jerome#example.com',
'password' => 'password'
]));
echo JSON::prettyPrint($auth->createUser([
'uid' => 'phone-only',
'phoneNumber' => '+49-123-1234567',
]));
echo JSON::prettyPrint($auth->createUser([
'uid' => 'email+name+phone',
'email' => 'jerome#example.net',
'displayName' => 'Jérôme Gamez',
'phoneNumber' => '+49-123-7654321',
]));
echo JSON::prettyPrint($auth->createUser([
'uid' => 'email+name+password+phone',
'email' => 'jerome#example.de',
'displayName' => 'Jérôme Gamez',
'password' => 'example123',
'phoneNumber' => '+49-321-7654321',
]));

Related

Discord API / Restcord : Create private channel

I'm trying to create private channels on Discord using Restcord, a PHP library that is very closely mapped to the Discord API.
Currently, I have managed to create voice channels, but all users on the server are able to see and join the channels. How can I make the channels so they are only available when invited ?
My current test code is :
$discord = new \RestCord\DiscordClient(['token' => config('services.discord.bot_token')]);
$channel = $discord->guild->createGuildChannel([
'guild.id' => config('services.discord.guild_id'),
'name' => 'lobby_' . uniqid(),
'type' => 2, // Voice
'permission_overwrites' => [
],
]);
$invitation = $discord->channel->createChannelInvite([
'channel.id' => $channel->id,
]);
return "https://discordapp.com/invite/{$invitation->code}";
Thanks in advance, any help is much appreciated, either with Restcord or directly using the API.
You need to use the permission_overwrites field. This fields accepts arrays of the overwrite object kind.
Example:
$discord = new \RestCord\DiscordClient(['token' => <token>]);
$channel = $discord->guild->createGuildChannel([
'guild.id' => config(<your guild ID>),
'name' => '<channel name>',
'type' => 2, // Voice
'permission_overwrites' => [
'id' => <role OR user id>,
'type' => 'role' (if id is role) OR 'user' (if id is single user),
'allow' => <permission ID for allowed permissions>,
'deny' => <permissions ID for denied permissions>
]
]);
Note that if you'd leave "allow" and "deny" as 0 (default value), the role/user with id 'id' would have the same permissions as otherwise defined server-wide. To get the permissions ID for a certain set of permissions, use a Discord Permission Calculator like this one (here the ID is called permission number).
Hope it helps!

Why is PayPal GetVerifiedStatus API not working for certain account

I am using this library to verify PayPal users through email ID. The API I am using is GetVerifiedStatus. The developer of the library provides an email address in the code and it works fine. The code returns the status as "VERIFIED" for his email ID.
However, whenever I try to use my email ID, it shows "Cannot determine PayPal Account status" with ErrorID --580023. I tried another person's email ID and still it does not work. I am sure there is no typo in the email, firstname, lastname fields.
Seems, these links addresses the same issue.
PayPal GetVerifiedStatus not working with other accounts and Does PayPal's GetVerifiedStatus `belong' to the api caller's country?
This is the code that comes with the library. (added my paypal email ID and name)
require_once('../includes/config.php');
require_once('../autoload.php');
// Create PayPal object.
$PayPalConfig = array(
'Sandbox' => $sandbox,
'DeveloperAccountEmail' => $developer_account_email,
'ApplicationID' => $application_id,
'DeviceID' => $device_id,
'IPAddress' => $_SERVER['REMOTE_ADDR'],
'APIUsername' => $api_username,
'APIPassword' => $api_password,
'APISignature' => $api_signature,
'APISubject' => $api_subject,
'PrintHeaders' => $print_headers,
'LogResults' => $log_results,
'LogPath' => $log_path,
);
$PayPal = new angelleye\PayPal\Adaptive($PayPalConfig);
// Prepare request arrays
$GetVerifiedStatusFields = array(
'EmailAddress' => 'xxxxxx', // Required. The email address of the PayPal account holder.
'FirstName' => 'xxxxxx', // The first name of the PayPal account holder. Required if MatchCriteria is NAME
'LastName' => 'xxxxxx', // The last name of the PayPal account holder. Required if MatchCriteria is NAME
'MatchCriteria' => 'NAME' // Required. The criteria must be matched in addition to EmailAddress. Currently, only NAME is supported. Values: NAME, NONE To use NONE you have to be granted advanced permissions
);
$PayPalRequestData = array('GetVerifiedStatusFields' => $GetVerifiedStatusFields);
// Pass data into class for processing with PayPal and load the response array into $PayPalResult
$PayPalResult = $PayPal->GetVerifiedStatus($PayPalRequestData);
// Write the contents of the response array to the screen for demo purposes.
echo '<pre />';
print_r($PayPalResult);
I am not getting any idea why it is happening. Can anyone help please?
You are testing in sandbox mode. You must create sandbox accounts, either via https://developer.paypal.com/docs/classic/lifecycle/sb_about-accounts/#create-and-manage-sandbox-accounts or using the standard user flow on https://www.sandbox.paypal.com/us/home .

Laravel Cashier - Create New Subscription With Existing Customer Object

I'm using Laravel Cashier along with Stripe to manage subscriptions. The user will supply their credit card information when signing up, but they won't be subscribed in a specific plan at this point. So I can successfully use Stripe Checkout to create a Stripe customer object and save the Stripe customer ID in my database. But when it comes time for the user to enroll in a plan, I can't see a way to use the Stripe customer ID to enroll them in the plan they want.
Of course, I could ask for their credit card information again and get a Stripe token to use with Laravel Cashier, but I'd like to avoid this since the app already created a Stripe customer object when they signed up and I'd like to simply use the existing customer object to charge their credit card rather than asking for their card number again.
To try illustrate what I'm trying to do, here is some sample code from the Laravel docs:
$user->newSubscription('main', 'monthly')->create($creditCardToken);
But what I'd like to be able to do is something like this (note the change to the create method:
$user->newSubscription('main', 'monthly')->create($user->stripe_id);
Any advice?
If there is a stripe ID for the user, you don't have to supply the token
$user->newSubscription('main', 'monthly')->create();
Have a look at the SubscriptionBuilder class.
You can try this.
$user->setStripeKey(env("STRIPE_KEY"));
# card details
$card = [
'card_number' => 'xxxxxxxx',
'card_cvc' => 'xxx',
'exp_month' => 'xx',
'exp_year' => 'xxxx',
'name' => 'xxx',
];
# generate token
$token = $this->generateAccessToken($card);
private function generateAccessToken($card)
{
$client = new \GuzzleHttp\Client();
$url = 'https://api.stripe.com/v1/tokens';
$pubKey = env("STRIPE_SECRET");
$postBody = [
'key' => $pubKey,
'payment_user_agent' => 'stripe.js/Fbebcbe6',
'card' => [
'number' => $card['card_number'],
'cvc' => $card['card_cvc'],
'exp_month' => $card['exp_month'],
'exp_year' => $card['exp_year'],
'name' => $card['name']
]
];
$response = $client->post($url, [
'form_params' => $postBody
]);
$response_obj = json_decode($response->getbody()->getContents());
return $response_obj->id;
}
# main or primary
$subscription_obj = $user->newSubscription('subscription_name', 'stripe_plan_id')->create($token);

How to override native socialite provider?

As I am interested in getting the gender info form social network when user register, I've noticed that gender mapping is missing in the native Laravel socialite provider, for ex:
GoogleProvider.php
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['id'], 'nickname' => array_get($user, 'nickname'), 'name' => $user['displayName'],
'email' => $user['emails'][0]['value'], 'avatar' => array_get($user, 'image')['url'],
]);
}
So, I can't just edit the above method and map the gender field, because GoogleProvider.php is located in the vendor folder.
The question is, how would I override mapUserToObject in different social providers afforded by Laravel?
Or, what is the recommended approach in such case?
Providing Google passes a gender field you should be able to access it like...
$user = Socialite::driver('google')->user();
echo $user['gender'];

Fetch emails from Gmail via OAuth2

I'm trying to fetch emails from gmail using PHP and CodeIgniter, and an OAuth2 library. I have already got OAuth2 set up to get the users access token, etc. I can get the users info by doing
public function get_user_info(OAuth2_Token_Access $token)
{
$url = 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json&'.http_build_query(array(
'access_token' => $token->access_token,
));
$user = json_decode(file_get_contents($url), true);
return array(
'uid' => $user['id'],
'nickname' => url_title($user['name'], '_', true),
'name' => $user['name'],
'first_name' => $user['given_name'],
'last_name' => $user['family_name'],
'email' => $user['email'],
'location' => null,
'image' => (isset($user['picture'])) ? $user['picture'] : null,
'description' => null,
'urls' => array(),
);
}
What I want to do now is fetch some emails. I've googled for some code on how to get emails but the only thing I can see is https://mail.google.com/mail/feed/atom/. I found this on the Google OAuth2 Playground but I can't figure out how to use it apart from navigating to it directly.
Can anyone give me some suggestions? Ideally I want to fetch emails that are not just new (this seems to be what the link above does).
Using a REST-based interface you can currently only get the unread messages feed. If you want to access all emails of a user, you have to use IMAP. Google developed a method to do IMAP/SMTP authentication using OAuth2 that you can use.

Categories