EramCloud SSO

راهنمای اتصال پنل‌ها و وب‌سایت‌ها

این صفحه برای ادمین و برنامه‌نویس پروژه‌های دیگر است. طبق همین سند، ورود با نام کاربری و رمز را از پنل خودتان بردارید و به Identity Provider مرکزی روی sso.eramcloud.com وصل شوید. کاربر یک‌بار در پورتال لاگین می‌کند؛ بعد با کلیک روی لوگو، بدون رمز دوباره وارد داشبورد شما می‌شود.

۱) معرفی

پروتکل: OpenID Connect روی OAuth 2.0 با Authorization Code. اپ شما هرگز username/password کاربر را نمی‌گیرد. فقط یک code می‌گیرد، آن را در سمت سرور با client_secret عوض می‌کند، و از روی توکن session محلی می‌سازد.

  • رمز عبور فقط در پورتال SSO وارد می‌شود.
  • client_secret فقط در Backend پنل شما بماند؛ در مرورگر و فرانت نگذارید.
  • اگر کاربر در پورتال به سرویس شما دسترسی نداشته باشد، حتی با دانستن URL هم وارد نمی‌شود.

۲) ثبت سرویس در پورتال

مدیر SSO باید سرویس شما را در مدیریت سرویس‌ها ثبت کند و این مقادیر را به شما بدهد:

نام و لوگوهمان چیزی که در Homepage دیده می‌شود
Client IDشناسه OIDC سرویس شما
Client Secretفقط یک‌بار نمایش داده می‌شود؛ در سرور خودتان ذخیره کنید
Redirect URIمثلاً https://panel.eramcloud.com/sso/callback — باید دقیقاً همین باشد
حالت وروداز پورتال (idp) تا کلیک روی لوگو مستقیم به callback شما برسد

اولین Redirect URI برای کلیک روی لوگو استفاده می‌شود. بعد از ثبت، مدیر SSO باید در صفحه کاربر، تیک دسترسی همین سرویس را بزند.

۳) آدرس‌های SSO

Discovery:

https://sso.eramcloud.com/.well-known/openid-configuration
نقشآدرس
Authorizehttps://sso.eramcloud.com/oauth/authorize
Tokenhttps://sso.eramcloud.com/oauth/token
UserInfohttps://sso.eramcloud.com/oauth/userinfo
JWKShttps://sso.eramcloud.com/oauth/jwks
Logouthttps://sso.eramcloud.com/logout
کلیک لوگوhttps://sso.eramcloud.com/launch/<slug>

۴) دو روش ورود

الف) از Homepage پورتال (پیشنهادی)

کاربر روی لوگوی سرویس شما کلیک می‌کند. پورتال یک code می‌سازد و به اولین Redirect URI شما می‌فرستد:

$REDIRECT_URI?code=...&state=eram-sso-launch

همین کافی است که callback را پیاده کنید. فرم لاگین پنل شما نباید وسط کار بیاید.

ب) کاربر مستقیم وارد پنل شما شود

اگر session محلی ندارد، او را به Authorize بفرستید. اگر قبلاً در پورتال لاگین باشد، فرم رمز SSO هم نشان داده نمی‌شود:

GET https://sso.eramcloud.com/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https%3A%2F%2FYOUR-APP%2Fsso%2Fcallback
  &response_type=code
  &scope=openid%20profile%20email
  &state=RANDOM_CSRF_TOKEN

state را خودتان بسازید و بعد از برگشت چک کنید. مقدار redirect_uri باید با مقدار ثبت‌شده در پورتال حرف‌به‌حرف یکی باشد.

۵) پیاده‌سازی Callback

در مسیر callback (سمت سرور):

  1. اگر error=access_denied بود، صفحه «دسترسی ندارید» نشان دهید.
  2. code را بخوانید. اعتبار آن حدود ۲ دقیقه است و یک‌بارمصرف است.
  3. با درخواست زیر توکن بگیرید. این درخواست فقط از Backend زده شود.
POST https://sso.eramcloud.com/oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "CODE_FROM_QUERY",
  "redirect_uri": "https://YOUR-APP/sso/callback",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET"
}

پاسخ موفق:

{
  "access_token": "...",
  "id_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid profile email"
}

نمونه Node.js:

