diff options
| author | Paul Buetow <paul@buetow.org> | 2024-10-09 11:25:49 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2024-10-09 11:25:49 +0300 |
| commit | 6622cc6402df56dd416e3ca3894d5b208fa26077 (patch) | |
| tree | d49582c4bb2216000b01ea7d8eb2ef37ba784977 | |
| parent | fba8665dcc22d11f8ac7b7583701951dc4d9544f (diff) | |
can re-use access token for linkedin oauth
| -rw-r--r-- | cmd/gos/main.go | 2 | ||||
| -rw-r--r-- | internal/config/args.go | 1 | ||||
| -rw-r--r-- | internal/config/secrets.go | 7 | ||||
| -rw-r--r-- | internal/entry/entry.go | 1 | ||||
| -rw-r--r-- | internal/platforms/linkedin/linkedin.go | 2 | ||||
| -rw-r--r-- | internal/platforms/linkedin/oauth2/oauth2.go | 62 |
6 files changed, 67 insertions, 8 deletions
diff --git a/cmd/gos/main.go b/cmd/gos/main.go index 5b98386..6ea4995 100644 --- a/cmd/gos/main.go +++ b/cmd/gos/main.go @@ -20,6 +20,7 @@ func main() { dry := flag.Bool("dry", false, "Dry run") version := flag.Bool("version", false, "Display version") gosDir := flag.String("gosDir", "./gosdir", "Gos' directory") + browser := flag.String("browser", "firefox", "OAuth2 browser") secretsConfigPath := filepath.Join(os.Getenv("HOME"), ".config/gos/gosec.json") secretsConfigPath = *flag.String("secretsConfig", secretsConfigPath, "Gos' secret config") platforms := flag.String("platforms", "Mastodon,LinkedIn", "Platforms enabled") @@ -40,6 +41,7 @@ func main() { Lookback: time.Duration(*lookback) * time.Hour * 24, SecretsConfigPath: secretsConfigPath, Secrets: secrets, + OAuth2Browser: *browser, } if err := args.Validate(); err != nil { diff --git a/internal/config/args.go b/internal/config/args.go index 601a2d1..3bfa78e 100644 --- a/internal/config/args.go +++ b/internal/config/args.go @@ -17,6 +17,7 @@ type Args struct { Lookback time.Duration SecretsConfigPath string Secrets Secrets + OAuth2Browser string } func (a Args) Validate() error { diff --git a/internal/config/secrets.go b/internal/config/secrets.go index dc3f8e7..6ed1c1f 100644 --- a/internal/config/secrets.go +++ b/internal/config/secrets.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "log" "os" ) @@ -15,9 +16,9 @@ type Secrets struct { LinkedInSecret string LinkedInRedirectURL string // Will be updated by gos automatically, after successful oauth2 - LinkedInAccessToken string `json:"LinedInAccessToken,omitempty"` + LinkedInAccessToken string `json:"LinkedInAccessToken,omitempty"` // Will be updated by gos automatically, after successful oauth2 - LinkedInPersonID string `json:"LinedInPersonID,omitempty"` + LinkedInPersonID string `json:"LinkedInPersonID,omitempty"` } func NewSecrets(configPath string) (Secrets, error) { @@ -41,6 +42,8 @@ func NewSecrets(configPath string) (Secrets, error) { } func (s Secrets) WriteToDisk(configPath string) error { + log.Println("Writing", configPath) + bytes, err := json.MarshalIndent(s, "", " ") if err != nil { return fmt.Errorf("failed to marshal JSON: %w", err) diff --git a/internal/entry/entry.go b/internal/entry/entry.go index 7bb4930..0874e64 100644 --- a/internal/entry/entry.go +++ b/internal/entry/entry.go @@ -95,6 +95,7 @@ func (e *Entry) MarkPosted() error { if e.State == Posted { return errors.New("entry is already posted") } + // TODO: Also update the timestamp to reflect the posting time in the file path. if err := os.Rename(e.Path, strings.TrimSuffix(e.Path, ".queued")+".posted"); err != nil { return err } diff --git a/internal/platforms/linkedin/linkedin.go b/internal/platforms/linkedin/linkedin.go index da1ce0d..5b07077 100644 --- a/internal/platforms/linkedin/linkedin.go +++ b/internal/platforms/linkedin/linkedin.go @@ -21,7 +21,7 @@ func Post(ctx context.Context, args config.Args, ent entry.Entry) error { return nil } - personID, accessToken, err := oauth2.LinkedInOAuth2Creds(args) + personID, accessToken, err := oauth2.LinkedInCreds(args) if err != err { return err } diff --git a/internal/platforms/linkedin/oauth2/oauth2.go b/internal/platforms/linkedin/oauth2/oauth2.go index e800bac..bbc60fb 100644 --- a/internal/platforms/linkedin/oauth2/oauth2.go +++ b/internal/platforms/linkedin/oauth2/oauth2.go @@ -8,6 +8,9 @@ import ( "io" "log" "net/http" + "os/exec" + "runtime" + "time" "codeberg.org/snonux/gos/internal/config" "golang.org/x/oauth2" @@ -55,6 +58,10 @@ func getOauthPersonID(token *oauth2.Token) (string, error) { return user.Sub, nil } +func upHandler(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte("I am up!\n")) +} + func oauthIndexHandler(w http.ResponseWriter, r *http.Request) { url := oauthConfig.AuthCodeURL("state", oauth2.AccessTypeOffline) http.Redirect(w, r, url, http.StatusTemporaryRedirect) @@ -64,6 +71,8 @@ func oauthCallbackHandler(w http.ResponseWriter, r *http.Request) { defer close(errCh) code := r.URL.Query().Get("code") + log.Println("Exchanging OAuth2 token") + // TODO: Insert the propper context token, err := oauthConfig.Exchange(context.Background(), code) if err != nil { _, _ = w.Write([]byte(err.Error())) @@ -81,11 +90,11 @@ func oauthCallbackHandler(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("Successfully fetched LinkedIn person ID\n")) } -func LinkedInOAuth2Creds(args config.Args) (string, string, error) { +func LinkedInCreds(args config.Args) (string, string, error) { secrets := args.Secrets if secrets.LinkedInAccessToken != "" && secrets.LinkedInPersonID != "" { // TODO: Check, whether the access token is still valid. If not, get a new one. - return secrets.LinkedInPersonID, secrets.MastodonAccessToken, nil + return secrets.LinkedInPersonID, secrets.LinkedInAccessToken, nil } oauthConfig = &oauth2.Config{ @@ -95,18 +104,27 @@ func LinkedInOAuth2Creds(args config.Args) (string, string, error) { Scopes: []string{"openid", "profile", "w_member_social"}, Endpoint: linkedin.Endpoint, } - errCh = make(chan error) + errCh = make(chan error, 10) http.HandleFunc("/", oauthIndexHandler) http.HandleFunc("/callback", oauthCallbackHandler) + http.HandleFunc("/up", upHandler) - log.Println("Listening on http://localhost:8080 for LinkedIn oauth2") + log.Println("Listening on http://localhost:8080 for LinkedIn OAuth2") go func() { if err := http.ListenAndServe(":8080", nil); err != nil { errCh <- err } }() + if err := waitUntilURLIsReachable("http://localhost:8080/up"); err != nil { + return "", "", err + } + + if err := openURLInFirefox(args.OAuth2Browser, "http://localhost:8080"); err != nil { + return "", "", err + } + var errs error for err := range errCh { errs = errors.Join(errs, err) @@ -115,7 +133,41 @@ func LinkedInOAuth2Creds(args config.Args) (string, string, error) { return "", "", errs } - secrets.MastodonAccessToken = oauthAccessToken + secrets.LinkedInAccessToken = oauthAccessToken secrets.LinkedInPersonID = oauthPersonID return oauthPersonID, oauthAccessToken, secrets.WriteToDisk(args.SecretsConfigPath) } + +func openURLInFirefox(browser, url string) error { + log.Println("Opening", url, "in", browser) + switch runtime.GOOS { + case "windows": + cmd := exec.Command("cmd", "/C", "start", browser, url) + return cmd.Start() + case "darwin": + cmd := exec.Command("open", "-a", browser, url) + return cmd.Start() + default: + // Linux and other Unix like (e.g. *BSDs) + cmd := exec.Command(browser, url) + return cmd.Start() + } +} + +func waitUntilURLIsReachable(url string) error { + var counter int + for counter < 10 { + counter++ + time.Sleep(1 * time.Second) + resp, err := http.Get(url) + + if err != nil { + log.Printf("URL is not reachable: %v\n", err) + } else { + log.Printf("URL is reachable: %s - Status Code: %d\n", url, resp.StatusCode) + resp.Body.Close() + return nil + } + } + return fmt.Errorf("%s not reachable after %d tries", url, counter) +} |
