gusmanwidodo/auth-kit-organization

Organization (multi-tenant) plugin for Auth-Kit. Organizations, members, invitations, and active-org, with owner/admin/member as organization-scoped roles powered by auth-kit-permissions.

Maintainers

Package info

github.com/gusmanwidodo/auth-kit-organization

pkg:composer/gusmanwidodo/auth-kit-organization

Transparency log

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.2.1 2026-08-25 15:26 UTC

This package is auto-updated.

Last update: 2026-08-26 05:09:39 UTC


README

Organization (multi-tenant) plugin for Auth-Kit. Organizations, members, invitations, and an active organization per session — with owner / admin / member implemented as organization-scoped roles powered by auth-kit-permissions.

Tests License: MIT

What it demonstrates

This is the plugin that proves the Auth-Kit ecosystem composes: instead of re-implementing role-based access control, it consumes auth-kit-permissions and its polymorphic scope. Creating an organization bootstraps three roles scoped to that organization, and every authorization check flows through the permissions plugin's PermissionManager with Scope::for('organization', $id).

See ADR 003 for the design rationale (organization as a consumer, not a second RBAC system).

Requirements

  • PHP ^8.3
  • gusmanwidodo/auth-kit ^0.1
  • gusmanwidodo/auth-kit-permissions ^0.1
  • Laravel 12

Installation

composer require gusmanwidodo/auth-kit-organization
php artisan migrate
php artisan vendor:publish --tag=auth-kit-organization-config

All three packages (core, permissions, organization) are auto-discovered.

Default roles

Every new organization gets three scoped roles (mirroring better-auth):

Role Abilities
owner organization.update/delete, member.create/update/delete, invitation.create/cancel
admin organization.update, member.*, invitation.* — but not organization.delete
member organization.read (read only)

Customize them in config/auth-kit-organization.php. The permissions plugin's statement must declare the resources/actions these roles reference.

Usage

use Gusmanwidodo\AuthKitOrganization\OrganizationService;

$orgs = app(OrganizationService::class);

// Create an org — creator becomes owner, scoped roles are bootstrapped.
$org = $orgs->create('Acme Inc', 'App\\User', $user->id);

// Add a member with a role (grants the matching scoped role).
$orgs->addMember($org->id, 'App\\User', $other->id, 'admin');

// Authorization — delegated to auth-kit-permissions, scoped to this org.
$orgs->can($org->id, 'App\\User', $other->id, 'member.create');       // true
$orgs->can($org->id, 'App\\User', $other->id, 'organization.delete'); // false (admin)

// Invitations.
$invitation = $orgs->invite($org->id, 'newbie@example.com', 'member');
$orgs->acceptInvitation($invitation->token, 'App\\User', $newUser->id);

Active organization (session)

use Gusmanwidodo\AuthKitOrganization\ActiveOrganization;

$active = app(ActiveOrganization::class);
$active->set($org->id);
$active->get();   // current active org id
$active->clear();

Endpoints

Method URI Body
POST /auth-kit/organization/create { name, subject_type, subject_id, slug? }
POST /auth-kit/organization/invite { organization_id, email, role }
POST /auth-kit/organization/accept-invitation { token, subject_type, subject_id }
POST /auth-kit/organization/set-active { organization_id }
POST /auth-kit/organization/check { organization_id, subject_type, subject_id, ability }

Scope isolation

Because authorization is scoped, a role in one organization grants nothing in another:

$a = $orgs->create('Org A', 'App\\User', 1); // user 1 = owner of A
$b = $orgs->create('Org B', 'App\\User', 2); // user 2 = owner of B

$orgs->can($a->id, 'App\\User', 1, 'organization.delete'); // true
$orgs->can($b->id, 'App\\User', 1, 'organization.read');   // false — no power in B

This is enforced by the permissions plugin's polymorphic scope and covered by tests.

Teams (v0.2)

Teams are nested inside organizations and act as an independent team authorization scope — the same mechanism as organizations, with no change to auth-kit-permissions (see ADR 004).

Default team roles (configurable under team_roles):

Role Abilities
lead team.update, team-member.create/update/delete
member team.read (read only)
use Gusmanwidodo\AuthKitOrganization\TeamService;

$teams = app(TeamService::class);

// Create a team under an org — creator becomes lead. The creator MUST already
// be a member of the organization (nested tenancy).
$team = $teams->create($org->id, 'Engineering', 'App\\User', $user->id);

// Add another org member to the team.
$teams->addMember($team->id, 'App\\User', $other->id, 'member');

// Authorization — team-scoped, delegated to auth-kit-permissions.
$teams->can($team->id, 'App\\User', $other->id, 'team.read');   // true
$teams->can($team->id, 'App\\User', $other->id, 'team.update'); // false (member)

Rules (enforced + tested):

  • A team belongs to exactly one organization (organization_id FK). Deleting the org cascades to its teams.
  • A subject may only join a team if they are already a member of that team's organization.
  • Team scope is independent of org scope — there is no hierarchical fallback. An org owner does not automatically get team abilities and vice versa; compose both checks explicitly if you want that behavior.

Team endpoints

Method URI Body
POST /auth-kit/organization/teams/create { organization_id, name, subject_type, subject_id, slug? }
POST /auth-kit/organization/teams/add-member { team_id, subject_type, subject_id, role }
POST /auth-kit/organization/teams/check { team_id, subject_type, subject_id, ability }

Developing against local packages

composer config repositories.auth-kit path ../auth-kit
composer config repositories.auth-kit-permissions path ../auth-kit-permissions
composer require gusmanwidodo/auth-kit:@dev gusmanwidodo/auth-kit-permissions:@dev
composer install
composer test    # 13 tests, incl. cross-plugin scoped-permission integration

License

MIT © Gusman Widodo. See LICENSE.