###実現したいこと
PHPで複数のWEBサービスを運営しています。
実現したいのはサービスAでログインしているユーザーをサービスBにもログインさせることです。
自分なりに方法を考えてみましたが、これは安全でしょうか?
また、別にどのような方法が考えられるでしょうか?
よろしくお願いします。
###前提
- AとBどちらもID・パスワードでログインする
- 同一人物がAとB両方に登録している場合、ログインID・パスワードはAとBで共通
- AとBは別サーバー、別ドメインであるためセッションは共有できない
- AとBのDBサーバーは同じ(DBは別)
- Aにログインすると同時にBでログイン状態になる必要はない(AからBにログインするときはリンクをクリックする)
追記
- ログイン後にA⇔Bで行き来したい(「Aへ」「Bへ」のようなリンクで移動できればOK)
- AとBのDBは別である必要がある(AでもBでもない別DBから共通のviewは作成できる)
追記ここまで
###自分なりに考えた方法
ログインIDとトークンを使用する方法を考えました。
A→Bにログインする場合
最初にトークンを保存するテーブルを作成します。
テーブル構造
- id: autoincrement 主キー
- login_id: varchar
- content: varchar ランダムな文字列
md5(uniqid(mt_rand(), true))
- expiration_date: datetime トークンの有効期限(トークン作成から10秒後)
- is_used: tinyint 未使用=0, 使用済み=1
####Bにtokenを発行するapiを追加
他者にapiを使われないようにurlに?key=abcd1234のような固定のキーを持たせ、一致した場合のみ処理をするようにします。
以下のようなイメージです。
function createTokenAction($request) { $key = $request->get('key'); $loginId = $request->get('login_id'); if ($key !== self::KEY) { throw new \Exception(); } $token = new Token; $token->loginId = $loginId; $token->content = md5(uniqid(mt_rand(), true)); $token->expirationDate = date('Y-m-d H:i:s', strtotime('+10 seconds')) $token->isUsed = 0; $token->save(); return json_encode(array( 'token' => $token->content, )); }
####BにログインIDとtokenを使用したログインを追加
URL: b.com/loginWithToken/?login_id=hoge&token=abcd1234
functin loginWithTokenAction($request) { $loginId = $request->get('login_id'); $tokenContent = $request->get('token'); $token = Token::findBy(array( 'loginId' => $loginId, 'content' => $tokenContent, )); if (!$token) { throw new \NotFoundExpcetion(); } $now = new \DateTime(); if ($now > $token->expirationDate || $token->isUsed) { throw new \Exception(); } $token->isUsed = 1; $token->save(); // ログインさせる Auth::login($loginId); $this->redirectHome(); }
####AにBへのログイン処理を追加
Bのトークン作成apiでトークンを取得し、BのloginWithTokenにリダイレクトします。
URL: a.com/loginToB
function loginToBAction($request) { $key = self::KEY; $loginId = $this->getUser()->loginId; $url = sprintf('https://b.com/createToken/?key=%s&login_id=%s', $key, $loginId); $json = file_get_contents($url); $data = json_decode($json, true); $token = $data['token']; $url = sprintf('https://b.com/loginWithToken/?login_id=%s&token=%s', $loginId, $token); return $this->redirect($url); }
以上のような方法でBへのログインが実現できそうですが、この方法が安全なのか、また、他にもっといい方法が無いか、教えていただきたいです。