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\OAuth;
24:
25: use Psr\Log\LogLevel;
26: use SimpleID\Module;
27: use SimpleID\ModuleManager;
28:
29: /**
30: * Base class for SimpleID modules providing access to resources protected
31: * by OAuth.
32: *
33: * This class contains various convenience functions to {@link OAuthManager}
34: * and its methods.
35: */
36: class OAuthProtectedResource extends Module {
37:
38: /**
39: * @var OAuthManager the OAuth manager
40: */
41: protected $oauth;
42:
43: /**
44: * @var bool whether to detect access tokens from the request body
45: */
46: protected $oauth_include_request_body = false;
47:
48:
49: public function __construct() {
50: parent::__construct();
51: $this->oauth = OAuthManager::instance();
52: }
53:
54: /**
55: * FatFree Framework event handler.
56: *
57: * This event handler initialises the user system. It starts the PHP session
58: * and loads data for the currently logged-in user, if any.
59: *
60: * @return void
61: */
62: public function beforeroute() {
63: $this->oauth->initAccessToken($this->oauth_include_request_body);
64: }
65:
66: /**
67: * Returns the current access token.
68: *
69: * This is a shortcut for {@link SimpleID\Protocols\OAuth\OAuthManager::getAccessToken()}.
70: *
71: * @return AccessToken the access token
72: */
73: protected function getAccessToken() {
74: return $this->oauth->getAccessToken();
75: }
76:
77: /**
78: * Returns the authorisation associated with the current access token.
79: *
80: * This is a shortcut for {@link SimpleID\Protocols\OAuth\OAuthManager::getAuthorization()}.
81: *
82: * @return Authorization|null the authorisation
83: */
84: protected function getAuthorization() {
85: if (!$this->oauth->getAccessToken()) return null;
86: return $this->oauth->getAccessToken()->getAuthorization();
87: }
88:
89: /**
90: * Returns the owner of the authorisation associated with the current
91: * access token.
92: *
93: * This is a shortcut for {@link SimpleID\Protocols\OAuth\Authorization::getOwner()}.
94: *
95: * @return \SimpleID\Store\Storable|null the owner
96: */
97: protected function getTokenOwner() {
98: if (!$this->oauth->getAccessToken()) return null;
99: return $this->oauth->getAccessToken()->getAuthorization()->getOwner();
100: }
101:
102: /**
103: * Returns the client of the authorisation associated with the current
104: * access token.
105: *
106: * This is a shortcut for {@link SimpleID\Protocols\OAuth\Authorization::getClient()}.
107: *
108: * @return \SimpleID\Store\Storable|null the client
109: */
110: protected function getTokenClient() {
111: if (!$this->oauth->getAccessToken()) return null;
112: return $this->oauth->getAccessToken()->getAuthorization()->getClient();
113: }
114:
115: /**
116: * Returns whether the current access token is authorised under the
117: * specified scope.
118: *
119: * This is a shortcut for {@link SimpleID\Protocols\OAuth\OAuthManager::isTokenAuthorized()}.
120: *
121: * @param array<string>|string $scope the scope
122: * @param string &$error the error code returned if the access token
123: * is not authorised
124: * @return bool true if the access token is authorised
125: */
126: protected function isTokenAuthorized($scope, &$error = null) {
127: return $this->oauth->isTokenAuthorized($scope, $error);
128: }
129:
130: /**
131: * Sends an OAuth unauthorised response with a WWW-Authenticate header.
132: *
133: * @param string $error the error code
134: * @param string $error_description human readable error information
135: * @param array<string, string> $additional any additional data to be sent with the error
136: * message
137: * @param string $format the format of the error message
138: * @param int $status the HTTP status to send
139: * @return void
140: */
141: protected function unauthorizedError($error, $error_description = NULL, $additional = [], $format = 'html', $status = 401) {
142: $this->f3->status($status);
143:
144: if ($error) {
145: $header = 'WWW-Authenticate: Bearer ';
146: $header .= 'realm="' . addcslashes($this->f3->get('REALM'), '"') . '", ';
147: $header .= 'error="' . addcslashes($error, '"') . '"';
148: if ($error_description != NULL) $header .= ', error_description="' . addcslashes($error_description, '"') . '"';
149: foreach ($additional as $param => $value) {
150: $header .= ', ' . $param . '="' . addcslashes($value, '"') . '"';
151: }
152:
153: header($header);
154: }
155:
156: switch ($format) {
157: case 'json':
158: $result = array_merge($additional, [ 'error' => $error ]);
159: if ($error_description) $result['error_description'] = $error_description;
160: header('Content-Type: application/json');
161: print json_encode($result);
162: break;
163: case 'html':
164: default:
165: $this->fatalError($error_description, 400);
166: break;
167: }
168: exit;
169: }
170: }
171:
172:
173: ?>
174: