mirror of
https://github.com/openvk/openvk
synced 2024-12-22 16:42:32 +03:00
feat(OAuth): add oauth flow
This commit is contained in:
parent
0b80c0a6a8
commit
2bdb4f03d0
5 changed files with 251 additions and 0 deletions
|
@ -318,4 +318,42 @@ final class VKAPIPresenter extends OpenVKPresenter
|
||||||
header("Content-Length: $size");
|
header("Content-Length: $size");
|
||||||
exit($payload);
|
exit($payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderOAuthLogin() {
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$client = $this->queryParam("client_name");
|
||||||
|
$postmsg = $this->queryParam("prefers_postMessage") ?? '0';
|
||||||
|
$stale = $this->queryParam("accepts_stale") ?? '0';
|
||||||
|
$origin = NULL;
|
||||||
|
$url = $this->queryParam("redirect_uri");
|
||||||
|
if(is_null($url) || is_null($client))
|
||||||
|
exit("<b>Error:</b> redirect_uri and client_name params are required.");
|
||||||
|
|
||||||
|
if($url != "about:blank") {
|
||||||
|
if(!filter_var($url, FILTER_VALIDATE_URL))
|
||||||
|
exit("<b>Error:</b> Invalid URL passed to redirect_uri.");
|
||||||
|
|
||||||
|
$parsedUrl = (object) parse_url($url);
|
||||||
|
if($parsedUrl->scheme != 'https' && $parsedUrl->scheme != 'http')
|
||||||
|
exit("<b>Error:</b> redirect_uri should either point to about:blank or to a web resource.");
|
||||||
|
|
||||||
|
$origin = "$parsedUrl->scheme://$parsedUrl->host";
|
||||||
|
if(!is_null($parsedUrl->port ?? NULL))
|
||||||
|
$origin .= ":$parsedUrl->port";
|
||||||
|
|
||||||
|
$url .= strpos($url, '?') === false ? '?' : '&';
|
||||||
|
} else {
|
||||||
|
$url .= "#";
|
||||||
|
if($postmsg == '1') {
|
||||||
|
exit("<b>Error:</b> prefers_postMessage can only be set if redirect_uri is not about:blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->template->clientName = $client;
|
||||||
|
$this->template->usePostMessage = $postmsg == '1';
|
||||||
|
$this->template->acceptsStale = $stale == '1';
|
||||||
|
$this->template->origin = $origin;
|
||||||
|
$this->template->redirectUri = $url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
193
Web/Presenters/templates/VKAPI/OAuthLogin.xml
Normal file
193
Web/Presenters/templates/VKAPI/OAuthLogin.xml
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Получение доступа | OpenVK</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
background-color: #ebedf0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 0 10px #d4d6d8;
|
||||||
|
padding: 13px 0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > div {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 620px;
|
||||||
|
margin: auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ovkUser a:not(#pfpLink) {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ovkUser img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ovkLogo a {
|
||||||
|
display: flex;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #fff;
|
||||||
|
background: #606060;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 21px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > div {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 620px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
border: 1px solid #e1e3e6;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 18px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authHeading, #authExplainer {
|
||||||
|
color: #818c99;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(#authHeading, #authExplainer) b {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authButtons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authButtons a {
|
||||||
|
color: #818c99;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authButtons a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#authButtons button {
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
border: none;
|
||||||
|
background-color: #606060;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 7px;
|
||||||
|
border-radius: 7px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<div style="width: 85px;">
|
||||||
|
</div>
|
||||||
|
<div id="ovkLogo">
|
||||||
|
<a href="/" target="_blank">O</a>
|
||||||
|
</div>
|
||||||
|
<div id="ovkUser">
|
||||||
|
<a href="/logout?hash={rawurlencode($csrfToken)}">{_header_log_out}</a>
|
||||||
|
<a id="pfpLink" href="/id0" target="_blank">
|
||||||
|
<img src="{$thisUser->getAvatarUrl('miniscule')}" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<main>
|
||||||
|
<div id="authHeading">
|
||||||
|
{_app},
|
||||||
|
<b>
|
||||||
|
{if is_null($origin)}
|
||||||
|
{tr("identifies_itself_as", $clientName)}{else}
|
||||||
|
{tr("located_at_url", $origin)}{/if}</b>, {_wants_your_token}.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="authExplainer">
|
||||||
|
<b>{_app_will_have_access_to}</b><br/>
|
||||||
|
{_oauth_scope_all|noescape}.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="authButtons">
|
||||||
|
<button id="authAllow">{_oauth_grant}</button>
|
||||||
|
<a id="authDeny" href="javascript:void(0)">{_oauth_deny}</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{script "js/node_modules/msgpack-lite/dist/msgpack.min.js"}
|
||||||
|
{script "js/al_api.js"}
|
||||||
|
<script>
|
||||||
|
//<![CDATA[
|
||||||
|
let clientName = {$clientName};
|
||||||
|
let usePostMessage = {$usePostMessage} && window.opener != null;
|
||||||
|
let acceptsStale = {$acceptsStale};
|
||||||
|
let origin = {$origin};
|
||||||
|
let redirectUri = {$redirectUri};
|
||||||
|
|
||||||
|
document.querySelector("#authDeny").addEventListener("click", () => {
|
||||||
|
if (usePostMessage) {
|
||||||
|
window.opener.postMessage({
|
||||||
|
error: 'access_denied',
|
||||||
|
error_reason: 'user_denied',
|
||||||
|
error_description: 'User denied your request'
|
||||||
|
}, origin);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = redirectUri + 'error=access_denied&error_reason=user_denied&error_description=User%20denied%20your%20request';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelector("#authAllow").addEventListener("click", async () => {
|
||||||
|
let response = await API.Apps.getRegularToken(clientName, acceptsStale);
|
||||||
|
let ret = {
|
||||||
|
access_token: response.token,
|
||||||
|
expires_in: 0,
|
||||||
|
user_id: {$thisUser->getId()},
|
||||||
|
is_stale: response.is_stale
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usePostMessage) {
|
||||||
|
window.opener.postMessage(ret, origin);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.href = redirectUri + (new URLSearchParams(ret)).toString();
|
||||||
|
});
|
||||||
|
//]]>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -373,6 +373,8 @@ routes:
|
||||||
handler: "VKAPI->route"
|
handler: "VKAPI->route"
|
||||||
- url: "/token"
|
- url: "/token"
|
||||||
handler: "VKAPI->tokenLogin"
|
handler: "VKAPI->tokenLogin"
|
||||||
|
- url: "/authorize"
|
||||||
|
handler: "VKAPI->OAuthLogin"
|
||||||
- url: "/admin/sandbox"
|
- url: "/admin/sandbox"
|
||||||
handler: "About->sandbox"
|
handler: "About->sandbox"
|
||||||
- url: "/admin/chandler/groups"
|
- url: "/admin/chandler/groups"
|
||||||
|
|
|
@ -775,6 +775,15 @@
|
||||||
"disable_2fa" = "Turn off 2FA";
|
"disable_2fa" = "Turn off 2FA";
|
||||||
"viewing" = "View";
|
"viewing" = "View";
|
||||||
|
|
||||||
|
/* OAuth */
|
||||||
|
"identifies_itself_as" = "that identifies itself as $1";
|
||||||
|
"located_at_url" = "located at $1";
|
||||||
|
"wants_your_token" = "wants to access your account";
|
||||||
|
"app_will_have_access_to" = "App will have access to:";
|
||||||
|
"oauth_scope_all" = "profile information, status, list of friends, photos, wall posts, audios, videos, notifications, fishing rod handle, messages, gifts, <b>your e-mail</b>, polls, communities, discussions, notes, <b>payment method</b>, likes and comments";
|
||||||
|
"oauth_grant" = "Allow";
|
||||||
|
"oauth_deny" = "Deny";
|
||||||
|
|
||||||
/* Sorting */
|
/* Sorting */
|
||||||
|
|
||||||
"sort_randomly" = "Sort randomly";
|
"sort_randomly" = "Sort randomly";
|
||||||
|
|
|
@ -737,6 +737,15 @@
|
||||||
"disable_2fa" = "Отключить 2FA";
|
"disable_2fa" = "Отключить 2FA";
|
||||||
"viewing" = "Просмотреть";
|
"viewing" = "Просмотреть";
|
||||||
|
|
||||||
|
/* OAuth */
|
||||||
|
"identifies_itself_as" = "идентифицирующее себя как $1";
|
||||||
|
"located_at_url" = "располагающееся по адресу $1";
|
||||||
|
"wants_your_token" = "запрашивает доступ к вашему аккаунту";
|
||||||
|
"app_will_have_access_to" = "Приложению будут доступны:";
|
||||||
|
"oauth_scope_all" = "информация страницы, обновление статуса, список друзей, фотографии, публикация записей, аудиозаписи, видео, уведомления, сообщения, подарки, <b>ваш адрес электронной почты</b>, опросы, группы, обсуждения, заметки, <b>голоса</b>, лайки и комментарии";
|
||||||
|
"oauth_grant" = "Разрешить";
|
||||||
|
"oauth_deny" = "Отмена";
|
||||||
|
|
||||||
/* Sorting */
|
/* Sorting */
|
||||||
|
|
||||||
"sort_randomly" = "Сортировать случайно";
|
"sort_randomly" = "Сортировать случайно";
|
||||||
|
|
Loading…
Reference in a new issue