Skip to content

Commit 6c7ad93

Browse files
authored
Add example.php back in project root
The file was originally deleted in 918bafe (v0.8.0) as it broke the build because the file was run as if it was a test.
1 parent f2f6906 commit 6c7ad93

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed

example.php

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
<?php
2+
3+
// =============================================================================
4+
// The 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+
*
26+
* They have previously been provided to or by the Client, using a Dynamic
27+
* Registration request.
28+
/*/
29+
$clientName = 'Example Client Name';
30+
$clientRedirectUris = [
31+
'https://server/client/redirect-url',
32+
'https://server/client/another-redirect-url',
33+
];
34+
$clientSecret = 'client secret';
35+
// =============================================================================
36+
37+
38+
// =============================================================================
39+
// Create the Authorization Server
40+
// -----------------------------------------------------------------------------
41+
$keyPath = dirname(__DIR__) . '/tests/fixtures/keys';
42+
$encryptionKey = file_get_contents($keyPath . '/encryption.key');
43+
$privateKey = file_get_contents($keyPath . '/private.key');
44+
$publicKey = file_get_contents($keyPath . '/public.key');
45+
46+
$config = (new \Pdsinterop\Solid\Auth\Factory\ConfigFactory(
47+
new \Pdsinterop\Solid\Auth\Config\Client(
48+
$clientIdentifier,
49+
$clientSecret,
50+
$clientRedirectUris,
51+
$clientName
52+
),
53+
$encryptionKey,$privateKey, $publicKey,
54+
[
55+
/* URL of the OP's OAuth 2.0 Authorization Endpoint [OpenID.Core]. */
56+
\Pdsinterop\Solid\Auth\Enum\OpenId\OpenIdConnectMetadata::AUTHORIZATION_ENDPOINT => 'https://server/authorize',
57+
58+
/* URL using the https scheme with no query or fragment component that
59+
* the OP asserts as its Issuer Identifier. If Issuer discovery is
60+
* supported, this value MUST be identical to the issuer value returned
61+
* by WebFinger. This also MUST be identical to the iss Claim value in
62+
* ID Tokens issued from this Issuer.
63+
*/
64+
\Pdsinterop\Solid\Auth\Enum\OpenId\OpenIdConnectMetadata::ISSUER => 'https://server/identifier',
65+
66+
/* URL of the OP's JSON Web Key Set [JWK] document. This contains the
67+
* signing key(s) the RP uses to validate signatures from the OP. The
68+
* JWK Set MAY also contain the Server's encryption key(s), which are
69+
* used by RPs to encrypt requests to the Server.
70+
*
71+
* When both signing and encryption keys are made available, a use
72+
* (Key Use) parameter value is REQUIRED for all keys in the referenced
73+
* JWK Set to indicate each key's intended usage. Although some
74+
* algorithms allow the same key to be used for both signatures and
75+
* encryption, doing so is NOT RECOMMENDED, as it is less secure.
76+
*
77+
* The JWK x5c parameter MAY be used to provide X.509 representations
78+
* of keys provided. When used, the bare key values MUST still be
79+
* present and MUST match those in the certificate.
80+
*/
81+
\Pdsinterop\Solid\Auth\Enum\OpenId\OpenIdConnectMetadata::JWKS_URI => 'https://server/.well-known/jwks.json'
82+
]
83+
))->create();
84+
85+
$authorizationServer = (new \Pdsinterop\Solid\Auth\Factory\AuthorizationServerFactory($config))->create();
86+
87+
$user = null;
88+
if ($userId !== '') {
89+
$user = new \Pdsinterop\Solid\Auth\Entity\User();
90+
$user->setIdentifier($userId);
91+
}
92+
93+
$server = new \Pdsinterop\Solid\Auth\Server($authorizationServer, $config, $response);
94+
// =============================================================================
95+
96+
97+
// =============================================================================
98+
// Handle requests
99+
// -----------------------------------------------------------------------------
100+
switch ($request->getMethod() . $request->getRequestTarget()) {
101+
case 'GET/.well-known/jwks.json':
102+
$response = $server->respondToJwksMetadataRequest();
103+
break;
104+
105+
case 'GET/.well-known/openid-configuration':
106+
$response = $server->respondToOpenIdMetadataRequest();
107+
break;
108+
109+
case 'POST/access_token':
110+
$response = $server->respondToAccessTokenRequest($request);
111+
break;
112+
113+
case 'GET/authorize':
114+
case 'POST/authorize':
115+
/*/
116+
* The HTTP request is validate on every call.
117+
*
118+
* There are three steps to the Authorization request/response cycle:
119+
*
120+
* 1. Redirect the user to a login endpoint
121+
* - The user logs in
122+
*
123+
* 2. Redirect the user to an authorization page
124+
* - The user gives authorization to a client for certain scopes
125+
*
126+
* 3. Redirect the user to the URL provided by the Client
127+
* - The user is returned to the client
128+
*
129+
* The returned response depends on the given parameters.
130+
*
131+
* A callback can be given to receive the AuthorizationRequest, for
132+
* instance to saves the serialized object into the user's session or
133+
* to read/compare state, scope, or other values.
134+
*
135+
* Please note that this callback is called _after_ any logic that runs
136+
* to create the response
137+
/*/
138+
$callback = static function (\League\OAuth2\Server\RequestTypes\AuthorizationRequest $authRequest) {
139+
if (empty($_SESSION['authRequest'])) {
140+
$_SESSION['authRequest'] = serialize($authRequest);
141+
}
142+
143+
/** @var \League\OAuth2\Server\RequestTypes\AuthorizationRequest $sessionAuthRequest */
144+
$sessionAuthRequest = unserialize($_SESSION['authRequest'], \League\OAuth2\Server\RequestTypes\AuthorizationRequest::class);
145+
146+
if ($authRequest->getState() !== $sessionAuthRequest->getState()) {
147+
throw new \UnexpectedValueException('Auth state does not match session state!');
148+
}
149+
};
150+
151+
/*/ Step 1: The user is redirected to a login endpoint
152+
*
153+
* As the user is not yet logged in, no $user Entity object is provided.
154+
*
155+
* As the user has not yet approved (or denied) the Authorization
156+
* request, no approval status is given.
157+
*
158+
* $response = $server->respondToAuthorizationRequest($request, null, null, $callback);
159+
/*/
160+
161+
/*/ Step 2: The user is redirected to an authorization page
162+
*
163+
* As the user is now logged in, a $user Entity object is provided.
164+
*
165+
* As the user has not yet approved (or denied) the Authorization
166+
* request, no approval status is given.
167+
*
168+
* $response = $server->respondToAuthorizationRequest($request, $user, null, $callback);
169+
/*/
170+
171+
/*/ Step 3: The user is redirected to the URL provided by the Client
172+
*
173+
* As the user is now logged in, a $user Entity object _can_ be provided.
174+
*
175+
* As the user has now approved (or denied) the Authorization request,
176+
* an approval status is given.
177+
*
178+
* This is usually the user's response to a form asking them to approve
179+
* scopes requested by the client. If previous consent has been given,
180+
* this response _may_ come from the database without asking the user
181+
* again, in which case the form can be skipped completely.
182+
*
183+
* If other scopes are requested than those that have been stored, the
184+
* user will have to be asked to expand their permission to include the
185+
* new scopes.
186+
*
187+
* $approval = Pdsinterop\Solid\Auth\Enum\Authorization::DENIED || $approval = Pdsinterop\Solid\Auth\Enum\Authorization::APPROVED;
188+
* $response = $server->respondToAuthorizationRequest($request, null, $approval, $callback);
189+
/*/
190+
$approval = null; // <-- Change this in this example to emulate a user approving (or denying) the request
191+
$response = $server->respondToAuthorizationRequest($request, $user, $approval, $callback);
192+
break;
193+
194+
case 'GET/.well-known/jwks.json':
195+
$response = $server->respondToJwksRequest();
196+
break;
197+
198+
default:
199+
$response->getBody()->write('404');
200+
$response = $response->withStatus(404);
201+
break;
202+
}
203+
// =============================================================================
204+
205+
206+
// =============================================================================
207+
// Handling the response is usually also handled by your framework
208+
// -----------------------------------------------------------------------------
209+
foreach ($response->getHeaders() as $name => $values) {
210+
foreach ($values as $value) {
211+
header(sprintf('%s: %s', $name, $value), false);
212+
}
213+
}
214+
215+
echo (string) $response->getBody();
216+
exit;
217+
// =============================================================================

0 commit comments

Comments
 (0)