How to implement server to server communication using google api in ruby? - ruby-on-rails

I have a rails application in which I wanted to send email using Gmail API(using google-api-client for ruby). I have created one application in google developers and I have generated a service account key. I am using google specification for storing that credential JSON file(storing in /home/user/.config/gcloud/applicaton_default_credentials.json).
For starters, I wanted to fetch user labels below is my code
require 'google/apis/gmail_v1'
gmail_v1 = Google::Apis::GmailV1
service = gmail_v1::GmailService.new
service.authorization = Google::Auth.get_application_default([gmail_v1::AUTH_SCOPE, gmail_v1::AUTH_GMAIL_COMPOSE, gmail_v1::AUTH_GMAIL_MODIFY, gmail_v1::AUTH_GMAIL_READONLY])
user_id = 'me'
result = service.list_user_labels(user_id)
But this is giving me Google::Apis::ClientError: failedPrecondition: Bad Request error.
My end goal is to send email using an email for which I have generated the credentials JSON file, as I don't want to put my username and password in the smtp config of the ActionMailer.

Remember Service accounts isn't YOU. A service account is a dummy user who has a Google drive account and a google calendar account and probably a few more. However it does not have a gmail account.
user_id = 'me'
Will not work with a Service account.
Note: You cant use service accounts with normal gmail user accounts.
If this is a domains account user then you can take the service account email address and add it as a user on your domains admin account. Granting it permission to access the users gmail accounts. Then you will need to do something like
user_id = 'user#domain'

After many hours of debugging, I finally found a working solution. DalmTo is right about the concept that a service account can't access Gmail as Gmail requires a User account, but we can leverage the power of impersonation in this case. The steps to do impersonation are:
Go to your service account.
Give permission to the user you want to impersonate.
We need to change our code for this setup like below
service = gmail_v1::GmailService.new
service.authorization = Google::Auth.get_application_default([gmail_v1::AUTH_SCOPE, gmail_v1::AUTH_GMAIL_COMPOSE, gmail_v1::AUTH_GMAIL_MODIFY, gmail_v1::AUTH_GMAIL_READONLY])
auth_client = service.authorization.dup
auth_client.sub = 'user#domain'
user_id = 'me'
result = auth_client.list_user_labels(user_id)
Note: For this, to work, we need to go to our G-suit account and give proper permission to our service account.

Related

Create an alias for a Google Service Account Email?

I've shared a Google Sheet with my Google Service account email, which looks something like:
myappname-service#myappname-266229.iam.gserviceaccount.com
This permits my application to access that Google Sheet.
I'd like to be able to share the Google Sheet with a custom email address (e.g. google#myappname.com) aliased to that ugly autogenerated service account email (myappname-service#myappname-266229.iam.gserviceaccount.com).
How could I go about doing this?
edit:
Example of code used for interacting with Google APIs
from google_auth_httplib2 import AuthorizedHttp
from google.oauth2 import service_account
import pygsheets
def _get_gc():
scope = ['https://www.googleapis.com/auth/spreadsheets']
credentials = service_account.Credentials.from_service_account_file(
settings.GOOGLE_SERVICE_AUTH_FILE,
scopes=scope,
)
http = AuthorizedHttp(credentials, http=HTTP)
logger.info('Created GC creds, returning...')
return pygsheets.authorize(custom_credentials=credentials, http=http)
def do_something(url):
gc = _get_gc()
spreadsheet = gc.open_by_url(url)
Issue – Service accounts cannot have aliases:
Unlike regular accounts, service accounts cannot have aliases. Their email address is defined by:
The name of the corresponding project.
The name of the service account.
You cannot give it additional aliases.
Workaround – Impersonate a regular account:
If you want to avoid sharing the Google Sheets with the autogenerated service account email address, but you want to keep using the service account to interact with the API, your best option would be to share the Sheets with a regular account that has an acceptable email address, and use the service account to impersonate this regular account, when interacting with the API.
1. Delegating domain-wide authority:
One of the most useful things about a service account is that you can grant it the ability to impersonate any user in your domain and access data on behalf of it. This is called domain-wide delegation, and it can be activated for a service account by following these steps:
Important: You need to be an administrator of the G Suite domain in order to delegate domain-wide authority.
2. Impersonating:
At this point, your service account can impersonate any user in the account. To actually impersonate an account, you would just need to specify which account you want to impersonate when building the credentials.
In your specific case, you would need to provide the parameter subject when calling from_service_account_file, as you can see on the domain-wide delegation section of this page:
credentials = service_account.Credentials.from_service_account_file(
settings.GOOGLE_SERVICE_AUTH_FILE,
scopes=scope,
subject="your-account#your-domain.com"
)
Reference:
Delegating domain-wide authority to the service account

