package auth import ( "context" "errors" "fmt" "os" "golang.org/x/oauth2" "golang.org/x/oauth2/google" ) type OAuthConfig struct { *oauth2.Config } const ( base = "https://www.googleapis.com" scopeEmail = base + "/auth/userinfo.email" scopeProfile = base + "/auth/userinfo.profile" ) func NewOauthConfig(path string) (OAuthConfig, error) { key, err := os.ReadFile(path) if err != nil { return OAuthConfig{}, fmt.Errorf("could not open file: %w", err) } config, err := google.ConfigFromJSON(key, scopeEmail, scopeProfile) if err != nil { return OAuthConfig{}, fmt.Errorf("could not load config: %w", err) } return OAuthConfig{ Config: config, }, nil } var ErrInvalidState = errors.New("invalid ouath state provided") type OAuthManager interface { Create(callback string) (code string) Validate(code string) (callback string, valid bool) } func (cfg OAuthConfig) AuthCodeURL(manager OAuthManager, callback string) string { state := manager.Create(callback) fmt.Printf("state: %v\n", state) return cfg.Config.AuthCodeURL(state, oauth2.AccessTypeOffline) } func (cfg OAuthConfig) Exchange(ctx context.Context, manager OAuthManager, state, code string) (string, *oauth2.Token, error) { var ok bool var callback string if callback, ok = manager.Validate(state); !ok { return "", nil, ErrInvalidState } tk, err := cfg.Config.Exchange(ctx, code, oauth2.AccessTypeOffline) return callback, tk, err }