Accessing Google Reseller API using Service Accounts - go

We are having issues accessing the reseller api using service accounts.
The example with client secrets work well, but we would need to deploy this in k8s (Kubernetes Engine) without the need to refresh the oauth session on a recurring basis (especially doing this once, as it is kinda hard in a docker container).
While there is a lot of documentation on how to do this with python we could not find any way of getting access using a service account.
We tried two accounts, the default compute engine one and one created directly for our use case.
Both got the reseller scope in G Suite.
https://www.googleapis.com/auth/apps.order,
https://www.googleapis.com/auth/admin.directory.user,
https://www.googleapis.com/auth/siteverification,
We keep getting "googleapi: Error 403: Authenticated user is not authorized to perform this action., insufficientPermissions" errors though when using
client, err = google.DefaultClient(ctx, reseller.AppsOrderScope)
if err != nil {
log.Fatal("creating oauth client failed", zap.Error(err))
}
subs, err := client.Subscriptions.List().Do()
if err != nil {
log.Fatal("listing subscriptions failed", zap.Error(err))
}
I read on a post on StackOverflow, that the Reseller API requires user impersonation but searching on this throughout Google and the oauth2 and client lib repos did not result in a way to do this.
Python does this like described in the End-to-End Tutorial
credentials = ServiceAccountCredentials.from_json_keyfile_name(
JSON_PRIVATE_KEY_FILE,
OAUTH2_SCOPES).create_delegated(RESELLER_ADMIN_USER)
but for Go i could not find any documented way of doing this.

So few points here:
Reseller API only requires impersonation / domain-wide delegation when using a service account. In other words, the service account itself has no rights to call the API directly but it does have the ability to impersonate a reseller user (e.g. admin#reseller.example.com or such) who has rights to call the Reseller API.
You may be able to use regular 3-legged OAuth instead of a service account. You just need to make sure you request offline access so that you get a refresh token that is long lived.
Impersonation / domain-wide delegation is not compatible with the default service accounts built-in to AppEngine and ComputeEngine. You must use a service account you created in your API project.
See if the samples Google provides get you where you need to be.

The problem was resolved by using a different config and then setting the jwt.Subject which apparently does impersonation:
const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
if filename := os.Getenv(envVar); filename != "" {
serviceAccount, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal("creating oauth client failed", zap.Error(err))
}
config, err := google.JWTConfigFromJSON(serviceAccount,
reseller.AppsOrderScope,
)
config.Subject = *impersonationUser // like user#google.com
client = config.Client(ctx)
}

Related

Golang - spotify : This request requires user authentication

I'm writing a bot that takes the name of the music and the artist and adds it to the selected playlist with the zmb3 Spotify library. Everything works except the section add music to the playlist ;
the bot gives me this error: this request requires user authentication.
but Spotify token and Spotify secret are valid.
I searched, but I didn't get anything.
Is there anything more needed than Spotify Token and Spotify Secret?
What should I do?
Code :
// create spotify client connection and context to connect spotify
client, ctx := api.SpotifyConnection(SPOTIFYTOKEN, SPOTIFYSECRET)
// add playlist
playlist, err := client.GetPlaylist(ctx, PLAYLISTID)
if err != nil {
log.Fatal(err.Error())
}
/* SOME CODES */
if results.Tracks != nil {
items := results.Tracks.Tracks
musicID := items[0].ID.String()
cmd.AddMusic(client, ctx, playlist.ID.String(), musicID)
ERROR:
2021/12/26 11:06:25 This request requires user authentication.
Look at it this way - can you add a track to your playlist while not logged into your account? The same goes for your code. It's not an issue of authenticating to the API with TOKEN and SECRET_KEY, but you need to introduce user authentication, so that when a user opens your app, they log in and can add tracks to their own playlists.
There is an Authentication section in the zmb3 README that should help:
You can authenticate using a client credentials flow, but this does not provide any authorization to access a user's private data. For most use cases, you'll want to use the authorization code flow. This package includes an Authenticator type to handle the details for you.

Gmail API - Oauth2/google: no credentials found (Golang)

​Hello All,
Wanted to do server-side integration for Gmail API. The basic need is using enabling Gmail API I want to read my Gmail inbox for some analytics purpose.
Golang Package - "google.golang.org/api/gmail/v1"
As per documentation, I have followed the below steps.
New Signed up with Gmail
Added billing details to use GCP services
Created test project
Enabled Gmail API
Created Service Account and key inside it. Got credentials.json file
On the backend side, I am using the Golang package to extract the
Gmail inbox.
After successful integration, I am trying to run my code but getting
the below error
{"level":"error","msg":"Error while creating gmail service : oauth2/google: no credentials found","time":"2021-07-25T15:11:23+05:30"}
Can anyone help me to figure out what is missing?
I currently use OAuth with YouTube and the device flow [1], so maybe this can be helpful. First you need to make a one time request like this:
data := url.Values{
"client_id": {"something.apps.googleusercontent.com"},
"scope": {"https://www.googleapis.com/auth/youtube"},
}
res, err := http.PostForm("https://oauth2.googleapis.com/device/code", data)
You'll get a response like this:
type OAuth struct {
Device_Code string
User_Code string
Verification_URL string
}
which you can do a one time prompt to user like this:
1. Go to
https://www.google.com/device
2. Enter this code
HNDN-ZWBL
3. Sign in to your Google Account
Then, after user log in, you can do an exchange request like this:
data := url.Values{
"client_id": {"something.apps.googleusercontent.com"},
"client_secret": {"super secret"},
"device_code": {"device code from above"},
"grant_type": {"urn:ietf:params:oauth:grant-type:device_code"},
}
res, err := http.PostForm("https://oauth2.googleapis.com/token", data)
This will give you an access_token and refresh_token, that you can save locally for reuse. In addition to "device flow", you also have "native app" flow [2], but I found the former to be a simpler process.
https://developers.google.com/identity/sign-in/devices
https://developers.google.com/identity/protocols/oauth2/native-app

Golang and gcloud API: how to get an auth token

Because Google AutoML does not have a golang client, I have to use the AutoML http client. To do so, requires an auth token from google which comes from running the following cli command:
gcloud auth application-default print-access-token
I am currently authing my Golang server with a credentials json file that has access to AutoML as well (example usage)
storageClient, err := storage.NewClient(ctx, option.WithCredentialsFile(gcloudCredsJSONPath))
My question is: how would I get an auth token from the Golang Google client if I have a JSON credentials file? Is this even possible?
Thank you for any help!
You can only use API tokens with certain Google Cloud APIs. Using tokens is discourage by Google Cloud as you can read in this article:
https://cloud.google.com/docs/authentication/
If your production environment is also Google Cloud, you might not need to use any JSON file at all. Google Cloud has the concept of "DefaultCredentials" that it injects in your services via the environment. You might be able to simplify your code to:
storageClient, err := storage.NewClient(ctx)
It's also recommended to use a "ServiceAccount" so the credentials that your application use can be scopes to it. You can read more here:
https://cloud.google.com/docs/authentication/getting-started

Google Service Account permission denied gmail pub/sub Golang

Trying to create an application that updates links in my domains emails. I have got the service account element working, and can retrieve all the email accounts in my domain on Google Apps.
So with
config, err := google.JWTConfigFromJSON(data,
"https://www.googleapis.com/auth/admin.directory.user",
"https://www.googleapis.com/auth/gmail.readonly")
I create a new admin object, and srv.Users.List() giving me the users.
I now want to start a watch on each account so that Gmail can push emails to my app.
To do this, I need an access token and the impersonate the email address of the account I want to start the watch on.
ctx := context.Background()
tokenSource := config.TokenSource(ctx)
token, err := tokenSource.Token()
where config comes from JWTConfigFromJSON earlier.
It attemps to make the watch request, but I get the error googleapi: Error 403: Insufficient Permission, insufficientPermissions
First issue: I'm not sure I can get the token like this, do I have to have a seperate oAuth config and then use that to get the token? (That would surely require user interaction).
Second issue: I suspect I have not set the correct permissions, in either the code, or in the Service Account, but I'm not sure what they should be
Last issue: in the JWTConfigFromJSON call, When I remove the gmail scope, it it successfully gets a token. When I put it back I get "Unauthorized client or scope in request."
Any help greatly appreciated!
Thanks

Server to server OAuth2

I'm trying to implement a Golang application for accessing Google Analytics data. but all the examples uses tokens that dies in one hour.
In the api access i found a "Certificate access" that are designed to be used to access from servers, but i failed to find examples of its implementation in Golang. there is some reading or may you can enlighten my path for this?
I'm using this library.
code.google.com/p/google-api-go-client/
reading some post here I found this Service Applications and Google Analytics API V3: Server-to-server OAuth2 authentication?
but it seems that it will not work directly. is really no way of doing this w/o hacking it around?
have you checked out the OAuth2 package? I've used it for user-authorised calls, and hacked it around a bit to allow it to handle multiple authorisation sources.I haven't tested it with pure server-to-server comms, but you should be able to hack the transport code to get it to do what it needs...
this might be a little late but I havent found a good example to get people started.
Before you start make sure you
install golang 1.5
install google cloud SDK (cloud.google.com/sdk - this will allow for local development)
Create a service account in your google appengine / cloud console and download the json (API's and auth > Credentials )
Once above is setup:
set a path for your security credentials that you downloaded earlier
export GOOGLE_APPLICATION_CREDENTIALS=~/DIRECTORY/CREDENTIALS.json
now you can authenticate with go.
package main
import (
"fmt"
"golang.org/x/net/context"
"golang.org/x/oauth2/google"
analytics "google.golang.org/api/analytics/v3"
)
var (
scope = analytics.AnalyticsReadonlyScope
)
func main() {
// Authentication is provided by the gcloud tool when running locally, and
// by the associated service account when running on Compute Engine.
client, err := google.DefaultClient(context.Background(), scope)
if err != nil {
fmt.Printf("Unable to get default client: %v", err)
}
service, err := analytics.New(client)
if err != nil {
fmt.Printf("Unable to create storage service: %v", err)
}
fmt.Println(service)
}

Resources