See Release Notes
Long Term Support Release
Differences Between: [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 3 namespace Packback\Lti1p3; 4 5 use Packback\Lti1p3\Interfaces\ICache; 6 use Packback\Lti1p3\Interfaces\ICookie; 7 use Packback\Lti1p3\Interfaces\IDatabase; 8 9 class LtiOidcLogin 10 { 11 public const COOKIE_PREFIX = 'lti1p3_'; 12 13 public const ERROR_MSG_LAUNCH_URL = 'No launch URL configured'; 14 public const ERROR_MSG_ISSUER = 'Could not find issuer'; 15 public const ERROR_MSG_LOGIN_HINT = 'Could not find login hint'; 16 17 private $db; 18 private $cache; 19 private $cookie; 20 21 /** 22 * Constructor. 23 * 24 * @param IDatabase $database instance of the database interface used for looking up registrations and deployments 25 * @param ICache $cache Instance of the Cache interface used to loading and storing launches. If non is provided launch data will be store in $_SESSION. 26 * @param ICookie $cookie Instance of the Cookie interface used to set and read cookies. Will default to using $_COOKIE and setcookie. 27 */ 28 public function __construct(IDatabase $database, ICache $cache = null, ICookie $cookie = null) 29 { 30 $this->db = $database; 31 $this->cache = $cache; 32 $this->cookie = $cookie; 33 } 34 35 /** 36 * Static function to allow for method chaining without having to assign to a variable first. 37 */ 38 public static function new(IDatabase $database, ICache $cache = null, ICookie $cookie = null) 39 { 40 return new LtiOidcLogin($database, $cache, $cookie); 41 } 42 43 /** 44 * Calculate the redirect location to return to based on an OIDC third party initiated login request. 45 * 46 * @param string $launch_url URL to redirect back to after the OIDC login. This URL must match exactly a URL white listed in the platform. 47 * @param array|string $request An array of request parameters. If not set will default to $_REQUEST. 48 * 49 * @return Redirect returns a redirect object containing the fully formed OIDC login URL 50 */ 51 public function doOidcLoginRedirect($launch_url, array $request = null) 52 { 53 if ($request === null) { 54 $request = $_REQUEST; 55 } 56 57 if (empty($launch_url)) { 58 throw new OidcException(static::ERROR_MSG_LAUNCH_URL, 1); 59 } 60 61 // Validate Request Data. 62 $registration = $this->validateOidcLogin($request); 63 64 /* 65 * Build OIDC Auth Response. 66 */ 67 68 // Generate State. 69 // Set cookie (short lived) 70 $state = static::secureRandomString('state-'); 71 $this->cookie->setCookie(static::COOKIE_PREFIX.$state, $state, 60); 72 73 // Generate Nonce. 74 $nonce = static::secureRandomString('nonce-'); 75 $this->cache->cacheNonce($nonce, $state); 76 77 // Build Response. 78 $auth_params = [ 79 'scope' => 'openid', // OIDC Scope. 80 'response_type' => 'id_token', // OIDC response is always an id token. 81 'response_mode' => 'form_post', // OIDC response is always a form post. 82 'prompt' => 'none', // Don't prompt user on redirect. 83 'client_id' => $registration->getClientId(), // Registered client id. 84 'redirect_uri' => $launch_url, // URL to return to after login. 85 'state' => $state, // State to identify browser session. 86 'nonce' => $nonce, // Prevent replay attacks. 87 'login_hint' => $request['login_hint'], // Login hint to identify platform session. 88 ]; 89 90 // Pass back LTI message hint if we have it. 91 if (isset($request['lti_message_hint'])) { 92 // LTI message hint to identify LTI context within the platform. 93 $auth_params['lti_message_hint'] = $request['lti_message_hint']; 94 } 95 96 $auth_login_return_url = $registration->getAuthLoginUrl().'?'.http_build_query($auth_params, '', '&'); 97 98 // Return auth redirect. 99 return new Redirect($auth_login_return_url, http_build_query($request, '', '&')); 100 } 101 102 public function validateOidcLogin($request) 103 { 104 // Validate Issuer. 105 if (empty($request['iss'])) { 106 throw new OidcException(static::ERROR_MSG_ISSUER, 1); 107 } 108 109 // Validate Login Hint. 110 if (empty($request['login_hint'])) { 111 throw new OidcException(static::ERROR_MSG_LOGIN_HINT, 1); 112 } 113 114 // Fetch Registration Details. 115 $clientId = $request['client_id'] ?? null; 116 $registration = $this->db->findRegistrationByIssuer($request['iss'], $clientId); 117 118 // Check we got something. 119 if (empty($registration)) { 120 $errorMsg = LtiMessageLaunch::getMissingRegistrationErrorMsg($request['iss'], $clientId); 121 122 throw new OidcException($errorMsg, 1); 123 } 124 125 // Return Registration. 126 return $registration; 127 } 128 129 public static function secureRandomString(string $prefix = ''): string 130 { 131 return $prefix.hash('sha256', random_bytes(64)); 132 } 133 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body