Initial commit
Go CI / build (push) Failing after 2m42s

feat: create basic server to manage google oauth, account, sessions, places, attributes and ratings.
This commit is contained in:
2025-09-19 02:43:04 -07:00
commit f1909da1ad
26 changed files with 2619 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
package auth
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"git.pengzhan.dev/noteplace-server/internal/models"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
// Authenticator handles the OAuth2 flow.
type Authenticator struct {
Config *oauth2.Config
}
// NewAuthenticator creates a new Authenticator.
// It requires GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET environment variables.
func NewAuthenticator() (*Authenticator, error) {
clientID := os.Getenv("GOOGLE_CLIENT_ID")
clientSecret := os.Getenv("GOOGLE_CLIENT_SECRET")
if clientID == "" || clientSecret == "" {
return nil, fmt.Errorf("GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET must be set")
}
config := &oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
RedirectURL: "urn:ietf:wg:oauth:2.0:oob", // Special redirect URI for desktop/CLI apps
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"},
Endpoint: google.Endpoint,
}
return &Authenticator{Config: config}, nil
}
// GetAuthURL generates the URL the user must visit to authorize the application.
func (a *Authenticator) GetAuthURL() string {
// We don't need a state for this simple CLI flow, but it's good practice for web apps.
return a.Config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
}
// ExchangeCodeForToken takes an authorization code and exchanges it for a token.
func (a *Authenticator) ExchangeCodeForToken(code string) (*oauth2.Token, error) {
return a.Config.Exchange(context.Background(), code)
}
// GetUserInfo uses the token to fetch the user's profile from Google.
func (a *Authenticator) GetUserInfo(token *oauth2.Token) (models.User, error) {
client := a.Config.Client(context.Background(), token)
resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo")
if err != nil {
return models.User{}, err
}
defer func() {
_ = resp.Body.Close()
}()
var userInfo struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
}
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
return models.User{}, err
}
log.Printf("Fetched user info from Google: ID=%s, Name=%s", userInfo.ID, userInfo.Name)
// We create a new User model from the Google info.
user := models.User{
GoogleID: userInfo.ID,
DisplayName: userInfo.Name,
Email: userInfo.Email,
}
return user, nil
}