Skip to content

Instantly share code, notes, and snippets.

@hnaohiro
Last active April 11, 2024 16:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hnaohiro/1473e31ea9317f6c03f18ec12a3c949a to your computer and use it in GitHub Desktop.
Save hnaohiro/1473e31ea9317f6c03f18ec12a3c949a to your computer and use it in GitHub Desktop.
Sample code Go, OAuth, Google
package main
import (
"encoding/json"
"io"
"log"
"log/slog"
"net/http"
"os"
"time"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
func main() {
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)
r := mux.NewRouter()
r.HandleFunc("/", indexHandler).Methods(http.MethodGet)
r.HandleFunc("/auth/callback/google", authGoogleCallbackHandler).Methods(http.MethodGet)
srv := &http.Server{
Addr: ":8080",
Handler: r,
ReadHeaderTimeout: 30 * time.Second,
}
if err := srv.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
func newConf() (*oauth2.Config, error) {
credentialsJSON := []byte(os.Getenv("GOOGLE_CREDENTIALS_JSON"))
scope := "https://www.googleapis.com/auth/userinfo.email"
return google.ConfigFromJSON(credentialsJSON, scope)
}
var (
key = []byte("orchestration-gateway-google-auth-test")
store = sessions.NewCookieStore(key)
)
func indexHandler(w http.ResponseWriter, r *http.Request) {
conf, err := newConf()
if err != nil {
slog.Error("indexHandler, newConf", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
session, err := store.Get(r, "auth")
if err != nil {
slog.Error("indexHandler, store Get", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
email := session.Values["email"]
if email != nil {
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("Hello, " + email.(string) + "!"))
return
}
state := os.Getenv("OAUTH_STATE")
session.Values["state"] = state
if err = session.Save(r, w); err != nil {
slog.Error("indexHandler, store Save", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
redirectURL := conf.AuthCodeURL(state, oauth2.AccessTypeOnline, oauth2.ApprovalForce)
http.Redirect(w, r, redirectURL, http.StatusSeeOther)
}
func authGoogleCallbackHandler(w http.ResponseWriter, r *http.Request) {
session, err := store.Get(r, "auth")
if err != nil {
slog.Error("authGoogleCallbackHandler, store Get", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
state := session.Values["state"]
if state != r.URL.Query().Get("state") {
slog.Error("authGoogleCallbackHandler, state mismatch", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
conf, err := newConf()
if err != nil {
slog.Error("authGoogleCallbackHandler, newConf", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
token, err := conf.Exchange(r.Context(), r.URL.Query().Get("code"))
if err != nil {
slog.Error("authGoogleCallbackHandler, conf Exchange", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
client := conf.Client(r.Context(), token)
response, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo")
if err != nil {
slog.Error("authGoogleCallbackHandler, client Get", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
defer func() {
_ = response.Body.Close()
}()
if response.StatusCode != http.StatusOK {
slog.Error("authGoogleCallbackHandler, response StatusCode", slog.Int("value", response.StatusCode))
w.WriteHeader(http.StatusInternalServerError)
return
}
body, err := io.ReadAll(response.Body)
if err != nil {
slog.Error("authGoogleCallbackHandler, io ReadAll", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
var payload map[string]any
if err := json.Unmarshal(body, &payload); err != nil {
slog.Error("authGoogleCallbackHandler, json Unmarshal", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
email, ok := payload["email"].(string)
if !ok {
slog.Error("authGoogleCallbackHandler, payload email", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
session.Values["email"] = email
if err = session.Save(r, w); err != nil {
slog.Error("authGoogleCallbackHandler, store Save", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/", http.StatusSeeOther)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment