Changeset View
Standalone View
src/app/Http/Controllers/API/AuthController.php
<?php | <?php | ||||
namespace App\Http\Controllers\API; | namespace App\Http\Controllers\API; | ||||
use App\Http\Controllers\Controller; | use App\Http\Controllers\Controller; | ||||
use App\User; | use App\User; | ||||
use Illuminate\Http\Request; | use Illuminate\Http\Request; | ||||
use Illuminate\Support\Facades\Auth; | use Illuminate\Support\Facades\Auth; | ||||
use Illuminate\Support\Facades\Validator; | use Illuminate\Support\Facades\Validator; | ||||
use Laravel\Passport\TokenRepository; | |||||
use Laravel\Passport\RefreshTokenRepository; | |||||
class AuthController extends Controller | class AuthController extends Controller | ||||
{ | { | ||||
/** | /** | ||||
* Get the authenticated User | * Get the authenticated User | ||||
* | * | ||||
* @return \Illuminate\Http\JsonResponse | * @return \Illuminate\Http\JsonResponse | ||||
*/ | */ | ||||
public function info() | public function info() | ||||
{ | { | ||||
$user = Auth::guard()->user(); | $user = Auth::guard()->user(); | ||||
$response = V4\UsersController::userResponse($user); | $response = V4\UsersController::userResponse($user); | ||||
if (!empty(request()->input('refresh_token'))) { | if (!empty(request()->input('refresh'))) { | ||||
// @phpstan-ignore-next-line | return $this->refreshAndRespond(request(), $response); | ||||
return $this->respondWithToken(Auth::guard()->refresh(), $response); | |||||
} | } | ||||
return response()->json($response); | return response()->json($response); | ||||
} | } | ||||
/** | /** | ||||
* Helper method for other controllers with user auto-logon | * Helper method for other controllers with user auto-logon | ||||
* functionality | * functionality | ||||
* | * | ||||
* @param \App\User $user User model object | * @param \App\User $user User model object | ||||
* @param string $password Plain text password | |||||
* @param string|null $secondFactor Second factor code if available | |||||
machniak: Descriptions should also be aligned with each other. | |||||
*/ | */ | ||||
public static function logonResponse(User $user) | public static function logonResponse(User $user, string $password, string $secondFactor = null) | ||||
{ | { | ||||
// @phpstan-ignore-next-line | $proxyRequest = Request::create('/oauth/token', 'POST', [ | ||||
$token = Auth::guard()->login($user); | 'username' => $user->email, | ||||
'password' => $password, | |||||
'grant_type' => 'password', | |||||
'client_id' => config('auth.proxy.client_id'), | |||||
'client_secret' => config('auth.proxy.client_secret'), | |||||
'scopes' => '[*]', | |||||
'secondfactor' => $secondFactor | |||||
]); | |||||
$tokenResponse = app()->handle($proxyRequest); | |||||
$response = V4\UsersController::userResponse($user); | $response = V4\UsersController::userResponse($user); | ||||
$response['status'] = 'success'; | $response['status'] = 'success'; | ||||
return self::respondWithToken($tokenResponse, $response); | |||||
return self::respondWithToken($token, $response); | |||||
} | } | ||||
/** | /** | ||||
* Get a JWT token via given credentials. | * Get an oauth token via given credentials. | ||||
* | * | ||||
* @param \Illuminate\Http\Request $request The API request. | * @param \Illuminate\Http\Request $request The API request. | ||||
* | * | ||||
* @return \Illuminate\Http\JsonResponse | * @return \Illuminate\Http\JsonResponse | ||||
*/ | */ | ||||
public function login(Request $request) | public function login(Request $request) | ||||
{ | { | ||||
// TODO: Redirect to dashboard if authenticated. | // TODO: Redirect to dashboard if authenticated. | ||||
$v = Validator::make( | $v = Validator::make( | ||||
$request->all(), | $request->all(), | ||||
[ | [ | ||||
'email' => 'required|min:2', | 'email' => 'required|min:2', | ||||
'password' => 'required|min:4', | 'password' => 'required|min:4', | ||||
] | ] | ||||
); | ); | ||||
if ($v->fails()) { | if ($v->fails()) { | ||||
return response()->json(['status' => 'error', 'errors' => $v->errors()], 422); | return response()->json(['status' => 'error', 'errors' => $v->errors()], 422); | ||||
} | } | ||||
$credentials = $request->only('email', 'password'); | $user = \App\User::where('email', $request->email)->first(); | ||||
if (!$user) { | |||||
if ($token = Auth::guard()->attempt($credentials)) { | |||||
$user = Auth::guard()->user(); | |||||
$sf = new \App\Auth\SecondFactor($user); | |||||
if ($response = $sf->requestHandler($request)) { | |||||
return $response; | |||||
} | |||||
$response = V4\UsersController::userResponse($user); | |||||
return $this->respondWithToken($token, $response); | |||||
} | |||||
return response()->json(['status' => 'error', 'message' => __('auth.failed')], 401); | return response()->json(['status' => 'error', 'message' => __('auth.failed')], 401); | ||||
} | } | ||||
return self::logonResponse($user, $request->password, $request->secondfactor); | |||||
} | |||||
/** | /** | ||||
* Log the user out (Invalidate the token) | * Log the user out (Invalidate the token) | ||||
* | * | ||||
* @return \Illuminate\Http\JsonResponse | * @return \Illuminate\Http\JsonResponse | ||||
*/ | */ | ||||
public function logout() | public function logout() | ||||
{ | { | ||||
Done Inline ActionsI think we should check if user exists before the proxy request. And we should return 401 response not throw an exception. machniak: I think we should check if user exists before the proxy request. And we should return 401… | |||||
Auth::guard()->logout(); | $tokenId = Auth::user()->token()->id; | ||||
$tokenRepository = app(TokenRepository::class); | |||||
$refreshTokenRepository = app(RefreshTokenRepository::class); | |||||
Done Inline ActionsWhat if the user or token does not exist here? machniak: What if the user or token does not exist here? | |||||
Done Inline ActionsThis is an authenticated api call, so that doesn't seems like something we can run into, no? mollekopf: This is an authenticated api call, so that doesn't seems like something we can run into, no? | |||||
Done Inline ActionsThen the authenticated call shouldn't have been possible. mollekopf: Then the authenticated call shouldn't have been possible. | |||||
// Revoke an access token... | |||||
$tokenRepository->revokeAccessToken($tokenId); | |||||
// Revoke all of the token's refresh tokens... | |||||
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId); | |||||
return response()->json([ | return response()->json([ | ||||
'status' => 'success', | 'status' => 'success', | ||||
'message' => __('auth.logoutsuccess') | 'message' => __('auth.logoutsuccess') | ||||
]); | ]); | ||||
} | } | ||||
/** | /** | ||||
* Refresh a token. | * Refresh a token. | ||||
* | * | ||||
* @return \Illuminate\Http\JsonResponse | * @return \Illuminate\Http\JsonResponse | ||||
*/ | */ | ||||
public function refresh() | public function refresh(Request $request) | ||||
{ | { | ||||
// @phpstan-ignore-next-line | return self::refreshAndRespond($request); | ||||
return $this->respondWithToken(Auth::guard()->refresh()); | } | ||||
/** | |||||
* Refresh the token and respond with it. | |||||
* | |||||
* @param \Illuminate\Http\Request $request The API request. | |||||
* @param array $response Additional response data | |||||
* | |||||
* @return \Illuminate\Http\JsonResponse | |||||
*/ | |||||
protected static function refreshAndRespond(Request $request, array $response = []) | |||||
{ | |||||
$proxyRequest = Request::create('/oauth/token', 'POST', [ | |||||
'grant_type' => 'refresh_token', | |||||
'refresh_token' => $request->refresh_token, | |||||
'client_id' => config('auth.proxy.client_id'), | |||||
'client_secret' => config('auth.proxy.client_secret'), | |||||
Done Inline ActionsSome documentation to this method would be nice. machniak: Some documentation to this method would be nice. | |||||
]); | |||||
$tokenResponse = app()->handle($proxyRequest); | |||||
return self::respondWithToken($tokenResponse, $response); | |||||
} | } | ||||
/** | /** | ||||
* Get the token array structure. | * Get the token array structure. | ||||
* | * | ||||
* @param string $token Respond with this token. | * @param \Illuminate\Http\JsonResponse $tokenResponse The response containing the token. | ||||
* @param array $response Additional response data | * @param array $response Additional response data | ||||
* | * | ||||
* @return \Illuminate\Http\JsonResponse | * @return \Illuminate\Http\JsonResponse | ||||
*/ | */ | ||||
protected static function respondWithToken($token, array $response = []) | protected static function respondWithToken($tokenResponse, array $response = []) | ||||
{ | { | ||||
$response['access_token'] = $token; | $data = json_decode($tokenResponse->getContent()); | ||||
if ($tokenResponse->getStatusCode() != 200) { | |||||
if (isset($data->error) && $data->error == 'secondfactor') { | |||||
Done Inline ActionsYou have to heck if $data is not empty here. I got: ErrorException: Trying to get property 'error' of non-object in /home/alec/repos/kolab/src/app/Http/Controllers/API/AuthController.php:156 when running tests. machniak: You have to heck if $data is not empty here. I got: ErrorException: Trying to get property… | |||||
Done Inline ActionsI can't reproduce this error, but I'm now checking if error isset (so we just run into the generic error case otherwise). mollekopf: I can't reproduce this error, but I'm now checking if error isset (so we just run into the… | |||||
$errors = ['secondfactor' => $data['error_description']]; | |||||
Not Done Inline ActionsIt should be $data->error_description, also are we sure it's always set? machniak: It should be `$data->error_description`, also are we sure it's always set? | |||||
return response()->json(['status' => 'error', 'errors' => $errors], 422); | |||||
} | |||||
return response()->json(['status' => 'error', 'message' => __('auth.failed')], 401); | |||||
} | |||||
$response['access_token'] = $data->access_token; | |||||
$response['refresh_token'] = $data->refresh_token; | |||||
$response['token_type'] = 'bearer'; | $response['token_type'] = 'bearer'; | ||||
// @phpstan-ignore-next-line | $response['expires_in'] = $data->expires_in; | ||||
$response['expires_in'] = Auth::guard()->factory()->getTTL() * 60; | |||||
return response()->json($response); | return response()->json($response); | ||||
} | } | ||||
} | } |
Descriptions should also be aligned with each other.