1<?php 2 3/** 4 * An implementation of the OpenID Provider Authentication Policy 5 * Extension 1.0 6 * 7 * See: 8 * http://openid.net/developers/specs/ 9 */ 10 11require_once "Auth/OpenID/Extension.php"; 12 13define('Auth_OpenID_PAPE_NS_URI', 14 "http://specs.openid.net/extensions/pape/1.0"); 15 16define('PAPE_AUTH_MULTI_FACTOR_PHYSICAL', 17 'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical'); 18define('PAPE_AUTH_MULTI_FACTOR', 19 'http://schemas.openid.net/pape/policies/2007/06/multi-factor'); 20define('PAPE_AUTH_PHISHING_RESISTANT', 21 'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant'); 22 23define('PAPE_TIME_VALIDATOR', 24 '/^[0-9]{4,4}-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]Z$/'); 25/** 26 * A Provider Authentication Policy request, sent from a relying party 27 * to a provider 28 * 29 * preferred_auth_policies: The authentication policies that 30 * the relying party prefers 31 * 32 * max_auth_age: The maximum time, in seconds, that the relying party 33 * wants to allow to have elapsed before the user must re-authenticate 34 */ 35class Auth_OpenID_PAPE_Request extends Auth_OpenID_Extension { 36 37 public $ns_alias = 'pape'; 38 public $ns_uri = Auth_OpenID_PAPE_NS_URI; 39 40 private $max_auth_age = 0; 41 private $preferred_auth_policies = []; 42 43 function __construct($preferred_auth_policies=null, 44 $max_auth_age=null) 45 { 46 if ($preferred_auth_policies === null) { 47 $preferred_auth_policies = []; 48 } 49 50 $this->preferred_auth_policies = $preferred_auth_policies; 51 $this->max_auth_age = $max_auth_age; 52 } 53 54 /** 55 * Add an acceptable authentication policy URI to this request 56 * 57 * This method is intended to be used by the relying party to add 58 * acceptable authentication types to the request. 59 * 60 * policy_uri: The identifier for the preferred type of 61 * authentication. 62 * 63 * @param string $policy_uri 64 */ 65 function addPolicyURI($policy_uri) 66 { 67 if (!in_array($policy_uri, $this->preferred_auth_policies)) { 68 $this->preferred_auth_policies[] = $policy_uri; 69 } 70 } 71 72 /** 73 * Get the string arguments that should be added to an OpenID 74 * message for this extension. 75 * 76 * @param Auth_OpenID_Request|null $request 77 * @return null 78 */ 79 function getExtensionArgs($request = null) 80 { 81 $ns_args = [ 82 'preferred_auth_policies' => implode(' ', $this->preferred_auth_policies), 83 ]; 84 85 if ($this->max_auth_age !== null) { 86 $ns_args['max_auth_age'] = strval($this->max_auth_age); 87 } 88 89 return $ns_args; 90 } 91 92 /** 93 * Instantiate a Request object from the arguments in a checkid_* 94 * OpenID message 95 * 96 * @param Auth_OpenID_Request $request 97 * @return Auth_OpenID_PAPE_Request|null 98 */ 99 static function fromOpenIDRequest($request) 100 { 101 $obj = new Auth_OpenID_PAPE_Request(); 102 $args = $request->message->getArgs(Auth_OpenID_PAPE_NS_URI); 103 104 if ($args === null || $args === []) { 105 return null; 106 } 107 108 $obj->parseExtensionArgs($args); 109 return $obj; 110 } 111 112 /** 113 * Set the state of this request to be that expressed in these 114 * PAPE arguments 115 * 116 * @param args: The PAPE arguments without a namespace 117 */ 118 function parseExtensionArgs($args) 119 { 120 // preferred_auth_policies is a space-separated list of policy 121 // URIs 122 $this->preferred_auth_policies = []; 123 124 $policies_str = Auth_OpenID::arrayGet($args, 'preferred_auth_policies'); 125 if ($policies_str) { 126 foreach (explode(' ', $policies_str) as $uri) { 127 if (!in_array($uri, $this->preferred_auth_policies)) { 128 $this->preferred_auth_policies[] = $uri; 129 } 130 } 131 } 132 133 // max_auth_age is base-10 integer number of seconds 134 $max_auth_age_str = Auth_OpenID::arrayGet($args, 'max_auth_age'); 135 if ($max_auth_age_str) { 136 $this->max_auth_age = Auth_OpenID::intval($max_auth_age_str); 137 } else { 138 $this->max_auth_age = null; 139 } 140 } 141 142 /** 143 * Given a list of authentication policy URIs that a provider 144 * supports, this method returns the subsequence of those types 145 * that are preferred by the relying party. 146 * 147 * @param supported_types: A sequence of authentication policy 148 * type URIs that are supported by a provider 149 * 150 * @return array The sub-sequence of the supported types that are 151 * preferred by the relying party. This list will be ordered in 152 * the order that the types appear in the supported_types 153 * sequence, and may be empty if the provider does not prefer any 154 * of the supported authentication types. 155 */ 156 function preferredTypes($supported_types) 157 { 158 $result = []; 159 160 foreach ($supported_types as $st) { 161 if (in_array($st, $this->preferred_auth_policies)) { 162 $result[] = $st; 163 } 164 } 165 return $result; 166 } 167} 168 169/** 170 * A Provider Authentication Policy response, sent from a provider to 171 * a relying party 172 */ 173class Auth_OpenID_PAPE_Response extends Auth_OpenID_Extension { 174 175 public $ns_alias = 'pape'; 176 public $ns_uri = Auth_OpenID_PAPE_NS_URI; 177 178 private $auth_time = 0; 179 private $nist_auth_level = 0; 180 private $auth_policies = []; 181 182 function __construct($auth_policies=null, $auth_time=null, 183 $nist_auth_level=null) 184 { 185 if ($auth_policies) { 186 $this->auth_policies = $auth_policies; 187 } else { 188 $this->auth_policies = []; 189 } 190 191 $this->auth_time = $auth_time; 192 $this->nist_auth_level = $nist_auth_level; 193 } 194 195 /** 196 * Add a authentication policy to this response 197 * 198 * This method is intended to be used by the provider to add a 199 * policy that the provider conformed to when authenticating the 200 * user. 201 * 202 * @param policy_uri: The identifier for the preferred type of 203 * authentication. 204 */ 205 function addPolicyURI($policy_uri) 206 { 207 if (!in_array($policy_uri, $this->auth_policies)) { 208 $this->auth_policies[] = $policy_uri; 209 } 210 } 211 212 /** 213 * Create an Auth_OpenID_PAPE_Response object from a successful 214 * OpenID library response. 215 * 216 * @param Auth_OpenID_SuccessResponse $success_response A SuccessResponse 217 * from Auth_OpenID_Consumer::complete() 218 * 219 * @return Auth_OpenID_PAPE_Response A provider authentication policy response from the 220 * data that was supplied with the id_res response. 221 */ 222 static function fromSuccessResponse($success_response) 223 { 224 $obj = new Auth_OpenID_PAPE_Response(); 225 226 // PAPE requires that the args be signed. 227 $args = $success_response->getSignedNS(Auth_OpenID_PAPE_NS_URI); 228 229 if ($args === null || $args === []) { 230 return null; 231 } 232 233 $result = $obj->parseExtensionArgs($args); 234 235 if ($result === false) { 236 return null; 237 } else { 238 return $obj; 239 } 240 } 241 242 /** 243 * Parse the provider authentication policy arguments into the 244 * internal state of this object 245 * 246 * @param array $args unqualified provider authentication policy 247 * arguments 248 * 249 * @param bool $strict Whether to return false when bad data is 250 * encountered 251 * 252 * @return null|bool The data is parsed into the internal fields of 253 * this object. 254 */ 255 function parseExtensionArgs($args, $strict=false) 256 { 257 $policies_str = Auth_OpenID::arrayGet($args, 'auth_policies'); 258 if ($policies_str && $policies_str != "none") { 259 $this->auth_policies = explode(" ", $policies_str); 260 } 261 262 $nist_level_str = Auth_OpenID::arrayGet($args, 'nist_auth_level'); 263 if ($nist_level_str !== null) { 264 $nist_level = Auth_OpenID::intval($nist_level_str); 265 266 if ($nist_level === false) { 267 if ($strict) { 268 return false; 269 } else { 270 $nist_level = null; 271 } 272 } 273 274 if (0 <= $nist_level && $nist_level < 5) { 275 $this->nist_auth_level = $nist_level; 276 } else if ($strict) { 277 return false; 278 } 279 } 280 281 $auth_time = Auth_OpenID::arrayGet($args, 'auth_time'); 282 if ($auth_time !== null) { 283 if (preg_match(PAPE_TIME_VALIDATOR, $auth_time)) { 284 $this->auth_time = $auth_time; 285 } else if ($strict) { 286 return false; 287 } 288 } 289 return null; 290 } 291 292 /** 293 * Get the string arguments that should be added to an OpenID 294 * message for this extension. 295 * 296 * @param Auth_OpenID_Request|null $request 297 * @return null 298 */ 299 function getExtensionArgs($request = null) 300 { 301 $ns_args = []; 302 if (count($this->auth_policies) > 0) { 303 $ns_args['auth_policies'] = implode(' ', $this->auth_policies); 304 } else { 305 $ns_args['auth_policies'] = 'none'; 306 } 307 308 if ($this->nist_auth_level !== null) { 309 if (!in_array($this->nist_auth_level, range(0, 4), true)) { 310 return false; 311 } 312 $ns_args['nist_auth_level'] = strval($this->nist_auth_level); 313 } 314 315 if ($this->auth_time !== null) { 316 if (!preg_match(PAPE_TIME_VALIDATOR, $this->auth_time)) { 317 return false; 318 } 319 320 $ns_args['auth_time'] = $this->auth_time; 321 } 322 323 return $ns_args; 324 } 325} 326 327