You've already forked AstralRinth
forked from didirus/AstralRinth
Authenticate protected routes
This commit is contained in:
@@ -1,22 +1,20 @@
|
||||
use crate::models::error::ApiError;
|
||||
use log::{info};
|
||||
use actix_web::web::{Query, ServiceConfig, scope, Data};
|
||||
use actix_web::{get, HttpResponse};
|
||||
use actix_web::http::StatusCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use crate::auth::get_github_user_from_token;
|
||||
use crate::database::models::{generate_state_id, User, UserId};
|
||||
use sqlx::postgres::PgPool;
|
||||
use crate::models::ids::base62_impl::{to_base62, parse_base62};
|
||||
use crate::models::error::ApiError;
|
||||
use crate::models::ids::base62_impl::{parse_base62, to_base62};
|
||||
use crate::models::ids::DecodingError;
|
||||
use crate::models::users::Role;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{scope, Data, Query, ServiceConfig};
|
||||
use actix_web::{get, HttpResponse};
|
||||
use chrono::Utc;
|
||||
use crate::models::ids::{DecodingError};
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::postgres::PgPool;
|
||||
use thiserror::Error;
|
||||
|
||||
pub fn config(cfg: &mut ServiceConfig) {
|
||||
cfg.service(
|
||||
scope("/auth/")
|
||||
.service(auth_callback)
|
||||
.service(init)
|
||||
);
|
||||
cfg.service(scope("/auth/").service(auth_callback).service(init));
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -33,6 +31,8 @@ pub enum AuthorizationError {
|
||||
GithubError(#[from] reqwest::Error),
|
||||
#[error("Invalid Authentication credentials")]
|
||||
InvalidCredentialsError,
|
||||
#[error("Authentication Error: {0}")]
|
||||
AuthenticationError(#[from] crate::auth::AuthenticationError),
|
||||
#[error("Error while decoding Base62")]
|
||||
DecodingError(#[from] DecodingError),
|
||||
}
|
||||
@@ -46,6 +46,7 @@ impl actix_web::ResponseError for AuthorizationError {
|
||||
AuthorizationError::GithubError(..) => StatusCode::FAILED_DEPENDENCY,
|
||||
AuthorizationError::InvalidCredentialsError => StatusCode::UNAUTHORIZED,
|
||||
AuthorizationError::DecodingError(..) => StatusCode::BAD_REQUEST,
|
||||
AuthorizationError::AuthenticationError(..) => StatusCode::UNAUTHORIZED,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +60,7 @@ impl actix_web::ResponseError for AuthorizationError {
|
||||
AuthorizationError::GithubError(..) => "github_error",
|
||||
AuthorizationError::InvalidCredentialsError => "invalid_credentials",
|
||||
AuthorizationError::DecodingError(..) => "decoding_error",
|
||||
AuthorizationError::AuthenticationError(..) => "authentication_error",
|
||||
},
|
||||
description: &self.to_string(),
|
||||
})
|
||||
@@ -83,60 +85,59 @@ pub struct AccessToken {
|
||||
pub token_type: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GitHubUser {
|
||||
pub login: String,
|
||||
pub id: u64,
|
||||
pub avatar_url: String,
|
||||
pub name: String,
|
||||
pub email: Option<String>,
|
||||
pub bio: String,
|
||||
}
|
||||
|
||||
//http://localhost:8000/api/v1/auth/init?url=https%3A%2F%2Fmodrinth.com%2Fmods
|
||||
#[get("init")]
|
||||
pub async fn init(Query(info): Query<AuthorizationInit>, client: Data<PgPool>) -> Result<HttpResponse, AuthorizationError> {
|
||||
pub async fn init(
|
||||
Query(info): Query<AuthorizationInit>,
|
||||
client: Data<PgPool>,
|
||||
) -> Result<HttpResponse, AuthorizationError> {
|
||||
let mut transaction = client.begin().await?;
|
||||
|
||||
let state = generate_state_id(&mut transaction).await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
"
|
||||
INSERT INTO states (id, url)
|
||||
VALUES ($1, $2)
|
||||
",
|
||||
state.0,
|
||||
info.url
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
state.0,
|
||||
info.url
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
let client_id = dotenv::var("GITHUB_CLIENT_ID")?;
|
||||
let url = format!("https://github.com/login/oauth/authorize?client_id={}&state={}&scope={}", client_id, to_base62(state.0 as u64), "%20repo%20read%3Aorg%20read%3Auser%20user%3Aemail");
|
||||
let url = format!(
|
||||
"https://github.com/login/oauth/authorize?client_id={}&state={}&scope={}",
|
||||
client_id,
|
||||
to_base62(state.0 as u64),
|
||||
"%20repo%20read%3Aorg%20read%3Auser%20user%3Aemail"
|
||||
);
|
||||
|
||||
Ok(HttpResponse::TemporaryRedirect()
|
||||
.header("Location", &*url)
|
||||
.json(AuthorizationInit {
|
||||
url,
|
||||
}))
|
||||
.json(AuthorizationInit { url }))
|
||||
}
|
||||
|
||||
#[get("callback")]
|
||||
pub async fn auth_callback(Query(info): Query<Authorization>, client: Data<PgPool>) -> Result<HttpResponse, AuthorizationError> {
|
||||
pub async fn auth_callback(
|
||||
Query(info): Query<Authorization>,
|
||||
client: Data<PgPool>,
|
||||
) -> Result<HttpResponse, AuthorizationError> {
|
||||
let mut transaction = client.begin().await?;
|
||||
let state_id = parse_base62(&*info.state)?;
|
||||
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
"
|
||||
SELECT url,expires FROM states
|
||||
WHERE id = $1
|
||||
",
|
||||
state_id as i64
|
||||
)
|
||||
.fetch_one(&mut *transaction)
|
||||
.await?;
|
||||
state_id as i64
|
||||
)
|
||||
.fetch_one(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let now = Utc::now();
|
||||
let duration = result.expires.signed_duration_since(now);
|
||||
@@ -146,14 +147,14 @@ pub async fn auth_callback(Query(info): Query<Authorization>, client: Data<PgPoo
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
"
|
||||
DELETE FROM states
|
||||
WHERE id = $1
|
||||
",
|
||||
state_id as i64
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
state_id as i64
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let client_id = dotenv::var("GITHUB_CLIENT_ID")?;
|
||||
let client_secret = dotenv::var("GITHUB_CLIENT_SECRET")?;
|
||||
@@ -163,9 +164,7 @@ pub async fn auth_callback(Query(info): Query<Authorization>, client: Data<PgPoo
|
||||
client_id, client_secret, info.code
|
||||
);
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let token : AccessToken = client
|
||||
let token: AccessToken = reqwest::Client::new()
|
||||
.post(&url)
|
||||
.header(reqwest::header::ACCEPT, "application/json")
|
||||
.send()
|
||||
@@ -173,22 +172,14 @@ pub async fn auth_callback(Query(info): Query<Authorization>, client: Data<PgPoo
|
||||
.json()
|
||||
.await?;
|
||||
|
||||
let user : GitHubUser = client
|
||||
.get("https://api.github.com/user")
|
||||
.header(reqwest::header::USER_AGENT, "Modrinth")
|
||||
.header(reqwest::header::AUTHORIZATION, format!("token {}", token.access_token))
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await?;
|
||||
let user = get_github_user_from_token(&*token.access_token).await?;
|
||||
|
||||
let user_result = User::get_from_github_id(UserId(user.id as i64), &mut *transaction).await?;
|
||||
match user_result{
|
||||
Some(x) => {
|
||||
info!("{:?}", x.id)
|
||||
}
|
||||
match user_result {
|
||||
Some(x) => info!("{:?}", x.id),
|
||||
None => {
|
||||
let user_id = crate::database::models::generate_user_id(&mut transaction).await?.into();
|
||||
let user_id = crate::database::models::generate_user_id(&mut transaction)
|
||||
.await?;
|
||||
|
||||
User {
|
||||
id: user_id,
|
||||
@@ -198,8 +189,11 @@ pub async fn auth_callback(Query(info): Query<Authorization>, client: Data<PgPoo
|
||||
email: user.email,
|
||||
avatar_url: user.avatar_url,
|
||||
bio: user.bio,
|
||||
created: Utc::now()
|
||||
}.insert(&mut transaction).await?;
|
||||
created: Utc::now(),
|
||||
role: Role::Developer.to_string(),
|
||||
}
|
||||
.insert(&mut transaction)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +203,5 @@ pub async fn auth_callback(Query(info): Query<Authorization>, client: Data<PgPoo
|
||||
|
||||
Ok(HttpResponse::TemporaryRedirect()
|
||||
.header("Location", &*redirect_url)
|
||||
.json(AuthorizationInit {
|
||||
url: redirect_url,
|
||||
}))
|
||||
.json(AuthorizationInit { url: redirect_url }))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user