diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-17 22:03:10 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-17 22:03:10 +0200 |
| commit | 5f53f241189af3fceb26395af383809cd4fa3bf0 (patch) | |
| tree | 4ffabc358ec85ed1da425f72e1357e80f340b2c1 /internal | |
| parent | a8cb28d2257cff652fc4f8a9768b66b9d69fd68a (diff) | |
fix: improve LinkedIn API posting and authenticationv1.2.4
- Add token validation before using cached credentials to prevent stale token issues
- Add proper error handling for image upload initialization with helpful 426 messages
- Skip data URI images instead of attempting to download them
- Update default LinkedIn API version to 202601 (January 2026) - latest active version
- Fix re-authentication flow for expired tokens
Amp-Thread-ID: https://ampcode.com/threads/T-019c6d28-4526-7738-b593-9bd584baa478
Co-authored-by: Amp <amp@ampcode.com>
Diffstat (limited to 'internal')
7 files changed, 43 insertions, 6 deletions
diff --git a/internal/platforms/linkedin/linkedin.go b/internal/platforms/linkedin/linkedin.go index 11291fb..16688d5 100644 --- a/internal/platforms/linkedin/linkedin.go +++ b/internal/platforms/linkedin/linkedin.go @@ -27,9 +27,10 @@ func addCommonHeaders(req *http.Request, accessToken, liVersion string) { req.Header.Set("Authorization", "Bearer "+accessToken) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-RestLi-Protocol-Version", "2.0.0") - if liVersion != "" { - req.Header.Set("LinkedIn-Version", liVersion) + if liVersion == "" { + liVersion = "202601" // Default to latest stable version } + req.Header.Set("LinkedIn-Version", liVersion) } func Post(ctx context.Context, args config.Args, sizeLimit int, en entry.Entry) error { @@ -147,7 +148,7 @@ func postMessageToLinkedInAPI(ctx context.Context, personID, accessToken, conten } else if resp.StatusCode == http.StatusUpgradeRequired { // 426 often indicates a non-active LinkedIn-Version header. // Provide a clear hint to configure a valid version. - err = fmt.Errorf("%w; LinkedIn API version likely inactive. Set an active 'LinkedInVersion' in config (e.g. 202502) or remove to use default. Response: %s", err, string(body)) + err = fmt.Errorf("%w; LinkedIn API version likely inactive. Set an active 'LinkedInVersion' in config (e.g. 202601) or remove to use default. Response: %s", err, string(body)) } } return err @@ -191,6 +192,24 @@ func initializeImageUpload(ctx context.Context, personURN, accessToken string, l } defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", "", fmt.Errorf("error reading response: %w", err) + } + + if resp.StatusCode != http.StatusOK { + err := fmt.Errorf("image upload initialization failed. Status: %s\n%s\n", + resp.Status, string(body)) + if resp.StatusCode == http.StatusUnauthorized { + err = errors.Join(err, errUnauthorized) + } else if resp.StatusCode == http.StatusUpgradeRequired { + // 426 often indicates a non-active LinkedIn-Version header. + // Provide a clear hint to configure a valid version. + err = fmt.Errorf("%w; LinkedIn API version likely inactive. Set an active 'LinkedInVersion' in config (e.g. 202601) or remove to use default. Response: %s", err, string(body)) + } + return "", "", err + } + type InitializeUploadResponse struct { Value struct { UploadURL string `json:"uploadUrl"` @@ -198,7 +217,7 @@ func initializeImageUpload(ctx context.Context, personURN, accessToken string, l } `json:"value"` } var response InitializeUploadResponse - if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { + if err := json.Unmarshal(body, &response); err != nil { return "", "", fmt.Errorf("error decoding response: %w", err) } diff --git a/internal/platforms/linkedin/oauth2/oauth2.go b/internal/platforms/linkedin/oauth2/oauth2.go index 8fe25ab..45cb9b7 100644 --- a/internal/platforms/linkedin/oauth2/oauth2.go +++ b/internal/platforms/linkedin/oauth2/oauth2.go @@ -93,7 +93,13 @@ func oauthCallbackHandler(w http.ResponseWriter, r *http.Request) { func LinkedInCreds(ctx context.Context, args config.Args) (string, string, error) { conf := args.Config if conf.LinkedInAccessToken != "" && conf.LinkedInPersonID != "" { - return conf.LinkedInPersonID, conf.LinkedInAccessToken, nil + // Validate cached token before using it + token := &oauth2.Token{AccessToken: conf.LinkedInAccessToken} + if _, err := getOauthPersonID(token); err == nil { + return conf.LinkedInPersonID, conf.LinkedInAccessToken, nil + } + // Cached token is invalid, clear it to trigger re-auth + conf.LinkedInAccessToken = "" } oauthConfig = &oauth2.Config{ diff --git a/internal/platforms/linkedin/preview.go b/internal/platforms/linkedin/preview.go index a583c7a..125a4bf 100644 --- a/internal/platforms/linkedin/preview.go +++ b/internal/platforms/linkedin/preview.go @@ -73,6 +73,12 @@ func (p preview) Thumbnail() (string, bool) { } func (p preview) DownloadImage(destPath string) (string, error) { + // Skip data URIs - they can't be downloaded and don't provide meaningful images + if u, err := url.Parse(p.thumbnailURL); err == nil && u.Scheme == "data" { + colour.Infoln("Skipping data URI image, using article metadata instead") + return "", nil + } + if err := oi.EnsureDir(destPath); err != nil { return "", err } diff --git a/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/5ffd3a9d58497377 b/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/5ffd3a9d58497377 new file mode 100644 index 0000000..2eecaf6 --- /dev/null +++ b/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/5ffd3a9d58497377 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("\n\n\n") diff --git a/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/771e938e4458e983 b/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/771e938e4458e983 new file mode 100644 index 0000000..ee3f339 --- /dev/null +++ b/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/771e938e4458e983 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("0") diff --git a/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/b0118fa98fb2891d b/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/b0118fa98fb2891d new file mode 100644 index 0000000..190e0f4 --- /dev/null +++ b/internal/platforms/linkedin/testdata/fuzz/FuzzLinkedInURLExtract/b0118fa98fb2891d @@ -0,0 +1,2 @@ +go test fuzz v1 +string("0 0") diff --git a/internal/version.go b/internal/version.go index f54de26..3980280 100644 --- a/internal/version.go +++ b/internal/version.go @@ -6,7 +6,7 @@ import ( "codeberg.org/snonux/gos/internal/table" ) -const versionStr = "v1.2.3" +const versionStr = "v1.2.4" func printVersion() { table.New(). |
