mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-02 08:28:18 +00:00
OAuth flow compiles, but has issues
This commit is contained in:
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,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)
|
||||
}
|
||||
Reference in new issue
Block a user