Skip to content

fix(auth): repair PAT login by validating token with asana@3 SDK#31

Merged
amondnet merged 3 commits into
mainfrom
fix/pat-login-v3-sdk
Jun 19, 2026
Merged

fix(auth): repair PAT login by validating token with asana@3 SDK#31
amondnet merged 3 commits into
mainfrom
fix/pat-login-v3-sdk

Conversation

@amondnet

@amondnet amondnet commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Problem

asana auth login --token <pat> is broken. It calls the v1 helper
asana.Client.create().useAccessToken() (src/commands/auth.ts), which was
removed in asana@3 — the installed SDK. Running it throws:

✗ Authentication failed:
undefined is not an object (evaluating 'import_asana2.default.Client.create')

The rest of the CLI already authenticates correctly via the v3 SDK in
getAsanaClient() (ApiClient.instance + per-API classes); only the PAT
login validation step still used the old API.

Fix

  • Validate the token with the v3 SDK before persisting it:
    set asana.ApiClient.instance.authentications.token.accessToken and call
    new asana.UsersApi().getUser('me', {}).
  • resetClient() after saving so the new credential is picked up.

Test

Adds test/commands/auth-login-pat.test.ts — a regression test that mocks the
asana SDK and the config module (no disk, no network) and asserts the PAT login
path validates the token and saves a { authType: 'pat' } credential. Fails
against the old Client.create() code, passes with the fix.

Notes

  • Scope limited to the PAT login path; OAuth flow is unchanged.
  • Verified bun run lint and the new test pass locally.

Summary by cubic

Fixes PAT login by validating tokens with the asana v3 SDK instead of the removed v1 helper. Prevents the runtime error and ensures the saved PAT is used right away.

  • Bug Fixes

    • Validate the token with asana@3 by setting ApiClient.instance.authentications.token.accessToken, guarding when the token auth scheme is missing, and calling new asana.UsersApi().getUser('me', {}); then resetClient() after saving so the new PAT is used.
  • Tests

    • Add a regression test that mocks asana and config, and skip E2E suites when ASANA_ACCESS_TOKEN/ASANA_WORKSPACE are missing via a hasE2ECredentials helper.

Written for commit ef4e16f. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved personal access token handling in the auth login command by validating the token before saving credentials, with extra safeguards for token-auth initialization.
  • Tests

    • Added a regression test covering auth login --token to ensure token validation occurs and the expected authentication settings are persisted.
    • Updated E2E suites to automatically skip when required live credentials are not available.

`asana auth login --token <pat>` called the removed v1 helper
`asana.Client.create().useAccessToken()`, which throws
"undefined is not an object (evaluating 'Client.create')" on asana@3.
Validate the token through the v3 SDK (`ApiClient.instance` + `UsersApi`)
before persisting it, matching how `getAsanaClient()` already authenticates,
and reset the cached client so the new credential is picked up.

Adds a regression test that mocks the SDK and config (no disk/network) to
verify the PAT path validates the token and saves a `pat` credential.
@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
asana Ready Ready Preview, Comment Jun 19, 2026 8:30am

Request Review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 76498295-782a-4ed2-8c75-b2a9ddccf473

📥 Commits

Reviewing files that changed from the base of the PR and between 2b3327c and ef4e16f.

📒 Files selected for processing (7)
  • tests/e2e/advanced-features.test.ts
  • tests/e2e/asana-api.test.ts
  • tests/e2e/cli-commands.test.ts
  • tests/e2e/collaboration.test.ts
  • tests/e2e/helpers.ts
  • tests/e2e/subtasks-dependencies.test.ts
  • tests/e2e/team-workspace-user.test.ts

📝 Walkthrough

Walkthrough

The PR contains two independent changes: (1) PAT token validation in auth login is updated to use Asana SDK v3's ApiClient.instance and UsersApi().getUser pattern, with an explicit resetClient() call added post-save, along with a new regression test; and (2) all E2E test suites are gated to conditionally skip when live Asana credentials are unavailable, using a new hasE2ECredentials helper.

Changes

PAT Auth SDK v3 Migration

Layer / File(s) Summary
PAT validation and resetClient wiring
src/commands/auth.ts, test/commands/auth-login-pat.test.ts
auth login --token now sets ApiClient.instance.authentications.token.accessToken and calls UsersApi().getUser('me', {}) for validation, then saves config and explicitly calls resetClient(). The new regression test mocks the Asana SDK and saveConfig to assert the PAT is used during API validation and that the persisted config contains accessToken and authType: 'pat'.

E2E Test Credential Gating

Layer / File(s) Summary
E2E credential availability helper
tests/e2e/helpers.ts
Exports hasE2ECredentials boolean derived from ASANA_ACCESS_TOKEN and ASANA_WORKSPACE environment variables, enabling test suites to determine whether live credentials are available.
E2E test suite conditional skip integration
tests/e2e/advanced-features.test.ts, tests/e2e/asana-api.test.ts, tests/e2e/cli-commands.test.ts, tests/e2e/collaboration.test.ts, tests/e2e/subtasks-dependencies.test.ts, tests/e2e/team-workspace-user.test.ts
All E2E test suites now import hasE2ECredentials and wrap their top-level describe blocks with describe.skipIf(!hasE2ECredentials), causing entire suites to skip when credentials are unavailable instead of hard-failing.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐇 Hop hop, the old SDK fell away,
A new ApiClient hops in to stay.
The token is set with a flick of a paw,
getUser('me') — no bug, no flaw!
E2E tests skip when credentials sleep,
This rabbit ships clean code so deep. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing PAT login by validating tokens with the asana@3 SDK, which directly corresponds to the changeset's core objective.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pat-login-v3-sdk

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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Personal Access Token (PAT) authentication path in the CLI to use the Asana v3 SDK instead of the deprecated v1 helper, and introduces a regression test to verify this behavior. The reviewer recommended adding a defensive null check on the API client's token authentication object to prevent potential runtime errors during token assignment.

