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.
Package info
github.com/gusmanwidodo/auth-kit-organization
pkg:composer/gusmanwidodo/auth-kit-organization
Requires
- php: ^8.3
- gusmanwidodo/auth-kit: ^0.1
- gusmanwidodo/auth-kit-permissions: ^0.1
- illuminate/contracts: ^12.0
- illuminate/database: ^12.0
- illuminate/support: ^12.0
Requires (Dev)
- orchestra/testbench: ^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
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.
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.1gusmanwidodo/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_idFK). 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.