From 5f130129dc32743f650f2daa2c52a31ca5dbd885 Mon Sep 17 00:00:00 2001 From: Terraciano Date: Wed, 8 May 2024 17:17:10 +0200 Subject: [PATCH 1/6] add facebook provider --- Cargo.toml | 3 ++ examples/facebook.rs | 69 +++++++++++++++++++++++++++++++++++++++ src/providers/facebook.rs | 15 +++++++++ src/providers/mod.rs | 1 + 4 files changed, 88 insertions(+) create mode 100644 examples/facebook.rs create mode 100644 src/providers/facebook.rs diff --git a/Cargo.toml b/Cargo.toml index ee42b78..6f0da70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,6 @@ name = "discord" [[example]] name = "twitter" + +[[example]] +name = "facebook" diff --git a/examples/facebook.rs b/examples/facebook.rs new file mode 100644 index 0000000..3d94c84 --- /dev/null +++ b/examples/facebook.rs @@ -0,0 +1,69 @@ +mod utils; +use std::sync::Arc; + +use axum::extract::Query; +use axum::Router; +use axum::{routing::get, Extension}; +use oauth_axum::providers::facebook::FacebookProvider; +use oauth_axum::{CustomProvider, OAuthClient}; + +use crate::utils::memory_db_util::AxumState; + +#[derive(Clone, serde::Deserialize)] +pub struct QueryAxumCallback { + pub code: String, + pub state: String, +} + +#[tokio::main] +async fn main() { + dotenv::from_filename("examples/.env").ok(); + println!("Starting server..."); + + let state = Arc::new(AxumState::new()); + let app = Router::new() + .route("/", get(create_url)) + .route("/api/v1/facebook/callback", get(callback)) + .layer(Extension(state.clone())); + + println!("🚀 Server started successfully"); + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") + .await + .unwrap(); + axum::serve(listener, app).await.unwrap(); +} + +fn get_client() -> CustomProvider { + FacebookProvider::new( + std::env::var("FACEBOOK_CLIENT_ID").expect("FACEBOOK_CLIENT_ID must be set"), + std::env::var("FACEBOOK_SECRET").expect("FACEBOOK_SECRET must be set"), + "http://localhost:3000/api/v1/facebook/callback".to_string(), + ) +} + +pub async fn create_url(Extension(state): Extension>) -> String { + let state_oauth = get_client() + .generate_url( + Vec::from(["public_profile".to_string(), "email".to_string()]), + |state_e| async move { + state.set(state_e.state, state_e.verifier); + }, + ) + .await + .unwrap() + .state + .unwrap(); + + state_oauth.url_generated.unwrap() +} + +pub async fn callback( + Extension(state): Extension>, + Query(queries): Query, +) -> String { + println!("{:?}", state.clone().get_all_items()); + let item = state.get(queries.state.clone()); + get_client() + .generate_token(queries.code, item.unwrap()) + .await +} diff --git a/src/providers/facebook.rs b/src/providers/facebook.rs new file mode 100644 index 0000000..bfa0f87 --- /dev/null +++ b/src/providers/facebook.rs @@ -0,0 +1,15 @@ +use crate::CustomProvider; + +pub struct FacebookProvider {} + +impl FacebookProvider { + pub fn new(client_id: String, client_secret: String, redirect_url: String) -> CustomProvider { + CustomProvider::new( + String::from("https://www.facebook.com/v19.0/dialog/oauth"), + String::from("https://graph.facebook.com/v19.0/oauth/access_token"), + client_id, + client_secret, + redirect_url, + ) + } +} diff --git a/src/providers/mod.rs b/src/providers/mod.rs index b4d5b11..571f2f8 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -1,4 +1,5 @@ pub mod discord; +pub mod facebook; pub mod github; pub mod google; pub mod twitter; From a40149ac0f42b3d153c9f5d3d358b0a7884121be Mon Sep 17 00:00:00 2001 From: Terraciano Date: Wed, 8 May 2024 17:25:24 +0200 Subject: [PATCH 2/6] readme.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 55fc862..e055f75 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,14 @@ To use it, it's very simple. Just create a new instance of some provider: - DiscordProvider - TwitterProvider - GoogleProvider +- FacebookProvider in your project, pass to the `new` function: - **client_id:** Unique ID from the app created in your provider - **secret_id:** Secret token from your app inside the provider, this token needs to be hidden from the users - **redirect_url:** URL from your backend that will accept the return from the provider - + If you are using **`CustomProvider`** you need to pass: - **auth_url:** URL from your provider that is used to get the permission of your app access user account @@ -36,7 +37,7 @@ This step is important because that will generate the VERIFIER field, it is need ### 2. Callback URL -After the user accepts the auth from the provider, it will redirect the user to the specific URL that you added in the config of the provider ``redirect_url``, and is important to remember that the same URL should be set in the oauth-axum params, if it is not the same an error will happen. +After the user accepts the auth from the provider, it will redirect the user to the specific URL that you added in the config of the provider `redirect_url`, and is important to remember that the same URL should be set in the oauth-axum params, if it is not the same an error will happen. This redirect will have two query parameters, CODE and STATE, we need to generate a token from the code and verifier fields, which is the reason that in the first step, you need to save the verifier and state together. After that, you will have a token to access the API in the provider. @@ -44,7 +45,7 @@ After that, you will have a token to access the API in the provider. This method is for a small project that will run in one unique instance of Axum. It saves the state and verifier in memory, which can be accessible in the callback URL call. -```rust +```rust mod utils; use std::sync::Arc; From 0d0e995f53e32e2cb6cb5a46aee4cd0743a60892 Mon Sep 17 00:00:00 2001 From: Terraciano Date: Wed, 8 May 2024 17:29:56 +0200 Subject: [PATCH 3/6] add facebook provider to crate docs --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 28b4617..35067e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,13 +12,14 @@ //! - DiscordProvider //! - TwitterProvider //! - GoogleProvider +//! - FacebookProvider //! //! in your project, pass to the ```new``` function: //! //! - **client_id:** Unique ID from the app created in your provider //! - **secret_id:** Secret token from your app inside the provider, this token needs to be hidden from the users //! - **redirect_url:** URL from your backend that will accept the return from the provider -//! +//! //! If you are using **``CustomProvider``** you need to pass: //! //! - **auth_url:** URL from your provider that is used to get the permission of your app access user account From 42af3cb80af250d3e0d366581a8b4eeb8a41d216 Mon Sep 17 00:00:00 2001 From: Terraciano Date: Wed, 8 May 2024 17:44:15 +0200 Subject: [PATCH 4/6] add spotify provider --- Cargo.toml | 3 ++ README.md | 1 + examples/spotify.rs | 72 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/providers/mod.rs | 1 + src/providers/spotify.rs | 15 +++++++++ 6 files changed, 93 insertions(+) create mode 100644 examples/spotify.rs create mode 100644 src/providers/spotify.rs diff --git a/Cargo.toml b/Cargo.toml index 6f0da70..4fc83ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,6 @@ name = "twitter" [[example]] name = "facebook" + +[[example]] +name = "spotify" diff --git a/README.md b/README.md index e055f75..0359765 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ To use it, it's very simple. Just create a new instance of some provider: - TwitterProvider - GoogleProvider - FacebookProvider +- SpotifyProvider in your project, pass to the `new` function: diff --git a/examples/spotify.rs b/examples/spotify.rs new file mode 100644 index 0000000..f61d057 --- /dev/null +++ b/examples/spotify.rs @@ -0,0 +1,72 @@ +mod utils; +use std::sync::Arc; + +use axum::extract::Query; +use axum::Router; +use axum::{routing::get, Extension}; +use oauth_axum::providers::spotify::SpotifyProvider; +use oauth_axum::{CustomProvider, OAuthClient}; + +use crate::utils::memory_db_util::AxumState; + +#[derive(Clone, serde::Deserialize)] +pub struct QueryAxumCallback { + pub code: String, + pub state: String, +} + +#[tokio::main] +async fn main() { + dotenv::from_filename("examples/.env").ok(); + println!("Starting server..."); + + let state = Arc::new(AxumState::new()); + let app = Router::new() + .route("/", get(create_url)) + .route("/api/v1/spotify/callback", get(callback)) + .layer(Extension(state.clone())); + + println!("🚀 Server started successfully"); + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") + .await + .unwrap(); + axum::serve(listener, app).await.unwrap(); +} + +fn get_client() -> CustomProvider { + SpotifyProvider::new( + std::env::var("SPOTIFY_CLIENT_ID").expect("SPOTIFY_CLIENT_ID must be set"), + std::env::var("SPOTIFY_SECRET").expect("SPOTIFY_SECRET must be set"), + "http://localhost:3000/api/v1/spotify/callback".to_string(), + ) +} + +pub async fn create_url(Extension(state): Extension>) -> String { + let state_oauth = get_client() + .generate_url( + Vec::from([ + "user-read-email".to_string(), + "user-read-private".to_string(), + ]), + |state_e| async move { + state.set(state_e.state, state_e.verifier); + }, + ) + .await + .unwrap() + .state + .unwrap(); + + state_oauth.url_generated.unwrap() +} + +pub async fn callback( + Extension(state): Extension>, + Query(queries): Query, +) -> String { + println!("{:?}", state.clone().get_all_items()); + let item = state.get(queries.state.clone()); + get_client() + .generate_token(queries.code, item.unwrap()) + .await +} diff --git a/src/lib.rs b/src/lib.rs index 35067e1..ba79438 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ //! - TwitterProvider //! - GoogleProvider //! - FacebookProvider +//! - SpotifyProvider //! //! in your project, pass to the ```new``` function: //! diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 571f2f8..e20966f 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -2,4 +2,5 @@ pub mod discord; pub mod facebook; pub mod github; pub mod google; +pub mod spotify; pub mod twitter; diff --git a/src/providers/spotify.rs b/src/providers/spotify.rs new file mode 100644 index 0000000..b1a33cb --- /dev/null +++ b/src/providers/spotify.rs @@ -0,0 +1,15 @@ +use crate::CustomProvider; + +pub struct SpotifyProvider {} + +impl SpotifyProvider { + pub fn new(client_id: String, client_secret: String, redirect_url: String) -> CustomProvider { + CustomProvider::new( + String::from("https://accounts.spotify.com/authorize"), + String::from("https://accounts.spotify.com/api/token"), + client_id, + client_secret, + redirect_url, + ) + } +} From ec4f67f98745c0a26db8931f44f06928449061de Mon Sep 17 00:00:00 2001 From: Guilherme Berghauser Date: Wed, 8 May 2024 18:55:07 +0200 Subject: [PATCH 5/6] add codeowner --- .github/CODEOWNERS | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..e69de29 From 90c600cce3fc4e737e6a6c8281b93a895703ba9e Mon Sep 17 00:00:00 2001 From: Guilherme Costa Berghauser Date: Wed, 8 May 2024 19:03:00 +0200 Subject: [PATCH 6/6] Update CODEOWNERS --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e69de29..1086aa6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @torto