diff --git a/oatmeal.php b/oatmeal.php index 377aefe..d5d453e 100644 --- a/oatmeal.php +++ b/oatmeal.php @@ -26,5 +26,5 @@ $oatmeal = new OatmealContext((function() { $oatmeal->register(new HomeRoutes); $oatmeal->register(new AuthzCodeRoutes($oatmeal->getCSRFP())); $oatmeal->register(new RefreshTokenRoutes($oatmeal->getCSRFP())); -$oatmeal->register(new ClientCredsRoutes); +$oatmeal->register(new ClientCredsRoutes($oatmeal->getCSRFP())); $oatmeal->register(new DeviceCodeRoutes); diff --git a/src/ClientCredsRoutes.php b/src/ClientCredsRoutes.php index 37d6cfe..aa8ffc8 100644 --- a/src/ClientCredsRoutes.php +++ b/src/ClientCredsRoutes.php @@ -1,11 +1,160 @@ + +Oatmeal / Client credentials + +

Oatmeal / Client credentials

+

+ This is an option only available to confidential clients that allows you to get an access token that represent the app itself rather than a user. + Usually doesn't provide refresh tokens because you can literally just dispense them on demand like what are you doing hello? +

+
+ +
+ +
+
+ +
+
+ +
+
+ Authentication: + + + +
+
+ + +
+
+Return +HTML; + } + + #[HttpPost('/client_credentials')] + public function postClientCredentials($response, $request): string { + if(!$request->isFormContent()) + return 400; + $content = $request->getContent(); + + $csrfp = (string)$content->getParam('csrfp'); + if(!$this->csrfp->verifyToken($csrfp)) + return 403; + + $tokenUri = (string)$content->getParam('token_uri'); + if(filter_var($tokenUri, FILTER_VALIDATE_URL) === false) { + $response->setStatusCode(400); + return << + +Oatmeal / Client credentials +

Oatmeal / Client credentials

+

Provided Token URI was not a valid absolute URI.

+Return +HTML; + } + + $clientId = (string)$content->getParam('client_id'); + $clientSecret = (string)$content->getParam('client_secret'); + $auth = (string)$content->getParam('auth'); + + $headers = []; + $body = ['grant_type' => 'client_credentials']; + + if($auth === 'body' || ($auth !== 'header' && mt_rand(0, 10) > 5)) { + $body['client_id'] = $clientId; + $body['client_secret'] = $clientSecret; + } else + $headers[] = sprintf('Authorization: Basic %s', base64_encode(sprintf('%s:%s', $clientId, $clientSecret))); + + $body = Tools::shuffleArray($body); + $response = Tools::fetch($tokenUri, headers: $headers, body: $body); + + $tokenUri = htmlspecialchars($tokenUri); + $headers = htmlspecialchars(json_encode($headers, JSON_PRETTY_PRINT)); + $body = htmlspecialchars(json_encode($body, JSON_PRETTY_PRINT)); + + $decoded = json_decode($response); + if($decoded !== null) + $response = json_encode($decoded, JSON_PRETTY_PRINT); + + $response = htmlspecialchars($response); + + return << + +Oatmeal / Client credentials + +

Oatmeal / Client credentials

+

Below is the request and response data from your client credentials result.

+
+ +
+
+ +
+
+ +
+
+ +
+Return +HTML; } } diff --git a/src/RefreshTokenRoutes.php b/src/RefreshTokenRoutes.php index 7063c2d..52597ff 100644 --- a/src/RefreshTokenRoutes.php +++ b/src/RefreshTokenRoutes.php @@ -140,7 +140,7 @@ HTML; textarea { min-width: 100%; max-width: 100%; min-height: 200px; box-sizing: border-box; }

Oatmeal / Refresh token

-

Below is the request and response data from your request token result. If the response contains a refresh_token field, you can restart the Refresh token flow again and again and again and again!

+

Below is the request and response data from your refresh token result. If the response contains a refresh_token field, you can restart the Refresh token flow again and again and again and again!