PHPackages                             labrodev/laravel-linear - 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. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. labrodev/laravel-linear

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

labrodev/laravel-linear
=======================

Stateless Linear GraphQL and OAuth 2 client for Laravel.

v1.0.0(yesterday)01↑2900%MITPHP ^8.4

Since Jul 19Compare

[ Source](https://github.com/labrodev/laravel-linear)[ Packagist](https://packagist.org/packages/labrodev/laravel-linear)[ Docs](https://github.com/labrodev/laravel-linear)[ RSS](/packages/labrodev-laravel-linear/feed)WikiDiscussions Synced today

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

Laravel Linear
==============

[](#laravel-linear)

A stateless [Linear](https://linear.app) GraphQL + OAuth 2 client for Laravel.

The package holds no tokens and no state of its own. Your application stores the token set wherever it wants (encrypted columns, a vault, ...) and passes the access token into every call. Linear access tokens expire after roughly 24 hours, and refresh tokens **rotate on every refresh** — always persist the full pair returned by `refresh()`.

Requirements
------------

[](#requirements)

- PHP 8.4+
- Laravel 13+

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

[](#installation)

```
composer require labrodev/laravel-linear
```

Publish the config file if you want to customize it:

```
php artisan vendor:publish --tag=linear-config
```

Add your Linear OAuth application credentials to `.env`:

```
LINEAR_CLIENT_ID=your-client-id
LINEAR_CLIENT_SECRET=your-client-secret
LINEAR_REDIRECT_URI=https://your-app.test/linear/callback
```

Create the OAuth application at Linear under `Settings -> API -> OAuth applications`, using the same callback URL.

OAuth flow
----------

[](#oauth-flow)

`Labrodev\Linear\LinearOAuthClient` implements the full authorization-code flow against Linear (`https://linear.app/oauth/authorize` and `https://api.linear.app/oauth/token`).

### 1. Redirect the user to Linear

[](#1-redirect-the-user-to-linear)

```
use Labrodev\Linear\LinearOAuthClient;

$state = bin2hex(random_bytes(16));

return redirect()->away(
    app(LinearOAuthClient::class)->authorizeUrl(state: $state),
);
```

The URL requests the `read,write` scope, `actor=user` and `prompt=consent`.

### 2. Exchange the callback code

[](#2-exchange-the-callback-code)

```
$linearTokenSet = app(LinearOAuthClient::class)->exchangeCode(
    code: $request->string('code')->toString(),
);

// Persist all three values:
$linearTokenSet->accessToken;   // string, valid ~24h
$linearTokenSet->refreshToken;  // string, single use — rotates on refresh
$linearTokenSet->expiresAt;     // CarbonImmutable
```

### 3. Refresh when the access token expires

[](#3-refresh-when-the-access-token-expires)

```
use Labrodev\Linear\Exceptions\LinearInvalidGrant;

try {
    $linearTokenSet = app(LinearOAuthClient::class)->refresh(
        refreshToken: $storedRefreshToken,
    );

    // Store the ROTATED pair — the old refresh token is now dead.
} catch (LinearInvalidGrant) {
    // The refresh token is no longer valid: the user must re-authorize.
}
```

### 4. Revoke on disconnect

[](#4-revoke-on-disconnect)

```
app(LinearOAuthClient::class)->revoke(accessToken: $storedAccessToken);
```

GraphQL client
--------------

[](#graphql-client)

`Labrodev\Linear\LinearClient` talks to `https://api.linear.app/graphql`with the access token you pass in.

```
use Labrodev\Linear\LinearClient;

$linearClient = app(LinearClient::class);

// List teams — returns list (id, name, key)
$teams = $linearClient->teams(accessToken: $accessToken);

// Create an issue — returns the Linear issue id
$issueId = $linearClient->issueCreate(
    accessToken: $accessToken,
    teamId: $teams[0]->id,
    title: 'Monitor down: example.com',
    description: 'The monitor stopped responding at 12:00 UTC.',
    priority: 1,
);

// Close an issue — moves it to the team's "completed" workflow state.
// Silent no-op when the team has no completed state.
$linearClient->issueClose(accessToken: $accessToken, issueId: $issueId);
```

Exceptions
----------

[](#exceptions)

All exceptions carry fixed messages — tokens, codes and response bodies are never interpolated, so they are safe to log.

`LinearOAuthFailed` - Missing client config, or a failed exchange/refresh/revoke call. `LinearInvalidGrant` - The refresh token is dead; the user must re-authorize. `LinearAuthFailed` - Linear rejected the access token on a GraphQL call. `LinearRequestFailed` - A GraphQL call failed for a non-authentication reason.

Testing
-------

[](#testing)

```
composer test      # pest
composer phpstan   # larastan, level 7
composer pint      # laravel preset
composer rector    # laravel preset
composer check     # all of the above
```

License
-------

[](#license)

MIT - see [LICENSE.md](LICENSE.md).

Credits
-------

[](#credits)

Petro Lashyn,

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

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

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/151143718?v=4)[Lashyn Petro ](/maintainers/labrodev)[@labrodev](https://github.com/labrodev)

---

Tags

laravelgraphqloauthapi clientlinearlabrodev

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/labrodev-laravel-linear/health.svg)

```
[![Health](https://phpackages.com/badges/labrodev-laravel-linear/health.svg)](https://phpackages.com/packages/labrodev-laravel-linear)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k108.5M922](/packages/laravel-socialite)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

813336.8k3](/packages/defstudio-telegraph)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[spatie/laravel-export

Create a static site bundle from a Laravel app

674146.0k6](/packages/spatie-laravel-export)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)

PHPackages © 2026

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