Initial Laravel API import
Some checks failed
continuous-integration/drone/push Build is failing

- Complete GGZ Ecademy Laravel backend application
- RESTful API for learning products, members, filters
- Authentication and authorization system
- Database migrations and seeders
- Custom CRUD generator commands
- Email notification system
- Integration with frontend applications
This commit is contained in:
Joris Slagter
2025-12-02 17:40:21 +01:00
parent 786b6b6a78
commit df155bb13d
341 changed files with 116385 additions and 2 deletions

View File

@@ -0,0 +1,199 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Response\ApiResponse;
use Illuminate\Http\Request;
use App\Services\UserService;
use App\Services\MemberService;
use Illuminate\Support\Carbon;
use App\Http\Requests\User\Login;
use Illuminate\Support\Facades\Hash;
use App\Http\Resources\UserLoggedResource;
use App\Repositories\Role;
use Redirect;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Support\Facades\Mail;
use Laravel\Sanctum\PersonalAccessToken;
use Illuminate\Support\Facades\Log;
class AuthController extends Controller
{
private $userService;
private $memberService;
public function __construct(
UserService $userService,
MemberService $memberService
) {
$this->userService = $userService;
$this->memberService = $memberService;
$this->middleware('auth:sanctum', [
'except' => [
'get_sso_data',
'login',
'signup',
'sso',
'status',
],
]);
}
public function login(Login $request)
{
if (empty($request->token)) {
$user = $this->userService->getByEmailWith($request->email, ['roles']);
if (!$user || !Hash::check($request->password, $user->password)) {
return response(['error' => ['The provided credentials are incorrect.']]);
}
} else {
$client = new \GuzzleHttp\Client(['base_uri' => static::determineIdpUri()]);
try {
$response = $client->request('POST', 'protocol/openid-connect/token', [
'form_params' => [
'code' => $request->token,
'client_id' => config('sso.client_id'),
'client_secret' => config('sso.secret'),
'redirect_uri' => config('sso.redirect_uri'),
'scope' => 'openid profile',
'grant_type' => 'authorization_code'
]
]);
$token = json_decode($response->getBody(), true)['id_token'];
$decodedToken = \Firebase\JWT\JWT::decode($token, $this->getJwks(), false);
} catch (ClientException $e) {
$html = 'Login faild with token:' . $request->token;
Mail::send([], [], function ($message) use ($html) {
$message
->to('joris@ggzecademy.nl')
->subject('Login failed via SSO')
->setBody($html, 'text/html');
});
}
if (empty($decodedToken->kg) || !stristr($decodedToken->kg, 'KG')) {
return response(['error' => ['The provided credentials are incorrect.']]);
}
$organization_id = str_replace('KG_', '', $decodedToken->kg);
$member = $this->memberService->get($organization_id);
$user = $this->userService->getByEmailWith($decodedToken->email, ['roles']);
if (!$user) {
$new_user = [
'first_name' => $decodedToken->given_name,
'last_name' => $decodedToken->family_name,
'email' => $decodedToken->email,
'password' => 'aT@5*Wb*W7gseVhC',
];
$user = $this->userService->save($new_user, false);
$user->roles()->attach(Role::whereName('user')->firstOrFail());
}
$member->users()->syncWithoutDetaching([$user->id]);
$member->save();
}
$user->last_login_at = $user->logged_at;
$user->last_login_ip = $request->getClientIp();
$user->logged_at = Carbon::now();
$user->save();
$token = $user->createToken('cms-token')->plainTextToken;
$response = ['token' => $token];
return response($response, 201);
}
private function getJwks(): array
{
$jwkData = json_decode(
file_get_contents(static::determineIdpUri() . 'protocol/openid-connect/certs'),
true
);
return \Firebase\JWT\JWK::parseKeySet($jwkData);
}
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return response()->json('Logged-out', 201);
}
public function status(Request $request)
{
$bearerToken = $request->bearerToken();
if (is_null($bearerToken)) {
return ApiResponse::error(
new Error\AuthController\MissingBearerTokenError(),
);
} else {
$accessToken = PersonalAccessToken::findToken($bearerToken);
return ApiResponse::success([
'authenticated' => $accessToken instanceof PersonalAccessToken,
'user' => $accessToken ? $accessToken->tokenable->email : null,
]);
}
}
public function me(Request $request)
{
return response()->json(new UserLoggedResource($request->user()));
}
public function sso()
{
return Redirect::to(static::buildSsoEndpointUri());
}
private static function buildSsoEndpointUri(): string
{
$endpointQuery = http_build_query([
'client_id' => config('sso.client_id'),
'redirect_uri' => config('sso.redirect_uri'),
'scope' => 'openid profile',
'response_type' => 'code',
'nonce' => md5(rand()), // TODO: proper nonce handling
], '', '&', PHP_QUERY_RFC3986);
$endpointQuery = str_replace('%2B', '+', $endpointQuery);
return sprintf('%s?%s', static::determineIdpUri('protocol/openid-connect/auth'), $endpointQuery);
}
private static function determineIdpUri(string $path = ''): string
{
$baseUri = rtrim(config('sso.idp_base_uri'), '/');
return sprintf('%s/%s', $baseUri, $path);
}
public function get_sso_data() {
$client = new \GuzzleHttp\Client([
'base_uri' => static::determineIdpUri(),
'headers' => [
'Authorization' => sprintf('Bearer %s', $_GET['token']),
'Accept' => 'application/json',
],
]);
$response = $client->request('GET', 'oidc/userinfo.php');
return response($response->getBody()->getContents(), 201);
}
}