Google Calendar API invalid_grant getting token (Golang) - go

I'm trying to retrieve an access token, in order to authenticate users using Oauth2. I'm using mostly code found on google's HOW-TO page for using the Calendar API with golang. The problem is that whenever I try to obtain a token, google sends back this:
Response: {
"error" : "invalid_grant"
}
With the error oauth2: cannot fetch token: 400 Bad Request
As I said, I'm using some code got from google's howto, just slightly modified to fit my needs.
//Somewhere...
authURL = config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
//Somewhere else...
func getClient(ctx context.Context, config *oauth2.Config, code string) *http.Client {
cacheFile := tokenCacheFile()
tok, err := tokenFromFile(cacheFile)
if err != nil {
log.Printf("Google auth code not cached. Obtaining from the web...")
tok, err = getTokenFromWeb(code) //This returns an error
if err == nil {
log.Printf("Got token!")
saveToken("calendar-go-quickstart.json", tok)
} else { //Prevent saving token when error
log.Printf("Couldn't get OAUTH2 token! %s", err)
}
}
return config.Client(ctx, tok)
}
The error occurs at "getTokenFromWeb(code)" (if I understood correctly, code must be some random string, no matter its value, it just needs to be the same during the whole process).
This is the problematic code:
func getTokenFromWeb(code string) (*oauth2.Token, error) {
tok, err := config.Exchange(context.Background(), code)
return tok, err
}
After executing, what I see is that error. I even get the exact same error when trying to copy-paste google's own example code!
Any idea? I really can't find a solution online.
Extra details: using IRIS web framework; using the latest version of google calendar api; using the latest version of Golang; I've created a client ID for OAuth2 on Google Cloud Console; The website has got a trusted SSL cert; it listens on port 80 (HTTP) and 4433 (HTTPS);

Here is Google's example:
// getTokenFromWeb uses Config to request a Token.
// It returns the retrieved Token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser then type the "+
"authorization code: \n%v\n", authURL)
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatalf("Unable to read authorization code %v", err)
}
...
}
code is an authorization code given to the user after visiting the displayed link. fmt.Scan() is going to scan the input from the user.
If you're going to be acting on a different user's behalf, you will have to do something similar to this example.
If you're only acting as yourself, you should be able to authenticate as yourself without the code.
Either way, code cannot be a random string.

Related

OAuth Authorization in Golang CLI Applications

I've been using the code from the OAuth2 package documentation to add authorization to my CLI application, but I've run into a bit of a snag.
package main
import (
"context"
"fmt"
"log"
"golang.org/x/oauth2"
)
func main() {
ctx := context.Background()
conf := &oauth2.Config{
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Scopes: []string{"SCOPE1", "SCOPE2"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://provider.com/o/oauth2/auth",
TokenURL: "https://provider.com/o/oauth2/token",
},
}
// Redirect user to consent page to ask for permission
// for the scopes specified above.
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
fmt.Printf("Visit the URL for the auth dialog: %v", url)
// Use the authorization code that is pushed to the redirect
// URL. Exchange will do the handshake to retrieve the
// initial access token. The HTTP Client returned by
// conf.Client will refresh the token as necessary.
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatal(err)
}
tok, err := conf.Exchange(ctx, code)
if err != nil {
log.Fatal(err)
}
client := conf.Client(ctx, tok)
client.Get("...")
}
I'm using StackOverflow as the authorization server and api.stackexchange.com doc says the following. But I'm not sure what it means.
Desktop applications cannot participate directly in OAuth 2.0 flows, however the embeddable browser controls available in most frameworks make it possible to work around this limitation.
The issue is that when I use the method described in the documentation, I have to manually click the URL generated in the CLI and manually copy the access token to continue.
Do you happen to know of a way to automate these two steps in Golang or a different approach I could take for authorization in CLI applications?
What I'm hoping for is something like what you see in the heroku-cli but with OAuth 2.0 - it would be awesome if my app could handle authorization in a similar way. Thanks for your help!
I'd really appreciate any help or advice you can offer. Thanks!

Golang google client api, how to get the user information with `Userinfo` struct

