Improved flow for using OAuth2 while logged out.
This commit is contained in:
parent
8be630531a
commit
8f63b57c0c
9 changed files with 35 additions and 93 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
20250202
|
||||
20250202.1
|
||||
|
|
|
@ -161,8 +161,14 @@ const MszOAuth2Authorise = async () => {
|
|||
const { body } = await $x.get(`/oauth2/resolve-authorise-app?${resolveParams}`, { authed: true, csrf: true, type: 'json' });
|
||||
if(!body)
|
||||
throw 'authorisation resolve failed';
|
||||
if(typeof body.error === 'string')
|
||||
if(typeof body.error === 'string') {
|
||||
if(body.error === 'auth') {
|
||||
window.location.assign(`/auth/login.php?oauth2=1&redirect=${encodeURIComponent(`${window.location.pathname}${window.location.search}`)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
return translateError(body.error, body);
|
||||
}
|
||||
|
||||
const userHeader = new MszOAuth2UserHeader(body.user);
|
||||
header.setElement(userHeader);
|
||||
|
|
|
@ -104,9 +104,10 @@ const MszOAuth2Verify = () => {
|
|||
|
||||
if(typeof body.error === 'string') {
|
||||
// TODO: nicer errors
|
||||
if(body.error === 'auth')
|
||||
alert('You are not logged in.');
|
||||
else if(body.error === 'csrf')
|
||||
if(body.error === 'auth') {
|
||||
window.location.assign(`/auth/login.php?oauth2=1&redirect=${encodeURIComponent(`${window.location.pathname}${window.location.search}`)}`);
|
||||
return;
|
||||
} else if(body.error === 'csrf')
|
||||
alert('Request verification failed, please refresh and try again.');
|
||||
else if(body.error === 'code')
|
||||
alert('This code is not associated with any authorisation request.');
|
||||
|
|
|
@ -120,7 +120,7 @@ while(!empty($_POST['login']) && is_array($_POST['login'])) {
|
|||
|
||||
if($userInfo->hasTOTP) {
|
||||
$tfaToken = $msz->authCtx->tfaSessions->createToken($userInfo);
|
||||
Tools::redirect($msz->urls->format('auth-two-factor', ['token' => $tfaToken]));
|
||||
Tools::redirect($msz->urls->format('auth-two-factor', ['token' => $tfaToken, 'redirect' => $loginRedirect]));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -149,6 +149,7 @@ while(!empty($_POST['login']) && is_array($_POST['login'])) {
|
|||
}
|
||||
|
||||
$welcomeMode = !empty($_GET['welcome']);
|
||||
$oauth2Mode = !empty($_GET['oauth2']);
|
||||
$loginUsername = !empty($_POST['login']['username']) && is_string($_POST['login']['username']) ? $_POST['login']['username'] : (
|
||||
!empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : ''
|
||||
);
|
||||
|
@ -163,6 +164,7 @@ Template::render('auth.login', [
|
|||
'login_can_register' => $canRegisterAccount,
|
||||
'login_attempts_remaining' => $remainingAttempts,
|
||||
'login_welcome' => $welcomeMode,
|
||||
'login_oauth2' => $oauth2Mode,
|
||||
'login_private' => [
|
||||
'enabled' => $siteIsPrivate,
|
||||
'message' => $sitePrivateMessage,
|
||||
|
|
|
@ -26,7 +26,7 @@ class LegacyRoutes implements RouteHandler, UrlSource {
|
|||
$urls->register('auth-reset', '/auth/password.php', ['user' => '<user>']);
|
||||
$urls->register('auth-logout', '/auth/logout.php', ['csrf' => '<csrf>']);
|
||||
$urls->register('auth-resolve-user', '/auth/login.php', ['resolve' => '1', 'name' => '<username>']);
|
||||
$urls->register('auth-two-factor', '/auth/twofactor.php', ['token' => '<token>']);
|
||||
$urls->register('auth-two-factor', '/auth/twofactor.php', ['token' => '<token>', 'redirect' => '<redirect>']);
|
||||
$urls->register('auth-revert', '/auth/revert.php', ['csrf' => '<csrf>']);
|
||||
|
||||
$urls->register('forum-leaderboard', '/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']);
|
||||
|
|
|
@ -48,6 +48,7 @@ final class OAuth2ApiRoutes implements RouteHandler {
|
|||
* }|array{ error: string, error_description: string }
|
||||
*/
|
||||
#[HttpPost('/oauth2/request-authorise')]
|
||||
#[HttpPost('/oauth2/request-authorize')]
|
||||
public function postRequestAuthorise(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
$response->setHeader('Cache-Control', 'no-store');
|
||||
|
||||
|
|
|
@ -24,12 +24,6 @@ final class OAuth2WebRoutes implements RouteHandler {
|
|||
#[HttpGet('/oauth2/authorise')]
|
||||
#[HttpGet('/oauth2/authorize')]
|
||||
public function getAuthorise(HttpResponseBuilder $response, HttpRequest $request): string {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return Template::renderRaw('oauth2.login', [
|
||||
'login_url' => $this->urls->format('auth-login'),
|
||||
'register_url' => $this->urls->format('auth-register'),
|
||||
]);
|
||||
|
||||
return Template::renderRaw('oauth2.authorise');
|
||||
}
|
||||
|
||||
|
@ -50,14 +44,14 @@ final class OAuth2WebRoutes implements RouteHandler {
|
|||
|
||||
// TODO: RATE LIMITING
|
||||
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
|
||||
return ['error' => 'csrf'];
|
||||
|
||||
$response->setHeader('X-CSRF-Token', CSRF::token());
|
||||
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
$codeChallengeMethod = 'plain';
|
||||
if($request->content->hasParam('ccm')) {
|
||||
$codeChallengeMethod = $request->content->getParam('ccm');
|
||||
|
@ -164,14 +158,14 @@ final class OAuth2WebRoutes implements RouteHandler {
|
|||
public function getResolveAuthorise(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
// TODO: RATE LIMITING
|
||||
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
|
||||
return ['error' => 'csrf'];
|
||||
|
||||
$response->setHeader('X-CSRF-Token', CSRF::token());
|
||||
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
try {
|
||||
$appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(
|
||||
clientId: (string)$request->getParam('client'),
|
||||
|
@ -235,12 +229,6 @@ final class OAuth2WebRoutes implements RouteHandler {
|
|||
|
||||
#[HttpGet('/oauth2/verify')]
|
||||
public function getVerify(HttpResponseBuilder $response, HttpRequest $request): string {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return Template::renderRaw('oauth2.login', [
|
||||
'login_url' => $this->urls->format('auth-login'),
|
||||
'register_url' => $this->urls->format('auth-register'),
|
||||
]);
|
||||
|
||||
return Template::renderRaw('oauth2.verify');
|
||||
}
|
||||
|
||||
|
@ -260,14 +248,14 @@ final class OAuth2WebRoutes implements RouteHandler {
|
|||
|
||||
// TODO: RATE LIMITING
|
||||
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
|
||||
return ['error' => 'csrf'];
|
||||
|
||||
$response->setHeader('X-CSRF-Token', CSRF::token());
|
||||
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
$approve = (string)$request->content->getParam('approve');
|
||||
if(!in_array($approve, ['yes', 'no']))
|
||||
return ['error' => 'invalid'];
|
||||
|
@ -356,14 +344,14 @@ final class OAuth2WebRoutes implements RouteHandler {
|
|||
public function getResolveVerify(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
// TODO: RATE LIMITING
|
||||
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
|
||||
return ['error' => 'csrf'];
|
||||
|
||||
$response->setHeader('X-CSRF-Token', CSRF::token());
|
||||
|
||||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
try {
|
||||
$deviceInfo = $this->oauth2Ctx->devices->getDeviceInfo(userCode: (string)$request->getParam('code'));
|
||||
} catch(RuntimeException $ex) {
|
||||
|
|
|
@ -32,6 +32,12 @@
|
|||
<p class="auth__warning__paragraph">Welcome to Flashii, you may now log in!</p>
|
||||
</div>
|
||||
</div>
|
||||
{% elseif login_oauth2 %}
|
||||
<div class="warning auth__warning auth__warning--welcome">
|
||||
<div class="warning__content">
|
||||
<p class="auth__warning__paragraph">You must be logged in to authorise applications.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<label class="auth__label">
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
{% extends 'oauth2/master.twig' %}
|
||||
|
||||
{% set body_header_icon = 'login' %}
|
||||
{% set body_header_text = 'Not logged in' %}
|
||||
{% set body_title = 'Authorisation Request' %}
|
||||
|
||||
{% block body_content %}
|
||||
<div class="oauth2-authorise-requesting">
|
||||
{% if app is defined and not app.isTrusted %}
|
||||
<p>A third-party application is requesting permission to access your account.</p>
|
||||
{% endif %}
|
||||
|
||||
<p>You must be logged in to authorise applications. <a href="{{ login_url }}" target="_blank">Log in</a> or <a href="{{ register_url }}" target="_blank">create an account</a> and reload this page to try again.</p>
|
||||
|
||||
{% if app is defined and app.isTrusted %}
|
||||
<p>You will be redirected to the following application after logging in.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if app is defined %}
|
||||
<div class="oauth2-appinfo">
|
||||
<div class="oauth2-appinfo-name">
|
||||
{{ app.name }}
|
||||
</div>{# TODO: author should be listed #}
|
||||
<div class="oauth2-appinfo-links">
|
||||
<a href="{{ app.website }}" target="_blank" rel="noopener noreferrer" class="oauth2-appinfo-link" title="Website">
|
||||
<div class="oauth2-appinfo-link-icon oauth2-appinfo-link-icon-globe"></div>
|
||||
<div class="oauth2-appinfo-link-text">{{ app.websiteDisplay }}</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="oauth2-appinfo-summary">
|
||||
<p>{{ app.summary }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oauth2-scope">
|
||||
<div class="oauth2-scope-header">This application will be able to:</div>
|
||||
<div class="oauth2-scope-perms">
|
||||
<div class="oauth2-scope-perm">
|
||||
<div class="oauth2-scope-perm-icon"></div>
|
||||
<div class="oauth2-scope-perm-text">Do anything because I have not made up scopes yet.</div>
|
||||
</div>
|
||||
<div class="oauth2-scope-perm">
|
||||
<div class="oauth2-scope-perm-icon"></div>
|
||||
<div class="oauth2-scope-perm-text">Eat soup.</div>
|
||||
</div>
|
||||
<div class="oauth2-scope-perm">
|
||||
<div class="oauth2-scope-perm-icon"></div>
|
||||
<div class="oauth2-scope-perm-text">These are placeholders.</div>
|
||||
</div>
|
||||
<div class="oauth2-scope-perm">
|
||||
<div class="oauth2-scope-perm-icon"></div>
|
||||
<div class="oauth2-scope-perm-text">This one is really long because I want to test wrapping and how the chevron icon thing will handle it so there will be a lot of text here, the app will not be gaining anything from it but yeah sometimes you just need to explode seventy times.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="oauth2-authorise-device">
|
||||
<p>More details about the application will be displayed once you're logged in.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
Loading…
Reference in a new issue