Skip to content

Conversation

ntindle
Copy link
Member

@ntindle ntindle commented Sep 25, 2025

Need 💡

This PR addresses Linear issue SECRT-1665, which mandates an update to Linear's OAuth2 implementation. Linear is transitioning from long-lived access tokens to short-lived access tokens with refresh tokens, with a deadline of April 1, 2026. This change is crucial to ensure continued integration with Linear and to support their new token management system, including a migration path for existing long-lived tokens.

Changes 🏗️

  • autogpt_platform/backend/backend/blocks/linear/_oauth.py:
    • Implemented full support for refresh tokens, including HTTP Basic Authentication for token refresh requests.
    • Added migrate_old_token() method to exchange old long-lived access tokens for new short-lived tokens with refresh tokens using Linear's /oauth/migrate_old_token endpoint.
    • Enhanced get_access_token() to automatically detect and attempt migration for old tokens, and to refresh short-lived tokens when they expire.
    • Improved error handling and token expiration management.
    • Updated _request_tokens to handle both authorization code and refresh token flows, supporting Linear's recommended authentication methods.
  • autogpt_platform/backend/backend/blocks/linear/_config.py:
    • Updated TEST_CREDENTIALS_OAUTH mock data to include realistic access_token_expires_at and refresh_token for testing the new token lifecycle.
  • LINEAR_OAUTH_IMPLEMENTATION.md:
    • Added documentation detailing the new Linear OAuth refresh token implementation, including technical details, migration strategy, and testing notes.

Checklist 📋

For code changes:

  • I have clearly listed my changes in the PR description
  • I have made a test plan
  • I have tested my changes according to the test plan:
    • Verified OAuth URL generation and parameter encoding.
    • Confirmed HTTP Basic Authentication header creation for refresh requests.
    • Tested token expiration logic with a 5-minute buffer.
    • Validated migration detection for old vs. new token types.
    • Checked code syntax and import compatibility.

For configuration changes:

  • .env.default is updated or already compatible with my changes
  • docker-compose.yml is updated or already compatible with my changes
  • I have included a list of my configuration changes in the PR description (under Changes)

Linear Issue: SECRT-1665

Open in Cursor Open in Web

Copy link

cursor bot commented Sep 25, 2025

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@github-project-automation github-project-automation bot moved this to 🆕 Needs initial review in AutoGPT development kanban Sep 25, 2025
Copy link
Contributor

