자동 로그인 (#196)
자동 로그인 구현 - login_token 테이블 - reqNonce -> loginByToken - sha512(token + nonce) Reviewed-on: https://storage.hided.net/gitea/devsam/core/pulls/196 Co-authored-by: hide_d <hided62@gmail.com> Co-committed-by: hide_d <hided62@gmail.com>
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\API\Login;
|
||||
|
||||
use DateTime;
|
||||
use sammo\Session;
|
||||
use DateTimeInterface;
|
||||
use sammo\BaseAPI;
|
||||
use sammo\Json;
|
||||
use sammo\KakaoUtil;
|
||||
use sammo\RootDB;
|
||||
use sammo\TimeUtil;
|
||||
use sammo\Util;
|
||||
use sammo\Validator;
|
||||
|
||||
class LoginByID extends \sammo\BaseAPI
|
||||
{
|
||||
public function validateArgs(): ?string
|
||||
{
|
||||
$v = new Validator($this->args);
|
||||
$v
|
||||
->rule('required', [
|
||||
'username',
|
||||
'password'
|
||||
]);
|
||||
|
||||
if (!$v->validate()) {
|
||||
return $v->errorStr();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getRequiredSessionMode(): int
|
||||
{
|
||||
return BaseAPI::NO_SESSION;
|
||||
}
|
||||
|
||||
public function scrubToken(int $userID)
|
||||
{
|
||||
$RootDB = RootDB::db();
|
||||
$nowDate = TimeUtil::now();
|
||||
$RootDB->delete(
|
||||
'login_token',
|
||||
'user_id = %i AND expire_date < %s',
|
||||
$userID,
|
||||
$nowDate
|
||||
);
|
||||
$RootDB->query(
|
||||
'DELETE invalid FROM login_token AS invalid
|
||||
JOIN
|
||||
( SELECT id
|
||||
FROM login_token
|
||||
WHERE user_id = %i
|
||||
ORDER BY id DESC
|
||||
LIMIT 8,1
|
||||
) AS valid
|
||||
ON invalid.id < valid.id WHERE user_id = %i',
|
||||
$userID,
|
||||
$userID
|
||||
);
|
||||
}
|
||||
|
||||
public function addToken(int $userID)
|
||||
{
|
||||
$RootDB = RootDB::db();
|
||||
$nowDate = TimeUtil::now();
|
||||
$token = Util::randomStr(20);
|
||||
$RootDB->insert('login_token', [
|
||||
'user_id' => $userID,
|
||||
'base_token' => $token,
|
||||
'reg_ip' => Util::get_client_ip(true),
|
||||
'reg_date' => $nowDate,
|
||||
'expire_date' => TimeUtil::nowAddDays(7)
|
||||
]);
|
||||
$tokenID = $RootDB->insertId();
|
||||
return [$tokenID, $token];
|
||||
}
|
||||
|
||||
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag)
|
||||
{
|
||||
$RootDB = RootDB::db();
|
||||
if ($session->isLoggedIn()) {
|
||||
$session->logout();
|
||||
}
|
||||
|
||||
$username = $this->args['username'];
|
||||
$password = $this->args['password'];
|
||||
|
||||
$userInfo = $RootDB->queryFirstRow(
|
||||
'SELECT `no`, `id`, `name`, `grade`, `delete_after`, `acl`, oauth_id, oauth_type, oauth_info, token_valid_until ' .
|
||||
'from member where id=%s_username AND ' .
|
||||
'pw=sha2(concat(salt, %s_password, salt), 512)',
|
||||
[
|
||||
'username' => $username,
|
||||
'password' => $password
|
||||
]
|
||||
);
|
||||
|
||||
if (!$userInfo) {
|
||||
return '아이디나 비밀번호가 올바르지 않습니다.';
|
||||
}
|
||||
|
||||
$canLogin = $RootDB->queryFirstField('SELECT `LOGIN` FROM `system` WHERE `NO` = 1');
|
||||
if ($canLogin != 'Y' && $userInfo['grade'] < 5) {
|
||||
return '현재는 로그인이 금지되어있습니다!';
|
||||
}
|
||||
|
||||
|
||||
$nowDate = TimeUtil::now();
|
||||
if ($userInfo['delete_after']) {
|
||||
if ($userInfo['delete_after'] < $nowDate) {
|
||||
$RootDB->delete('member', 'no=%i', $userInfo['no']);
|
||||
return [
|
||||
'result' => false,
|
||||
'reqOTP' => false,
|
||||
'reason' => "기간 만기로 삭제되었습니다. 재 가입을 시도해주세요."
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'result' => false,
|
||||
'reqOTP' => false,
|
||||
'reason' => "삭제 요청된 계정입니다.[{$userInfo['delete_after']}]"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$RootDB->insert('member_log', [
|
||||
'member_no' => $userInfo['no'],
|
||||
'action_type' => 'login',
|
||||
'action' => Json::encode([
|
||||
'ip' => Util::get_client_ip(true),
|
||||
'type' => 'plain'
|
||||
])
|
||||
]);
|
||||
|
||||
if ($userInfo['oauth_type'] == 'KAKAO') {
|
||||
$oauthFailResult = KakaoUtil::kakaoOAuthCheck($userInfo);
|
||||
if ($oauthFailResult !== null) {
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], true, $userInfo['token_valid_until'], null, Json::decode($userInfo['acl'] ?? '{}'));
|
||||
[$oauthReqOTP, $oauthFailReason] = $oauthFailResult;
|
||||
return [
|
||||
'result' => false,
|
||||
'reqOTP' => $oauthReqOTP,
|
||||
'reason' => $oauthFailReason
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->scrubToken($userInfo['no']);
|
||||
$nextToken = $this->addToken($userInfo['no']);
|
||||
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], false, $userInfo['token_valid_until'], $nextToken[0], Json::decode($userInfo['acl'] ?? '{}'));
|
||||
|
||||
|
||||
return [
|
||||
'result' => true,
|
||||
'reqOTP' => false,
|
||||
'nextToken' => $nextToken,
|
||||
'reason' => '로그인 되었습니다.'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\API\Login;
|
||||
|
||||
use sammo\Session;
|
||||
use DateTimeInterface;
|
||||
use sammo\BaseAPI;
|
||||
use sammo\Json;
|
||||
use sammo\KakaoUtil;
|
||||
use sammo\RootDB;
|
||||
use sammo\TimeUtil;
|
||||
use sammo\Util;
|
||||
use sammo\Validator;
|
||||
|
||||
class LoginByToken extends LoginByID
|
||||
{
|
||||
|
||||
public function getRequiredSessionMode(): int
|
||||
{
|
||||
return \sammo\BaseAPI::NO_SESSION; //XXX: Token 때문에 엄밀히는 NO_SESSION이 아님
|
||||
}
|
||||
|
||||
public function validateArgs(): ?string
|
||||
{
|
||||
$v = new Validator($this->args);
|
||||
$v
|
||||
->rule('required', [
|
||||
'token_id',
|
||||
'hashedToken'
|
||||
]);
|
||||
|
||||
if (!$v->validate()) {
|
||||
return $v->errorStr();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag)
|
||||
{
|
||||
$loginNonce = $session->loginNonce ?? null;
|
||||
$loginNonceExpired = $session->loginNonceExpired ?? null;
|
||||
if (!is_string($loginNonce) || !is_string($loginNonceExpired)) {
|
||||
return '자동 로그인: 절차 오류';
|
||||
}
|
||||
|
||||
$RootDB = RootDB::db();
|
||||
if ($session->isLoggedIn()) {
|
||||
$session->logout();
|
||||
}
|
||||
|
||||
$token_id = $this->args['token_id'];
|
||||
|
||||
$token_info = $RootDB->queryFirstRow('SELECT * FROM login_token WHERE id = %i', $token_id);
|
||||
if (!$token_info) {
|
||||
return [
|
||||
'result' => false,
|
||||
'silent' => true,
|
||||
'reason' => 'failed'
|
||||
];
|
||||
}
|
||||
|
||||
$hashedToken = $this->args['hashedToken'];
|
||||
$tokenAnswer = hash('sha512', "{$token_info['base_token']}{$loginNonce}");
|
||||
if (strtolower($hashedToken) != strtolower($tokenAnswer)) {
|
||||
return [
|
||||
'result' => false,
|
||||
'silent' => true,
|
||||
'reason' => 'failed'
|
||||
];
|
||||
}
|
||||
|
||||
$userInfo = $RootDB->queryFirstRow(
|
||||
'SELECT `no`, `id`, `name`, `grade`, `delete_after`, `acl`, oauth_id, oauth_type, oauth_info, token_valid_until ' .
|
||||
'from member where NO = %i',
|
||||
$token_info['user_id']
|
||||
);
|
||||
if(!$userInfo){
|
||||
return '자동 로그인: 올바른 계정이 아닙니다.';
|
||||
}
|
||||
|
||||
$canLogin = $RootDB->queryFirstField('SELECT `LOGIN` FROM `system` WHERE `NO` = 1');
|
||||
if ($canLogin != 'Y' && $userInfo['grade'] < 5) {
|
||||
return '자동 로그인: 현재는 로그인이 금지되어있습니다!';
|
||||
}
|
||||
|
||||
$nowDate = TimeUtil::now();
|
||||
if ($userInfo['delete_after']) {
|
||||
if ($userInfo['delete_after'] < $nowDate) {
|
||||
$RootDB->delete('member', 'no=%i', $userInfo['no']);
|
||||
return [
|
||||
'result' => false,
|
||||
'silent' => true,
|
||||
'reason' => "기간 만기"
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
'result' => false,
|
||||
'silent' => true,
|
||||
'reason' => "삭제 요청된 계정[{$userInfo['delete_after']}]"
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$RootDB->insert('member_log', [
|
||||
'member_no' => $userInfo['no'],
|
||||
'action_type' => 'login',
|
||||
'action' => Json::encode([
|
||||
'ip' => Util::get_client_ip(true),
|
||||
'type' => 'auto'
|
||||
])
|
||||
]);
|
||||
|
||||
if ($userInfo['oauth_type'] == 'KAKAO') {
|
||||
$oauthFailResult = KakaoUtil::kakaoOAuthCheck($userInfo);
|
||||
if ($oauthFailResult !== null) {
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], true, $userInfo['token_valid_until'], null, Json::decode($userInfo['acl'] ?? '{}'));
|
||||
[$oauthReqOTP, $oauthFailReason] = $oauthFailResult;
|
||||
$RootDB->delete(
|
||||
'login_token',
|
||||
'id = %i', $token_id
|
||||
);
|
||||
return $oauthFailReason;
|
||||
}
|
||||
}
|
||||
|
||||
$session->login($userInfo['no'], $userInfo['id'], $userInfo['grade'], false, $userInfo['token_valid_until'], $token_id, Json::decode($userInfo['acl'] ?? '{}'));
|
||||
$this->scrubToken($userInfo['no']);
|
||||
|
||||
$nextDate = TimeUtil::nowAddDays(2);
|
||||
if($nextDate < $token_info['expire_date']){
|
||||
return [
|
||||
'result' => true,
|
||||
'silent' => true,
|
||||
'reason' => 'success'
|
||||
];
|
||||
}
|
||||
|
||||
$RootDB->delete(
|
||||
'login_token',
|
||||
'id = %i', $token_id
|
||||
);
|
||||
$nextToken = $this->addToken($token_id);
|
||||
return [
|
||||
'result' => true,
|
||||
'silent' => true,
|
||||
'nextToken' => $nextToken,
|
||||
'reason' => 'success'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace sammo\API\Login;
|
||||
|
||||
use sammo\Session;
|
||||
use DateTimeInterface;
|
||||
use sammo\BaseAPI;
|
||||
use sammo\Json;
|
||||
use sammo\KakaoUtil;
|
||||
use sammo\RootDB;
|
||||
use sammo\TimeUtil;
|
||||
use sammo\Util;
|
||||
use sammo\Validator;
|
||||
|
||||
class ReqNonce extends BaseAPI{
|
||||
|
||||
public function getRequiredSessionMode(): int {
|
||||
return \sammo\BaseAPI::NO_SESSION;
|
||||
}
|
||||
|
||||
public function validateArgs(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function launch(Session $session, ?DateTimeInterface $modifiedSince, ?string $reqEtag) {
|
||||
$loginNonce = Util::randomStr(16);
|
||||
$loginNonceExpired = TimeUtil::nowAddSeconds(2);
|
||||
$session->loginNonce = $loginNonce;
|
||||
$session->loginNonceExpired = $loginNonceExpired;
|
||||
return [
|
||||
'result'=>true,
|
||||
'loginNonce'=>$loginNonce,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class APIHelper
|
||||
|
||||
$sessionMode = $obj->getRequiredSessionMode();
|
||||
if ($sessionMode === BaseAPI::NO_SESSION) {
|
||||
$session = null;
|
||||
$session = Session::getInstance();//XXX: NoSession이면 진짜 NoSession이어야..?
|
||||
} else {
|
||||
if ($sessionMode & BaseAPI::REQ_GAME_LOGIN) {
|
||||
$session = Session::requireGameLogin();
|
||||
@@ -94,7 +94,7 @@ class APIHelper
|
||||
], $cacheResult === null ? Json::NO_CACHE : 0);
|
||||
}
|
||||
Json::die($result, $cacheResult === null ? Json::NO_CACHE : 0);
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
Json::dieWithReason($e->getMessage());
|
||||
} catch (mixed $e) {
|
||||
Json::dieWithReason(strval($e));
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace sammo;
|
||||
|
||||
abstract class BaseAPI
|
||||
{
|
||||
const NO_SESSION = 0;
|
||||
const REQ_LOGIN = 1;
|
||||
const REQ_GAME_LOGIN = 2;
|
||||
const REQ_READ_ONLY = 4;
|
||||
|
||||
protected array $args;
|
||||
protected string $rootPath;
|
||||
public function __construct(string $rootPath, array $args)
|
||||
{
|
||||
$this->rootPath = $rootPath;
|
||||
$this->args = $args;
|
||||
}
|
||||
abstract public function getRequiredSessionMode(): int;
|
||||
abstract function validateArgs(): ?string;
|
||||
|
||||
/** @return null|string|array */
|
||||
abstract function launch(Session $session, ?\DateTimeInterface $modifiedSince, ?string $reqEtag);
|
||||
|
||||
public function tryCache():?string{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace sammo;
|
||||
|
||||
use kakao\Kakao_REST_API_Helper;
|
||||
|
||||
class KakaoUtil
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
static function checkUsernameDup($username)
|
||||
{
|
||||
if (!$username) {
|
||||
return '계정명을 입력해주세요';
|
||||
}
|
||||
|
||||
$username = mb_strtolower($username, 'utf-8');
|
||||
$length = strlen($username);
|
||||
if ($length < 4 || $length > 64) {
|
||||
return '적절하지 않은 길이입니다.';
|
||||
}
|
||||
|
||||
$cnt = RootDB::db()->queryFirstField('SELECT count(no) FROM member WHERE `id` = %s LIMIT 1', $username);
|
||||
if ($cnt != 0) {
|
||||
return '이미 사용중인 계정명입니다';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static function checkNicknameDup($nickname)
|
||||
{
|
||||
if (!$nickname) {
|
||||
return '닉네임을 입력해주세요';
|
||||
}
|
||||
|
||||
$length = mb_strwidth($nickname, 'utf-8');
|
||||
if ($length < 1 || $length > 18) {
|
||||
return '적절하지 않은 길이입니다.';
|
||||
}
|
||||
|
||||
$cnt = RootDB::db()->queryFirstField('SELECT count(no) FROM member WHERE `name` = %s LIMIT 1', $nickname);
|
||||
if ($cnt != 0) {
|
||||
return '이미 사용중인 닉네임입니다';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static function checkEmailDup($email)
|
||||
{
|
||||
if (!$email) {
|
||||
return '이메일을 입력해주세요';
|
||||
}
|
||||
|
||||
$length = strlen($email);
|
||||
if ($length < 1 || $length > 64) {
|
||||
return '적절하지 않은 길이입니다.';
|
||||
}
|
||||
|
||||
$userInfo = RootDB::db()->queryFirstField('SELECT `no`, `delete_after` FROM member WHERE `email` = %s LIMIT 1', $email);
|
||||
if ($userInfo) {
|
||||
if (!$userInfo['delete_after']) {
|
||||
return '이미 사용중인 이메일입니다. 관리자에게 문의해주세요.';
|
||||
}
|
||||
|
||||
if ($userInfo['delete_after'] >= $userInfo) {
|
||||
return "삭제 요청된 계정입니다.[{$userInfo['delete_after']}]";
|
||||
}
|
||||
|
||||
//$userInfo['delete_after'] < $userInfo
|
||||
RootDB::db()->delete('member', 'no=%i', $userInfo['no']);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static function createOTPbyUserNO(int $userNo): bool
|
||||
{
|
||||
$userInfo = RootDB::db()->queryFirstRow('SELECT oauth_info FROM member WHERE no=%i', $userNo);
|
||||
if (!$userInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oauthInfo = Json::decode($userInfo['oauth_info']);
|
||||
if (!$oauthInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$accessToken = $oauthInfo['accessToken'];
|
||||
$OTPValue = $oauthInfo['OTPValue'] ?? null;
|
||||
$OTPTrialUntil = $oauthInfo['OTPTrialUntil'] ?? null;
|
||||
|
||||
$now = TimeUtil::now();
|
||||
|
||||
|
||||
if ($OTPTrialUntil && $OTPValue && $OTPTrialUntil > $now) {
|
||||
return true;
|
||||
}
|
||||
|
||||
[$OTPValue, $OTPTrialUntil] = static::createOTP($accessToken);
|
||||
|
||||
if (!$OTPValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oauthInfo['OTPValue'] = $OTPValue;
|
||||
$oauthInfo['OTPTrialUntil'] = $OTPTrialUntil;
|
||||
$oauthInfo['OTPTrialCount'] = 3;
|
||||
|
||||
RootDB::db()->update('member', [
|
||||
'oauth_info' => Json::encode($oauthInfo)
|
||||
], 'no=%i', $userNo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static function createOTP(string $accessToken): ?array
|
||||
{
|
||||
$restAPI = new Kakao_REST_API_Helper($accessToken);
|
||||
|
||||
$OTPValue = Util::randRangeInt(1000, 9999);
|
||||
$OTPTrialUntil = TimeUtil::nowAddSeconds(180);
|
||||
|
||||
$sendResult = $restAPI->talk_to_me_default([
|
||||
"object_type" => "text",
|
||||
"text" => "인증 코드는 $OTPValue 입니다. $OTPTrialUntil 이내에 입력해주세요.",
|
||||
"link" => [
|
||||
"web_url" => ServConfig::getServerBasepath(),
|
||||
"mobile_web_url" => ServConfig::getServerBasepath()
|
||||
],
|
||||
"button_title" => "로그인 페이지 열기"
|
||||
]);
|
||||
$sendResult['code'] = Util::array_get($sendResult['code'], 0);
|
||||
if ($sendResult['code'] < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [$OTPValue, $OTPTrialUntil];
|
||||
}
|
||||
|
||||
static function kakaoOAuthCheck(array $userInfo): ?array
|
||||
{
|
||||
|
||||
if (!\kakao\KakaoKey::REST_KEY) {
|
||||
return [false, '카카오 API 앱이 등록되지 않았습니다. 관리자에게 문의해 주세요.'];
|
||||
}
|
||||
|
||||
$oauthID = $userInfo['oauth_id'];
|
||||
$oauthInfo = Json::decode($userInfo['oauth_info']) ?? [];
|
||||
if (!$oauthInfo) {
|
||||
return [false, 'OAuth 정보가 보관되어 있지 않습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
|
||||
$accessToken = $oauthInfo['accessToken'] ?? null;
|
||||
$refreshToken = $oauthInfo['refreshToken'] ?? null;
|
||||
$accessTokenValidUntil = $oauthInfo['accessTokenValidUntil'] ?? null;
|
||||
$refreshTokenValidUntil = $oauthInfo['refreshTokenValidUntil'] ?? null;
|
||||
$OTPValue = $oauthInfo['OTPValue'] ?? null;
|
||||
$OTPTrialUntil = $oauthInfo['OTPTrialUntil'] ?? null;
|
||||
$tokenValidUntil = $userInfo['token_valid_until'];
|
||||
|
||||
if (!$accessToken || !$refreshToken || !$accessTokenValidUntil || !$refreshTokenValidUntil) {
|
||||
return [false, 'OAuth 정보가 보관되어 있지 않습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
|
||||
$now = TimeUtil::now();
|
||||
|
||||
if ($now > $refreshTokenValidUntil) {
|
||||
return [false, '로그인 토큰이 만료되었습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
|
||||
if ($now > $accessTokenValidUntil) {
|
||||
$apiHelper = new Kakao_REST_API_Helper($accessToken);
|
||||
$refreshResult = $apiHelper->refresh_access_token($refreshToken);
|
||||
if (!$refreshResult) {
|
||||
return [false, '로그인 토큰 자동 갱신을 실패했습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
|
||||
$accessToken = $refreshResult['access_token'] ?? null;
|
||||
|
||||
if (!$accessToken) {
|
||||
trigger_error("refreshToken 에러 " . Json::encode($refreshResult) . "," . $refreshToken . "," . substr(\kakao\KakaoKey::REST_KEY, 0, 6), E_USER_NOTICE);
|
||||
return [false, '로그인 토큰 자동 갱신을 실패했습니다. 카카오 로그인을 수행해 주세요.'];
|
||||
}
|
||||
$accessTokenValidUntil = TimeUtil::nowAddSeconds($refreshResult['expires_in']);
|
||||
|
||||
$oauthInfo['accessToken'] = $accessToken;
|
||||
$oauthInfo['accessTokenValidUntil'] = $accessTokenValidUntil;
|
||||
|
||||
$refreshToken = $refreshResult['refresh_token'] ?? null;
|
||||
if ($refreshToken) {
|
||||
$refreshTokenValidUntil = TimeUtil::nowAddSeconds($refreshResult['refresh_token_expires_in']);
|
||||
|
||||
$oauthInfo['refreshToken'] = $refreshToken;
|
||||
$oauthInfo['refresh_token_expires_in'] = $refreshTokenValidUntil;
|
||||
}
|
||||
|
||||
RootDB::db()->update('member', [
|
||||
'oauth_info' => Json::encode($oauthInfo)
|
||||
], 'no=%i', $userInfo['no']);
|
||||
}
|
||||
|
||||
if ($tokenValidUntil && $now <= $tokenValidUntil) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//인증 시스템 가동
|
||||
$session = Session::getInstance();
|
||||
$session->access_token = $accessToken;
|
||||
$session->expires = $accessTokenValidUntil;
|
||||
$session->refresh_token = $refreshToken;
|
||||
$session->refresh_token_expires = $refreshTokenValidUntil;
|
||||
|
||||
if (!createOTPbyUserNO($userInfo['no'])) {
|
||||
return [false, '인증 코드를 보내는데 실패했습니다.'];
|
||||
}
|
||||
|
||||
return [true, '인증 코드를 입력해주세요'];
|
||||
}
|
||||
}
|
||||
+16
-10
@@ -10,8 +10,9 @@ namespace sammo;
|
||||
* @property string $ip IP
|
||||
* @property bool $reqOTP 인증 코드 필요
|
||||
* @property array $acl 권한
|
||||
* @property int|null $tokenID 토큰ID
|
||||
* @property string $tokenValidUntil 로그인 토큰 길이
|
||||
*
|
||||
*
|
||||
* @property int $generalID 장수 번호 (게임 로그인 필요)
|
||||
* @property string $generalName 장수 이름 (게임 로그인 필요)
|
||||
*/
|
||||
@@ -35,7 +36,7 @@ class Session
|
||||
const GAME_KEY_GENERAL_ID = '_g_no';
|
||||
const GAME_KEY_GENERAL_NAME = '_g_name';
|
||||
const GAME_KEY_EXPECTED_DEADTIME = '_g_deadtime';
|
||||
|
||||
|
||||
|
||||
private $writeClosed = false;
|
||||
private $sessionID = null;
|
||||
@@ -170,7 +171,7 @@ class Session
|
||||
return Util::array_get($_SESSION[$name]);
|
||||
}
|
||||
|
||||
public function login(int $userID, string $userName, int $grade, bool $reqOTP, ?string $tokenValidUntil, array $acl): Session
|
||||
public function login(int $userID, string $userName, int $grade, bool $reqOTP, ?string $tokenValidUntil, ?int $tokenID, array $acl): Session
|
||||
{
|
||||
$this->set('userID', $userID);
|
||||
$this->set('userName', $userName);
|
||||
@@ -180,6 +181,7 @@ class Session
|
||||
$this->set('acl', $acl);
|
||||
$this->set('reqOTP', $reqOTP);
|
||||
$this->set('tokenValidUntil', $tokenValidUntil);
|
||||
$this->set('tokenID', $tokenID);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -197,7 +199,11 @@ class Session
|
||||
if (class_exists('\\sammo\\UniqueConst')) {
|
||||
$this->logoutGame();
|
||||
}
|
||||
|
||||
|
||||
if($this->tokenID??null){
|
||||
RootDB::db()->delete('login_token', 'id = %i', $this->tokenID);
|
||||
}
|
||||
|
||||
$this->set('userID', null);
|
||||
$this->set('userName', null);
|
||||
$this->set('userGrade', null);
|
||||
@@ -261,7 +267,7 @@ class Session
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
$turnterm = $gameStor->turnterm;
|
||||
$isUnited = $gameStor->isunited != 0;
|
||||
|
||||
@@ -319,7 +325,7 @@ class Session
|
||||
} else {
|
||||
$obj = self::getInstance();
|
||||
}
|
||||
|
||||
|
||||
return $obj->userGrade;
|
||||
}
|
||||
|
||||
@@ -335,12 +341,12 @@ class Session
|
||||
} else {
|
||||
$obj = self::getInstance();
|
||||
}
|
||||
|
||||
|
||||
return $obj->userID;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static function getGeneralID(bool $requireLogin = false, string $exitPath = '..')
|
||||
{
|
||||
@@ -349,7 +355,7 @@ class Session
|
||||
} else {
|
||||
$obj = self::getInstance();
|
||||
}
|
||||
|
||||
|
||||
return $obj->generalID??0;
|
||||
}
|
||||
|
||||
@@ -367,7 +373,7 @@ class Session
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($this->userID) {
|
||||
return true;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user