diff --git a/oatmeal.php b/oatmeal.php index d5d453e..6713a12 100644 --- a/oatmeal.php +++ b/oatmeal.php @@ -27,4 +27,4 @@ $oatmeal->register(new HomeRoutes); $oatmeal->register(new AuthzCodeRoutes($oatmeal->getCSRFP())); $oatmeal->register(new RefreshTokenRoutes($oatmeal->getCSRFP())); $oatmeal->register(new ClientCredsRoutes($oatmeal->getCSRFP())); -$oatmeal->register(new DeviceCodeRoutes); +$oatmeal->register(new DeviceCodeRoutes($oatmeal->getCSRFP())); diff --git a/src/DeviceCodeRoutes.php b/src/DeviceCodeRoutes.php index d8fac98..f679e54 100644 --- a/src/DeviceCodeRoutes.php +++ b/src/DeviceCodeRoutes.php @@ -1,11 +1,364 @@ redirect('/device_code/authorise'); + } + + #[HttpGet('/device_code/authorise')] + public function getAuthorise(): string { + return << + +
+ Device codes allow you to do authentications on device where its impractical or impossible to enter your password. + I will also be using it as a way to secure do authentication over plain HTTP! +
+ +Return +HTML; + } + + #[HttpPost('/device_code/authorise')] + public function postAuthorise($response, $request) { + if(!$request->isFormContent()) + return 400; + $content = $request->getContent(); + + $csrfp = (string)$content->getParam('csrfp'); + if(!$this->csrfp->verifyToken($csrfp)) + return 403; + + $authoriseUri = (string)$content->getParam('device_authorise_uri'); + if(filter_var($authoriseUri, FILTER_VALIDATE_URL) === false) { + $response->setStatusCode(400); + return << + +Provided Device Authorise URI was not a valid absolute URI.
+Return +HTML; + } + + $clientId = (string)$content->getParam('client_id'); + $clientSecret = (string)$content->getParam('client_secret'); + $scope = (string)$content->getParam('scope'); + + $headers = []; + $body = ['scope' => $scope]; + + if($clientSecret === '') + $body['client_id'] = $clientId; + else + $headers[] = sprintf('Authorization: Basic %s', base64_encode(sprintf('%s:%s', $clientId, $clientSecret))); + + $body = Tools::shuffleArray($body); + $response = Tools::fetch($authoriseUri, headers: $headers, body: $body); + + $authoriseUri = htmlspecialchars($authoriseUri); + $headers = htmlspecialchars(json_encode($headers, JSON_PRETTY_PRINT)); + $body = htmlspecialchars(json_encode($body, JSON_PRETTY_PRINT)); + + $form = 'Could not display form because the request failed.
'; + + $decoded = json_decode($response); + if($decoded !== null) { + $response = json_encode($decoded, JSON_PRETTY_PRINT); + + $vericationUri = htmlspecialchars($decoded->verification_uri ?? ''); + $vericationUriComplete = htmlspecialchars($decoded->verification_uri_complete ?? ''); + $deviceCode = htmlspecialchars($decoded->device_code ?? ''); + $userCode = htmlspecialchars($decoded->user_code ?? ''); + + if(!is_string($deviceCode)) + $form = 'Could not display form because the device_code
is missing from the response.
Could not display form because the user_code
is missing from the response.
Could not display form because the verification_uri
is missing from the response.
+ Below is the request and response data from your device authorisation request.
+ Even further below is the form you can use to actually request an access token!
+ Normally you would open verification_uri
on a separate device and enter the user_code
or just scan the verification_uri_complete
as a QR Code or something like that (if present in the response), but you can also just open it in a new tab.
+
Provided Token URI was not a valid absolute URI.
+Return +HTML; + } + + $clientId = (string)$request->getParam('client_id'); + $clientSecret = (string)$request->getParam('client_secret'); + $deviceCode = (string)$request->getParam('device_code'); + $grantType = (string)$request->getParam('type'); + $auth = (string)$request->getParam('auth'); + + $headers = []; + $body = [ + 'grant_type' => $grantType === 'short' ? 'device_code' : 'urn:ietf:params:oauth:grant-type:device_code', + 'device_code' => $deviceCode, + ]; + + if($clientSecret === '') + $body['client_id'] = $clientId; + elseif($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 << + +Below is the request and response data from your device code result. If the response contains a refresh_token field, you can move on to the Refresh token flow!
+Hello and welcome to Oatmeal! This is a quick and dirty utility for testing your OAuth2 server implementation. - It is tailored to my own Hanyuu project for Flashii ID, however it should do for OAuth2.1 and the Device Code extension. - I'm not going to bother with making this page look nice, the most you'll get it functional Javascript and/or CSS touch ups if really necessary. + It is tailored to my own Hanyuu project for Flashii ID, however it should do for general implementations of OAuth2.1 and the Device Code extension. + I'm not going to bother with making this page look nice, the most you'll get is utilitarian Javascript and/or CSS touch ups if really necessary.