calcinai/oauth2-xero
OAuth 2.0 provider for Xero built on the League OAuth2 Client. Supports the authorization code flow, scope configuration, fetching the authenticated user (OpenID) and retrieving authorized Xero tenants for making API requests.
Pros:
getTenants() method simplifies multi-org access.accounting.transactions, payroll.employees), enabling least-privilege access.openid/profile scopes) without custom endpoints, useful for user provisioning or SSO.Cons:
Laravel Compatibility:
$_SESSION).openid/profile to Laravel users via custom guards).XeroTokenRefreshed events for side effects).Dependencies:
league/oauth2-client (v1 or v2) and firebase/php-jwt (v5–7), both stable and Laravel-compatible.access_token, refresh_token, expires).Xero API Alignment:
Medium Risk Areas:
RefreshXeroToken job triggered by token expiration or manual user action.Low Risk Areas:
Authentication Flow:
Token Storage:
oauth_access_tokens table with user_id, tenant_id, token, expires_at).cache:forever for short-lived tokens).encrypt() for sensitive fields like refresh_token.Multi-Tenant Routing:
/xero/{tenantId}/invoices).tenant_id in the user model and inject it into API calls.Error Handling:
XeroAuthException handler with user-friendly messages.Performance Optimization:
Compliance and Auditing:
XeroTokenRefreshed events with metadata (user ID, tenant ID, scopes).Testing Strategy:
getAccessToken()).Laravel-Specific Components:
$this->app->singleton(XeroProvider::class, function ($app) {
return new \Calcinai\OAuth2\Client\Provider\Xero([
'clientId' => config('services.xero.client_id'),
'clientSecret' => config('services.xero.client_secret'),
'redirectUri' => config('services.xero.redirect_uri'),
]);
});
namespace App\Http\Middleware;
use Closure;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
class ValidateXeroToken
{
public function handle($request, Closure $next)
{
try {
$token = $request->user()->xeroToken;
$provider = app(XeroProvider::class);
$provider->getResourceOwner($token);
} catch (IdentityProviderException $e) {
return redirect()->route('xero.reconnect');
}
return $next($request);
}
}
event(new XeroTokenRefreshed($user, $newToken));
php artisan xero:auth --tenant=123
Xero facade for cleaner syntax.
use App\Facades\Xero;
$tenants = Xero::getTenants();
Third-Party Tools:
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.xero.com/api.xro/2.0/Invoices', [
'headers' => [
'Authorization' => 'Bearer ' . $token->getToken(),
],
]);
RefreshXeroToken::dispatch($user)->delay(now()->addMinutes(30
How can I help you explore Laravel packages today?