1: <?php
2: /*
3: * SimpleID
4: *
5: * Copyright (C) Kelvin Mo 2014-2025
6: *
7: * This program is free software; you can redistribute it and/or
8: * modify it under the terms of the GNU General Public
9: * License as published by the Free Software Foundation; either
10: * version 2 of the License, or (at your option) any later version.
11: *
12: * This program is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15: * General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public
18: * License along with this program; if not, write to the Free
19: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20: *
21: */
22:
23: namespace SimpleID\Protocols\OpenID\Extensions;
24:
25: use SimpleID\Auth\AuthManager;
26: use SimpleID\Module;
27: use SimpleID\Protocols\ProtocolResult;
28: use SimpleID\Protocols\OpenID\OpenIDCheckEvent;
29: use SimpleID\Protocols\OpenID\OpenIDModule;
30: use SimpleID\Protocols\OpenID\Message;
31: use SimpleID\Protocols\OpenID\Request;
32: use SimpleID\Protocols\OpenID\OpenIDResponseBuildEvent;
33: use SimpleID\Store\StoreManager;
34: use SimpleID\Util\OpaqueIdentifier;
35: use SimpleID\Util\Events\BaseDataCollectionEvent;
36:
37: /**
38: * Implements the Provider Authentication Policy Extension
39: */
40: class PAPEOpenIDExtensionModule extends Module implements ProtocolResult {
41:
42: /** Namespace for the PAPE extension */
43: const OPENID_NS_PAPE = 'http://specs.openid.net/extensions/pape/1.0';
44:
45: /** Namespaces for PAPE policies */
46: const PAPE_POLICY_NONE = 'http://schemas.openid.net/pape/policies/2007/06/none';
47:
48: /** Namespaces for PAPE levels */
49: const PAPE_LEVEL_NIST800_63 = 'http://csrc.nist.gov/publications/nistpubs/800-63/SP800-63V1_0_2.pdf';
50:
51: /**
52: * Returns the support for PAPE in SimpleID XRDS document
53: *
54: * @param BaseDataCollectionEvent $event
55: * @return void
56: */
57: public function onXrdsTypes(BaseDataCollectionEvent $event) {
58: $event->addResult([
59: self::OPENID_NS_PAPE,
60: self::PAPE_LEVEL_NIST800_63
61: ]);
62: }
63:
64: /**
65: * @see SimpleID\Protocols\OpenID\OpenIDCheckEvent
66: * @return void
67: */
68: public function onOpenIDCheckEvent(OpenIDCheckEvent $event) {
69: $request = $event->getRequest();
70:
71: // We only respond if the extension is requested
72: if (!$request->hasExtension(self::OPENID_NS_PAPE)) return;
73:
74: // See if we are choosing an identity and save for later
75: // This may be used by pape_response() to produce a private identifier
76: //if ($request['openid.identity'] == Request::OPENID_IDENTIFIER_SELECT) $this->identifier_select = true;
77:
78: $pape_request = $request->getParamsForExtension(self::OPENID_NS_PAPE);
79:
80: // If the relying party provides a max_auth_age
81: if (isset($pape_request['max_auth_age'])) {
82: $auth = AuthManager::instance();
83:
84: // If we are not logged in then we don't need to do anything
85: if (!$auth->isLoggedIn()) return;
86:
87: $auth_level = $auth->getAuthLevel();
88: if ($auth_level == null) $auth_level = AuthManager::AUTH_LEVEL_SESSION;
89:
90: $auth_time = $auth->getAuthTime();
91: if ($auth_time == null) $auth_time = 0;
92:
93: // If the last time we logged on actively (i.e. using a password) is greater than
94: // max_auth_age, we then require the user to log in again
95: if (($auth_level < AuthManager::AUTH_LEVEL_CREDENTIALS)
96: || ((time() - $auth->getAuthTime()) > $pape_request['max_auth_age'])) {
97: $this->f3->set('message', $this->f3->get('intl.common.reenter_credentials'));
98: $event->setResult(self::CHECKID_REENTER_CREDENTIALS);
99: }
100: }
101: }
102:
103: /**
104: * @see SimpleID\Protocols\OpenID\OpenIDResponseBuildEvent
105: * @return void
106: */
107: public function onOpenIDResponseBuildEvent(OpenIDResponseBuildEvent $event) {
108: $auth = AuthManager::instance();
109:
110: // We only deal with positive assertions
111: if (!$event->isPositiveAssertion()) return;
112:
113: // We only respond if we are using OpenID 2 or later
114: $request = $event->getRequest();
115: $response = $event->getResponse();
116:
117: if ($request->getVersion() < Message::OPENID_VERSION_2) return;
118:
119: // Get what is requested
120: $pape_request = $request->getParamsForExtension(self::OPENID_NS_PAPE);
121:
122: // If the extension is requested, we use the same alias, otherwise, we
123: // make one up
124: $alias = $response->getAliasForExtension(self::OPENID_NS_PAPE, 'pape');
125:
126: // The PAPE specification recommends us to respond even when the extension
127: // is not present in the request.
128: $response['ns.' . $alias] = self::OPENID_NS_PAPE;
129:
130: // We return the last time the user logged in using the login form
131: $response[$alias . '.auth_time'] = gmdate('Y-m-d\TH:i:s\Z', $auth->getAuthTime());
132:
133: // We don't comply with NIST_SP800-63
134: $response[$alias . '.auth_level.ns.nist'] = self::PAPE_LEVEL_NIST800_63;
135: $response[$alias . '.auth_level.nist'] = 0;
136:
137: // The default is that we don't apply any authentication policies.
138: $response[$alias . '.auth_policies'] = self::PAPE_POLICY_NONE;
139: }
140: }
141:
142: ?>
143: