Server to server OAuth2 - go

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)
}

Related

Using Default Credentials to Authenticate for the Gmail API

Summary:
The example for using the Gmail API in Go includes code for reading credentials from a known JSON file inside the filesystem. I would like to take advantage of Application Default Credentials (ADC) since we're deploying to k8s with access to the Gmail API.
Code:
Please find an excerpt from the full demo below:
//error handling omited for brevity
b, _ := ioutil.ReadFile("credentials.json")
config, _ := google.ConfigFromJSON(b, gmail.GmailReadonlyScope) //is it possible to replace this?
client := getClient(config)
srv, _ := gmail.New(client)
How can I replace line 2 in the excerpt to get the same configuration but without using an explicit JSON credentials file?
You can print an access token using gcloud auth application-default print-access-token . Use it to make requests to the API, instead of reading from the file. – Aerials

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

Accessing Google Reseller API using Service Accounts

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)
}

Does the golang.org/x/oauth2/google library support service to service authentication?

I developped a Python backend service which connects to the Google Calendar API (v3), retrieves calendar entries and exposes them (they are retrieved by via an HTTP call). In order to do so, I use OAuth 2.0 for Server to Server Applications.
When developping my application a few years ago, the module provided by Google was not yet ported to Python 3 and I went the JWT way ("HTTP/REST" in the documentation linked above).
I now would like to learn Go and start by porting this application.
The Google Calendar API quickstart for Go provides a full example, but it assumes that the authentication will include a consent screen from the user (3-legged oAuth). This is similar to the Python example, but there is also a Python version using the service to service approach.
Is this service to service authentication funtionality available in the Go library?
If it is not I will manually generate a JWT (as I do it now with the Python version of my code) but since I am just starting with Go, I would prefer using libraries as much as possible while I learn on the fly.
I'm not setup to test this at the moment but I believe something like this should work:
package main
import (
"context"
"io/ioutil"
"log"
"golang.org/x/oauth2/google"
calendar "google.golang.org/api/calendar/v3"
)
func main() {
cred, err := ioutil.ReadFile("service_account.json")
if err != nil {
log.Fatalf("Unable to read JSON credentials config %v", err)
}
conf, err := google.JWTConfigFromJSON(cred, "https://www.googleapis.com/auth/calendar")
if err != nil {
log.Fatalf("Unable to obtain JWT conf %v", err)
}
client := conf.Client(context.Background())
srv, err := calendar.New(client)
if err != nil {
log.Fatalf("Unable to retrieve calendar Client %v", err)
}
...
}
The contents of service_account.json should be obtained from the service account configuration somehow, I'm not 100% sure about that part though.

Firebase Cloud Messaging authorization failure

I'm currently trying to implement a server in Go working with Firebase Cloud Messaging. I inserted my API key and sender ID (which I both got from the Firebase console at Project Settings -> Cloud Messaging) and did this:
err := gcm.Listen(senderID, apiKey, onMessageReceived, nil)
and all I get is this
error creating xmpp client>error connecting client>auth failure: not-authorized
I'm using the same library Google's using in it's examples. Is the library maybe not working with FCM yet or am I doing something wrong?
If it's the library, how would I implement this without it?
This library will works perfectly with firebase, if you do several things:
Change xmppHost from https://gcm-http.googleapis.com/gcm/send to https://fcm.googleapis.com/fcm/send
Add new const xmppDomain = "gcm.googleapis.com"
Change function xmppUser, it should be like
func xmppUser(senderId string) string {
return senderId + "#" + xmppDomain
}

Resources