Skip to content

Commit d56faf3

Browse files
committed
Add (mostly complete) example of work thus far.
1 parent 432f74b commit d56faf3

1 file changed

Lines changed: 170 additions & 0 deletions

File tree

tests/example.php

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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

Comments
 (0)