<?php
declare(strict_types=1);
namespace SHServ\Integrations\GAuth;
use SHServ\Integrations\GAuth\AuthService;
use SHServ\Integrations\GAuth\PermissionResolver;
trait AuthControllerTrait
{
/**
* Require authenticated user. Returns error response if not auth.
*/
protected function require_auth(): ?string
{
if (!$this->resolve_user()) {
return $this->utils()->response_error('unauthenticated', [], [], 401);
}
return null;
}
/**
* Require specific permission. Returns error response if denied.
*/
protected function require_permission(string $permissionSlug): ?string
{
$authError = $this->require_auth();
if ($authError !== null) {
return $authError;
}
$user = $this->resolve_user();
if (!$user) {
return $this->utils()->response_error('unauthenticated', [], [], 401);
}
$resolver = new PermissionResolver();
if (!$resolver->has($user['id'], $user['system_role'], $permissionSlug)) {
return $this->utils()->response_error('permission_denied', [$permissionSlug], [], 403);
}
return null;
}
/**
* Resolve current user from session or Bearer token.
*/
protected function resolve_user(): ?array
{
if (session_status() === PHP_SESSION_NONE) {
@session_start();
}
// 1. Session-based auth
$sessionUserId = $_SESSION['shserv_user_id'] ?? null;
if ($sessionUserId) {
return $this->load_user_by_id((int) $sessionUserId);
}
// 2. Bearer token auth
$bearer = $this->get_bearer_token();
if ($bearer) {
return $this->resolve_user_by_bearer($bearer);
}
return null;
}
/**
* Extract Bearer token from Authorization header.
*/
protected function get_bearer_token(): ?string
{
$header = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (str_starts_with($header, 'Bearer ')) {
return substr($header, 7);
}
return null;
}
/**
* Resolve user by OAuth access token via local session table.
* Checks token expiration before resolving.
*/
protected function resolve_user_by_bearer(string $token): ?array
{
$tb = app()->thin_builder;
$result = $tb->select(
'shserv_sessions',
['user_id', 'expires_at'],
[['access_token', '=', $token]]
);
if (!$result) {
return null;
}
$row = $result[0];
if (!empty($row['expires_at'])) {
$expiresAt = strtotime((string) $row['expires_at']);
if ($expiresAt !== false && time() > $expiresAt) {
return null;
}
}
return $this->load_user_by_id((int) $row['user_id']);
}
/**
* Load user row from shserv_users by local ID.
* Returns null if user is inactive or banned.
*/
protected function load_user_by_id(int $userId): ?array
{
$tb = app()->thin_builder;
$result = $tb->select(
'shserv_users',
['id', 'gauth_user_id', 'email', 'display_name', 'avatar_url', 'system_role', 'status'],
[['id', '=', $userId]]
);
if (!$result) {
return null;
}
$user = $result[0];
if (($user['status'] ?? '') !== 'active') {
return null;
}
return $user;
}
/**
* Get current user data (session or Bearer).
*/
protected function get_current_user(): ?array
{
return $this->resolve_user();
}
/**
* Get effective permissions for current user.
*/
protected function get_current_permissions(): array
{
$user = $this->resolve_user();
if (!$user) {
return [];
}
$resolver = new PermissionResolver();
return $resolver->resolve((int) $user['id'], $user['system_role']);
}
}