99use Illuminate \Auth \Access \AuthorizationException ;
1010use Illuminate \Auth \AuthenticationException ;
1111use Illuminate \Auth \Middleware \Authenticate ;
12+ use Illuminate \Contracts \Auth \Authenticatable ;
13+ use Illuminate \Contracts \Encryption \DecryptException ;
1214use Illuminate \Contracts \Encryption \Encrypter ;
1315use Illuminate \Cookie \CookieValuePrefix ;
1416use Illuminate \Cookie \Middleware \EncryptCookies ;
1517use Illuminate \Http \Request ;
18+ use Javaabu \Passport \Traits \HasUserIdentifier ;
19+ use Laravel \Passport \Client ;
1620use Laravel \Passport \Exceptions \MissingScopeException ;
17- use Laravel \Passport \Http \Middleware \CheckClientCredentials ;
21+ use Laravel \Passport \Http \Middleware \CheckToken ;
1822use Laravel \Passport \Passport ;
23+ use Laravel \Passport \TransientToken ;
1924
2025class AuthenticateOAuthClient
2126{
27+ use HasUserIdentifier;
28+
2229 /**
23- * The encrypter implementation.
24- *
25- * @var Encrypter
30+ * The currently authenticated client.
2631 */
27- protected $ encrypter ;
32+ protected ? Client $ client = null ;
2833
2934 /**
3035 * Create a new token guard instance.
@@ -33,10 +38,10 @@ class AuthenticateOAuthClient
3338 * @return void
3439 */
3540 public function __construct (
36- Encrypter $ encrypter
41+ protected Encrypter $ encrypter ,
42+ protected Request $ request ,
3743 )
3844 {
39- $ this ->encrypter = $ encrypter ;
4045 }
4146
4247 /**
@@ -74,9 +79,9 @@ public function handle($request, Closure $next, ...$scopes)
7479 } catch (AuthenticationException $ e ) {
7580 try {
7681 //authentication failed, try client auth
77- return app (CheckClientCredentials ::class)->handle ($ request , $ next , ...$ scopes );
82+ return app (CheckToken ::class)->handle ($ request , $ next , ...$ scopes );
7883 } catch (AuthenticationException $ e ) {
79- $ this ->authenticateViaCookie ($ request );
84+ $ this ->authenticateViaCookie ();
8085
8186 return $ next ($ request );
8287 }
@@ -91,42 +96,52 @@ protected function getApiGuards(): array
9196 return config ('auth.passport_guards ' , ['api ' ]);
9297 }
9398
94- /**
95- * Authenticate the incoming request via the token cookie.
96- *
97- * @param Request $request
98- * @return void
99- */
100- protected function authenticateViaCookie ($ request )
99+ protected function authenticateViaCookie (): ?Authenticatable
101100 {
102- if (!$ token = $ this ->getTokenViaCookie ($ request )) {
103- throw new AuthenticationException () ;
101+ if (! $ token = $ this ->getTokenViaCookie ()) {
102+ return null ;
104103 }
104+
105+ // If this user exists, we will return this user and attach a "transient" token to
106+ // the user model. The transient token assumes it has all scopes since the user
107+ // is physically logged into the application via the application's interface.
108+ try {
109+ $ user = $ this ->retrieveUserById ($ token ['sub ' ]);
110+ } catch (Exception ) {
111+ return null ;
112+ }
113+
114+ return $ user ?->withAccessToken(new TransientToken );
105115 }
106116
107117 /**
108118 * Get the token cookie via the incoming request.
109119 *
110- * @param Request $request
111- * @return mixed
120+ * @return array<string, mixed>|null
112121 */
113- protected function getTokenViaCookie ($ request )
122+ protected function getTokenViaCookie (): ? array
114123 {
115124 // If we need to retrieve the token from the cookie, it'll be encrypted so we must
116125 // first decrypt the cookie and then attempt to find the token value within the
117126 // database. If we can't decrypt the value we'll bail out with a null return.
118127 try {
119- $ token = $ this ->decodeJwtTokenCookie ($ request );
120- } catch (Exception $ e ) {
121- return ;
128+ $ token = $ this ->decodeJwtTokenCookie ();
129+ } catch (Exception ) {
130+ return null ;
131+ }
132+
133+ // Token's expiration time is checked using the "exp" claim during decoding, but
134+ // legacy tokens may have an "expiry" claim instead of the standard "exp". So
135+ // we must manually check token's expiry, if the "expiry" claim is present.
136+ if (isset ($ token ['expiry ' ]) && time () >= $ token ['expiry ' ]) {
137+ return null ;
122138 }
123139
124140 // We will compare the CSRF token in the decoded API token against the CSRF header
125141 // sent with the request. If they don't match then this request isn't sent from
126142 // a valid source and we won't authenticate the request for further handling.
127- if (!Passport::$ ignoreCsrfToken && (!$ this ->validCsrf ($ token , $ request ) ||
128- time () >= $ token ['expiry ' ])) {
129- return ;
143+ if (! Passport::$ ignoreCsrfToken && ! $ this ->validCsrf ($ token )) {
144+ return null ;
130145 }
131146
132147 return $ token ;
@@ -135,56 +150,74 @@ protected function getTokenViaCookie($request)
135150 /**
136151 * Decode and decrypt the JWT token cookie.
137152 *
138- * @param Request $request
139- * @return array
153+ * @return array<string, mixed>
140154 */
141- protected function decodeJwtTokenCookie ($ request )
155+ protected function decodeJwtTokenCookie (): array
142156 {
143- return (array )JWT ::decode (
144- CookieValuePrefix::remove ($ this ->encrypter ->decrypt ($ request ->cookie (Passport::cookie ()), Passport::$ unserializesCookies )),
145- new Key (Passport::tokenEncryptionKey ($ this ->encrypter ), 'HS256 ' ),
157+ $ jwt = $ this ->request ->cookie (Passport::cookie ());
158+
159+ return (array ) JWT ::decode (
160+ Passport::$ decryptsCookies
161+ ? CookieValuePrefix::remove ($ this ->encrypter ->decrypt ($ jwt , Passport::$ unserializesCookies ))
162+ : $ jwt ,
163+ new Key (Passport::tokenEncryptionKey ($ this ->encrypter ), 'HS256 ' )
146164 );
147165 }
148166
149167 /**
150168 * Determine if the CSRF / header are valid and match.
151169 *
152- * @param array $token
153- * @param Request $request
154- * @return bool
170+ * @param array<string, mixed> $token
155171 */
156- protected function validCsrf ($ token , $ request )
172+ protected function validCsrf (array $ token ): bool
157173 {
158- return isset ($ token ['csrf ' ]) && hash_equals (
159- $ token ['csrf ' ],
160- (string )$ this ->getTokenFromRequest ($ request )
161- );
174+ $ requestToken = $ this ->getTokenFromRequest ();
175+
176+ return isset ($ token ['csrf ' ]) &&
177+ is_string ($ requestToken ) &&
178+ hash_equals ($ token ['csrf ' ], $ requestToken );
162179 }
163180
164181 /**
165182 * Get the CSRF token from the request.
166- *
167- * @param Request $request
168- * @return string
169183 */
170- protected function getTokenFromRequest ($ request )
184+ protected function getTokenFromRequest (): ? string
171185 {
172- $ token = $ request ->header ('X-CSRF-TOKEN ' );
186+ $ token = $ this -> request ->header ('X-CSRF-TOKEN ' );
173187
174- if (!$ token && $ header = $ request ->header ('X-XSRF-TOKEN ' )) {
175- $ token = CookieValuePrefix::remove ($ this ->encrypter ->decrypt ($ header , static ::serialized ()));
188+ if (! $ token && $ header = $ this ->request ->header ('X-XSRF-TOKEN ' )) {
189+ try {
190+ $ token = CookieValuePrefix::remove ($ this ->encrypter ->decrypt ($ header , static ::serialized ()));
191+ } catch (DecryptException ) {
192+ $ token = null ;
193+ }
176194 }
177195
178196 return $ token ;
179197 }
180198
199+ public function setRequest (Request $ request ): static
200+ {
201+ $ this ->request = $ request ;
202+
203+ return $ this ;
204+ }
205+
181206 /**
182207 * Determine if the cookie contents should be serialized.
183- *
184- * @return bool
185208 */
186- public static function serialized ()
209+ public static function serialized (): bool
187210 {
188211 return EncryptCookies::serialized ('XSRF-TOKEN ' );
189212 }
213+
214+ /**
215+ * Set the client for the current request.
216+ */
217+ public function setClient (Client $ client ): static
218+ {
219+ $ this ->client = $ client ;
220+
221+ return $ this ;
222+ }
190223}
0 commit comments