This PR targets the master branch but does not come from dev or a hotfix/* branch.

Automatically setting the base branch to dev.

Copy link

netlify bot commented Sep 25, 2025

Deploy Preview for auto-gpt-docs canceled.

Name Link
🔨 Latest commit 146f9b8
🔍 Latest deploy log https://app.netlify.com/projects/auto-gpt-docs/deploys/68dffbc8351c7500088cfcb1

@github-actions github-actions bot changed the base branch from master to dev September 25, 2025 23:22
Copy link

deepsource-io bot commented Sep 25, 2025

Here's the code health analysis summary for commits 8b4eb6f..146f9b8. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource JavaScript LogoJavaScript✅ SuccessView Check ↗
DeepSource Python LogoPython✅ Success
❗ 1 occurence introduced
View Check ↗

💡 If you’re a repository administrator, you can configure the quality gates from the settings.

@AutoGPT-Agent
Copy link

Thank you for the detailed PR implementing Linear's OAuth refresh token system ahead of their 2026 migration deadline! The implementation looks well thought out and thoroughly tested. Here are a few suggestions to improve the PR:

  1. PR Title Format: The title should follow conventional commit format. Consider updating it to something like: fix(platform/blocks): update linear oauth to use refresh tokens

  2. Unrelated Changes: There are some error handling improvements in the DataForSEO files that appear unrelated to the Linear OAuth implementation. While they're good improvements, it might be clearer to separate these into their own PR or at least mention them in the PR description.

  3. Documentation: The added LINEAR_OAUTH_IMPLEMENTATION.md is excellent. Consider also updating any existing documentation that might reference the current OAuth implementation.

  4. Testing: Your test plan is detailed, but does it include any end-to-end testing with actual Linear credentials? If not, consider adding a note about how this would be validated in a real environment.

Overall, this is a well-implemented change with good forward planning for Linear's upcoming API changes. The migration path for existing tokens is especially valuable.

Copy link

netlify bot commented Sep 25, 2025

Deploy Preview for auto-gpt-docs-dev canceled.

Name Link
🔨 Latest commit 146f9b8
🔍 Latest deploy log https://app.netlify.com/projects/auto-gpt-docs-dev/deploys/68dffbc863db0a0008dec16c

@AutoGPT-Agent
Copy link

Thank you for this thorough PR implementing Linear's new OAuth refresh token system! The implementation and documentation are comprehensive and well-organized.

The changes look solid, with good handling for both new tokens and migration of existing long-lived tokens. I particularly appreciate the detailed documentation file explaining the implementation details and migration strategy.

One small issue: Your PR title doesn't follow our conventional commit format. It should include a type and scope, like:

fix(platform/blocks): update linear oauth to use refresh tokens

Or:

refactor(platform/blocks): update linear oauth to use refresh tokens

Please update the title to follow this format before merging. Everything else looks great!

@AutoGPT-Agent
Copy link

Thank you for this well-documented PR implementing the Linear OAuth refresh token update. Your detailed description and implementation look thorough and well-tested.

One thing to address:

  • The PR title needs to follow the conventional commit format. It should include a type and scope, such as: feat(platform/blocks): Update linear oauth to use refresh tokens

Your PR is otherwise well-structured with excellent documentation and clear implementation of the refresh token support. The new documentation file and code changes are comprehensive and thoughtfully address the migration path for existing tokens.

@ntindle
Copy link
Member Author

ntindle commented Sep 25, 2025

@claude does this do what needs to happen

Copy link
Contributor

claude bot commented Sep 25, 2025

Claude finished @ntindle's task —— View job


Reviewing Linear OAuth refresh token implementation...

Todo List:

  • Read and analyze the Linear OAuth implementation changes
  • Verify refresh token handling and HTTP Basic Auth implementation
  • Check token migration path for old long-lived tokens
  • Review token expiration logic and buffer handling
  • Validate error handling and edge cases
  • Provide comprehensive review feedback

Review Summary: ✅ Yes, this implementation does what needs to happen

Your PR successfully implements Linear's new OAuth refresh token system with excellent backward compatibility and migration support. The implementation meets all the requirements for the April 2026 deadline.

What the implementation does correctly:

1. Refresh Token Support

  • Properly implements refresh token flow with HTTP Basic Authentication (Linear's preferred method) - _oauth.py:192-200
  • Correctly handles both authorization code and refresh token grant types - _oauth.py:174-186
  • Properly extracts and stores token expiration times - _oauth.py:223-227

2. Token Migration Path

  • Implements the migrate_old_token() method using Linear's /oauth/migrate_old_token endpoint - _oauth.py:88-150
  • Automatic migration detection for old tokens (no expiration + no refresh token) - _oauth.py:285-294
  • Graceful fallback if migration fails, maintaining backward compatibility - _oauth.py:274-277

3. Token Lifecycle Management

  • Overrides get_access_token() to handle automatic migration when needed - _oauth.py:257-283
  • Proper expiration handling with the base class needs_refresh() check - _oauth.py:280-281
  • Preserves user context and credentials during refresh operations - _oauth.py:230-232

4. Error Handling

  • Enhanced error messages including both error and error_description fields - _oauth.py:117-121, 208-212
  • Custom LinearAPIException for better error tracking - _oauth.py:21-26
  • Proper error handling for JSON decode failures - _oauth.py:79-80, 123-124, 214

5. OAuth Flow Compliance

  • Correct OAuth URL generation with comma-separated scopes (Linear's requirement) - _oauth.py:54
  • Includes required response_type parameter - _oauth.py:53
  • Supports both HTTP Basic Auth and form parameters for flexibility - _oauth.py:192-200

Minor Observations (not issues):

  1. The implementation correctly preserves the existing credentials ID during migration and refresh - _oauth.py:149, 253
  2. Test credentials have been updated with realistic expiration times - _config.py:65-66
  3. Comprehensive documentation in LINEAR_OAUTH_IMPLEMENTATION.md explains the migration strategy clearly

Verification Checklist:

  • ✅ Implements refresh token support with proper authentication
  • ✅ Provides migration path for existing long-lived tokens
  • ✅ Handles token expiration and automatic refresh
  • ✅ Maintains backward compatibility
  • ✅ Includes proper error handling
  • ✅ Follows Linear's OAuth2 specifications

This is a well-architected implementation that proactively addresses Linear's upcoming changes while ensuring a smooth transition for existing users. The automatic migration approach is particularly thoughtful, as it will upgrade tokens transparently without requiring user intervention.


Copy link

qodo-merge-pro bot commented Sep 30, 2025

PR-Agent failed to apply 'global' repo settings

The configuration file needs to be a valid TOML, please fix it.


Error message:
'bool' object has no attribute 'items'

Configuration content:
[pr_description]
enable_help_text=false

[pr_reviewer]
enable_review_labels_effort = true
enable_auto_approval = false
maximal_review_effort = 3
enable_help_text=false
num_code_suggestions=0

[github_app]
pr_commands = [
    "/review --pr_reviewer.num_code_suggestions=0",
  ]

[pr_code_suggestions]
commitable_code_suggestions=false
num_code_suggestions=0
rank_suggestions=true
enable_help_text=false
suggestions_score_threshold=8

[checks] # /checks (pro feature) #
enable_auto_checks_feedback=false
persistent_comment=false
enable_help_text=false

Copy link

qodo-merge-pro bot commented Sep 30, 2025

PR-Agent failed to apply 'local' repo settings

The configuration file needs to be a valid TOML, please fix it.


Error message:
'bool' object has no attribute 'items'

Configuration content:
[pr_reviewer]
num_code_suggestions=0

[pr_code_suggestions]
commitable_code_suggestions=false
num_code_suggestions=0

Copy link

coderabbitai bot commented Sep 30, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cursor/SECRT-1665-update-linear-oauth-to-use-refresh-tokens-e0b5

Comment @coderabbitai help to get the list of available commands and usage tips.

@ntindle
Copy link
Member Author

ntindle commented Sep 30, 2025

@claude run the linters

Copy link
Contributor

claude bot commented Sep 30, 2025

Claude finished @ntindle's task —— View job


✅ Linters run successfully!

Todo List:

  • Check for repo-specific instructions (CLAUDE.md)
  • Identify available linters and their configuration
  • Set up the environment (install dependencies)
  • Run Python formatters (Black, isort, ruff) - ✅ Completed successfully
  • Run linting checks to identify any remaining issues
  • Fix formatting issues found in backend/blocks/linear/_oauth.py
  • Commit and push changes

Summary:

Successfully ran Python linters and formatters on the PR. The following formatting improvements were applied to backend/blocks/linear/_oauth.py:

  • Removed trailing whitespace
  • Improved line breaking for better readability
  • Fixed conditional expression formatting

All linting checks now pass! The changes have been committed and pushed to the branch.


View job |

github-actions bot and others added 5 commits September 30, 2025 17:10
Applied Black and isort formatting to clean up code style:
- Removed trailing whitespace
- Improved line breaking for better readability
- Fixed conditional expression formatting

Co-authored-by: Nicholas Tindle <[email protected]>
@AutoGPT-Agent
Copy link

Thanks for this comprehensive PR implementing Linear's new OAuth token refresh system! The implementation looks thorough and well-documented.

One issue to address:

  • Your PR title needs to follow the conventional commit format with type and scope. Since this is an enhancement to existing functionality, consider updating it to something like:
    feat(platform/blocks): Update linear oauth to use refresh tokens

The code changes themselves look good - I appreciate the thorough implementation of both the refresh flow and the migration path for existing tokens. The buffer for token expiration and the detailed error handling will help ensure a smooth user experience with this integration.

@ntindle ntindle marked this pull request as ready for review October 3, 2025 16:38
@ntindle ntindle requested a review from a team as a code owner October 3, 2025 16:38
@ntindle ntindle requested review from kcze and majdyz and removed request for a team October 3, 2025 16:38
@AutoGPT-Agent
Copy link

Thank you for submitting this PR to update Linear's OAuth implementation with refresh token support. The code changes are well-structured and thoroughly documented.

I have one required change before this can be merged:

  • The PR title needs to follow our conventional commit format. Please update it to include both type and scope, for example: fix(platform/blocks): Update linear oauth to use refresh tokens

Your PR description and changes are excellent - clearly explaining the need, the implementation details, and providing a thorough test plan. The code changes themselves look well-implemented with proper error handling and support for both old and new token types.

Once you update the PR title, this should be ready for approval.

@AutoGPT-Agent
Copy link

Thank you for this detailed PR implementing Linear's new OAuth token refresh system. The code changes look comprehensive and well-tested.

One item needs to be addressed before merging:

  • The PR title should follow conventional commit format with a type and scope. Based on the content, something like fix(platform/blocks): update linear oauth to use refresh tokens would be appropriate.

Your implementation looks solid, with good handling of token migration, refresh logic, and error cases. I especially like the approach for maintaining backward compatibility during the transition period.

Copy link

qodo-merge-pro bot commented Oct 3, 2025

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

In _request_tokens, the variable is_refresh is used later when constructing username and scopes, but it is not guaranteed to be in scope if control flow changes; also, scope parsing assumes comma-separated values while OAuth often returns space-delimited scopes. Verify both the lifetime of is_refresh and delimiter used by Linear.

# Extract token expiration if provided (for new refresh token implementation)
now = int(time.time())
expires_in = token_data.get("expires_in")
access_token_expires_at = None
if expires_in:
    access_token_expires_at = now + expires_in

# Get username - preserve from current credentials if refreshing
username = None
if current_credentials and is_refresh:
    username = current_credentials.username
elif "user" in token_data:
    username = token_data["user"].get("name", "Unknown User")
else:
    # Fetch username using the access token
    username = await self._request_username(token_data["access_token"])

new_credentials = OAuth2Credentials(
    provider=self.PROVIDER_NAME,
    title=current_credentials.title if current_credentials else None,
    username=username or "Unknown User",
    access_token=token_data["access_token"],
    scopes=(
        token_data["scope"].split(",")
        if "scope" in token_data
        else (current_credentials.scopes if current_credentials else [])
    ),
    refresh_token=token_data.get("refresh_token"),
Error Handling

migrate_old_token sets Content-Type to application/x-www-form-urlencoded but sends a dict without explicit encoding. Ensure Requests().post expects form-encoded data; otherwise encode via urlencode or pass the correct parameter to avoid content-type/body mismatch.

request_body = {
    "client_id": self.client_id,
    "client_secret": self.client_secret,
}

headers = {
    "Authorization": f"Bearer {credentials.access_token.get_secret_value()}",
    "Content-Type": "application/x-www-form-urlencoded",
}

response = await Requests().post(
    self.migrate_url, data=request_body, headers=headers
)
Credential Persistence

get_access_token may migrate tokens but only updates local variable; callers must persist migrated credentials. Confirm the calling code handles persistence, or provide a hook/callback to save updated credentials to avoid repeated migrations.

"""
Returns a valid access token, handling migration and refresh as needed.

This overrides the base implementation to handle Linear's token migration
from old long-lived tokens to new short-lived tokens with refresh tokens.
"""
# If token has no expiration and no refresh token, it might be an old token
# that needs migration
if (
    credentials.access_token_expires_at is None
    and credentials.refresh_token is None
):
    try:
        # Attempt to migrate the old token
        migrated_credentials = await self.migrate_old_token(credentials)
        # Update the credentials store would need to be handled by the caller
        # For now, use the migrated credentials for this request
        credentials = migrated_credentials
    except LinearAPIException:
        # Migration failed, try to use the old token as-is
        # This maintains backward compatibility
        pass

# Use the standard refresh logic from the base class
if self.needs_refresh(credentials):
    credentials = await self.refresh_tokens(credentials)

return credentials.access_token.get_secret_value()

@ntindle ntindle requested a review from Bentlybro October 3, 2025 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 🆕 Needs initial review
Development

Successfully merging this pull request may close these issues.

3 participants