From 75aa1f81aaeb5c08307cf9ea1f677c0b6cd73e47 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 15 Nov 2021 02:36:05 -0500 Subject: [PATCH] redirect oauth endpoint --- crates/app/obs_gateway/src/endpoints/mod.rs | 1 + .../obs_gateway/src/endpoints/oauth_begin.rs | 15 ++-- .../src/endpoints/oauth_begin_redirect.rs | 80 +++++++++++++++++++ crates/app/obs_gateway/src/main.rs | 5 ++ 4 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 crates/app/obs_gateway/src/endpoints/oauth_begin_redirect.rs diff --git a/crates/app/obs_gateway/src/endpoints/mod.rs b/crates/app/obs_gateway/src/endpoints/mod.rs index 436ec98e87..bb1fc37db2 100644 --- a/crates/app/obs_gateway/src/endpoints/mod.rs +++ b/crates/app/obs_gateway/src/endpoints/mod.rs @@ -1,2 +1,3 @@ pub mod oauth_begin; +pub mod oauth_begin_redirect; pub mod oauth_end; \ No newline at end of file diff --git a/crates/app/obs_gateway/src/endpoints/oauth_begin.rs b/crates/app/obs_gateway/src/endpoints/oauth_begin.rs index 4f4cd5828c..ff0ca8e840 100644 --- a/crates/app/obs_gateway/src/endpoints/oauth_begin.rs +++ b/crates/app/obs_gateway/src/endpoints/oauth_begin.rs @@ -3,13 +3,14 @@ use actix_web::{HttpRequest, web, HttpResponse, ResponseError, HttpResponseBuild use crate::ObsGatewayServerState; use http_server_common::response::response_error_helpers::to_simple_json_error; use http_server_common::response::response_success_helpers::simple_json_success; +use http_server_common::response::to_json_success_response::to_json_success_response; use log::info; +use log::warn; use std::fmt; use std::sync::Arc; +use twitch_api2::twitch_oauth2::ClientId; use twitch_oauth2::tokens::UserTokenBuilder; use twitch_oauth2::{Scope, ClientSecret}; -use twitch_api2::twitch_oauth2::ClientId; -use http_server_common::response::to_json_success_response::to_json_success_response; #[derive(Serialize)] pub struct OauthBeginEnrollResult { @@ -41,10 +42,6 @@ impl ResponseError for OauthBeginEnrollError { }; to_simple_json_error(&error_reason, self.status_code()) - - //HttpResponseBuilder::new(self.status_code()) - // .set_header(header::CONTENT_TYPE, "application/json") - // .body("TODO") } } @@ -54,7 +51,11 @@ pub async fn oauth_begin_enroll( ) -> Result { let redirect_url = - twitch_oauth2::url::Url::parse(&server_state.twitch_oauth_secrets.redirect_url).unwrap(); + twitch_oauth2::url::Url::parse(&server_state.twitch_oauth_secrets.redirect_url) + .map_err(|e| { + warn!("Error parsing url: {:?}", e); + OauthBeginEnrollError::ServerError + })?; let client_id = ClientId::new(&server_state.twitch_oauth_secrets.client_id); let client_secret = ClientSecret::new(&server_state.twitch_oauth_secrets.client_secret); diff --git a/crates/app/obs_gateway/src/endpoints/oauth_begin_redirect.rs b/crates/app/obs_gateway/src/endpoints/oauth_begin_redirect.rs new file mode 100644 index 0000000000..2e90635790 --- /dev/null +++ b/crates/app/obs_gateway/src/endpoints/oauth_begin_redirect.rs @@ -0,0 +1,80 @@ +use actix_http::{StatusCode, header}; +use actix_web::{HttpRequest, web, HttpResponse, ResponseError, HttpResponseBuilder}; +use crate::ObsGatewayServerState; +use http_server_common::response::response_error_helpers::to_simple_json_error; +use http_server_common::response::response_success_helpers::simple_json_success; +use http_server_common::response::to_json_success_response::to_json_success_response; +use log::info; +use log::warn; +use std::fmt; +use std::sync::Arc; +use twitch_api2::twitch_oauth2::ClientId; +use twitch_oauth2::tokens::UserTokenBuilder; +use twitch_oauth2::{Scope, ClientSecret}; + +#[derive(Serialize)] +pub struct OauthBeginEnrollResult { + redirect_url: String, +} + +#[derive(Debug)] +pub enum OauthBeginEnrollError { + ServerError, +} + +// NB: Not using DeriveMore since Clion doesn't understand it. +impl fmt::Display for OauthBeginEnrollError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OauthBeginEnrollError") + } +} + +impl ResponseError for OauthBeginEnrollError { + fn status_code(&self) -> StatusCode { + match *self { + Self::ServerError => StatusCode::INTERNAL_SERVER_ERROR, + } + } + + fn error_response(&self) -> HttpResponse { + let error_reason = match self { + Self::ServerError => "server error".to_string(), + }; + + to_simple_json_error(&error_reason, self.status_code()) + } +} + +pub async fn oauth_begin_enroll_redirect( + http_request: HttpRequest, + server_state: web::Data> +) -> Result { + + let redirect_url = + twitch_oauth2::url::Url::parse(&server_state.twitch_oauth_secrets.redirect_url) + .map_err(|e| { + warn!("Error parsing url: {:?}", e); + OauthBeginEnrollError::ServerError + })?; + + let client_id = ClientId::new(&server_state.twitch_oauth_secrets.client_id); + let client_secret = ClientSecret::new(&server_state.twitch_oauth_secrets.client_secret); + + // TODO: Not all scopes are needed! + let mut builder = UserTokenBuilder::new(client_id, client_secret, redirect_url) + .set_scopes(Scope::all()) + .force_verify(true); + + let (url, _csrf_token) = builder.generate_url(); + + let response = OauthBeginEnrollResult { + redirect_url: url.to_string(), + }; + + let response = to_json_success_response(&response) + .map_err(|_| OauthBeginEnrollError::ServerError)?; + + Ok(HttpResponse::Found() + .append_header((header::LOCATION, url.to_string())) + .finish()) +} diff --git a/crates/app/obs_gateway/src/main.rs b/crates/app/obs_gateway/src/main.rs index 35c5cd7562..b0446dea35 100644 --- a/crates/app/obs_gateway/src/main.rs +++ b/crates/app/obs_gateway/src/main.rs @@ -49,6 +49,7 @@ use twitch_api2::pubsub; use twitch_oauth2::tokens::UserTokenBuilder; use twitch_oauth2::{AppAccessToken, Scope, TwitchToken, tokens::errors::AppAccessTokenError, ClientId, ClientSecret}; use crate::server_state::{ObsGatewayServerState, EnvConfig, TwitchOauthSecrets}; +use crate::endpoints::oauth_begin_redirect::oauth_begin_enroll_redirect; const DEFAULT_BIND_ADDRESS : &'static str = "0.0.0.0:54321"; @@ -314,6 +315,10 @@ pub async fn serve(server_state: ObsGatewayServerState) -> AnyhowResult<()> .route(web::get().to(oauth_begin_enroll)) .route(web::head().to(|| HttpResponse::Ok())) ) + .service(web::resource("/oauth_enroll_redirect") + .route(web::get().to(oauth_begin_enroll_redirect)) + .route(web::head().to(|| HttpResponse::Ok())) + ) .service(web::resource("/oauth_redirect") .route(web::get().to(oauth_begin_enroll)) .route(web::head().to(|| HttpResponse::Ok()))