認証

Cliqerは、POST /api/auth/loginで発行されるhttpOnlyのセッションCookie(session_token)で認証を行います。ブラウザはすべてのリクエストでこのCookieを自動的に送信し、サーバー間連携のツールではCookie Jarに保持します。Studio 8KおよびEnterpriseのチームは、サーバー間アクセス用のAPIキーを追加で発行することもできます。APIキーはAuthorization: Bearer cliqer_<key>として送信します。詳しくは後述のAPIキー認証を参照してください。
セッションモデル
| 認証情報 | 保存場所 | 取得方法 |
|---|---|---|
セッションCookie(session_token) | Cliqerホスト上のhttpOnlyかつSecureなCookie | POST /api/auth/loginで設定され、サーバーによって自動的にローテーションされます |
このCookieはHttpOnlyのため、JavaScriptから読み取ることはできません。有効期間はサーバーのセッションTTLに従い、GET /api/auth/meを呼び出すたびに透過的に更新されます。別途呼び出すリフレッシュ用エンドポイントはありません。長時間稼働するクライアントでは、GET /api/auth/meを定期的に呼び出してセッションを維持してください。
セッションのライフサイクル
セッションの使用
ブラウザはCookieを自動的に付与します。サーバー間クライアントの場合は、ログインのレスポンスで設定されたCookieを保持し、以降のリクエストで送信してください。
# Save the session cookie on login, then reuse it
curl -X POST "$BASE_URL/api/auth/login" \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"email":"user@example.com","password":"your_password"}'
curl -X GET "$BASE_URL/api/auth/me" -b cookies.txt
// Same-origin $fetch sends the httpOnly cookie automatically.
// Cross-origin fetch must opt in with credentials: 'include'.
const me = await $fetch('/api/auth/me')
import requests
session = requests.Session() # persists the session cookie
session.post('$BASE_URL/api/auth/login',
json={'email': 'user@example.com', 'password': 'your_password'})
me = session.get('$BASE_URL/api/auth/me').json()
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
// Login stores the session cookie in the jar; later requests reuse it.
client.Post("$BASE_URL/api/auth/login", "application/json", body)
resp, _ := client.Get("$BASE_URL/api/auth/me")
$ch = curl_init('$BASE_URL/api/auth/login');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'email' => 'user@example.com', 'password' => 'your_password'
]));
curl_exec($ch);
// reqwest with cookie_store persists the session cookie across calls
let client = reqwest::Client::builder()
.cookie_store(true)
.build()?;
client.post("$BASE_URL/api/auth/login")
.json(&serde_json::json!({"email": "user@example.com", "password": "your_password"}))
.send().await?;
let me = client.get("$BASE_URL/api/auth/me").send().await?;
APIキー認証
Studio 8KおよびEnterpriseのチームは、サーバー間アクセス用に長期間有効なAPIキーを発行できます。ユーザーのログイン認証情報を保持する代わりとなる手段です。キーの作成と失効は、ダッシュボードのチーム → APIキー(/dash/team/api-keys)で行います。発行にはチームオーナー(またはAPIキー権限が付与されたカスタムロール)が必要です。キー管理はStudio 8KおよびEnterpriseプランでのみ利用できます。


