Skip to content

Instantly share code, notes, and snippets.

@AlkoKod
Forked from lynt-smitka/cross-login.php
Last active April 17, 2025 20:58
Show Gist options
  • Save AlkoKod/9394bba46faea31bd80028f8727c8ec8 to your computer and use it in GitHub Desktop.
Save AlkoKod/9394bba46faea31bd80028f8727c8ec8 to your computer and use it in GitHub Desktop.
WordPress cross-domain login system that auto-authenticates users across multiple Polylang language domains using secure AJAX calls.
<?php
/**
* Plugin Name: Polylang + Bricks Cross Domain usage
* Description: Enables automatic login across multiple domains and paths using Polylang with folder installation support
* Version: 1.1
* Author: Aleš Sýkora - forked from Vláďa Smitka - Lynt
*/
// Get site URLs from Polylang
function lynt_get_site_urls()
{
$site_urls = [];
if (function_exists('pll_languages_list')) {
$languages = pll_languages_list(array('fields' => 'home_url'));
foreach ($languages as $home_url) {
// Store complete URLs, not just domains
$site_urls[] = $home_url;
}
}
return apply_filters('lynt_login_site_urls', $site_urls);
}
function lynt_initialize_cross_login()
{
// Only run on the dashboard page
$screen = get_current_screen();
if (!$screen || $screen->id !== 'dashboard') {
return;
}
add_action('admin_footer', 'lynt_render_cross_login_script');
}
// Add script for multi-domain login to dashboard
function lynt_render_cross_login_script()
{
$site_urls = lynt_get_site_urls();
$user_id = get_current_user_id();
$current_url = home_url();
if ($user_id > 0) {
// Create a signature for secure transmission of user ID
$timestamp = time();
$signature = hash_hmac('sha256', $user_id . '|' . $timestamp, AUTH_KEY . SECURE_AUTH_KEY);
?>
<script>
jQuery(document).ready(function($) {
var currentUrl = '<?php echo esc_js($current_url); ?>';
var loginData = {
'action': 'lynt_manage',
'cmd': 'login',
'user_id': <?php echo intval($user_id); ?>,
'timestamp': <?php echo intval($timestamp); ?>,
'signature': '<?php echo esc_js($signature); ?>'
};
var siteUrls = <?php echo json_encode($site_urls); ?>;
siteUrls.forEach(function(siteUrl) {
if (siteUrl !== currentUrl) {
$.ajax({
type: "POST",
url: siteUrl + "/wp-admin/admin-ajax.php",
data: loginData,
xhrFields: {
withCredentials: true
}
});
}
});
});
</script>
<?php
}
}
// Handle AJAX - login process
function lynt_manage()
{
// Set CORS
lynt_add_cors_headers();
// Init result
$result = [
'status' => 'error',
'message' => 'Invalid request'
];
// Validate
if (!isset($_POST['cmd']) || !isset($_POST['user_id']) || !isset($_POST['timestamp']) || !isset($_POST['signature'])) {
wp_send_json($result);
return;
}
// Sanitize
$cmd = sanitize_text_field($_POST['cmd']);
$user_id = intval($_POST['user_id']);
$timestamp = intval($_POST['timestamp']);
$signature = sanitize_text_field($_POST['signature']);
if ($cmd === 'login' && is_user_logged_in()) {
$result = [
'status' => 'info',
'message' => 'User already logged in',
];
wp_send_json($result);
return;
}
// Process login command
if ($cmd === 'login' && !is_user_logged_in()) {
// Verify timestamp < 5 minutes
if (time() - $timestamp > 300) {
$result['message'] = 'Request expired';
wp_send_json($result);
return;
}
// Verify signature
$expected_signature = hash_hmac('sha256', $user_id . '|' . $timestamp, AUTH_KEY . SECURE_AUTH_KEY);
if (!hash_equals($expected_signature, $signature)) {
$result['message'] = 'Invalid signature';
wp_send_json($result);
return;
}
// Check user exists
$user = get_user_by('id', $user_id);
if (!$user) {
$result['message'] = 'User does not exist';
wp_send_json($result);
return;
}
// Generate auth cookies
$expiration = time() + 8 * HOUR_IN_SECONDS;
$expire = $expiration + (1 * HOUR_IN_SECONDS);
$secure = true; // Vždy nastavit jako secure
$auth_cookie = wp_generate_auth_cookie($user_id, $expiration, 'secure_auth');
$logged_in_cookie = wp_generate_auth_cookie($user_id, $expiration, 'logged_in');
// Set cookies with SameSite=None (it will slightly decrease SECURITY)
setcookie(
SECURE_AUTH_COOKIE,
$auth_cookie,
[
'expires' => $expire,
'path' => ADMIN_COOKIE_PATH,
'domain' => COOKIE_DOMAIN,
'secure' => $secure,
'httponly' => true,
'samesite' => 'None'
]
);
setcookie(
LOGGED_IN_COOKIE,
$logged_in_cookie,
[
'expires' => $expire,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => $secure,
'httponly' => true,
'samesite' => 'None'
]
);
// Optional call wp_login e.g. for auditing
// do_action('wp_login', $user->user_login, $user);
$result = [
'status' => 'success',
'message' => 'Login successful'
];
}
wp_send_json($result);
}
// Set CORS headers for cross-domain requests
function lynt_add_cors_headers()
{
$allowed_site_urls = lynt_get_site_urls();
// Check if request is from an allowed origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
$origin = $_SERVER['HTTP_ORIGIN'];
// Check if the origin matches one of our allowed site URLs
$origin_matches = false;
foreach ($allowed_site_urls as $site_url) {
// Extract domain from site URL
$site_domain = parse_url($site_url, PHP_URL_HOST);
// Extract domain from origin
$origin_domain = parse_url($origin, PHP_URL_HOST);
if ($site_domain === $origin_domain) {
$origin_matches = true;
break;
}
}
if ($origin_matches) {
// Set CORS headers
header('Access-Control-Allow-Origin: ' . $origin);
header('Access-Control-Allow-Credentials: true');
}
}
}
add_action('current_screen', 'lynt_initialize_cross_login');
add_action('wp_ajax_nopriv_lynt_manage', 'lynt_manage');
add_action('wp_ajax_lynt_manage', 'lynt_manage');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment