Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
namespace Packback\Lti1p3;
4
 
5
use Packback\Lti1p3\Helpers\Helpers;
6
use Packback\Lti1p3\Interfaces\ICache;
7
use Packback\Lti1p3\Interfaces\ICookie;
8
use Packback\Lti1p3\Interfaces\IDatabase;
9
use Packback\Lti1p3\Interfaces\ILtiRegistration;
10
 
11
class LtiOidcLogin
12
{
13
    public const COOKIE_PREFIX = 'lti1p3_';
14
    public const ERROR_MSG_LAUNCH_URL = 'No launch URL configured';
15
    public const ERROR_MSG_ISSUER = 'Could not find issuer';
16
    public const ERROR_MSG_LOGIN_HINT = 'Could not find login hint';
17
 
18
    public function __construct(
19
        public IDatabase $db,
20
        public ICache $cache,
21
        public ICookie $cookie
22
    ) {
23
    }
24
 
25
    /**
26
     * Static function to allow for method chaining without having to assign to a variable first.
27
     */
28
    public static function new(IDatabase $db, ICache $cache, ICookie $cookie): self
29
    {
30
        return new LtiOidcLogin($db, $cache, $cookie);
31
    }
32
 
33
    /**
34
     * Calculate the redirect location to return to based on an OIDC third party initiated login request.
35
     */
36
    public function getRedirectUrl(string $launchUrl, array $request): string
37
    {
38
        // Validate request data.
39
        $registration = $this->validateOidcLogin($request);
40
 
41
        // Build OIDC Auth response.
42
        $authParams = $this->getAuthParams($launchUrl, $registration->getClientId(), $request);
43
 
44
        return Helpers::buildUrlWithQueryParams($registration->getAuthLoginUrl(), $authParams);
45
    }
46
 
47
    public function validateOidcLogin(array $request): ILtiRegistration
48
    {
49
        if (!isset($request['iss'])) {
50
            throw new OidcException(static::ERROR_MSG_ISSUER);
51
        }
52
 
53
        if (!isset($request['login_hint'])) {
54
            throw new OidcException(static::ERROR_MSG_LOGIN_HINT);
55
        }
56
 
57
        // Fetch registration
58
        $clientId = $request['client_id'] ?? null;
59
        $registration = $this->db->findRegistrationByIssuer($request['iss'], $clientId);
60
 
61
        if (!isset($registration)) {
62
            $errorMsg = LtiMessageLaunch::getMissingRegistrationErrorMsg($request['iss'], $clientId);
63
 
64
            throw new OidcException($errorMsg);
65
        }
66
 
67
        return $registration;
68
    }
69
 
70
    public function getAuthParams(string $launchUrl, string $clientId, array $request): array
71
    {
72
        // Set cookie (short lived)
73
        $state = static::secureRandomString('state-');
74
        $this->cookie->setCookie(static::COOKIE_PREFIX.$state, $state, 60);
75
 
76
        $nonce = static::secureRandomString('nonce-');
77
        $this->cache->cacheNonce($nonce, $state);
78
 
79
        $authParams = [
80
            'scope' => 'openid', // OIDC Scope.
81
            'response_type' => 'id_token', // OIDC response is always an id token.
82
            'response_mode' => 'form_post', // OIDC response is always a form post.
83
            'prompt' => 'none', // Don't prompt user on redirect.
84
            'client_id' => $clientId, // Registered client id.
85
            'redirect_uri' => $launchUrl, // URL to return to after login.
86
            'state' => $state, // State to identify browser session.
87
            'nonce' => $nonce, // Prevent replay attacks.
88
            'login_hint' => $request['login_hint'], // Login hint to identify platform session.
89
        ];
90
 
91
        if (isset($request['lti_message_hint'])) {
92
            // LTI message hint to identify LTI context within the platform.
93
            $authParams['lti_message_hint'] = $request['lti_message_hint'];
94
        }
95
 
96
        return $authParams;
97
    }
98
 
99
    public static function secureRandomString(string $prefix = ''): string
100
    {
101
        return $prefix.hash('sha256', random_bytes(64));
102
    }
103
}