キーは作成時に一度だけ、cliqer_<prefix>_<secret>の形式で表示されます。安全に保管してください。キーは復元できないため、紛失した場合は失効させてから再作成してください。セッションを受け付けるすべてのRESTエンドポイントで、Bearerトークンとして送信します:
curl -X GET "$BASE_URL/api/licenses/status?license_key=YOUR_LICENSE_KEY" \
-H "Authorization: Bearer cliqer_ab12cd34_your_secret_here"
APIキーはRESTエンドポイントでのみ使用できます。WebSocket(スライド操作を含む)にはセッショントークンが必要です。
キーは作成したユーザーとして認証され、そのユーザーのチームとプランの範囲に限定されます。失効済みまたは不明なキーは、セッションがない場合とまったく同じく401を返します。管理者専用のエンドポイントは、APIキーを常に403で拒否します。キーにはセッションリクエストと同じレート制限が適用されます。
ログイン
ユーザーの認証情報をセッションと交換します。成功すると、レスポンスでsession_token Cookieが設定されます。
エンドポイント: POST /api/auth/login
| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
email | string | はい | ユーザーのメールアドレス(WordPressのユーザー名も使用できます) |
password | string | はい | ユーザーのパスワード |
otp | string | いいえ | アカウントが認証アプリを使用している場合のTOTPコード |
email_otp | string | いいえ | アカウントがメールによる2FAを使用している場合の、メールで届いたPIN |
curl -X POST "$BASE_URL/api/auth/login" \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{"email":"user@example.com","password":"your_password"}'
interface LoginResponse {
data: {
expires: number | null // session lifetime in ms
}
}
const res: LoginResponse = await $fetch('/api/auth/login', {
method: 'POST',
body: { email: 'user@example.com', password: 'your_password' }
})
// The session_token cookie is set by the response; no token is in the body.
import requests
session = requests.Session()
data = {'email': 'user@example.com', 'password': 'your_password'}
res = session.post('$BASE_URL/api/auth/login', json=data)
# The session cookie now lives in `session`.
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
payload := map[string]string{"email": "user@example.com", "password": "your_password"}
body, _ := json.Marshal(payload)
client.Post("$BASE_URL/api/auth/login", "application/json", bytes.NewBuffer(body))
$ch = curl_init('$BASE_URL/api/auth/login');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'email' => 'user@example.com',
'password' => 'your_password'
]));
$response = curl_exec($ch);
use serde::Serialize;
#[derive(Serialize)]
struct LoginRequest { email: String, password: String }
let client = reqwest::Client::builder().cookie_store(true).build()?;
let response = client
.post("$BASE_URL/api/auth/login")
.json(&LoginRequest { email: "user@example.com".into(), password: "your_password".into() })
.send()
.await?;
レスポンス(成功時): session_token Cookieを設定し、セッションのメタデータを返します。
{
"data": {
"expires": 604800000
}
}
レスポンス(2FAが必要な場合): エラーではなく、200による制御フローのシグナルです。同じ認証情報にotp(認証アプリ)またはemail_otp(メールで届いたPIN)を加えて、POST /api/auth/loginを再送信してください。
{
"requires_otp": true,
"method": "totp",
"email_sent": false
}
methodはtotp、email、bothのいずれかです。emailの場合、サーバーはPINをメールで送信し、email_sentはそのメールが送信されたかどうかを示します。bothのアカウントは認証アプリのコード(otp)でサインインします。このアカウントでは、メールで届いたPINはログインの認証要素になりません。
401を返します。TOTPコードが誤っているか期限切れの場合も同様です。メールで届いたPINが誤っているか期限切れの場合も、パスワードが誤っている場合と同じメッセージで401を返します。シングルサインオンを必須とする組織のメンバーには403が返され、SSOでサインインする必要があります(SSO FAQを参照)。セッションのローテーション
別途のリフレッシュ用エンドポイントはありません。サーバーがsession_token Cookieを自動的にローテーションします。セッションの有効期限が十分に近づくと、GET /api/auth/meはプロフィールとともに新しいSet-Cookieを返します。Cookie Jarを保持し、GET /api/auth/meを定期的に呼び出すクライアントは、追加の作業なしに無期限にサインイン状態を維持します。
現在のユーザー
認証済みユーザーのプロフィールを返します。有効なセッションがない場合は{ "data": null }を返します(匿名アクセスに対応しており、401を返すことはありません)。
エンドポイント: GET /api/auth/me
curl -X GET "$BASE_URL/api/auth/me" -b cookies.txt
const { data } = await $fetch('/api/auth/me')
if (data) console.log(data.email, data.is_admin)
me = session.get('$BASE_URL/api/auth/me').json()
resp, _ := client.Get("$BASE_URL/api/auth/me")
$ch = curl_init('$BASE_URL/api/auth/me');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
$response = curl_exec($ch);
let me = client.get("$BASE_URL/api/auth/me").send().await?;
レスポンス:
{
"data": {
"id": "uuid",
"email": "user@example.com",
"first_name": "Jane",
"last_name": "Doe",
"role": "uuid",
"tfa_method": "none",
"is_admin": false
}
}
ログアウト
現在のCookieに対応するセッションを終了し、ブラウザからCookieを削除します。
エンドポイント: POST /api/auth/logout
curl -X POST "$BASE_URL/api/auth/logout" -b cookies.txt -c cookies.txt
await $fetch('/api/auth/logout', { method: 'POST' })
session.post('$BASE_URL/api/auth/logout')
req, _ := http.NewRequest("POST", "$BASE_URL/api/auth/logout", nil)
client.Do(req)
$ch = curl_init('$BASE_URL/api/auth/logout');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_exec($ch);
client.post("$BASE_URL/api/auth/logout").send().await?;
レスポンス:
{
"success": true
}
セキュリティのベストプラクティス
- HTTPSのみを使用する:セッションCookieは
Secureです。すべてのリクエストでTLS 1.2以上を使用する必要があります。 - JavaScriptからCookieを読み取らない:設計上
HttpOnlyになっています。その状態を維持してください。 - 先回りして更新する:長時間開いたままのタブでは、セッションの有効期限が切れる前に
GET /api/auth/meを呼び出してください。Cookieはサーバーがローテーションします。 - ログアウトで失効させる:
POST /api/auth/logoutはサーバー側でセッションを即座に終了します。 - セッションではなく認証情報を保管する:サーバー間連携では、ログイン認証情報をシークレットマネージャーに保管してセッションを再確立してください。Cookieをソースコードに保存してはいけません。