I am currently building a backend Rest API with Golang to handle HTTP requests from a mobile application. One of the features that I am now implementing is a signup/login by using an external provider, e.g., Google, Apple, etc.
For Google, I've read this article on how to authenticate with a backend server. The main idea is to send a token id to the backend via a POST endpoint and validate the content of the Token. Once the Token is validated, I can retrieve the user information from the backend and create an account (if it does not exist).
So far, with the oath2 Golang package, I can validate the Token like so:
func verifyIdToken(idToken string) error {
ctx := context.Background()
oauth2Service, err := oauth2.NewService(ctx, option.WithoutAuthentication())
if err != nil {
return err
}
tokenInfoCall := oauth2Service.Tokeninfo()
tokenInfoCall.IdToken(idToken)
tokenInfo, err := tokenInfoCall.Do()
if err != nil {
e, _ := err.(*googleapi.Error)
return e
}
fmt.Println(tokenInfo.Email)
return nil
}
PLEASE NOTE: To obtain the token Id, I am using the Oauth playground, and I set these scopes:
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
opened
After searching on oauth2, I noticed a type UserInfo containing all the info I need. However, the tokenInfo object does not return all the information from the user, such as first name and last name. But, I'm having some difficulty on how to get UserInfo.
In short, I created a function called getUserInfo like so:
func getUserInfo(service *oauth2.Service) (*oauth2.Userinfo, error) {
userInfoService := oauth2.NewUserinfoV2MeService(service)
userInfo, err := userInfoService.Get().Do()
if err != nil {
e, _ := err.(*googleapi.Error)
fmt.Println(e.Message)
return nil, e
}
return userInfo, nil
}
NOTE: I called the getUserInfo within the verifyIdToken
userInfo, err := getUserInfo(oauth2Service)
However, I'm always getting this 401 error:
googleapi: Error 401: Request is missing required authentication credential.
Expected OAuth 2 access token, login cookie or other valid authentication credential.
See https://developers.google.com/identity/sign-in/web/devconsole-project.,
unauthorized
With that, I'm not sure what to do.
I think you have found the answer to your question for a long time, but I still leave my solution here.
The UserinfoService doesn't actually pass a token inside. To do this, you can use code like this:
import "google.golang.org/api/googleapi"
token := "ya29.a0Aa4xrXMAp..."
userInfo, err := userInfoService.Get().Do(googleapi.QueryParameter("access_token", token))
if err != nil {
e, _ := err.(*googleapi.Error)
fmt.Println(e.Message)
return nil, e
}

Go GRPC Refresh token for a bidirectional stream

TLDR: I am looking for a way to update headers on an open stream for each call to stream.Send(msg) without closing the stream and opening a new one.
Summary
I have a GRPC client and server built to handle bidirectional streams. To authenticate with the server the client must send a JWT in the request headers, set as "authorization". The token is valid for 30 minutes. After the token has expired, the server will terminate the connection.
I am looking for a way to refresh my authorization token from the client, and keep the stream open. The client should run in a loop executing a new request every 30 minutes with the updated token, and the updated payload. I have not seen a way to update a header from the client side for an already opened stream.
Let's look at some code to get an idea of what the client side looks like. The code below has a function to create a new instance of the client, and another function to establish the connection to the GRPC server.
func NewWatchClient(config *Config, logger *logrus.Logger) (*WatchClient, error) {
cc, err := newConnection(config, logger)
if err != nil {
return nil, err
}
service := proto.NewWatchServiceClient(cc)
return &WatchClient{
config: config,
conn: cc,
logger: entry,
service: service,
}, nil
}
func newConnection(config *Config, logger *logrus.Logger) (*grpc.ClientConn, error) {
address := fmt.Sprintf("%s:%d", config.Host, config.Port)
// rpcCredential implements credentials.PerRPCCredentials
rpcCredential := newTokenAuth(config.Auth, config.TenantID)
return grpc.Dial(
address,
grpc.WithPerRPCCredentials(rpcCredential),
)
}
Looking at the newConnection function above I can see that there is a call to another function, newTokenAuth, to create an auth token. This func returns a struct that implements the PerRPCCredentials interface.
There are two ways to set the authorization for a request.
Use grpc.WithPerRPCCredentials to add the authorization at the time of creating the connection to the server.
Use grpc.PerRPCCredentials to add the authorization to each stream opened on the connection to the server.
In this case, I am using grpc.WithPerRPCCredentials to attach the token at the time of creating the connection to the server.
Now, let's take a look at the definition of PerRPCCredentials.
type PerRPCCredentials interface {
// GetRequestMetadata gets the current request metadata, refreshing
// tokens if required. This should be called by the transport layer on
// each request, and the data should be populated in headers or other
// context. If a status code is returned, it will be used as the status
// for the RPC. uri is the URI of the entry point for the request.
// When supported by the underlying implementation, ctx can be used for
// timeout and cancellation. Additionally, RequestInfo data will be
// available via ctx to this call.
// TODO(zhaoq): Define the set of the qualified keys instead of leaving
// it as an arbitrary string.
GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
// RequireTransportSecurity indicates whether the credentials requires
// transport security.
RequireTransportSecurity() bool
}
The interface requires that you define two methods. The documentation of GetRequestMetadata says
GetRequestMetadata gets the current request metadata, refreshing tokens if required
So, it looks like my implementation of PerRPCCredentials should be able to handle a token refresh for my stream or connection. Let's take a look at my implementation of PerRPCCredentials.
// tokenAuth implements the PerRPCCredentials interface
type tokenAuth struct {
tenantID string
tokenRequester auth.PlatformTokenGetter
token string
}
// RequireTransportSecurity leave as false for now
func (tokenAuth) RequireTransportSecurity() bool {
return false
}
// GetRequestMetadata sets the http header prior to transport
func (t tokenAuth) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) {
token, err := t.tokenRequester.GetToken()
if err != nil {
return nil, err
}
t.token = token
go func() {
time.Sleep(25 * time.Minute)
token, _ := t.tokenRequester.GetToken()
t.token = token
}()
return map[string]string{
"tenant-id": t.tenantID,
"authorization": "Bearer " + t.token,
}, nil
}
As you can see, the call to GetRequestMetadata will establish a go routine that will attempt to refresh a token every 25 minutes. Adding a go routine right here is probably not the right way to do it. It was an attempt to get the auth header to refresh, which doesn't work.
Let's take a look at the stream.
func (w WatchClient) CreateWatch() error {
topic := &proto.Request{SelfLink: w.config.TopicSelfLink}
stream, err := w.service.CreateWatch(context.Background())
if err != nil {
return err
}
for {
err = stream.Send(topic)
if err != nil {
return err
}
time.Sleep(25 * time.Minute)
}
}
The client sends a message on the stream every 25 minutes. All I'm looking to get here is that when stream.Send is called, the updated token is also sent.
This function, GetRequestMetadata only gets called once, regardless if I am setting the auth through grpc.WithPerRPCCredentials or grpc.PerRPCCredsCallOption so there appears to be no way to update the authorization header.
If you have any idea what I have missed in my attempt to utilize the PerRPCCredentials for token refresh then please let me know.
Thank you.
Headers are sent at the beginning of an RPC, and cannot be updated during the RPC. If you need to send data during the life of a stream, it needs to be part of the request message in your proto definition.

