Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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 public const ERROR_MSG_REGISTRATION = 'Could not find registration details'; 17 18 private $db; 19 private $cache; 20 private $cookie; 21 22 /** 23 * Constructor. 24 * 25 * @param IDatabase $database instance of the database interface used for looking up registrations and deployments 26 * @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. 27 * @param ICookie $cookie Instance of the Cookie interface used to set and read cookies. Will default to using $_COOKIE and setcookie. 28 */ 29 public function __construct(IDatabase $database, ICache $cache = null, ICookie $cookie = null) 30 { 31 $this->db = $database; 32 $this->cache = $cache; 33 $this->cookie = $cookie; 34 } 35 36 /** 37 * Static function to allow for method chaining without having to assign to a variable first. 38 */ 39 public static function new(IDatabase $database, ICache $cache = null, ICookie $cookie = null) 40 { 41 return new LtiOidcLogin($database, $cache, $cookie); 42 } 43 44 /** 45 * Calculate the redirect location to return to based on an OIDC third party initiated login request. 46 * 47 * @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. 48 * @param array|string $request An array of request parameters. If not set will default to $_REQUEST. 49 * 50 * @return Redirect returns a redirect object containing the fully formed OIDC login URL 51 */ 52 public function doOidcLoginRedirect($launch_url, array $request = null) 53 { 54 if ($request === null) { 55 $request = $_REQUEST; 56 } 57 58 if (empty($launch_url)) { 59 throw new OidcException(static::ERROR_MSG_LAUNCH_URL, 1); 60 } 61 62 // Validate Request Data. 63 $registration = $this->validateOidcLogin($request); 64 65 /* 66 * Build OIDC Auth Response. 67 */ 68 69 // Generate State. 70 // Set cookie (short lived) 71 $state = static::secureRandomString('state-'); 72 $this->cookie->setCookie(static::COOKIE_PREFIX.$state, $state, 60); 73 74 // Generate Nonce. 75 $nonce = static::secureRandomString('nonce-'); 76 $this->cache->cacheNonce($nonce, $state); 77 78 // Build Response. 79 $auth_params = [ 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' => $registration->getClientId(), // Registered client id. 85 'redirect_uri' => $launch_url, // 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 // Pass back LTI message hint if we have it. 92 if (isset($request['lti_message_hint'])) { 93 // LTI message hint to identify LTI context within the platform. 94 $auth_params['lti_message_hint'] = $request['lti_message_hint']; 95 } 96 97 $auth_login_return_url = $registration->getAuthLoginUrl().'?'.http_build_query($auth_params, '', '&'); 98 99 // Return auth redirect. 100 return new Redirect($auth_login_return_url, http_build_query($request, '', '&')); 101 } 102 103 public function validateOidcLogin($request) 104 { 105 // Validate Issuer. 106 if (empty($request['iss'])) { 107 throw new OidcException(static::ERROR_MSG_ISSUER, 1); 108 } 109 110 // Validate Login Hint. 111 if (empty($request['login_hint'])) { 112 throw new OidcException(static::ERROR_MSG_LOGIN_HINT, 1); 113 } 114 115 // Fetch Registration Details. 116 $registration = $this->db->findRegistrationByIssuer($request['iss'], $request['client_id'] ?? null); 117 118 // Check we got something. 119 if (empty($registration)) { 120 throw new OidcException(static::ERROR_MSG_REGISTRATION, 1); 121 } 122 123 // Return Registration. 124 return $registration; 125 } 126 127 public static function secureRandomString(string $prefix = ''): string 128 { 129 return $prefix.hash('sha256', random_bytes(64)); 130 } 131 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body