SSO 문제: SSO 또는 SIG 파라미터 누락

안녕하세요, Discourse SSO에서 문제가 발생하고 있습니다. 제가 뭘 잘못하고 있는 건가요? 아래 코드는 다른 플랫폼에서 추출할 때는 문제가 없습니다. 하지만 nonce를 받지 못하고 있습니다.

<?php

// 디버깅을 위해 오류 로그 활성화
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// init 파일 로드
require_once '/init.php'; // 플랫폼 설정에 따라 이 경로를 업데이트하세요

// Discourse Connect 시크릿 키 및 페이로드 반환 URL
$sso_secret = 'keyhere'; // Discourse 공유 시크릿 키로 교체하세요
$discourse_url = 'https://urlhere.com/session/sso_login'; // Discourse URL로 교체하세요

// 플랫폼 사용자 로그인 상태를 확인하기 위해 세션 시작
session_start();

// 사용자가 로그인되어 있는지 확인
if (!isset($_SESSION['uid'])) {
    // 사용자가 로그인되지 않은 경우, 플랫폼 로그인 페이지로 리다이렉트
    header('Location: /clientarea.php');
    exit;
}

// 디버그: 수신된 요청 매개변수 기록
error_log("Received GET parameters: " . print_r($_GET, true));

// Discourse에서 'sso' 및 'sig' 매개변수를 받았는지 확인
if (!isset($_GET['sso']) || !isset($_GET['sig'])) {
    error_log("Missing SSO or SIG parameters.");
    header('Location: /clientarea.php');
    exit;
}

// 시크릿 키를 사용하여 SSO 서명을 검증
$sso = $_GET['sso'];
$sig = $_GET['sig'];
$expected_sig = hash_hmac('sha256', $sso, $sso_secret);

if ($sig !== $expected_sig) {
    error_log("Invalid SSO signature. Expected: $expected_sig, Received: $sig");
    header('Location: /clientarea.php');
    exit;
}

// 플랫폼 클라이언트 세부 정보 가져오기
$clientDetails = localAPI('getclientsdetails', ['clientid' => $_SESSION['uid']], 'admin');
if ($clientDetails['result'] !== 'success' || !isset($clientDetails['userid'])) {
    error_log("Failed to fetch Platform client details.");
    header('Location: /clientarea.php');
    exit;
}

// 사용자 세부 정보 추출
$userId = $clientDetails['userid'];
$email = $clientDetails['email'];
$firstName = $clientDetails['firstname'];
$lastName = $clientDetails['lastname'];
$username = $firstName . ' ' . $lastName;

// SSO 페이로드 디코딩
$sso_payload = base64_decode($sso);
parse_str($sso_payload, $sso_params);

if (!isset($sso_params['nonce'])) {
    error_log("Nonce is missing from SSO payload.");
    header('Location: /clientarea.php');
    exit;
}

// 사용자 세부 정보를 포함한 반환 SSO 페이로드 준비
$return_payload = [
    'nonce' => $sso_params['nonce'],
    'email' => $email,
    'external_id' => $userId,
    'username' => $username,
    'name' => $firstName . ' ' . $lastName,
    'admin' => 0, // 사용자가 관리자라면 1로 설정, 아니면 0으로 유지
    'moderator' => 0 // 사용자가 모더레이터라면 1로 설정, 아니면 0으로 유지
];

// 반환 페이로드를 Base64로 인코딩
$query_string = http_build_query($return_payload);
$encoded_payload = base64_encode($query_string);

// Discourse 시크릿으로 인코딩된 페이로드에 서명
$return_sig = hash_hmac('sha256', $encoded_payload, $sso_secret);

// 디버그: 페이로드 및 서명 기록
error_log("Return Payload: " . print_r($return_payload, true));
error_log("Encoded Payload: $encoded_payload");
error_log("Return Signature: $return_sig");

// 서명된 페이로드와 함께 Discourse로 리다이렉트
header("Location: $discourse_url?sso=" . urlencode($encoded_payload) . "&sig=" . urlencode($return_sig));
exit;

오류 로그:

[25-Sep-2024 14:50:17 UTC] Missing SSO or SIG parameters.
[25-Sep-2024 14:53:59 UTC] Received GET parameters: Array
(
[sso] => nonce_from_discourse
)

