update env

This commit is contained in:
manuel 2022-08-06 20:33:48 +02:00
parent 5d9b7def23
commit 77bfb05f20
4 changed files with 185 additions and 4 deletions

View File

@ -1,10 +1,8 @@
[package] [package]
name = "actix-web-sample-app" name = "actix-admin-example"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
actix-web = "4.0.1" actix-web = "4.0.1"
actix-rt = "2.7.0" actix-rt = "2.7.0"
@ -25,5 +23,6 @@ serde_derive = "1.0.136"
quote = "1.0" quote = "1.0"
sea-orm = { version = "^0.9.1", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = true } sea-orm = { version = "^0.9.1", features = [ "sqlx-sqlite", "runtime-actix-native-tls", "macros" ], default-features = true }
syn = "1.0.91" syn = "1.0.91"
actix_admin = { path = "../" } actix_admin = { path = "../" }
azure_auth = { path = "../azure_auth" } azure_auth = { path = "./azure_auth" }

View File

@ -0,0 +1,21 @@
[package]
name = "azure_auth"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "4.0.1"
actix-rt = "2.7.0"
actix-session = "0.7.1"
oauth2 = "4.1"
base64 = "0.13.0"
async-trait = "0.1.53"
rand = "0.8.5"
url = "2.2.2"
http = "0.2.6"
dotenv = "0.15"
futures = "0.3.21"
serde = "1.0.136"
serde_json = "1.0.79"
serde_derive = "1.0.136"

View File

@ -0,0 +1,161 @@
#[macro_use]
extern crate serde_derive;
use actix_session::{Session};
use actix_web::http::header;
use actix_web::{web, HttpResponse};
use http::{HeaderMap, Method};
use oauth2::basic::BasicClient;
use oauth2::reqwest::async_http_client;
use oauth2::{
AccessToken, AuthorizationCode, CsrfToken, //PkceCodeChallenge,
Scope, TokenResponse
};
use std::str;
use url::Url;
use oauth2::{
AuthUrl, ClientId, ClientSecret, TokenUrl,
};
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug)]
pub struct UserInfo {
mail: String,
userPrincipalName: String,
displayName: String,
givenName: String,
surname: String,
id: String,
role: String
}
// AppDataTrait
pub trait AppDataTrait {
fn get_oauth(&self) -> &BasicClient;
}
#[derive(Clone, Debug)]
pub struct AzureAuth {
auth_url: AuthUrl,
token_url: TokenUrl,
client_id: ClientId,
client_secret: ClientSecret
}
impl AzureAuth {
pub fn new(oauth2_server: &String, client_id: &String, client_secret: &String) -> Self {
let azure_auth = AzureAuth {
auth_url: AuthUrl::new(format!("https://{}/oauth2/v2.0/authorize", oauth2_server)).expect("Invalid authorization endpoint URL"),
token_url: TokenUrl::new(format!("https://{}/oauth2/v2.0/token", oauth2_server)).expect("Invalid token endpoint URL"),
client_id: ClientId::new(client_id.clone()),
client_secret: ClientSecret::new(client_secret.clone())
};
azure_auth
}
pub fn get_api_base_url() -> &'static str {
"https://graph.microsoft.com/v1.0"
}
pub fn get_oauth_client(self) -> BasicClient {
BasicClient::new(
self.client_id,
Some(self.client_secret),
self.auth_url,
Some(self.token_url),
)
}
pub fn create_scope<T: AppDataTrait + 'static>(self) -> actix_web::Scope {
let scope = web::scope("/auth")
.route("/login", web::get().to(login::<T>))
.route("/logout", web::get().to(logout))
.route("/auth", web::get().to(auth::<T>))
;
scope
}
}
pub async fn login<T: AppDataTrait>(data: web::Data<T>) -> HttpResponse {
// Create a PKCE code verifier and SHA-256 encode it as a code challenge.
// let (_pkce_code_challenge, _pkce_code_verifier) = PkceCodeChallenge::new_random_sha256();
// Generate the authorization URL to which we'll redirect the user.
let (auth_url, _csrf_token) = &data
.get_oauth()
.authorize_url(CsrfToken::new_random)
// Set the desired scopes.
.add_scope(Scope::new("openid".to_string()))
// Set the PKCE code challenge, need to pass verifier to /auth.
//.set_pkce_challenge(pkce_code_challenge)
.url();
HttpResponse::Found()
.append_header((header::LOCATION, auth_url.to_string()))
.finish()
}
pub async fn logout(session: Session) -> HttpResponse {
session.remove("user_info");
HttpResponse::Found()
.append_header((header::LOCATION, "/".to_string()))
.finish()
}
async fn read_user(api_base_url: &str, access_token: &AccessToken) -> UserInfo {
let url = Url::parse(format!("{}/me", api_base_url).as_str()).unwrap();
let mut headers = HeaderMap::new();
headers.insert(
"Authorization",
format!("Bearer {}", access_token.secret()).parse().unwrap(),
);
let resp = async_http_client(oauth2::HttpRequest {
url,
method: Method::GET,
headers: headers,
body: Vec::new(),
})
.await
.expect("Request failed");
let s: &str = match str::from_utf8(&resp.body) {
Ok(v) => v,
Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
};
serde_json::from_slice(&resp.body).unwrap()
}
#[derive(Deserialize)]
pub struct AuthRequest {
code: String,
state: String,
}
pub async fn auth<T: AppDataTrait>(
session: Session,
data: web::Data<T>,
params: web::Query<AuthRequest>,
) -> HttpResponse {
let code = AuthorizationCode::new(params.code.clone());
let _state = CsrfToken::new(params.state.clone());
let api_base_url = AzureAuth::get_api_base_url();
// Exchange the code with a token.
let token = &data
.get_oauth()
.exchange_code(code)
//.set_pkce_verifier()
.request_async(async_http_client)
.await
.expect("exchange_code failed");
let user_info = read_user(api_base_url, token.access_token()).await;
session.insert("user_info", &user_info).unwrap();
HttpResponse::Found().append_header(("location", "/")).finish()
}