Skip to content

Commit 60464a6

Browse files
authored
Merge pull request #38 from pdsinterop/docs/example
Add example.php back in project root
2 parents f2f6906 + ea6431d commit 60464a6

File tree

2 files changed

+218
-1
lines changed

2 files changed

+218
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ help of `League\OAuth2\Server\AuthorizationServer`.
5656

5757
Besides a Request, Response and AuthorizationServer, the `Pdsinterop\Solid\Auth\Server` requires a Configuration and User object.
5858

59-
A [full example](./tests/example.php) has been provided to help you understand how to integrate this library.
59+
A [full example](./example.php) has been provided to help you understand how to integrate this library.
6060

6161
See the "API" section for detailed usage examples.
6262

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)