OIDC test - test jumbojett oidc round two 1
This commit is contained in:
1 parent
6f8dd7bbe3
commit
65a69b585d
9 files changed
+85
-226
No files matched your search
@@ -1,9 +0,0 @@
|
||||
OIDC_CLIENT_ID=fitness-oidc
|
||||
OIDC_CLIENT_SECRET=egebuqzZfVnjjBG9JDQ8tjW3TzV9gJurvT7qfB6c2SX2nPBNeaGycDfx4788QzJd
|
||||
OIDC_REDIRECT_URI=https://fitness.matt-cloud.com/pgapi/auth/oidc/callback
|
||||
OIDC_AUTH_ENDPOINT=https://auth.matt-cloud.com/oidc/auth
|
||||
OIDC_TOKEN_ENDPOINT=https://auth.matt-cloud.com/oidc/token
|
||||
OIDC_USERINFO_ENDPOINT=https://auth.matt-cloud.com/oidc/userinfo
|
||||
OIDC_LOGOUT_ENDPOINT=https://auth.matt-cloud.com/logout
|
||||
# Optional: the default redirect after logout
|
||||
OIDC_POST_LOGOUT_REDIRECT_URI=https://fitness.matt-cloud.com/
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
use Jumbojett\OpenIDConnectClient;
|
||||
|
||||
session_start();
|
||||
$config = require __DIR__ . '/../config.php';
|
||||
|
||||
$oidc = new OpenIDConnectClient(
|
||||
$config['provider_url'],
|
||||
$config['client_id'],
|
||||
$config['client_secret']
|
||||
);
|
||||
|
||||
// Tell the library where to return after the user logs in
|
||||
$oidc->setRedirectURL($config['redirect_uri']);
|
||||
|
||||
// Tell it which scopes we want
|
||||
$oidc->addScope($config['scopes']);
|
||||
|
||||
// The OIDC flow we’ll use
|
||||
$oidc->setResponseTypes([$config['response_type']]);
|
||||
|
||||
// Optional: for local development you can relax TLS validation
|
||||
// $oidc->setVerifyHost(false);
|
||||
// $oidc->setVerifyPeer(false);
|
||||
|
||||
$oidc->authenticate(); // This automatically redirects to Authelia
|
||||
+22
-29
@@ -1,40 +1,33 @@
|
||||
<?php
|
||||
// callback.php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
use Jumbojett\OpenIDConnectClient;
|
||||
|
||||
session_start();
|
||||
$config = require __DIR__ . '/../config.php';
|
||||
|
||||
$oidc = new \Jumbojett\OpenIDConnectClient(
|
||||
'https://auth.matt-cloud.com/oidc',
|
||||
'fitness-oidc',
|
||||
'egebuqzZfVnjjBG9JDQ8tjW3TzV9gJurvT7qfB6c2SX2nPBNeaGycDfx4788QzJd'
|
||||
$oidc = new OpenIDConnectClient(
|
||||
$config['provider_url'],
|
||||
$config['client_id'],
|
||||
$config['client_secret']
|
||||
);
|
||||
$oidc->setRedirectURL('https://fitness.matt-cloud.com/pgapi/auth/oidc/callback');
|
||||
$oidc->setRedirectURL($config['redirect_uri']);
|
||||
$oidc->addScope($config['scopes']);
|
||||
$oidc->setResponseTypes([$config['response_type']]);
|
||||
|
||||
try {
|
||||
// Finish the auth flow – exchanges the code, validates the ID‑token, fetches userinfo
|
||||
$oidc->authenticate();
|
||||
// This will read the `code` query parameter, fetch the tokens
|
||||
// and validate everything.
|
||||
$oidc->authenticate();
|
||||
|
||||
// $oidc->idToken is a JWT object; you can cast to array if needed
|
||||
$idToken = $oidc->idToken;
|
||||
|
||||
// Userinfo (includes groups because you requested the `groups` scope)
|
||||
$userInfo = $oidc->getUserInfo(); // or $oidc->requestUserInfo('email')
|
||||
// Fetch the OpenID Connect userinfo claims
|
||||
$claims = $oidc->requestUserInfo(); // → stdClass with the claims
|
||||
|
||||
// Store what you need in the session
|
||||
$_SESSION['user'] = [
|
||||
'id_token' => $oidc->idToken, // JWT string
|
||||
'access_token'=> $oidc->accessToken, // opaque or JWT
|
||||
'refresh_token'=> $oidc->refreshToken,
|
||||
'expires_in' => $oidc->expiresIn,
|
||||
'claims' => (array)$idToken, // decoded claims
|
||||
'userinfo' => $userInfo,
|
||||
];
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo "Authentication failed: " . $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
// Store what we need in the PHP session
|
||||
$_SESSION['authenticated'] = true;
|
||||
$_SESSION['name'] = $claims->name ?? $claims->given_name ?? $claims->family_name ?? '';
|
||||
$_SESSION['email'] = $claims->email ?? '';
|
||||
$_SESSION['groups'] = $claims->groups ?? [];
|
||||
|
||||
// Redirect to the site’s landing page
|
||||
header('Location: /');
|
||||
// Redirect back to the home page
|
||||
header('Location: ../');
|
||||
exit;
|
||||
+1
-4
@@ -1,8 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"jumbojett/openid-connect-php": "^2.4",
|
||||
"vlucas/phpdotenv": "^5.4",
|
||||
"guzzlehttp/guzzle": "^7.0",
|
||||
"firebase/php-jwt": "^7.0"
|
||||
"jumbojett/openid-connect-php": "^1.0"
|
||||
}
|
||||
}
|
||||
+15
-19
@@ -1,23 +1,19 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
// config.php
|
||||
return [
|
||||
// Base URL for the Authelia OpenID Provider
|
||||
'provider_url' => 'https://auth.matt-cloud.com', // <-- change if you run locally
|
||||
|
||||
session_start();
|
||||
// Client (your application) credentials
|
||||
'client_id' => 'fitness-oidc',
|
||||
'client_secret' => 'egebuqzZfVnjjBG9JDQ8tjW3TzV9gJurvT7qfB6c2SX2nPBNeaGycDfx4788QzJd',
|
||||
|
||||
/**
|
||||
* Load environment variables. If you don't use phpdotenv,
|
||||
* copy the values from .env directly here.
|
||||
*/
|
||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||
$dotenv->load();
|
||||
// The callback URL **must** match exactly what is in Authelia
|
||||
'redirect_uri' => 'https://fitness.matt-cloud.com/pgapi/auth/oidc/callback',
|
||||
|
||||
/**
|
||||
* Basic OIDC configuration – keep all secrets out of source‑control.
|
||||
*/
|
||||
define('OIDC_CLIENT_ID', getenv('OIDC_CLIENT_ID'));
|
||||
define('OIDC_CLIENT_SECRET', getenv('OIDC_CLIENT_SECRET'));
|
||||
define('OIDC_REDIRECT_URI', getenv('OIDC_REDIRECT_URI'));
|
||||
define('OIDC_AUTH_ENDPOINT', getenv('OIDC_AUTH_ENDPOINT'));
|
||||
define('OIDC_TOKEN_ENDPOINT', getenv('OIDC_TOKEN_ENDPOINT'));
|
||||
define('OIDC_USERINFO_ENDPOINT', getenv('OIDC_USERINFO_ENDPOINT'));
|
||||
define('OIDC_LOGOUT_ENDPOINT', getenv('OIDC_LOGOUT_ENDPOINT'));
|
||||
define('OIDC_POST_LOGOUT_REDIRECT_URI', getenv('OIDC_POST_LOGOUT_REDIRECT_URI'));
|
||||
// Scopes we want – Authelia already provides “openid”, “profile”, “email”, “groups”
|
||||
'scopes' => ['openid', 'profile', 'email', 'groups'],
|
||||
|
||||
// Response type & grant type
|
||||
'response_type' => 'code',
|
||||
];
|
||||
+20
-16
@@ -1,19 +1,23 @@
|
||||
<?php
|
||||
session_start();
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Fitness - OIDC Demo</title></head>
|
||||
<body>
|
||||
<?php if (!isset($_SESSION['user'])): ?>
|
||||
<a href="/login.php"><button>Login with Authelia</button></a>
|
||||
<?php else: ?>
|
||||
<h2>Welcome, <?= htmlspecialchars($_SESSION['user']['userinfo']['name'] ?? '??') ?>!</h2>
|
||||
<p>Email: <?= htmlspecialchars($_SESSION['user']['userinfo']['email'] ?? '') ?></p>
|
||||
<p>Groups: <?= htmlspecialchars(implode(', ', $_SESSION['user']['userinfo']['groups'] ?? [])) ?></p>
|
||||
<p>JWT claims: <pre><?= htmlspecialchars(print_r($_SESSION['user']['claims'], true)) ?></pre></p>
|
||||
$config = require __DIR__ . '/../config.php';
|
||||
|
||||
<a href="/logout.php"><button>Logout</button></a>
|
||||
<?php endif; ?>
|
||||
</body>
|
||||
</html>
|
||||
if (!isset($_SESSION['authenticated'])) {
|
||||
// User not logged in – show a login button
|
||||
echo '<h1>Welcome to the demo</h1>';
|
||||
echo '<p><a href="auth.php">Login with Authelia SSO</a></p>';
|
||||
exit;
|
||||
}
|
||||
|
||||
// The user is logged in – display the attributes
|
||||
$name = $_SESSION['name'] ?? 'unknown';
|
||||
$email = $_SESSION['email'] ?? 'unknown';
|
||||
$groups = $_SESSION['groups'] ?? [];
|
||||
|
||||
echo '<h1>Logged in as ' . htmlspecialchars($name) . '</h1>';
|
||||
echo '<ul>';
|
||||
echo '<li><strong>Email:</strong> ' . htmlspecialchars($email) . '</li>';
|
||||
echo '<li><strong>Groups:</strong> ' . htmlspecialchars(implode(', ', $groups)) . '</li>';
|
||||
echo '</ul>';
|
||||
|
||||
echo '<p><a href="logout.php">Logout</a></p>';
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
// login.php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
session_start();
|
||||
|
||||
$oidc = new \Jumbojett\OpenIDConnectClient(
|
||||
'https://auth.matt-cloud.com/', // ***ISSUER***
|
||||
'fitness-oidc',
|
||||
'egebuqzZfVnjjBG9JDQ8tjW3TzV9gJurvT7qfB6c2SX2nPBNeaGycDfx4788QzJd'
|
||||
);
|
||||
|
||||
// If you’re on a dev environment with a self‑signed cert:
|
||||
# $oidc->setVerifyPeer(false); // <-- DO NOT USE IN PRODUCTION
|
||||
|
||||
// PKCE – optional but recommended for public clients
|
||||
$oidc->setCodeChallengeMethod('S256');
|
||||
|
||||
// Tell the client where to land after Auth
|
||||
$oidc->setRedirectURL('https://fitness.matt-cloud.com/pgapi/auth/oidc/callback');
|
||||
|
||||
// Optional: hard‑code the discovery URL
|
||||
$oidc->setDiscoveryEndpoint('https://auth.matt-cloud.com/.well-known/openid-configuration');
|
||||
|
||||
try {
|
||||
$oidc->authenticate(); // sends 302 → Authelia
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo 'Login failed: ' . $e->getMessage();
|
||||
exit;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
// logout.php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
session_start();
|
||||
|
||||
$oidc = new \Jumbojett\OpenIDConnectClient(
|
||||
'https://auth.matt-cloud.com/oidc',
|
||||
'fitness-oidc',
|
||||
'egebuqzZfVnjjBG9JDQ8tjW3TzV9gJurvT7qfB6c2SX2nPBNeaGycDfx4788QzJd'
|
||||
);
|
||||
|
||||
// Build the end‑session endpoint (back‑channel logout)
|
||||
$logoutUrl = $oidc->getLogoutEndpoint(); // e.g. https://auth.matt-cloud.com/logout
|
||||
|
||||
// Include the post‑logout redirect URI
|
||||
$logoutUrl .= '?post_logout_redirect_uri=' . urlencode('https://fitness.matt-cloud.com/');
|
||||
|
||||
// Optionally pass the ID‑token hint so the OP can immediately terminate the session
|
||||
if (isset($_SESSION['user']['id_token'])) {
|
||||
$logoutUrl .= '&id_token_hint=' . urlencode($_SESSION['user']['id_token']);
|
||||
}
|
||||
|
||||
// Destroy the local session first
|
||||
session_destroy();
|
||||
|
||||
// Redirect the browser
|
||||
header('Location: ' . $logoutUrl);
|
||||
exit;
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Fitness\OIDC;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Firebase\JWT\JWT;
|
||||
use Firebase\JWT\Key;
|
||||
|
||||
class OIDC
|
||||
{
|
||||
private Client $http;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->http = new Client(['http_errors' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Auth URL that redirects the user to Authelia.
|
||||
*/
|
||||
public function buildAuthUrl(string $state, string $nonce): string
|
||||
{
|
||||
$params = [
|
||||
'response_type' => 'code',
|
||||
'client_id' => OIDC_CLIENT_ID,
|
||||
'redirect_uri' => OIDC_REDIRECT_URI,
|
||||
'scope' => 'openid profile email groups',
|
||||
'state' => $state,
|
||||
'nonce' => $nonce,
|
||||
];
|
||||
|
||||
return OIDC_AUTH_ENDPOINT . '?' . http_build_query($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange the Authorization Code for tokens.
|
||||
*/
|
||||
public function exchangeCode(string $code): array
|
||||
{
|
||||
$response = $this->http->post(OIDC_TOKEN_ENDPOINT, [
|
||||
'auth' => [OIDC_CLIENT_ID, OIDC_CLIENT_SECRET],
|
||||
'form_params' => [
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $code,
|
||||
'redirect_uri' => OIDC_REDIRECT_URI,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() !== 200) {
|
||||
throw new \RuntimeException('Token endpoint returned ' . $response->getStatusCode());
|
||||
}
|
||||
|
||||
return json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call /userinfo with the access token.
|
||||
*/
|
||||
public function getUserInfo(string $accessToken): array
|
||||
{
|
||||
$response = $this->http->get(OIDC_USERINFO_ENDPOINT, [
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer ' . $accessToken,
|
||||
],
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() !== 200) {
|
||||
throw new \RuntimeException('Userinfo endpoint returned ' . $response->getStatusCode());
|
||||
}
|
||||
|
||||
return json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the ID Token (minimal validation for demo purposes).
|
||||
*/
|
||||
public function validateIDToken(string $idToken, string $nonce): array
|
||||
{
|
||||
// Authelia uses RS256 by default, but your config says "none".
|
||||
// We'll decode without verification if alg is "none".
|
||||
$decoded = JWT::decode($idToken, new Key('', ''), ['none', 'RS256']);
|
||||
|
||||
// Check nonce matches what we stored in session.
|
||||
if ($decoded->nonce !== $nonce) {
|
||||
throw new \RuntimeException('Invalid nonce');
|
||||
}
|
||||
|
||||
return (array)$decoded;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user