OAuth flow compiles, but has issues

This commit is contained in:
Brandon Thomas committed 2021-11-16 20:00:16 -05:00
1 parent 6bf35a8381
commit d370d13f2d
5 files changed
+118 -24

No files matched your search

@@ -11,6 +11,7 @@ use std::sync::Arc;
use twitch_api2::twitch_oauth2::ClientId;
use twitch_oauth2::tokens::UserTokenBuilder;
use twitch_oauth2::{Scope, ClientSecret};
use crate::twitch::oauth_token_builder::get_oauth_token_builder;
#[derive(Serialize)]
pub struct OauthBeginEnrollResult {
@@ -57,18 +58,11 @@ pub async fn oauth_begin_enroll(
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(vec![
Scope::BitsRead,
Scope::ChannelReadSubscriptions,
Scope::ChatEdit,
Scope::ChatRead,
])
.force_verify(true);
let mut builder = get_oauth_token_builder(
&server_state.twitch_oauth_secrets.client_id,
&server_state.twitch_oauth_secrets.client_secret,
&redirect_url,
true);
let (url, _csrf_token) = builder.generate_url();
@@ -11,6 +11,7 @@ use std::sync::Arc;
use twitch_api2::twitch_oauth2::ClientId;
use twitch_oauth2::tokens::UserTokenBuilder;
use twitch_oauth2::{Scope, ClientSecret};
use crate::twitch::oauth_token_builder::get_oauth_token_builder;
#[derive(Serialize)]
pub struct OauthBeginEnrollResult {
@@ -57,18 +58,11 @@ pub async fn oauth_begin_enroll_redirect(
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(vec![
Scope::BitsRead,
Scope::ChannelReadSubscriptions,
Scope::ChatEdit,
Scope::ChatRead,
])
.force_verify(true);
let mut builder = get_oauth_token_builder(
&server_state.twitch_oauth_secrets.client_id,
&server_state.twitch_oauth_secrets.client_secret,
&redirect_url,
true);
let (url, _csrf_token) = builder.generate_url();
@@ -1,13 +1,34 @@
use actix_http::StatusCode;
use actix_web::{HttpResponse, HttpRequest, web, ResponseError};
use crate::server_state::ObsGatewayServerState;
use crate::twitch::oauth_token_builder::get_oauth_token_builder;
use http_server_common::response::response_error_helpers::to_simple_json_error;
use log::error;
use log::info;
use log::warn;
use std::fmt;
use std::sync::Arc;
use twitch_oauth2::tokens::BearerTokenType::UserToken;
#[derive(Deserialize)]
pub struct QueryParams {
/// OAuth authorization code
pub code: Option<String>,
/// Opaque value used to avoid CSRF attacks.
pub state: Option<String>,
/// List of permission scopes
pub scope: Option<String>,
/// Only set in the event of an error
pub error: Option<String>,
/// Only set in the event of an error
pub error_description: Option<String>,
}
#[derive(Debug)]
pub enum OauthEndEnrollFromRedirectError {
ServerError,
TwitchOauthError { reason: String },
}
// NB: Not using DeriveMore since Clion doesn't understand it.
@@ -21,12 +42,14 @@ impl ResponseError for OauthEndEnrollFromRedirectError {
fn status_code(&self) -> StatusCode {
match *self {
Self::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
OauthEndEnrollFromRedirectError::TwitchOauthError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
let error_reason = match self {
Self::ServerError => "server error".to_string(),
OauthEndEnrollFromRedirectError::TwitchOauthError { .. } => "twitch error".to_string(),
};
to_simple_json_error(&error_reason, self.status_code())
@@ -35,8 +58,65 @@ impl ResponseError for OauthEndEnrollFromRedirectError {
pub async fn oauth_end_enroll_from_redirect(
http_request: HttpRequest,
query: web::Query<QueryParams>,
server_state: web::Data<Arc<ObsGatewayServerState>>
) -> Result<HttpResponse, OauthEndEnrollFromRedirectError> {
if let Some(error) = query.error.as_deref() {
return Err(OauthEndEnrollFromRedirectError::TwitchOauthError { reason: error.to_string() })
}
if let Some(error) = query.error_description.as_deref() {
return Err(OauthEndEnrollFromRedirectError::TwitchOauthError { reason: error.to_string() })
}
let (code, state) = match (query.code.as_deref(), query.state.as_deref()) {
(Some(code), Some(state)) => (code.to_string(), state.to_string()),
_ => {
return Err(OauthEndEnrollFromRedirectError::TwitchOauthError {
reason: "params not set".to_string()
});
}
};
let redirect_url =
twitch_oauth2::url::Url::parse(&server_state.twitch_oauth_secrets.redirect_url)
.map_err(|e| {
warn!("Error parsing url: {:?}", e);
OauthEndEnrollFromRedirectError::ServerError
})?;
let mut builder = get_oauth_token_builder(
&server_state.twitch_oauth_secrets.client_id,
&server_state.twitch_oauth_secrets.client_secret,
&redirect_url,
true);
let http_client = &reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.build()
.map_err(|e| {
error!("Problem creating HTTP client: {:?}", e);
OauthEndEnrollFromRedirectError::ServerError
})?;
let user_token = builder
.get_user_token(
http_client,
&state,
&code,
)
.await
.map_err(|e| {
warn!("Error fetching user token: {:?}", e);
OauthEndEnrollFromRedirectError::TwitchOauthError {
reason: "error fetching user token".to_string()
}
})?;
let auth_token = user_token.access_token.as_str();
info!("Auth token: {:?}", auth_token);
Ok(HttpResponse::Ok()
.body("TODO"))
}
+1
View File
@@ -1,3 +1,4 @@
pub mod oauth_token_builder;
pub mod twitch_client_wrapper;
pub mod twitch_secrets;
pub mod websocket_client;
@@ -0,0 +1,25 @@
use twitch_oauth2::tokens::UserTokenBuilder;
use twitch_oauth2::{Scope, ClientSecret, ClientId};
use twitch_oauth2::url::Url;
/// Get the token builder with the scopes our app needs
/// This is used in several places, so centralize creation here.
pub fn get_oauth_token_builder(
client_id: &str,
client_secret: &str,
redirect_url: &Url,
force_verify: bool
) -> UserTokenBuilder {
let client_id = ClientId::new(client_id);
let client_secret = ClientSecret::new(client_secret);
UserTokenBuilder::new(client_id, client_secret, redirect_url.clone())
.set_scopes(vec![
Scope::BitsRead,
Scope::ChannelReadSubscriptions,
Scope::ChatEdit,
Scope::ChatRead,
])
.force_verify(force_verify)
}