Files
Leantime/app/Domain/Oidc/Controllers/Login.php

87 lines
3.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Leantime\Domain\Oidc\Controllers;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
use Leantime\Core\Controller\Controller;
use Leantime\Core\Controller\Frontcontroller;
use Leantime\Domain\Oidc\Services\Oidc as OidcService;
use Leantime\Domain\Plugins\Services\Plugins;
use Symfony\Component\HttpFoundation\Response;
class Login extends Controller
{
private OidcService $oidc;
private Plugins $plugins;
/**
* Initializes dependencies.
*
* @throws GuzzleException
*/
public function init(OidcService $oidc, Plugins $plugins): void
{
$this->oidc = $oidc;
$this->plugins = $plugins;
}
/**
* Redirects to the OIDC provider login page.
*
* @param array $params Request parameters
*/
public function get(array $params): Response
{
try {
// Mobile-brokered SSO: the app passes ?mobile=1&redirect_uri=<app scheme>
// + a PKCE code_challenge, so the callback mints a token + one-time
// code (bound to that challenge) and redirects back to the app instead
// of establishing a web session. The service validates the redirect
// scheme; a non-mobile web login passes none of these.
$mobile = ! empty($params['mobile']);
$redirectUri = is_string($params['redirect_uri'] ?? null) ? $params['redirect_uri'] : '';
$codeChallenge = is_string($params['code_challenge'] ?? null) ? $params['code_challenge'] : '';
// The mobile-brokered branch is an AdvancedAuth capability. Without the
// plugin, ignore the mobile params and fall back to normal web login —
// the mint endpoint refuses too, so no mobile session can be brokered.
if ($mobile && ! $this->plugins->isEnabled('AdvancedAuth')) {
return Frontcontroller::redirect(BASE_URL.'/auth/login');
}
// Mobile flow requires a PKCE challenge — otherwise the callback
// would mint a code whose exchange can never succeed (pkceMatches
// rejects an empty challenge). Fail loudly at the front door.
if ($mobile && $codeChallenge === '') {
$this->tpl->setNotification('Mobile login requires a PKCE code_challenge.', 'error');
return Frontcontroller::redirect(BASE_URL.'/auth/login');
}
// A PKCE S256 challenge is base64url(sha256(verifier)) — the
// base64url charset, 43128 chars (RFC 7636). Reject a present-but-
// malformed value so a crafted request can't persist junk into the
// code store and to enforce the intended mobile contract.
if ($codeChallenge !== '' && ! preg_match('/^[A-Za-z0-9\-_]{43,128}$/', $codeChallenge)) {
$this->tpl->setNotification('Invalid PKCE code_challenge.', 'error');
return Frontcontroller::redirect(BASE_URL.'/auth/login');
}
$loginUrl = $this->oidc->buildLoginUrl($mobile, $redirectUri, $codeChallenge);
if ($loginUrl) {
return Frontcontroller::redirect($loginUrl, 302);
}
} catch (\Throwable $e) {
Log::error($e);
}
$this->tpl->setNotification('Auth URL could not be found. Check the logs for more details', 'error');
return Frontcontroller::redirect(BASE_URL.'/auth/login');
}
}