|
| 1 | +<?php |
| 2 | + |
| 3 | +// ============================================================================= |
| 4 | +// This majority of this part of the code is usually handled by your framework |
| 5 | +// ----------------------------------------------------------------------------- |
| 6 | +session_start(); |
| 7 | +ob_start(); |
| 8 | + |
| 9 | +require_once __DIR__ . '/../vendor/autoload.php'; |
| 10 | + |
| 11 | +/*/ The PSR Request and Response objects are usually provided by your framework /*/ |
| 12 | +$request = Laminas\Diactoros\ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES); |
| 13 | +$response = new \Laminas\Diactoros\Response(); |
| 14 | + |
| 15 | +/*/ The User (ID) is usually also provided by an entity in your framework /*/ |
| 16 | +$userId = $_SESSION['user_id'] ?? ''; |
| 17 | + |
| 18 | +/*/ An identifier for the requesting client is needed to ask your framework for information /*/ |
| 19 | +$clientIdentifier = \array_key_exists(\Pdsinterop\Solid\Auth\Enum\OAuth2\Parameter::CLIENT_ID, |
| 20 | + $request->getQueryParams()) |
| 21 | + ? $request->getQueryParams()[\Pdsinterop\Solid\Auth\Enum\OAuth2\Parameter::CLIENT_ID] |
| 22 | + : ''; |
| 23 | + |
| 24 | +/*/ These should come from a database, based on $clientIdentifier /*/ |
| 25 | +$clientSecret = 'client secret'; |
| 26 | +$clientName = ''; |
| 27 | +$clientRedirectUri = ['https://server/client/redirect-url']; |
| 28 | +// ============================================================================= |
| 29 | + |
| 30 | + |
| 31 | +// ============================================================================= |
| 32 | +// Create the Authorization Server provided by the PDS Interop package |
| 33 | +// ----------------------------------------------------------------------------- |
| 34 | +$keyPath = dirname(__DIR__) . '/tests/fixtures/keys'; |
| 35 | +$encryptionKey = file_get_contents($keyPath . '/encryption.key'); |
| 36 | +$privateKey = file_get_contents($keyPath . '/private.key'); |
| 37 | + |
| 38 | +$config = (new Pdsinterop\Solid\Auth\Factory\ConfigFactory( |
| 39 | + $clientIdentifier, |
| 40 | + $clientSecret, |
| 41 | + $encryptionKey, |
| 42 | + $privateKey |
| 43 | + [/* @FIXME: Server config keys go here */] |
| 44 | +))->create(); |
| 45 | + |
| 46 | +$authorizationServer = (new Pdsinterop\Solid\Auth\Factory\AuthorizationServerFactory($config))->create(); |
| 47 | + |
| 48 | +$user = null; |
| 49 | +if ($userId !== '') { |
| 50 | + $user = new Pdsinterop\Solid\Auth\Entity\User(); |
| 51 | + $user->setIdentifier($userId); |
| 52 | +} |
| 53 | + |
| 54 | +$server = new \Pdsinterop\Solid\Auth\Server($authorizationServer, $config, $response); |
| 55 | +// ============================================================================= |
| 56 | + |
| 57 | + |
| 58 | +// ============================================================================= |
| 59 | +// Handle requests |
| 60 | +// ----------------------------------------------------------------------------- |
| 61 | +switch ($request->getMethod() . $request->getUri()) { |
| 62 | + // @CHECKME: Do we also need 'GET/.well-known/oauth-authorization-server'? |
| 63 | + case 'GET/.well-known/openid-configuration': |
| 64 | + $server->respondToWellKnowRequest(); |
| 65 | + break; |
| 66 | + |
| 67 | + case 'POST/access_token': |
| 68 | + $server->respondToAccessTokenRequest($request); |
| 69 | + break; |
| 70 | + |
| 71 | + case 'GET/authorize': |
| 72 | + case 'POST/authorize': |
| 73 | + /*/ |
| 74 | + * The HTTP request is validate on every call. |
| 75 | + * |
| 76 | + * There are three steps to the Authorization request/response cycle: |
| 77 | + * |
| 78 | + * 1. Redirect the user to a login endpoint |
| 79 | + * - The user logs in |
| 80 | + * |
| 81 | + * 2. Redirect the user to an authorization page |
| 82 | + * - The user gives authorization to a client for certain scopes |
| 83 | + * |
| 84 | + * 3. Redirect the user to the URL provided by the Client |
| 85 | + * - The user is returned to the client |
| 86 | + * |
| 87 | + * The returned response depends on the given parameters. |
| 88 | + * |
| 89 | + * A callback can be given to receive the AuthorizationRequest, for |
| 90 | + * instance to saves the serialized object into the user's session or |
| 91 | + * to read/compare state, scope, or other values. |
| 92 | + * |
| 93 | + * Please note that this callback is called _after_ any logic that runs |
| 94 | + * to create the response |
| 95 | + /*/ |
| 96 | + $callback = static function (\League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest) { |
| 97 | + if (empty($_SESSION['authRequest'])) { |
| 98 | + $_SESSION['authRequest'] = serialize($authRequest); |
| 99 | + } |
| 100 | + |
| 101 | + /** @var \League\OAuth2\Server\RequestTypes\AuthorizationRequest $sessionAuthRequest */ |
| 102 | + $sessionAuthRequest = unserialize($_SESSION['authRequest'], \League\OAuth2\Server\RequestTypes\AuthorizationRequest::class); |
| 103 | + |
| 104 | + if ($authRequest->getState() !== $sessionAuthRequest->getState()) { |
| 105 | + throw new \UnexpectedValueException('Auth state does not match session state!'); |
| 106 | + } |
| 107 | + }; |
| 108 | + |
| 109 | + /*/ Step 1: The user is redirected to a login endpoint |
| 110 | + * |
| 111 | + * As the user is not yet logged in, no $user Entity object is provided. |
| 112 | + * |
| 113 | + * As the user has not yet approved (or denied) the Authorization |
| 114 | + * request, no approval status is given. |
| 115 | + * |
| 116 | + * $response = $server->respondToAuthorizationRequest($request, null, null, $callback); |
| 117 | + /*/ |
| 118 | + |
| 119 | + /*/ Step 2: The user is redirected to an authorization page |
| 120 | + * |
| 121 | + * As the user is now logged in, a $user Entity object is provided. |
| 122 | + * |
| 123 | + * As the user has not yet approved (or denied) the Authorization |
| 124 | + * request, no approval status is given. |
| 125 | + * |
| 126 | + * $response = $server->respondToAuthorizationRequest($request, $user, null, $callback); |
| 127 | + /*/ |
| 128 | + |
| 129 | + /*/ Step 3: The user is redirected to the URL provided by the Client |
| 130 | + * |
| 131 | + * As the user is now logged in, a $user Entity object _can_ be provided. |
| 132 | + * |
| 133 | + * As the user has now approved (or denied) the Authorization request, |
| 134 | + * an approval status is given. |
| 135 | + * |
| 136 | + * This is usually the user's response to a form asking them to approve |
| 137 | + * scopes requested by the client. If previous consent has been given, |
| 138 | + * this response _may_ come from the database without asking the user |
| 139 | + * again, in which case the form can be skipped completely. |
| 140 | + * |
| 141 | + * If other scopes are requested than those that have been stored, the |
| 142 | + * user will have to be asked to expand their permission to include the |
| 143 | + * new scopes. |
| 144 | + * |
| 145 | + * $approval = Pdsinterop\Solid\Auth\Enum\Authorization::DENIED || $approval = Pdsinterop\Solid\Auth\Enum\Authorization::APPROVED; |
| 146 | + * $response = $server->respondToAuthorizationRequest($request, null, $approval, $callback); |
| 147 | + /*/ |
| 148 | + $approval = null; // <-- Change this in this example to emulate a user approving (or denying) the request |
| 149 | + $response = $server->respondToAuthorizationRequest($request, $user, $approval, $callback); |
| 150 | + break; |
| 151 | + |
| 152 | + default: |
| 153 | + $response = $response->withStatus(404); |
| 154 | + break; |
| 155 | +} |
| 156 | +// ============================================================================= |
| 157 | + |
| 158 | + |
| 159 | +// ============================================================================= |
| 160 | +// Handling the response is usually also handled by your framework |
| 161 | +// ----------------------------------------------------------------------------- |
| 162 | +foreach ($response->getHeaders() as $name => $values) { |
| 163 | + foreach ($values as $value) { |
| 164 | + header(sprintf('%s: %s', $name, $value), false); |
| 165 | + } |
| 166 | +} |
| 167 | + |
| 168 | +echo $response->getBody()->getContents(); |
| 169 | +exit; |
| 170 | +// ============================================================================= |
0 commit comments