How to acquire OAuth2.0 token from Azure AD in Go?

I am attempting to consume Azure service bus entity using Go. Authentication with Azure service bus is possible by supplying either an SAS token or an Azure AD OAuth2.0 token, which will be obtained via the security principals of Azure AD app. Technically I prefer the security principals option rather than an SAS token as it has security vulnerabilities.
How do I acquire an OAuth2.0 token from Azure AD using Go for which Azure AD SDK is not available?
Is it possible to make a direct call to Azure AD REST APIs to access an OAuth2.0 token?
There are some methods to get access token using Go.
1. Use Http Request
For example with authorization code flow, the whole code sample here:
func GetTokens(c AuthorizationConfig, authCode AuthorizationCode, scope string) (t Tokens, err error) {
formVals := url.Values{}
formVals.Set("code", authCode.Value)
formVals.Set("grant_type", "authorization_code")
formVals.Set("redirect_uri", c.RedirectURL())
formVals.Set("scope", scope)
if c.ClientSecret != "" {
formVals.Set("client_secret", c.ClientSecret)
}
formVals.Set("client_id", c.ClientID)
response, err := http.PostForm(TokenURL, formVals)
if err != nil {
return t, errors.Wrap(err, "error while trying to get tokens")
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return t, errors.Wrap(err, "error while trying to read token json body")
}
err = json.Unmarshal(body, &t)
if err != nil {
return t, errors.Wrap(err, "error while trying to parse token json body")
}
return
}
2. Use MSAL Go
// 1.1 Initializing a public client:
publicClientapp, err := public.New("client_id", public.WithAuthority("https://login.microsoftonline.com/Enter_The_Tenant_Name_Here"))
// 1.2 Initializing a confidential client:
confidentialClientApp, err := confidential.New("client_id", cred, confidential.WithAuthority("https://login.microsoftonline.com/Enter_The_Tenant_Name_Here"))
// 2. MSAL comes packaged with an in-memory cache. Utilizing the cache is optional, but we would highly recommend it.
var userAccount public.Account
accounts := publicClientApp.Accounts()
if len(accounts) > 0 {
// Assuming the user wanted the first account
userAccount = accounts[0]
// found a cached account, now see if an applicable token has been cached
result, err := publicClientApp.AcquireTokenSilent(context.Background(), []string{"your_scope"}, public.WithSilentAccount(userAccount))
accessToken := result.AccessToken
}
// 3. If there is no suitable token in the cache, or you choose to skip this step, now we can send a request to AAD to obtain a token.
result, err := publicClientApp.AcquireToken"ByOneofTheActualMethods"([]string{"your_scope"}, ...(other parameters depending on the function))
if err != nil {
log.Fatal(err)
}
accessToken := result.AccessToken
Last, Azure SDK for Go seems used to authenticate with Azure, but it doesn't provide a SDK method to acquire an access token.

