1
votes

I'm having difficulty authenticating using Google OAuth2.

I've obtainedd client ID and secret from google developer console, and I came up with this code :

package main

    import (
        "fmt"
        "golang.org/x/oauth2"
        "golang.org/x/oauth2/google"
        "io/ioutil"
        "net/http"
        "os"
    )

    const htmlIndex = `<html><body>
    <a href="/GoogleLogin">Log in with Google</a>
    </body></html>
    `

    func init() {
        // Setup Google's example test keys
        os.Setenv("CLIENT_ID", "somrestring-otherstring.apps.googleusercontent.com")
        os.Setenv("SECRET_KEY", "alongcharachterjumble")
    }

    var (
        googleOauthConfig = &oauth2.Config{
            RedirectURL:  "http://127.0.0.1:8080/auth",  //defined in Google console
            ClientID:     os.Getenv("CLIENT_ID"),
            ClientSecret: os.Getenv("SECRET_KEY"),
            Scopes: []string{"https://www.googleapis.com/auth/userinfo.profile",
                "https://www.googleapis.com/auth/userinfo.email"},
            Endpoint: google.Endpoint,
        }
        // Some random string, random for each request
        oauthStateString = "random"
    )

    func main() {
        http.HandleFunc("/", handleMain)
        http.HandleFunc("/GoogleLogin", handleGoogleLogin)
        http.HandleFunc("/GoogleCallback", handleGoogleCallback)
        fmt.Println(http.ListenAndServe(":8080", nil))
    }

    func handleMain(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, htmlIndex)
        fmt.Println("another request made")
    }

    func handleGoogleLogin(w http.ResponseWriter, r *http.Request) {
        url := googleOauthConfig.AuthCodeURL(oauthStateString)
        http.Redirect(w, r, url, http.StatusTemporaryRedirect)
    }

    func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
        state := r.FormValue("state")
        if state != oauthStateString {
            fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }

        code := r.FormValue("code")
        token, err := googleOauthConfig.Exchange(oauth2.NoContext, code)
        if err != nil {
            fmt.Println("Code exchange failed with '%s'\n", err)
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }

        response, err := http.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token.AccessToken)

        defer response.Body.Close()
        contents, err := ioutil.ReadAll(response.Body)
        fmt.Fprintf(w, "Content: %s\n", contents)
    }

But I get this error from google:

  1. That’s an error.

Error: invalid_request

Missing required parameter: client_id

Learn more

Request Details client_id= redirect_uri=http://127.0.0.1:8080/auth response_type=code scope=https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email state=random

What's wrong here? How can I fix it?

1

1 Answers

2
votes

The error message indicates that ClientID is not initialized.

That looks consistent with the code, since var declarations are executed before the init functions.

So when your var requests os.Getenv("CLIENT_ID") the value is blank since init has not executed yet.

From the documentation:

A package with no imports is initialized by assigning initial values to all its package-level variables followed by calling all init functions in the order they appear in the source, possibly in multiple files, as presented to the compiler

https://golang.org/ref/spec#Package_initialization

To fix this, either put the string directly in the var initialization, or trigger the initialization from the init after you set the values.

Like:

var (
    googleOauthConfig *oauth2.Config
)

func init() {
     // init ENV
     // initialize the variable using ENV values
     googleOauthConfig = &oauth2.Config{ ... }
}

Alternatively, you can set those ENV values at the OS level before executing the actual Go program.