package v1 import ( "fmt" "net/url" "os" "strings" "time" "github.com/golang-jwt/jwt/v5" ) type chatwootEmailOAuthAuthorizationConfig struct { ClientIDEnv string ClientSecretEnv string AuthorizeURL string RedirectPath string Scope string ExtraParams map[string]string } func buildChatwootEmailOAuthAuthorizationURL(accountID uint, cfg chatwootEmailOAuthAuthorizationConfig) (string, error) { clientID := strings.TrimSpace(os.Getenv(cfg.ClientIDEnv)) clientSecret := strings.TrimSpace(os.Getenv(cfg.ClientSecretEnv)) if clientID == "" || clientSecret == "" { return "", fmt.Errorf("email OAuth is not configured") } state, err := signedChatwootOAuthState(accountID, clientSecret) if err != nil { return "", err } frontendURL := strings.TrimRight(envOrDefaultV1("FRONTEND_URL", "http://localhost:3000"), "/") params := url.Values{} params.Set("client_id", clientID) params.Set("redirect_uri", frontendURL+cfg.RedirectPath) params.Set("response_type", "code") params.Set("scope", cfg.Scope) params.Set("state", state) for key, value := range cfg.ExtraParams { params.Set(key, value) } return cfg.AuthorizeURL + "?" + params.Encode(), nil } func signedChatwootOAuthState(accountID uint, secret string) (string, error) { return signedChatwootOAuthStateWithReturnTo(accountID, secret, "") } func signedChatwootOAuthStateWithReturnTo(accountID uint, secret string, returnTo string) (string, error) { claims := jwt.MapClaims{ "sub": accountID, "iat": time.Now().Unix(), } if strings.TrimSpace(returnTo) != "" { claims["return_to"] = strings.TrimSpace(returnTo) } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString([]byte(secret)) }