Laravel AT Protocol Client (alpha & unstable)
1<?php
2
3namespace SocialDept\AtpClient\Http\Middleware;
4
5use Closure;
6use Illuminate\Http\Request;
7use SocialDept\AtpClient\Auth\ScopeChecker;
8use SocialDept\AtpClient\Contracts\HasAtpSession;
9use SocialDept\AtpClient\Enums\ScopeAuthorizationFailure;
10use SocialDept\AtpClient\Exceptions\ScopeAuthorizationException;
11use SocialDept\AtpClient\Session\SessionManager;
12use Symfony\Component\HttpFoundation\Response;
13
14class RequiresScopeMiddleware
15{
16 public function __construct(
17 protected SessionManager $sessions,
18 protected ScopeChecker $checker,
19 ) {}
20
21 /**
22 * Handle an incoming request.
23 *
24 * @param string ...$scopes
25 */
26 public function handle(Request $request, Closure $next, string ...$scopes): Response
27 {
28 $user = $request->user();
29
30 // Ensure user is authenticated
31 if (! $user) {
32 return $this->handleFailure(
33 new ScopeAuthorizationException($scopes, [], 'User not authenticated.')
34 );
35 }
36
37 // Ensure user implements HasAtpSession
38 if (! $user instanceof HasAtpSession) {
39 return $this->handleFailure(
40 new ScopeAuthorizationException($scopes, [], 'User model must implement HasAtpSession interface.')
41 );
42 }
43
44 $did = $user->getAtpDid();
45
46 if (! $did) {
47 return $this->handleFailure(
48 new ScopeAuthorizationException($scopes, [], 'User has no ATP session.')
49 );
50 }
51
52 try {
53 $session = $this->sessions->session($did);
54 } catch (\Exception $e) {
55 return $this->handleFailure(
56 new ScopeAuthorizationException($scopes, [], 'Could not retrieve ATP session: '.$e->getMessage())
57 );
58 }
59
60 // Check ALL scopes (AND logic)
61 if (! $this->checker->check($session, $scopes)) {
62 $granted = $session->scopes();
63 $missing = array_diff($scopes, $granted);
64
65 return $this->handleFailure(
66 new ScopeAuthorizationException($missing, $granted)
67 );
68 }
69
70 return $next($request);
71 }
72
73 protected function handleFailure(ScopeAuthorizationException $exception): Response
74 {
75 $action = config('atp-client.scope_authorization.failure_action', ScopeAuthorizationFailure::Abort);
76
77 return match ($action) {
78 ScopeAuthorizationFailure::Redirect => redirect(
79 config('atp-client.scope_authorization.redirect_to', '/login')
80 ),
81 ScopeAuthorizationFailure::Exception => throw $exception,
82 default => abort(403, $exception->getMessage()),
83 };
84 }
85}