How to implement authorization using Keycloak

I created a REST API in Go that is necessary an authorization layer, for this layer I am trying use Keycloak. The API will be consumed by a third-party backend service, anyone knows the workflow to integrate Go client and keycloak or already implemented it? I figured out an adapter called Gocloak but in its documentation there is not any example for this purpose.
Authorization is typically application specific, so I can't help much there, but here's some information on authenticating JWTs from Keycloak. After JWTs are authenticated, you can use their claims to authorize the request.
Keycloak exposes what's known as a JSON Web Key Set (JWKS). This resource should be used to authenticate JWTs. I've wrote a package for this purpose. It's an extension of github.com/golang-jwt/jwt/v4.
The package is called github.com/MicahParks/keyfunc. I've pasted the code example for Keycloak below.
package main
import (
"log"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/MicahParks/keyfunc"
)
func main() {
// Get the JWKS URL.
//
// This is a local Keycloak JWKS endpoint for the master realm.
jwksURL := "http://localhost:8080/auth/realms/master/protocol/openid-connect/certs"
// Create the keyfunc options. Use an error handler that logs. Refresh the JWKS when a JWT signed by an unknown KID
// is found or at the specified interval. Rate limit these refreshes. Timeout the initial JWKS refresh request after
// 10 seconds. This timeout is also used to create the initial context.Context for keyfunc.Get.
options := keyfunc.Options{
RefreshErrorHandler: func(err error) {
log.Printf("There was an error with the jwt.Keyfunc\nError: %s", err.Error())
},
RefreshInterval: time.Hour,
RefreshRateLimit: time.Minute * 5,
RefreshTimeout: time.Second * 10,
RefreshUnknownKID: true,
}
// Create the JWKS from the resource at the given URL.
jwks, err := keyfunc.Get(jwksURL, options)
if err != nil {
log.Fatalf("Failed to create JWKS from resource at the given URL.\nError: %s", err.Error())
}
// Get a JWT to parse.
jwtB64 := "eyJhbGciOiJQUzM4NCIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJMeDFGbWF5UDJZQnR4YXFTMVNLSlJKR2lYUktudzJvdjVXbVlJTUctQkxFIn0.eyJleHAiOjE2MTU0MDY5ODIsImlhdCI6MTYxNTQwNjkyMiwianRpIjoiMGY2NGJjYTktYjU4OC00MWFhLWFkNDEtMmFmZDM2OGRmNTFkIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL21hc3RlciIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJhZDEyOGRmMS0xMTQwLTRlNGMtYjA5Ny1hY2RjZTcwNWJkOWIiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJ0b2tlbmRlbG1lIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjE3Mi4yMC4wLjEiLCJjbGllbnRJZCI6InRva2VuZGVsbWUiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC10b2tlbmRlbG1lIiwiY2xpZW50QWRkcmVzcyI6IjE3Mi4yMC4wLjEifQ.Rxrq41AxbWKIQHWv-Tkb7rqwel3sKT_R_AGvn9mPIHqhw1m7nsQWcL9t2a_8MI2hCwgWtYdgTF1xxBNmb2IW3CZkML5nGfcRrFvNaBHd3UQEqbFKZgnIX29h5VoxekyiwFaGD-0RXL83jF7k39hytEzTatwoVjZ-frga0KFl-nLce3OwncRXVCGmxoFzUsyu9TQFS2Mm_p0AMX1y1MAX1JmLC3WFhH3BohhRqpzBtjSfs_f46nE1-HKjqZ1ERrAc2fmiVJjmG7sT702JRuuzrgUpHlMy2juBG4DkVcMlj4neJUmCD1vZyZBRggfaIxNkwUhHtmS2Cp9tOcwNu47tSg"
// Parse the JWT.
token, err := jwt.Parse(jwtB64, jwks.Keyfunc)
if err != nil {
log.Fatalf("Failed to parse the JWT.\nError: %s", err.Error())
}
// Check if the token is valid.
if !token.Valid {
log.Fatalf("The token is not valid.")
}
log.Println("The token is valid.")
// End the background refresh goroutine when it's no longer needed.
jwks.EndBackground()
}

Resources