Firebase authentication Login multiple provider with same email

I want users can login using many different provider Like Google+, Facebook and Email/Password but they will get same result if they use only same email address. Prevent multiple accounts and data managed by same UID
That is actually a feature of Firebase Authentication. If you configure it to allow only one account per email address (which is the default I think), it will only allow a single account per email address. When a user then logs in with a different provider, you can link the credentials for both under a single account/UID.

How can I sign in with X on my web site, when there are multiple accounts across providers for the same user?

If I have a website where it is possible to sign in with multiple different providers (Say Facebook, GitHub, Google), what do I use as the local-to-my-site unique identifier for users? For example, if these two steps happened:
I sign in with GitHub (For the first time) and my username is mogronalol and email is mogronalol#mogronalol.com.
A local-to-my-site acccount with an email address of mogronalol#mogronalol.com and username of mogronalol is created.
If I use the email address as the local unique identifier, what happens if my email address changes in GitHub to other#other.com? The same question applies to changing username also.
If I got some sort of unique ID from GitHub, and used that as the identifier, then what do I do if my email address or username changes in GitHub. Do I just updated my local-to-my-site-copy to be the same as the one on GitHub each time I log in?
Of course, this problem is worsened if I want to log in with my Facebook account as well as my GitHub account. What happens if my email address and / or username are different across both of these? How would my local site know to link the accounts together? And if things like email address are different once the accounts are linked, which one do I use?
First, maybe you could try on some tutorial to feel how OAuth work.
After your OAuth authentication succeed, your website will receive a series of information provided by OAuth provider.example
Within this information, there are two special columns called uid and provider used to recognize user from OAuth provider.
You will use these two columns to tell which provider the authentication come from (i.e. facebook or github), also you need to save these fields to your account columns.
Then use rest of information to create the account in your website.
For example, use OAuth provider's email as email(github's email as email).
After you create account, every time you login server from OAuth provider.
You only need to check provider and uid in account column.
Let's back to your question.
If I use the email address as the local unique identifier, what happens if my email address changes in GitHub to other#other.com? The same question applies to changing username also.
If I got some sort of unique ID from GitHub, and used that as the identifier, then what do I do if my email address or username changes in GitHub. Do I just updated my local-to-my-site-copy to be the same as the one on GitHub each time I log in?
Github's email or user change won't affect your login (We only check provider and uid fields to login user).
I suggest not to sync with your OAuth provider's information(We only use OAuth provider's information when create account).
If you are going to support multiple OAuth provider, I suggest you read through this article.
You have to separate uid and provider to other table called identity.
Each account has many identities.
I also did it before.
If you don't mind, here is the sample code snippet to deal with multiple OAuth providers.
You have to think about the logic in your login flow.
For example, user has signed in and login OAuth => Link account with OAuth provider
User not signed in and login OAuth => If find user with OAuth, login, else create account using OAuth provider's information
Of course, this problem is worsened if I want to log in with my Facebook account as well as my GitHub account. What happens if my email address and / or username are different across both of these? How would my local site know to link the accounts together? And if things like email address are different once the accounts are linked, which one do I use?
We only link account, when user is already signed in.
When you link account, you could determine to use OAuth provider's information to update account(just like you used to register account).
I suggest to use the original email not to update it from OAuth provider's information.

How do I add a customer to Quickbooks automatically when they sign into my site?

I have already implemented minimul's guide to getting started with Quickbooks Online. My administrator can authenticate with Quickbooks and perform any number of tasks.
However, I want to generate an automated task that creates a customer entry in my company's Quickbooks' application as soon as they sign up. When they choose an item to purchase I want an invoice created in Quickbooks. And so on.
Obviously I don't want my users to have to authenticate with Quickbooks in order to do this. The users of the site should have no knowledge of Quickbooks; only the proprietor should access it directly. Is there a way to do this?
I am assuming that when you say "My administrator can authenticate with QuickBooks [Online] (QBO) and perform any number of tasks" that this connection is to your company's QBO account.
Therefore, when there is a new sign up you create the customer on the QBO side by using your company's OAuth tokens, which should be persisted in some fashion (DB or .yaml file). In the example below the OAuth information is persisted to the Account model as the following attributes: qb_token, qb_secret, and qb_realm_id. The $qb_oauth_consumer global comes from config/initializer/quickeebooks.rb.
account = Account.find(1) # your company's account
oauth_client = OAuth::AccessToken.new($qb_oauth_consumer, account.qb_token, account.qb_secret)
service = Quickeebooks::Online::Service::Customer.new
service.access_token = oauth_client
service.realm_id = account.qb_realm_id
# Map the new user to QuickBooks Customer
qb_customer = Quickeebooks::Online::Model::Customer.new
qb_customer.name = params[:new_signup_name]
qb_customer.email_address = params[:new_signup_email]
# etc.
service.create(qb_customer)
This goes for invoices, purchases, and whatever else. You use your company's OAuth connection to QBO.

