commit 6653bc5e4769b574a242710bc0442785c46897f1 Author: Owen Rummage Date: Tue Jul 29 17:44:43 2025 -0500 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5727fb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +vendor +composer.lock +cache diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/app/controllers/AdminController.php b/app/controllers/AdminController.php new file mode 100644 index 0000000..8eae3a3 --- /dev/null +++ b/app/controllers/AdminController.php @@ -0,0 +1,12 @@ +render("admin/dashboard.latte", [ + "user" => Auth::user(), + "activities" => [], + ]); + } +} diff --git a/app/controllers/HomeController.php b/app/controllers/HomeController.php new file mode 100644 index 0000000..b0409a7 --- /dev/null +++ b/app/controllers/HomeController.php @@ -0,0 +1,12 @@ +render("home.latte", [ + "name" => "Owen", + "title" => "Index", + ]); + } +} diff --git a/app/lib/database.php b/app/lib/database.php new file mode 100644 index 0000000..c60eb5e --- /dev/null +++ b/app/lib/database.php @@ -0,0 +1,9 @@ + "authorization_code", + "code" => $code, + "redirect_uri" => self::getRedirectUri(), + "client_id" => self::$client_id, + "client_secret" => self::$client_secret, + ]; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $token_endpoint); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data)); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + "Content-Type: application/x-www-form-urlencoded", + ]); + + $response = curl_exec($ch); + curl_close($ch); + + $token_data = json_decode($response, true); + + if (isset($token_data["access_token"])) { + $_SESSION["access_token"] = $token_data["access_token"]; + $_SESSION["id_token"] = $token_data["id_token"] ?? null; + $_SESSION["refresh_token"] = + $token_data["refresh_token"] ?? null; + + Flight::redirect($redirect_url); + } else { + Flight::halt(500, "Failed to obtain access token"); + } + } else { + // Show login page + $auth_endpoint = self::getDiscoveryEndpoint( + "authorization_endpoint", + ); + $state = bin2hex(random_bytes(16)); + $_SESSION["oauth_state"] = $state; + + $params = [ + "response_type" => "code", + "client_id" => self::$client_id, + "redirect_uri" => self::getRedirectUri(), + "scope" => "openid profile email", + "state" => $state, + ]; + + $login_url = $auth_endpoint . "?" . http_build_query($params); + + echo ' + + + Login + + + +

Login Required

+

Please click the button below to authenticate

+ Login with OIDC + +'; + } + } + + public static function user() + { + self::init(); + + if (!isset($_SESSION["access_token"])) { + return null; + } + + $userinfo_endpoint = self::getDiscoveryEndpoint("userinfo_endpoint"); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $userinfo_endpoint); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + "Authorization: Bearer " . $_SESSION["access_token"], + ]); + + $response = curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($http_code === 200) { + return json_decode($response, true); + } + + return null; + } + + public static function logout() + { + self::init(); + + // Clear session + if (isset($_SESSION["access_token"])) { + unset($_SESSION["access_token"]); + } + if (isset($_SESSION["id_token"])) { + unset($_SESSION["id_token"]); + } + if (isset($_SESSION["refresh_token"])) { + unset($_SESSION["refresh_token"]); + } + + Flight::redirect("/"); + } + + private static function getDiscoveryEndpoint($endpoint) + { + $discovery_url = + rtrim(self::$oidc_issuer, "/") . + "/.well-known/openid-configuration"; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $discovery_url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + + $response = curl_exec($ch); + curl_close($ch); + + $discovery = json_decode($response, true); + + if (!isset($discovery[$endpoint])) { + throw new Exception( + "Endpoint {$endpoint} not found in OIDC discovery: " . + $discovery_url, + ); + } + + return $discovery[$endpoint]; + } +} diff --git a/app/routes.php b/app/routes.php new file mode 100644 index 0000000..e16b14a --- /dev/null +++ b/app/routes.php @@ -0,0 +1,10 @@ + +
+
+
+

Dashboard

+

Welcome back, {$user["name"]|capitalize}!

+
+
+
+ +
+
+
+
+

Recent Activity

+
+
+ {if $activities && count($activities) > 0} +
+ + + + + + + + + + {foreach $activities as $activity} + + + + + + {/foreach} + +
DateActivityStatus
{$activity['date']}{$activity['name']} + {if $activity['status'] === 'completed'} + Completed + {elseif $activity['status'] === 'pending'} + Pending + {elseif $activity['status'] === 'in_progress'} + In Progress + {/if} +
+
+ {else} +
+

No recent activities to display.

+
+ {/if} +
+
+
+ +
+
+
+

Quick Actions

+
+
+ + + + +
+
+
+
+ + +{/block} diff --git a/app/views/admin/layout.latte b/app/views/admin/layout.latte new file mode 100644 index 0000000..79c56e9 --- /dev/null +++ b/app/views/admin/layout.latte @@ -0,0 +1,28 @@ + + + + My App + + + + + +
+ {block content}{/block} +
+ + diff --git a/app/views/home.latte b/app/views/home.latte new file mode 100644 index 0000000..9819e42 --- /dev/null +++ b/app/views/home.latte @@ -0,0 +1,75 @@ +{extends 'layout.latte'} + +{block content} +
+ +
+
+
+
+
+

+ Welcome to our + amazing platform +

+

+ Experience the power of our platform with intuitive design and powerful features that help you achieve your goals. +

+ +
+
+
+
+
+ + +
+
+
+

Features

+

+ Everything you need to succeed +

+
+ +
+
+
+
+
+ + + +
+

Fast Performance

+
+
+ Lightning-fast performance with optimized code and efficient architecture. +
+
+ +
+
+
+ + + +
+

Reliable

+
+
+ Built with reliability in mind, ensuring your data is always safe and accessible. +
+
+
+
+
+
+
+{/block} diff --git a/app/views/layout.latte b/app/views/layout.latte new file mode 100644 index 0000000..04008a4 --- /dev/null +++ b/app/views/layout.latte @@ -0,0 +1,10 @@ + + + + My App + + + + {block content}{/block} + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..aab9c87 --- /dev/null +++ b/composer.json @@ -0,0 +1,8 @@ +{ + "require": { + "latte/latte": "^3.0", + "flightphp/core": "^3.17", + "vlucas/phpdotenv": "^5.6", + "gabordemooij/redbean": "^5.7" + } +} diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..3dbd7a6 --- /dev/null +++ b/public/index.php @@ -0,0 +1,42 @@ +load(); +} catch (Exception $e) { +} + +// Set paths for controllers +Flight::path("../app/controllers"); +Flight::path("../app/middlewares"); + +// Register latte view engine +Flight::register("view", Engine::class, [], function ($latte) { + $latte->setTempDirectory(__DIR__ . "/../cache/"); + $latte->setLoader( + new \Latte\Loaders\FileLoader(__DIR__ . "/../app/views/"), + ); +}); + +require "../app/routes.php"; + +// Login route +Flight::route("GET /auth/login", function () { + Auth::login("/admin"); +}); + +// Logout route +Flight::route("GET /auth/logout", function () { + Auth::logout(); +}); + +// API route to get current user data +Flight::route("GET /debug/user", function () { + $user = Auth::user(); + Flight::json($user); +}); + +Flight::start();