app.get("/sso/callback", async (req, res) => {
  if (req.query.error) {
    return res.status(403).send("دسترسی به این سرویس ندارید.");
  }

  const tokenRes = await fetch("https://sso.eramcloud.com/oauth/token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      grant_type: "authorization_code",
      code: req.query.code,
      redirect_uri: process.env.SSO_REDIRECT_URI,
      client_id: process.env.SSO_CLIENT_ID,
      client_secret: process.env.SSO_CLIENT_SECRET,
    }),
  });

  if (!tokenRes.ok) return res.status(401).send("ورود SSO ناموفق بود.");
  const tokens = await tokenRes.json();

  const userRes = await fetch("https://sso.eramcloud.com/oauth/userinfo", {
    headers: { Authorization: "Bearer " + tokens.access_token },
  });
  const profile = await userRes.json();

  // session محلی بسازید؛ دیگر username/password نخواهید
  req.session.user = {
    id: profile.sub,
    username: profile.preferred_username,
    email: profile.email,
    name: profile.name,
    role: profile.role,
  };
  res.redirect("/dashboard");
});

نمونه PHP:

<?php
$code = $_GET["code"] ?? "";
if (!$code) { http_response_code(403); exit("دسترسی ندارید"); }

$ch = curl_init("https://sso.eramcloud.com/oauth/token");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode([
    "grant_type" => "authorization_code",
    "code" => $code,
    "redirect_uri" => getenv("SSO_REDIRECT_URI"),
    "client_id" => getenv("SSO_CLIENT_ID"),
    "client_secret" => getenv("SSO_CLIENT_SECRET"),
  ]),
]);
$tokens = json_decode(curl_exec($ch), true);

$ch = curl_init("https://sso.eramcloud.com/oauth/userinfo");
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer ".$tokens["access_token"]],
]);
$profile = json_decode(curl_exec($ch), true);

$_SESSION["sso_user"] = $profile;
header("Location: /admin");

می‌توانید به‌جای UserInfo، id_token را با کلیدهای https://sso.eramcloud.com/oauth/jwks و الگوریتم RS256 و kid=eram-sso-1 هم verify کنید. Issuer باید دقیقاً https://sso.eramcloud.com و Audience همان Client ID شما باشد.

۶) اطلاعات کاربر

این فیلدها در access token، id_token و UserInfo می‌آید:

Claimمعنی
subشناسه پایدار کاربر در SSO؛ برای مپ کردن اکانت داخلی از این استفاده کنید
preferred_usernameنام کاربری پورتال
emailایمیل
nameنام نمایشی
roleنقش داخل سرویس شما؛ همان مقداری که ادمین SSO هنگام دادن دسترسی نوشته
appslug سرویس

کاربر داخلی را با sub بسازید یا به‌روز کنید. پسورد جدا در دیتابیس پنل لازم نیست. سطح دسترسی داخل داشبورد را از role بخوانید.

۷) چک‌لیست پنل اختصاصی و ادمین وب‌سایت

  • مسیر /sso/callback (یا معادل) فقط سمت سرور پیاده شود.
  • اگر کاربر مستقیم به داشبورد آمد و session نداشت، به Authorize ریدایرکت شود؛ فرم رمز محلی نشان داده نشود.
  • فرم لاگین قدیمی را غیرفعال کنید یا فقط برای اضطرار نگه دارید.
  • متغیرهای محیطی: SSO_CLIENT_ID، SSO_CLIENT_SECRET، SSO_REDIRECT_URI، SSO_ISSUER=https://sso.eramcloud.com
  • برای بخش ادمین سایت، کل /admin را پشت همین SSO بگذارید.
  • خروج از پنل، session محلی را پاک کند. خروج سراسری از پورتال: https://sso.eramcloud.com/logout

۸) خطاهای رایج

نشانهعلت معمول
invalid_redirect_uriآدرس callback با مقدار ثبت‌شده یکی نیست (حتی اسلش آخر یا http/https)
invalid_clientClient ID/Secret اشتباه است یا سرویس در پورتال غیرفعال شده
access_deniedبرای این کاربر تیک دسترسی این سرویس زده نشده
invalid_grantcode منقضی، تکراری، یا redirect_uri در توکن با authorize فرق دارد
لوگو فرم لاگین پنل را باز می‌کندحالت ورود باید idp باشد و callback همان اولین Redirect URI

لینک قابل ارسال برای تیم‌های دیگر: https://sso.eramcloud.com/docs