[25-Sep-2024 14:53:59 UTC] Missing SSO or SIG parameters.

이유를 모르겠네요?

저도 잘 모르겠습니다. Discourse 사이트에서 DiscourseConnect가 설정되어 있나요? 이 코드가 처리하는 URL에 사용자가 어떻게 도달하고 있나요?

브라우저 검사도구의 네트워크 탭을 열어 인증 과정을 진행하면 어떤 일이 일어나고 있는지 관련 세부 정보를 확인할 수 있을 것입니다.

답변 감사합니다.

WHMCS와 연동하는 중입니다. 코드에서 필요한 모든 설정을 적절히 완료했고, Discourse 내부에서도 설정이 활성화되어 있습니다.

일부 응답은 받을 수 있지만 서명(sig)이 빠져 있습니다. 두 시스템 간에 불일치가 있는 것 같아 디코딩 오류일 수도 있겠다는 생각이 들었습니다.

사실 이 부분은 제 전문 분야가 아니라 최대한 맞춰보려 노력하고 있지만, 이틀째 머리를 쥐어짜고 있습니다. 제 실수인 곳이 보이지가 않습니다.

할 수 있는 한 디버깅을 많이 해보았고, Discourse를 다시 구축했습니다. 단계별 로깅도 시도했고, ChatGPT도 활용해 보았습니다. WordPress 플러그인 코드와 비교도 해봤습니다. 정말 막막합니다.

무언가가 올바르게 설정되어 있지 않은 것 같습니다. 한 발 물러서서 전체 구조를 다시 살펴보는 것이 좋을 수도 있습니다. 현재 제 로컬 환경에는 SSO 제공자 사이트가 설정되어 있지 않지만, 이 정보가 도움이 될 수 있습니다.

Discourse에서 다음 설정들이 구성되었는지 확인하세요:

discourse connect url 설정은 게시하신 코드를 처리하는 URL로 설정되어야 합니다.

discourse connect secret 설정은 최소 10자 이상의 문자열로 설정하세요. 게시하신 코드에 keyhere라는 7자 문자열이 하드코딩되어 있는 것을 확인했습니다. 코드를 실행할 때 이 값을 변경하고 있다고 가정합니다. Discourse에 입력한 값과 동일한 값으로 설정하세요.

이제 Discourse 사이트에서 로그아웃하세요. 브라우저의 웹 인스펙터(개발자 도구)를 열고 네트워크 탭으로 이동합니다. Discourse의 “로그인” 버튼을 클릭합니다. 아래 스크린샷의 첫 두 요청과 유사한 요청을 확인해야 합니다:

첫 번째 요청은 http://forum.example.com/session/sso?return_path=%2F로 전송됩니다.

다음 요청은 https://example.com/?sso=<sso_payload_sent_from_discourse>&sig=<sso_signature>로 전송되어야 합니다.

example.com과 forum.example.com은 실제로 사용 중인 도메인으로 설정되어야 합니다.

모든 설정이 올바르게 이루어졌다면, sso와 sig 매개변수의 값이 여기서 설정한 변수에 할당되기를 기대합니다:

$sso = $_GET['sso'];
$sig = $_GET['sig'];

나였다면 나머지 코드를 주석 처리하고, 페이로드를 수신하여 변수에 할당할 수 있는지 확인하는 것부터 시작했을 것입니다.

DiscourseConnect가 활성화된 상태에서는 /u/admin-login 경로로 이동하여 Discourse 사이트에 다시 로그인할 수 있습니다. Discourse 사이트의 Rails 콘솔에 액세스 권한이 있다면, Rails 콘솔에서 DiscourseConnect를 비활성화하여 다시 로그인할 수도 있습니다:

SiteSetting.enable_discourse_connect = false

게시하신 코드 하단에 더 많은 오류가 있을 가능성이 있습니다. 예를 들어, 예상되는 sig를 생성하기 전에 sso 매개변수의 값에 urldecode를 호출해야 한다고 _생각_합니다. WP Discourse 플러그인이 이를 어떻게 처리하는지 확인해 보세요:

위 함수의 $payload는 다음 위치에서 정리(sanitized)된 후의 sso 쿼리 매개변수 값입니다: wp-discourse/lib/sso-provider/discourse-sso.php at main · discourse/wp-discourse · GitHub.