Comment thread src/commands/auth.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/commands/auth-login-pat.test.ts (1)

17-55: ⚡ Quick win

Strengthen this regression by asserting resetClient() and getUser('me').

This test proves persistence, but it doesn’t currently lock the two key contract points introduced by this PR (resetClient call and 'me' target argument).

💡 Suggested test hardening
 test('validates the token via the v3 SDK and saves a PAT config', async () => {
   let tokenSeenByApi: string | undefined
+  let userGidSeen: string | undefined
   const tokenAuth: { accessToken?: string } = {}

   // Stub the asana SDK: capture the token the UsersApi call would use.
   mock.module('asana', () => ({
     default: {
       ApiClient: { instance: { authentications: { token: tokenAuth } } },
       UsersApi: class {
-        async getUser() {
+        async getUser(userGid: string) {
+          userGidSeen = userGid
           tokenSeenByApi = tokenAuth.accessToken
           return { data: { gid: '1', name: 'Test User', email: 'test@example.com' } }
         }
       },
     },
   }))
+
+  const resetClientMock = mock(() => {})
+  mock.module('../../src/lib/asana-client', () => ({
+    getAsanaClient: () => ({ users: { me: async () => ({}) } }),
+    resetClient: resetClientMock,
+  }))

   // Capture saveConfig instead of writing to ~/.asana-cli.
   let savedConfig: any = null
   mock.module('../../src/lib/config', () => ({
@@
   await auth.parseAsync(['login', '--token', 'pat-secret-123'], { from: 'user' })

   // The token was validated against the API before being persisted...
   expect(tokenSeenByApi).toBe('pat-secret-123')
+  expect(userGidSeen).toBe('me')
+  expect(resetClientMock).toHaveBeenCalledTimes(1)
   // ...and stored as a PAT credential.
   expect(savedConfig).toMatchObject({
     accessToken: 'pat-secret-123',
     authType: 'pat',
   })
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/commands/auth-login-pat.test.ts` around lines 17 - 55, The test needs to
be strengthened to verify that resetClient() is called and that getUser() is
called with the 'me' argument. In the mock setup for ApiClient, add tracking to
capture when resetClient() is called. In the mock UsersApi class's getUser
method, capture the argument passed to verify it is 'me'. Then add assertions
after the existing expect statements to verify both that resetClient was called
and that getUser was called with the 'me' argument as the target.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/commands/auth-login-pat.test.ts`:
- Around line 17-55: The test needs to be strengthened to verify that
resetClient() is called and that getUser() is called with the 'me' argument. In
the mock setup for ApiClient, add tracking to capture when resetClient() is
called. In the mock UsersApi class's getUser method, capture the argument passed
to verify it is 'me'. Then add assertions after the existing expect statements
to verify both that resetClient was called and that getUser was called with the
'me' argument as the target.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d5d8c2b-b6c3-4f69-ab75-f7aa488441f4

📥 Commits

Reviewing files that changed from the base of the PR and between 9204df6 and 79302d1.

📒 Files selected for processing (2)
  • src/commands/auth.ts
  • test/commands/auth-login-pat.test.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Architecture diagram
sequenceDiagram
    participant CLI as CLI Runner
    participant AuthCmd as auth login Command
    participant ApiClient as asana ApiClient
    participant UsersApi as asana UsersApi
    participant Config as Config Module
    participant Env as User Environment

    Note over CLI,Env: PAT Login Flow

    CLI->>AuthCmd: parse(['login', '--token', 'pat-secret-123'])
    AuthCmd->>AuthCmd: Check for --token flag
    alt PAT authentication
        AuthCmd->>AuthCmd: Display "Authenticating with Personal Access Token..."
        
        AuthCmd->>ApiClient: Set accessToken on ApiClient.instance.authentications.token
        AuthCmd->>UsersApi: new UsersApi().getUser('me', {})
        UsersApi->>ApiClient: Use stored token for auth header
        ApiClient->>Asana API: GET /users/me (validate token)
        Asana API-->>ApiClient: User data (or 401)
        alt Token valid
            ApiClient-->>UsersApi: User object
            UsersApi-->>AuthCmd: { data: { gid, name, email } }
            
            AuthCmd->>Config: saveConfig({ accessToken, authType: 'pat', workspace })
            Config->>Env: Write to ~/.asana-cli config file
            Config-->>AuthCmd: Config saved
            
            AuthCmd->>AuthCmd: resetClient() - force re-init with new creds
            AuthCmd->>AuthCmd: Display "Successfully authenticated with PAT"
        else Token invalid
            ApiClient-->>UsersApi: 401 error
            UsersApi-->>AuthCmd: Error thrown
            AuthCmd->>AuthCmd: Display authentication failure message
            AuthCmd->>AuthCmd: Return with error
        end
    else OAuth flow (unchanged)
        AuthCmd->>AuthCmd: Proceed with existing OAuth logic
    end
    
    AuthCmd-->>CLI: Command result (success/failure)
Loading

Re-trigger cubic

@amondnet amondnet self-assigned this Jun 19, 2026
@amondnet
amondnet merged commit 97d7640 into main Jun 19, 2026
4 of 7 checks passed
@amondnet
amondnet deleted the fix/pat-login-v3-sdk branch July 6, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants