| 78 | } |
| 79 | |
| 80 | void JWTProvider::deviceCodeLogin() |
| 81 | { |
| 82 | if (oauth_client_id.empty() || oauth_url.empty()) |
| 83 | throw Exception(ErrorCodes::BAD_ARGUMENTS, "Error: --oauth-client-id and --oauth-url are required for --login."); |
| 84 | |
| 85 | Poco::URI device_code_url(oauth_url + "/oauth/device/code"); |
| 86 | Poco::URI token_url(oauth_url + "/oauth/token"); |
| 87 | |
| 88 | std::string scope = "openid profile email offline_access"; |
| 89 | std::string audience = getAudience(); |
| 90 | |
| 91 | std::string device_code; |
| 92 | Poco::Timestamp::TimeVal expires_at_ts = 0; |
| 93 | |
| 94 | auto device_code_session = createHTTPSession(device_code_url); |
| 95 | Poco::Net::HTTPRequest device_code_request(Poco::Net::HTTPRequest::HTTP_POST, device_code_url.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1); |
| 96 | device_code_request.setContentType("application/x-www-form-urlencoded"); |
| 97 | |
| 98 | std::string encoded_scope; |
| 99 | Poco::URI::encode(scope, "", encoded_scope); |
| 100 | std::string device_code_request_body = "client_id=" + oauth_client_id + "&scope=" + encoded_scope; |
| 101 | |
| 102 | if (!audience.empty()) |
| 103 | { |
| 104 | std::string encoded_audience; |
| 105 | Poco::URI::encode(audience, "", encoded_audience); |
| 106 | device_code_request_body += "&audience=" + encoded_audience; |
| 107 | } |
| 108 | |
| 109 | device_code_request.setContentLength(device_code_request_body.length()); |
| 110 | device_code_session->sendRequest(device_code_request) << device_code_request_body; |
| 111 | |
| 112 | Poco::Net::HTTPResponse device_code_response; |
| 113 | std::istream & device_code_rs = device_code_session->receiveResponse(device_code_response); |
| 114 | if (device_code_response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK) |
| 115 | { |
| 116 | std::string error_body; |
| 117 | Poco::StreamCopier::copyToString(device_code_rs, error_body); |
| 118 | throw Exception(ErrorCodes::NETWORK_ERROR, "Error requesting device code: {} {}\nResponse: {}", device_code_response.getStatus(), device_code_response.getReason(), error_body); |
| 119 | } |
| 120 | |
| 121 | Poco::JSON::Object::Ptr device_code_object = Poco::JSON::Parser().parse(device_code_rs).extract<Poco::JSON::Object::Ptr>(); |
| 122 | device_code = device_code_object->getValue<std::string>("device_code"); |
| 123 | std::string user_code = device_code_object->getValue<std::string>("user_code"); |
| 124 | std::string verification_uri_complete = device_code_object->getValue<std::string>("verification_uri_complete"); |
| 125 | int interval_seconds = device_code_object->getValue<int>("interval"); |
| 126 | expires_at_ts = Poco::Timestamp().epochTime() + device_code_object->getValue<int>("expires_in"); |
| 127 | |
| 128 | output_stream << "\nOpening your browser for login. If it doesn't open, visit:" |
| 129 | << "\n\n " << verification_uri_complete << "\n\n" |
| 130 | << "Then enter the code: \033[1m" << user_code << "\033[0m\n" << std::endl; |
| 131 | |
| 132 | openURLInBrowser(verification_uri_complete); |
| 133 | |
| 134 | while (Poco::Timestamp().epochTime() < expires_at_ts) |
| 135 | { |
| 136 | std::this_thread::sleep_for(std::chrono::seconds(interval_seconds)); |
| 137 |
nothing calls this directly
no test coverage detected