Context
By default, authorization is global, aka application-wide. However, it’s possible to set an authz context like a team, organization, or board. Rather than checking if a user is an manager in the application, you’d verify is the user is a manager of the team.
Any object that implements Jasny\Auth\ContextInterface
can be used as context. The getAuthId()
method should return a value that can be used by the Storage
implementation to fetch the context.
use Jasny\Auth;
class Team implements Auth\ContextInterface
{
public string $id;
public function getAuthId(): string
{
return $this->id;
}
}
use Jasny\Auth;
class User implements Auth\UserInterface
{
public string $id;
public string $username;
public array $roles = [];
public array $memberships;
protected string $hashedPassword;
// ...
public function getAuthRole(?Auth\ContextInterface $team = null): array
{
// Membership is an object that specifies the roles for the user within an team
$membership = $team !== null
? $this->getMembership($team)
: null;
return array_merge($this->roles, $membership->roles ?? []);
}
}
Different type of contexts
In some cases an application has multiple types of authorization contexts. Take Trello for instance, it defines application-wide, team, and board privileges.
use Jasny\Auth;
class Team implements Auth\ContextInterface
{
public string $id;
public function getAuthId(): string
{
return "team:{$this->id}";
}
}
class Board implements Auth\ContextInterface
{
public string $id;
public function getAuthId(): string
{
return "board:{$this->id}";
}
}
It’s up the storage class to determine which context to load
use Jasny\Auth;
class AuthStorage implements Auth\StorageInterface
{
// ...
public function fetchContext(string $contextId): ?Auth\ContextInterface
{
[$type, $id] = explode(':', $contextId, 2);
switch ($type) {
case 'team': return $this->fetchTeam($id);
case 'board': return $this->fetchBoard($id);
default: throw new \Exception("Unknown context type '$type'");
}
}
protected function fetchTeam(string $id): Team
{
// ...
}
protected function fetchBoard(string $id): Board
{
// ...
}
}
Default context for user
It might be possible to automatically determine the context for a user. For instance; the user might only be a member of one team. The storage service must implement the method getContextForUser()
. This method should return the default context of the user.
use Jasny\Auth;
class AuthStorage implements Auth\StorageInterface
{
// ...
public function getContextForUser(Auth\UserInterface $user): ?Auth\ContextInterface
{
return $user instanceof User && count($user->teams) === 1
? $user->teams[0]
: null;
}
}
Context from URL
In some applications the context will be determined on a slug in the URL (like jasny
in https://github.com/jasny/
). In that case Context::getAuthId()
should return null
to prevent the context from being storing in the session.
use Jasny\Auth;
class Team implements Auth\ContextInterface
{
public function getAuthId(): ?string
{
return null;
}
}
You need to manually set the context for each request.
$teamSlug = get_slug_from_url($_SERVER['REQUEST_URI']);
$team = $auth->getStorage()->fetchContext($teamSlug);
$auth->setContext($team);
if (!$auth->is('manager')) {
return forbidden();
}
$auth->context(); // returns $team
Alternatively, use a Authz
object for with context for authorization instead of Auth
.
if (!$auth->inContextOf($team)->is('manager')) {
return forbidden();
}
$auth->context(); // returns null