PHPackages                             brightleaf-digital/asana-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. brightleaf-digital/asana-client

ActiveLibrary[API Development](/categories/api)

brightleaf-digital/asana-client
===============================

A PHP client library for the Asana API

v0.2.0(2mo ago)241191↓50%235MITPHPPHP ^7.4 || ^8.0CI passing

Since Apr 3Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/BrightLeaf-Digital/asana-client)[ Packagist](https://packagist.org/packages/brightleaf-digital/asana-client)[ RSS](/packages/brightleaf-digital-asana-client/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (9)Versions (10)Used By (0)

Brightleaf Digital Asana API Client for PHP
===========================================

[](#brightleaf-digital-asana-api-client-for-php)

[![CI](https://github.com/Eitan-brightleaf/asana-client/actions/workflows/ci.yml/badge.svg)](https://github.com/Eitan-brightleaf/asana-client/actions/workflows/ci.yml)[![semgrep (php)](https://github.com/Eitan-brightleaf/asana-client/actions/workflows/semgrep.yml/badge.svg)](https://github.com/Eitan-brightleaf/asana-client/actions/workflows/semgrep.yml)

A modern, maintained PHP client library for the Asana API.

Common development commands
---------------------------

[](#common-development-commands)

- Lint: `composer lint`
- Auto-fix: `composer lint:fix`
- Test: `composer test`
- Coverage (outputs to build/logs): `composer test:coverage`

Motivation
----------

[](#motivation)

This library was created because the official Asana PHP library is no longer maintained, is outdated, and uses a library with a known security vulnerability. After searching for alternatives, I couldn't find any third-party libraries that appeared to be actively maintained.

Status
------

[](#status)

This library is currently pre‑v1. Breaking changes may occur between releases while we finalize the public API and internal architecture.

### Breaking Changes (v0.x → v0.y)

[](#breaking-changes-v0x--v0y)

As we approach v1.0, several foundational changes have been made:

- **Constructor Changes**: `AsanaClient` now requires a `Psr\Container\ContainerInterface`. Use the static factories (`withPAT`, `OAuth`, `withAccessToken`) for the simplest setup.
- **Dependency Injection**: The library now uses a service container. You can replace core components (HTTP client, Auth handler, Token storage) by providing your own container.
- **Token Management**: Direct methods like `saveToken`, `loadToken`, and `onTokenRefresh` have been removed. Use `TokenManager` (available via the container) or `subscribeToTokenRefresh()` on the client.
- **Response Constants**: `AsanaApiClient::RESPONSE_*` constants have moved to `HttpClientInterface::RESPONSE_*`.
- **Exceptions**: `AsanaApiException` has been renamed to `ApiException`.
- **Static Factories**: `withPersonalAccessToken` has been renamed to `withPAT`.
- **Webhook Handshake**: `WebhooksApiService::handleHandshake` (and `AsanaClient::handleHandshake`) signature changed to remove redundant arguments.

This is my first library of this kind, and I am still developing my skills as a junior developer. Any reviews, comments, contributions, or suggestions are highly welcome - especially since my only peer review so far has been from AI. I would particularly appreciate help with:

- Writing tests
- Reviewing documentation
- Identifying improvements

OAuth Scopes
------------

[](#oauth-scopes)

This library now supports Asana's new OAuth permission scopes. These scopes provide more granular control over what actions an app can perform following the principle of least privilege and should enhance user trust and so increase app adoption.

### Important Notes:

[](#important-notes)

- **Incomplete Rollout**: Asana has not yet introduced scopes for all API endpoints. More scopes will be added in the future and incorporated into this library.
- **Backward Compatibility**: Existing apps can continue to use the `default` scope (full access) for now, and new apps can still toggle this option on in the app settings.
- **Getting Full Access With This Library**:
    - When creating your app in Asana, request full access permissions
    - When generating an authorization URL, pass an empty array for scopes: `$authUrl = $asanaClient->getAuthorizationUrl([]);`
- **Helper `Scopes` Class**: To simplify things for developers using this library, I've created a helper `Scopes` class containing constants with available scopes. As noted below the library may not cover all API endpoints so some scopes in helper class may correspond to an endpoint without support. Contributions and help expanding the library to have full API coverage is welcome!

For more information about the new OAuth scopes and implementation details, refer to [Asana's announcement](https://forum.asana.com/t/new-oauth-permission-scopes/1048556/1) and its linked documentation.

Features
--------

[](#features)

- Modern PHP implementation
- Supports both OAuth 2.0 and Personal Access Tokens (PATs)
- Easy-to-use API that follows consistent patterns
- Fixes common pain points from the official Asana library
- Actively maintained
- Includes built-in token encryption utilities (`CryptoUtils::encrypt` and `CryptoUtils::decrypt`) to help secure sensitive token fields during storage. **Meant to be used only in local dev environments. Please use production grade security libraries in production.**
- Includes built-in support for automatic token refreshes, with customizable `onTokenRefresh` events to keep persisted storage up to date.

API Coverage
------------

[](#api-coverage)

This library provides comprehensive coverage of the Asana API, including:

### Core Resources

[](#core-resources)

- **Tasks** - Create, read, update, delete tasks and manage task relationships
- **Projects** - Manage projects, sections, and project memberships
- **Users** - Get user information and manage user settings
- **Workspaces** - Access workspace information and settings
- **Tags** - Create and manage tags for organizing tasks
- **Attachments** - Upload and manage file attachments
- **Custom Fields** - Work with custom fields and their settings
- **Sections** - Organize tasks within projects using sections
- **Memberships** - Manage project and workspace memberships

### Advanced Features

[](#advanced-features)

- **Webhooks** - Set up real-time notifications for resource changes
- **Events** - Poll for events and track changes to resources
- **Teams** - Manage teams and team memberships
- **Portfolios** - Create and manage project portfolios
- **Goals** - Track organizational goals and objectives
- **Time Tracking** - Record and manage time entries on tasks
- **Project Templates** - Create projects from templates
- **Batch API** - Execute multiple API requests in a single call
- **Status Updates** - Post and retrieve project status updates
- **User Task Lists** - Access "My Tasks" and personal task lists

### Quick Examples

[](#quick-examples)

```
// Webhooks - Real-time notifications
$webhook = $client->webhooks()->createWebhook([
    'resource' => $projectGid,
    'target' => 'https://example.com/webhook'
]);

// Events - Poll for changes
$events = $client->events()->getEvents($resourceGid);

// Teams - Manage teams
$teams = $client->teams()->getTeams($workspaceGid);
$team = $client->teams()->createTeam(['name' => 'Engineering', 'workspace' => $workspaceGid]);

// Portfolios - Organize projects
$portfolios = $client->portfolios()->getPortfolios($workspaceGid);
$portfolio = $client->portfolios()->createPortfolio(['name' => 'Q1 Projects', 'workspace' => $workspaceGid]);

// Goals - Track objectives
$goals = $client->goals()->getGoals(['workspace' => $workspaceGid]);
$goal = $client->goals()->createGoal(['name' => 'Increase Revenue', 'workspace' => $workspaceGid]);

// Time Tracking - Log time entries
$entry = $client->timeTrackingEntries()->createTimeTrackingEntry([
    'task' => $taskGid,
    'duration_minutes' => 120
]);

// Project Templates - Standardize workflows
$templates = $client->projectTemplates()->getProjectTemplates($workspaceGid);
$project = $client->projectTemplates()->instantiateProject($templateGid, ['name' => 'New Project']);

// Batch API - Optimize multiple requests
$responses = $client->batch()->submitBatch([
    ['method' => 'GET', 'relative_path' => '/tasks/12345'],
    ['method' => 'GET', 'relative_path' => '/projects/67890']
]);

// Status Updates - Project communication
$statusUpdate = $client->statusUpdates()->createStatusUpdate([
    'parent' => $projectGid,
    'text' => 'Project is on track',
    'status_type' => 'on_track'
]);

// User Task Lists - Personal task management
$myTasks = $client->userTaskLists()->getUserTaskList($userTaskListGid);
```

Design Decisions
----------------

[](#design-decisions)

- When a field is required by an Asana API endpoint (such as a workspace GID), it's typically required as a method argument
- Some exceptions exist where it made more sense to let users include required fields in the data array (for example, in `createTask()` where users need to provide several fields anyway, and might use a workspace GID or project GID)
- Consistent return patterns to make working with responses predictable
- Focus on developer experience and ease of use

Installation
------------

[](#installation)

```
composer require brightleafdigital/asana-client
```

then use Composer's autoload:

```
require __DIR__.'/vendor/autoload.php';
```

Basic Usage
-----------

[](#basic-usage)

To get started you need an Asana app configured with a proper redirect URL. You get the client ID and secret from the app. Remember to store them securely! Please read the [official documentation](https://developers.asana.com/docs/overview#authentication-basics) if you aren't sure how to set up an app.

### Using Personal Access Token (PAT)

[](#using-personal-access-token-pat)

```
use BrightleafDigital\AsanaClient;

$personalAccessToken = 'your-personal-access-token';
$client = AsanaClient::withPAT($personalAccessToken);

// Get user information
$me = $client->users()->getCurrentUser();

// Create a task
$taskData = [
    'name' => 'My new task',
    'notes' => 'Task description',
    'projects' => ['12345678901234'] // Project GID
];
$task = $client->tasks()->createTask($taskData);
```

### Using OAuth 2.0

[](#using-oauth-20)

```
use BrightleafDigital\AsanaClient;
use BrightleafDigital\Auth\Scopes;

$clientId = 'your-client-id';
$clientSecret = 'your-client-secret';
$redirectUri = 'https://your-app.com/callback';

// Bootstrap with defaults (uses an internal container and file token storage)
$client = AsanaClient::OAuth($clientId, $clientSecret, $redirectUri, __DIR__ . '/token.json');

// Request specific scopes and get URL + state + PKCE verifier
$auth = $client->getSecureAuthorizationUrl([
    Scopes::TASKS_READ,
    Scopes::PROJECTS_READ,
    Scopes::USERS_READ
]);

// Store $auth['state'] and $auth['codeVerifier'] in the session, then redirect to $auth['url']

// In your callback handler:
$code = $_GET['code'];
$pkceVerifier = $_SESSION['oauth2_pkce_verifier'] ?? null;
$client->handleCallback($code, $pkceVerifier); // Token is saved automatically via TokenManager

// Then use the client
$me = $client->users()->getCurrentUser();
```

### Token Management and Storage Options

[](#token-management-and-storage-options)

The `handleCallback()` method returns an array that contains the token itself, which expires in an hour; the timestamp of expiry; a refresh token you can use to get a new access token; and some additional metadata.

This library provides flexibility in how you manage and store tokens. By default, the `saveToken`, `loadToken` and `retrieveToken` methods offer a simple way for beginners to securely save tokens for future use. However, advanced users have full control over token handling and can store their tokens wherever and however they see fit.

#### Built-In Token Storage

[](#built-in-token-storage)

> ⚠️ **IMPORTANT SECURITY WARNING**: The `CryptoUtils` class are designed for **local development environments only** and are not recommended for production use. For production applications, please use a vetted security library or a secure credential management service.

The library provides several methods to manage and persist OAuth tokens. These methods are useful for developers looking for a quick and simple way to handle token storage without having to implement custom logic from scratch. They are intended for development settings to provide developers with an easy way to store tokens and explore the library. In production environments more secure methods should be used.

1. **`saveToken`**: Encrypts and stores the current token securely to file storage (default: `token.json` in the working directory). This ensures sensitive fields like `access_token` and `refresh_token` are safely stored in encrypted form.
2. **`loadToken`**: Reads the encrypted token from storage, decrypts it, and initializes the client for further use. If no token is available or decryption fails, the process gracefully returns with a failure.
3. **`retrieveToken`**: Similar to `loadToken`, this static method provides a convenient way to securely load and decrypt a stored token **outside the context of an instantiated client**.

> The password you supply to the CryptoUtils::encrypt and decrypt methods should be a regular string password or passphrase, not a pre-generated encryption key or binary blob. You can use a strong passphrase (e.g., 'my-long-dev-password') or store a more complex string (like one from a password manager) in your .env file. This value is run through PBKDF2 key derivation with a salt and never used directly as a raw encryption key.

The library's default methods use encryption to protect sensitive fields during storage, ensuring that tokens are not left exposed in plaintext. Developers still need to safeguard passwords and token files to maintain security.

##### **Automatic Token Refresh Support**

[](#automatic-token-refresh-support)

One major improvement in the library is the ability to automatically handle token refreshes and trigger callbacks when a token is refreshed. This ensures that tokens remain valid without manual intervention, and any changes to the token (after refreshing) are propagated to persistent storage.

```
use BrightleafDigital\AsanaClient;

$salt = 'your-secure-salt';

// Initialize the client with a stored token
$asanaClient = AsanaClient::withAccessToken('client-id', 'client-secret', AsanaClient::retrieveToken($salt));

// Subscribe to the 'token refreshed' event
$asanaClient->onTokenRefresh(function (array $token) use ($asanaClient, $salt) {
    // Save the refreshed token securely
    $asanaClient->saveToken($salt);

    // Optional: Log or process the refreshed token
    echo "Token refreshed successfully!";
});

// Example API call that triggers a token refresh if the token is expired
$userInfo = $asanaClient->users()->me();
```

- The library **automatically refreshes tokens** when they expire, ensuring uninterrupted API access.
- Developers can subscribe to the **token refresh event** by registering a callback through the `onTokenRefresh` method.
- The callback receives the refreshed token data as a parameter, allowing developers to persist or process the updated token as needed.
- You can still refresh the token manually if ever required with the `refreshToken` method.

#### **Flexible Token Handling for Advanced Users**

[](#flexible-token-handling-for-advanced-users)

While the library provides the `saveToken`, `loadToken`, and `retrieveToken` methods for built-in token handling, advanced users can (and should) bypass these methods entirely and manage tokens themselves.

1. Retrieve tokens directly using `$client->getAccessToken()` or upon refresh with `$asanaClient->onTokenRefresh()`.
2. Encrypt tokens using production grade libraries.
3. Store tokens using external methods or third-party services (e.g., databases, cloud secrets management services, etc.).

```
use BrightleafDigital\AsanaClient;
use BrightleafDigital\Utils\CryptoUtils;

// Retrieve the access token for custom handling
$tokenArray = $asanaClient->getAccessToken();

// Encrypt the token manually
$password = 'your-password';
$encryptedToken = CryptoUtils::encrypt(json_encode($tokenArray), $password); // or just encrypt $tokenArray['access_token'] and $tokenArray['refresh_token']

// Store the encrypted token in a database or a secure location
storeTokenInDatabase($encryptedToken);

// Later: Load and decrypt the token
$storedToken = retrieveTokenFromDatabase();
$tokenData = json_decode(CryptoUtils::decrypt($storedToken, $password), true);

// Initialize the client with the decrypted token
$asanaClient = AsanaClient::withAccessToken('client-id', 'client-secret', $tokenData);
```

#### **Security Best Practices**

[](#security-best-practices)

When using token storage methods:

1. The token storage file (`token.json` by default) should have restricted access permissions (e.g., `chmod 600`).
2. If possible, store sensitive credential files (like `token.json`) in secure locations outside your project directory or source control.
3. While your password is meant to be a human-memorable password or passphrase, not a random key or binary blob you still should keep it safe as an environment variable outside version control.

#### **Summary of Token Management Methods**

[](#summary-of-token-management-methods)

MethodDescriptionPrimary Use Case`saveToken`Encrypts and saves the current token to a file.Beginner-friendly token storage.`loadToken`Decrypts and loads the token from storage into the client.Quick token initialization.`retrieveToken`Static utility to securely load and decrypt tokens for external use.Advanced workflows requiring raw tokens.`onTokenRefresh`Register a callback to handle token updates after an automatic refresh.Keeping persistent storage up-to-date.`getAccessToken`Directly retrieves the current token in its raw array format for manual handling or storage.Custom storage workflows.If `loadToken()` or `retrieveToken()` fails (e.g., corrupt/missing token file, incorrect password), they return `false`or throw an exception. Use this behavior to handle missing tokens gracefully and re-run your OAuth flows if needed.

### Examples

[](#examples)

More examples are available in the `examples` folder, including:

- OAuth flow setup with PKCE and state validation
- OAuth flow without additional security measures
- Using Personal Access Tokens
- Basic API usage examples
- All examples can be run directly in a browser

Documentation Gaps
------------------

[](#documentation-gaps)

If you find something that isn't clear from either this library's documentation or the official Asana API documentation, the Asana developer forum is a valuable resource. There are often details or workarounds discussed there that aren't covered in the official documentation.

For example, creating a task in a specific section isn't documented in the API reference but can be found in forum discussions. If you discover such gaps:

1. Check the [Asana Developer Forum](https://forum.asana.com/c/developers/13)
2. Open an issue in this repository
3. Feel free to link to relevant forum or Stack Overflow posts

📘 Project Planning and Improvements
-----------------------------------

[](#-project-planning-and-improvements)

This library is actively developed with long-term maintainability in mind.
For design decisions, planned features, and deferred items, see the following documentation:

- [Code Architecture Improvements](./docs/tasks/code-architecture-improvements.md)
- [Feature Additions](./docs/tasks/feature-additions.md)
- [Performance Improvements](./docs/tasks/performance-improvements.md)
- [Testing Improvements](./docs/tasks/testing-improvements.md)
- See [Task Summary &amp; Prioritization](docs/tasks/roadmap.md) for a categorized and ranked view of all planned improvements.

Have an idea or want to help implement one of these? Open a [GitHub issue](https://github.com/Eitan-brightleaf/asana-client/issues/new) or submit a pull request.

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance84

Actively maintained with recent releases

Popularity34

Limited adoption so far

Community20

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 82.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~46 days

Recently: every ~75 days

Total

8

Last Release

81d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/54669e3e00af483851b63cd24af55ea1488f17b581ea0064b68473fdcb11b20e?d=identicon)[eitan@brightleaf](/maintainers/eitan@brightleaf)

---

Top Contributors

[![Eitan-brightleaf](https://avatars.githubusercontent.com/u/160501761?v=4)](https://github.com/Eitan-brightleaf "Eitan-brightleaf (193 commits)")[![MarketingByAli](https://avatars.githubusercontent.com/u/221856680?v=4)](https://github.com/MarketingByAli "MarketingByAli (31 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (8 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (3 commits)")

---

Tags

asanaasana-apioauth2php-library

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/brightleaf-digital-asana-client/health.svg)

```
[![Health](https://phpackages.com/badges/brightleaf-digital-asana-client/health.svg)](https://phpackages.com/packages/brightleaf-digital-asana-client)
```

###  Alternatives

[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M651](/packages/sylius-sylius)[irazasyed/telegram-bot-sdk

The Unofficial Telegram Bot API PHP SDK

3.3k4.5M84](/packages/irazasyed-telegram-bot-sdk)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

728272.9k20](/packages/civicrm-civicrm-core)[theodo-group/llphant

LLPhant is a library to help you build Generative AI applications.

1.5k311.5k5](/packages/theodo-group-llphant)[tempest/framework

The PHP framework that gets out of your way.

2.1k23.1k9](/packages/tempest-framework)[avalara/avataxclient

Client library for Avalara's AvaTax suite of business tax calculation and processing services. Uses the REST v2 API.

517.9M7](/packages/avalara-avataxclient)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