Is there a way to get a user's email ID after verifying his/her Twitter identity using OAuth?

I am new to OAuth and have been playing around with the Twitter API. I am able to fetch the credentials of a user after authentication by making a request to http://api.twitter.com/1/account/verify_credentials.xml. The response contains the user id, screen name etc. but not the email ID.
Is it possible at all to retrieve the email ID of the user?
Update
I believe Facebook provides this information if you specifically request for extended permissions. Is there something similar for Twitter?
The user's email address can not be retrieved via the API. This is a deliberate design decision by the API team.
UPDATE 2015.08.18:
It is possible to request an email address from users, but it requires your app to be whitelisted. See https://dev.twitter.com/rest/reference/get/account/verify_credentials for details of the API call and this form to request whitelisting of your app.
For OutsourceFactor, which is written in Python / Django, I get the username via oAuth1, then construct an email as "username#twitter.com" which is guaranteed to be unique throughout twitter. Then I hash it to get a nice UUID to be used and associated with my local user account. Same thing for Yahoo. Google and Facebook use oAuth2 and they give me the email address on request which is nice.
To ensure multiple social associations with a single account, I allow social account associations ONLY after the user has locally created an account and is logged in.
So, you have to create an account first (local account), then you can use any of the social oAuth providers to ease your future logins. This is the best bang for the buck for my site.
Anyways, you get some unique form of ID from twitter. So just use it. You can ask for an email address later or before the association.
Email address is obfuscated by Twitter in their OAuth responses. Which always have been a great issue for people wanting to include a "Register with Twitter" function.
More recently (early 2015), Twitter have added email address support through a second service call, but under certain, abused, conditions.
https://dev.twitter.com/rest/reference/get/account/verify_credentials
So now it is possible, but my opinion is to continue to implement an OAuth every-provider-but-twitter single sign on. They must be boycotted until they act normally, i mean like every single other OAuth provider.
In Android using Fabric, I request the user's email address like this:
TwitterAuthClient authClient = new TwitterAuthClient();
authClient.requestEmail(session, new Callback<String>() {
#Override
public void success(Result<String> result) {
// Do something with the result, which provides the email address
}
#Override
public void failure(TwitterException exception) {
// Do something on failure
}
});
See http://docs.fabric.io/android/twitter/request-user-email-address.html
In my case every time I get the response I got a unique authentication id for every user and its same for that user every time. So I used that id to create a email like unique_id#twitter.com and check if that's already on my site ( for first time it is not ) and then register the user. Then if he logins second time I just again create the email and check if its already on there. By this I don't have to make him create a local account first and can identify him to login.
Here is the example how to get twitter user email in Laravel, and on coditty.com you can find the full example using Angular+Laravel
// get token secret from db
$token = TwitterTokens::where('oauth_token', $request->input('oauth_token'))->first();
// open twitter connection
$connection = new \Abraham\TwitterOAuth\TwitterOAuth(
$this->twitter_consumer_key,
$this->twitter_secret,
$request->input('oauth_token'),
$token->oauth_token_secret// twitter secret from DB
);
// get acces token
$access_token = $connection->oauth("oauth/access_token", ["oauth_verifier" => $request->input('oauth_verifier')]);
// new TwitterOAuth instance to get email
$twitterOAuth = new \Abraham\TwitterOAuth\TwitterOAuth( $this->twitter_consumer_key, $this->twitter_secret, $access_token['oauth_token'], $access_token['oauth_token_secret'] );
// Let's get the user's info with email
$twitterUser = $twitterOAuth->get('account/verify_credentials', ['include_entities' => 'false','include_email'=>'true','skip_status'=>'true',]);
// output user object from twitter in your Log file
Log::info(['user'=>$twitterUser]);
Who said it's not possible ???
I have gotten in my iOS App after whitelisting the App.
Check my answer here.
Add this code!
$params = array('include_email' => 'true', 'include_entities' => 'false', 'skip_status' => 'true');
`$data = $connection->get('account/verify_credentials', $params); // get the data`
// getting twitter user profile details
$twt_id = $data->id; //twitter user id
$twt_email = $data->email; //twitter user email
Checkout full procedure here.
Who says you cant get users email, the “Request email addresses from users” checkbox is available under the app permissions on apps.twitter.com. Privacy Policy URL and Terms of Service URL fields must be completed in the app settings in order for email address access to function. If enabled, users will be informed via the oauth/authorize dialog that your app can access their email address.

Resources