home / login.php
soiz1's picture
Upload 14 files
240bcc5 verified
Raw
History Blame Contribute Delete
15.6 kB
<?php
// エラーログ設定
ini_set('log_errors', 1);
// 絶対パスを使用
ini_set('error_log', __DIR__ . '/logs.txt');
// ファイルが存在しない場合は作成
if (!file_exists(__DIR__ . '/logs.txt')) {
file_put_contents(__DIR__ . '/logs.txt', '');
chmod(__DIR__ . '/logs.txt', 0666);
}
ini_set('display_errors', 0);
header('Content-Type: text/html; charset=UTF-8');
mb_internal_encoding('UTF-8');
session_start();
const CLIENT_ID = '169451419993-v1b3s315s8dkui950j2nm15hetr5i0qk.apps.googleusercontent.com';
const CLIENT_SECRET = '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';
const REDIRECT_URI = 'https://s-4-s-auth.hf.space/close4';
const SCOPES = 'https://www.googleapis.com/auth/drive.file profile email';
// エラーログ関数
function logError($message, $context = []) {
$logMessage = '[' . date('Y-m-d H:i:s') . '] ' . $message;
if (!empty($context)) {
$logMessage .= ' Context: ' . json_encode($context, JSON_PRETTY_PRINT);
}
error_log($logMessage);
}
try {
// 設定ファイル読み込み
require_once 'config.php';
// 既にログインしている場合はリダイレクト
if (isset($_SESSION['user_id'])) {
header('Location: ' . ($_SESSION['return_to'] ?? 'index.php'));
exit;
}
// データベース接続
try {
$pdo = new PDO(
'mysql:host=' . $host . ';dbname=' . $dbname . ';charset=utf8mb4',
$username,
$password,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
} catch (PDOException $e) {
logError('Database connection failed', ['error' => $e->getMessage()]);
throw new Exception('データベース接続に失敗しました。しばらくしてから再度お試しください。');
}
// エラーメッセージ初期化
$error = '';
// Googleからのコールバック処理
if (isset($_GET['code'])) {
try {
// CSRFチェック
if (!isset($_GET['state']) || empty($_SESSION['oauth2state']) || $_GET['state'] !== $_SESSION['oauth2state']) {
logError('Invalid state parameter', [
'expected' => $_SESSION['oauth2state'] ?? 'NOT_SET',
'received' => $_GET['state'] ?? 'NOT_SET'
]);
throw new logError('セキュリティチェックに失敗しました。もう一度お試しください。');
}
// トークンエンドポイントにリクエスト
$tokenUrl = 'https://oauth2.googleapis.com/token';
$postData = [
'code' => $_GET['code'],
'client_id' => CLIENT_ID,
'client_secret' => CLIENT_SECRET,
'redirect_uri' => REDIRECT_URI,
'grant_type' => 'authorization_code'
];
$ch = curl_init($tokenUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($postData),
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($status !== 200 || $curlError) {
logError('Token request failed', [
'status' => $status,
'response' => $response,
'curl_error' => $curlError
]);
throw new logError('認証サーバーとの通信に失敗しました。');
}
$tokenData = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
logError('JSON decode error', ['response' => $response]);
throw new logError('認証情報の処理に失敗しました。');
}
$accessToken = $tokenData['access_token'] ?? null;
if (!$accessToken) {
logError('Access token missing', ['token_data' => $tokenData]);
throw new logError('認証トークンの取得に失敗しました。');
}
// ユーザー情報取得
$userInfoUrl = 'https://people.googleapis.com/v1/people/me?personFields=emailAddresses,names,photos';
$ch = curl_init($userInfoUrl);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $accessToken,
'Accept: application/json'
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_TIMEOUT => 10,
]);
$userResponse = curl_exec($ch);
$userStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$userCurlError = curl_error($ch);
curl_close($ch);
if ($userStatus !== 200 || $userCurlError) {
logError('User info request failed', [
'status' => $userStatus,
'response' => $userResponse,
'curl_error' => $userCurlError
]);
throw new logError('ユーザー情報の取得に失敗しました。');
}
$userData = json_decode($userResponse, true);
if (json_last_error() !== JSON_ERROR_NONE) {
logError('User info JSON decode error', ['response' => $userResponse]);
throw new logError('ユーザー情報の処理に失敗しました。');
}
// 必要なユーザーデータの検証
if (empty($userData['emailAddresses'][0]['value'])) {
logError('Incomplete user data', ['user_data' => $userData]);
throw new logError('必要なユーザー情報が取得できませんでした。');
}
// ユーザー情報を準備
$googleId = $userData['resourceName'] ?? '';
$email = $userData['emailAddresses'][0]['value'];
$googleName = $userData['names'][0]['displayName'] ?? 'Unknown';
$photoUrl = $userData['photos'][0]['url'] ?? '';
$currentDate = date('Y-m-d H:i:s');
$ipAddress = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
// トランザクション開始
$pdo->beginTransaction();
try {
// ユーザーが既に存在するか確認
$stmt = $pdo->prepare("SELECT * FROM user_data WHERE google_id = ?");
$stmt->execute([$googleId]);
$existingUser = $stmt->fetch();
if ($existingUser) {
// 既存ユーザー - ログイン履歴を更新
$history = json_decode($existingUser['history'], true) ?: [];
$history[] = [
'type' => 'login',
'date' => $currentDate,
'ip' => $ipAddress
];
$stmt = $pdo->prepare("UPDATE user_data SET
last_login = ?,
last_ip = ?,
history = ?
WHERE google_id = ?");
$stmt->execute([
$currentDate,
$ipAddress,
json_encode($history),
$googleId
]);
// セッションにユーザー情報を保存
$_SESSION['user_id'] = $googleId;
$_SESSION['user_email'] = $email;
$_SESSION['user_name'] = $existingUser['display_name'] ?? $googleName;
$_SESSION['display_name'] = $existingUser['display_name'] ?? $googleName;
$_SESSION['icon_url'] = $existingUser['icon_url'] ?? $photoUrl;
$_SESSION['access_token'] = $accessToken;
} else {
// 新規ユーザー - データベースに登録
$history = [
[
'type' => 'join',
'date' => $currentDate,
'ip' => $ipAddress
]
];
$stmt = $pdo->prepare("INSERT INTO user_data (
google_id, google_name, display_name, email, icon_url,
created_at, history, last_login, last_ip
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([
$googleId,
$googleName,
$googleName,
$email,
$photoUrl,
$currentDate,
json_encode($history),
$currentDate,
$ipAddress
]);
// セッションにユーザー情報を保存
$_SESSION['user_id'] = $googleId;
$_SESSION['user_email'] = $email;
$_SESSION['user_name'] = $googleName;
$_SESSION['display_name'] = $googleName;
$_SESSION['icon_url'] = $photoUrl;
$_SESSION['access_token'] = $accessToken;
}
$pdo->commit();
// 元のページにリダイレクト
$returnTo = $_SESSION['return_to'] ?? 'index.php';
unset($_SESSION['return_to'], $_SESSION['oauth2state']);
header('Location: ' . $returnTo);
exit;
} catch (Exception $e) {
$pdo->rollBack();
logError('Database operation failed', ['error' => $e->getMessage()]);
throw new Exception('ユーザーデータの処理中にエラーが発生しました。');
}
} catch (Exception $e) {
$error = $e->getMessage();
logError('Authentication failed', ['error' => $error]);
}
}
// ログインページ表示
$authUrl = 'https://accounts.google.com/o/oauth2/v2/auth?' . http_build_query([
'client_id' => CLIENT_ID,
'redirect_uri' => REDIRECT_URI,
'response_type' => 'code',
'scope' => SCOPES,
'access_type' => 'online',
'state' => ($_SESSION['oauth2state'] = bin2hex(random_bytes(16))),
'prompt' => 'select_account'
]);
// リダイレクト前のページを記録
if (!isset($_SESSION['return_to']) && isset($_SERVER['HTTP_REFERER'])) {
$_SESSION['return_to'] = $_SERVER['HTTP_REFERER'];
}
} catch (Exception $e) {
$error = $e->getMessage();
logError('System error', ['error' => $error]);
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin-allow-popups">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-PJXRKX3F');</script>
<!-- End Google Tag Manager -->
<title>ログイン - Scratch School</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap" rel="stylesheet">
<style>
.login-container {
max-width: 400px;
margin: 50px auto;
padding: 2rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
}
.error {
color: #dc3545;
margin-bottom: 1rem;
padding: 10px;
background: #f8d7da;
border-radius: 4px;
}
.login-button {
display: inline-flex;
align-items: center;
justify-content: center;
background: #4285f4;
color: white;
padding: 12px 24px;
border-radius: 4px;
text-decoration: none;
font-weight: 500;
transition: background 0.3s;
margin: 10px 0;
}
.login-button:hover {
background: #357abd;
}
.google-logo {
width: 20px;
height: 20px;
margin-right: 12px;
}
.debug-info {
margin-top: 20px;
padding: 10px;
background: #f8f9fa;
border-radius: 4px;
font-size: 12px;
text-align: left;
color: #6c757d;
}
</style>
</head>
<body>
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PJXRKX3F"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
<div class="login-container">
<h1>Scratch Schoolにログイン</h1>
<?php if ($error): ?>
<div class="error"><?= htmlspecialchars($error, ENT_QUOTES, 'UTF-8') ?></div>
<?php endif; ?>
<a class="login-button" href="<?= htmlspecialchars($authUrl, ENT_QUOTES, 'UTF-8') ?>">
<img src="https://upload.wikimedia.org/wikipedia/commons/5/53/Google_%22G%22_Logo.svg" class="google-logo">
Googleで続ける
</a>
<p style="margin-top: 1rem; color: #666;">
ログインすることで、当サービスの利用規約とプライバシーポリシーに同意するものとします。
</p>
<?php if (isset($_GET['debug'])): ?>
<div class="debug-info">
<strong>Debug Information:</strong><br>
Session: <?= htmlspecialchars(json_encode($_SESSION, JSON_PRETTY_PRINT), ENT_QUOTES, 'UTF-8') ?><br>
GET: <?= htmlspecialchars(json_encode($_GET, JSON_PRETTY_PRINT), ENT_QUOTES, 'UTF-8') ?>
</div>
<?php endif; ?>
</div>
</body>
</html>