false, 'silent' => false, 'reqOTP' => true, 'reason' => $reason, ]; } public static function shouldDiscardTokenAfterKakaoFailure(bool $reqOTP): bool { return !$reqOTP; } public static function pendingSessionTokenID(bool $reqOTP, int $tokenID): ?int { return $reqOTP ? $tokenID : null; } public function getRequiredSessionMode(): int { return \sammo\BaseAPI::NO_LOGIN; } 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): null | string | array | APIRecoveryType { $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) { [$oauthReqOTP, $oauthFailReason] = $oauthFailResult; $session->login( $userInfo['no'], $userInfo['name'], $userInfo['grade'], true, $userInfo['token_valid_until'], static::pendingSessionTokenID($oauthReqOTP, $token_id), Json::decode($userInfo['acl'] ?? '{}') ); if (static::shouldDiscardTokenAfterKakaoFailure($oauthReqOTP)) { $RootDB->delete( 'login_token', 'id = %i', $token_id ); } return static::kakaoFailureResponse($oauthReqOTP, $oauthFailReason); } } $session->login($userInfo['no'], $userInfo['name'], $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($userInfo['no']); return [ 'result' => true, 'silent' => true, 'nextToken' => $nextToken, 'reason' => 'success' ]; } }