Skip to content

Commit

Permalink
add first oauth draft
Browse files Browse the repository at this point in the history
  • Loading branch information
thebino committed Jul 5, 2023
1 parent 79202f4 commit 758dd73
Show file tree
Hide file tree
Showing 19 changed files with 1,989 additions and 114 deletions.
1,353 changes: 1,248 additions & 105 deletions Cargo.lock

Large diffs are not rendered by default.

25 changes: 18 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ include = ["src/**/*", "LICENSE.md", "README.md", "CHANGELOG.md"]
[workspace]
members = [
"crates/core_common",
"crates/core_oauth",
"crates/core_photos",
"crates/core_activity_pub",
"crates/plugin_interface"
Expand All @@ -33,11 +34,19 @@ members = [
# define dependencies to be inherited by members of the workspace
[workspace.dependencies]
core_common = { path = "./crates/core_common" }
# core_oauth = { path = "./crates/core_oauth" }
core_oauth = { path = "./crates/core_oauth" }
core_photos = { path = "./crates/core_photos" }
core_activity_pub = { path = "./crates/core_activity_pub" }
activitypub_federation = "~0.4.0"
serde = "~1.0.157"
axum = { version = "0.6.2", features = ["ws", "headers"] }
chrono = { version = "0.4.26", features = ["serde"] }
uuid = { version = "1.3.4", features = ["serde", "v4"] }
tower-http = { version = "0.4.1", features = ["tracing", "trace"] }

tracing = "0.1.37"
tracing-subscriber = { version = "0.3", features = ["registry", "fmt", "std", "json"] }
tracing-appender = "0.2.2"

# core_api = { version = "=0.1.0", path = "./crates/core_api" }
# core_api_crud = { version = "=0.1.0", path = "./crates/api_crud" }
Expand All @@ -56,7 +65,7 @@ path = "./crates/plugin_interface"
# core_api = { workspace = true }
core_common = { workspace = true }
core_photos = { workspace = true }
# core_oauth = { workspace = true }
core_oauth = { workspace = true }
core_activity_pub = { workspace = true }
# core_api_crud = { workspace = true }

Expand All @@ -74,8 +83,13 @@ smallvec = "1.8.0"

anyhow = "1.0.70"
axum = { version = "0.6.2", features = ["ws"] }
oxide-auth = "0.5"
oxide-auth-axum = "0.3.0"

tracing.workspace = true
tracing-subscriber.workspace = true
tracing-appender.workspace = true

# oxide-auth = "0.5"
# oxide-auth-axum = "0.3.0"

futures = "0.3.25"
futures-channel = "0.3.25"
Expand All @@ -89,9 +103,6 @@ tokio-util = { version = "0.7.4", features = ["rt"] }
tower = { version = "0.4.13", features = ["util"] }
tower-http = { version = "0.4.0", features = ["fs", "trace"] }

tracing = "0.1.37"
tracing-subscriber = { version = "0.3", features = ["registry", "fmt", "std", "json"] }
tracing-appender = "0.2.2"

[dev-dependencies]
pretty_assertions = "1.3.0"
Expand Down
33 changes: 33 additions & 0 deletions crates/core_oauth/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "core_oauth"
version.workspace = true
authors.workspace = true
description.workspace = true
homepage.workspace = true
documentation.workspace = true
repository.workspace = true
readme.workspace = true
license.workspace = true
edition.workspace = true

[lib]
name = "core_auth"
path = "src/lib.rs"
doctest = false

[dependencies]
core_common = { workspace = true }
openidconnect = { version = "3.2.0", features = ["accept-rfc3339-timestamps", "accept-string-booleans"] }
axum = { workspace = true }
serde = { workspace = true, features = ["derive"] }
chrono.workspace = true
uuid.workspace = true
rsa = "0.9.2"
thiserror = "1.0.40"
miette = "5.9.0"
url = { version = "2.4.0", features = ["serde"] }
dioxus = "0.3.2"
dioxus-ssr = "0.3.0"
tower-http.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
5 changes: 5 additions & 0 deletions crates/core_oauth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# core_oauth

This crate provides OAuth 2.0 [Authorization](https://datatracker.ietf.org/doc/html/rfc6749) and [Baerer Token](https://datatracker.ietf.org/doc/html/rfc6750) validation for [Photos.network](https://photos.network).

It uses [https://crates.io/crates/openidconnect](https://crates.io/crates/openidconnect) interfaces.
25 changes: 25 additions & 0 deletions crates/core_oauth/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* Photos.network · A privacy first photo storage and sharing service for fediverse.
* Copyright (C) 2020 Photos network developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Client {
pub(crate) id: String,
pub(crate) secret: Option<String>,
pub(crate) redirect_uri: String,
}
52 changes: 52 additions & 0 deletions crates/core_oauth/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* Photos.network · A privacy first photo storage and sharing service for fediverse.
* Copyright (C) 2020 Photos network developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

use dioxus::prelude::*;

Check warning on line 18 in crates/core_oauth/src/config.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unused import: `dioxus::prelude`

Check warning on line 18 in crates/core_oauth/src/config.rs

View workflow job for this annotation

GitHub Actions / Check

unused import: `dioxus::prelude`

use serde::{Deserialize, Serialize};

use std::path::{Path, PathBuf};

use crate::client::Client;

#[derive(Debug, Deserialize, Serialize)]
pub struct ServerConfig {
pub listen_addr: String,
pub domain: String,
pub use_ssl: bool,
pub realm_keys_base_path: PathBuf,
pub realms: Vec<ConfigRealm>,
}

impl Default for ServerConfig {
fn default() -> Self {
Self {
listen_addr: String::from("127.0.0.1:4200"),
domain: String::from("localhost:4200"),
use_ssl: false,
realm_keys_base_path: Path::new("keys").to_path_buf(),
realms: vec![],
}
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ConfigRealm {
pub name: String,
pub domain: Option<String>,
pub clients: Vec<Client>,
}
47 changes: 47 additions & 0 deletions crates/core_oauth/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* Photos.network · A privacy first photo storage and sharing service for fediverse.
* Copyright (C) 2020 Photos network developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

use miette::Diagnostic;

use thiserror::Error;

#[derive(Debug, Error, Diagnostic)]
pub enum Error {
#[error(transparent)]
OpenIDUrlParseError(#[from] openidconnect::url::ParseError),

#[error(transparent)]
AddrParseError(#[from] std::net::AddrParseError),

#[error("{0}")]
HyperError(String),

#[error("{0}")]
MappedError(String),

#[error(transparent)]
IOError(#[from] std::io::Error),

#[error(transparent)]
RSAError(#[from] rsa::Error),

#[error(transparent)]
Pkcs1Error(#[from] rsa::pkcs1::Error),

#[error("could not open key of realm {0}")]
CouldNotOpenRealmKey(String),
}
64 changes: 64 additions & 0 deletions crates/core_oauth/src/handler/authorize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Photos.network · A privacy first photo storage and sharing service for fediverse.
* Copyright (C) 2020 Photos network developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

//! Validate the request to ensure that all required parameters are present and valid.
//!
//! See Section 4.1.1: https://tools.ietf.org/html/rfc6749#section-4.1.1
//!
use axum::extract::{Query, State};
use axum::http::StatusCode;
use axum::response::Redirect;
use std::sync::{Arc, RwLock};

use crate::query::AuthorizeQuery;
use crate::request::AuthRequest;
use crate::state::ServerState;

pub(crate) type SharedState = Arc<RwLock<ServerState>>;

pub(crate) async fn authorization_handler(
Query(query): Query<AuthorizeQuery>,
State(state): State<SharedState>,
) -> std::result::Result<Redirect, StatusCode> {
let req = AuthRequest {
id: uuid::Uuid::new_v4(),
code_challenge: query.code_challenge,
code: None,
created_at: chrono::Utc::now().naive_utc(),
state: query.state,
nonce: query.nonce,
};
for realm in state.write().unwrap().realms.iter_mut() {
for client in realm.clients.iter() {
if &client.id == &query.client_id {
realm.requests.push(req);
let realm_login_url = format!("/{}/login", &realm.name);
return Ok(Redirect::to(&realm_login_url));
}
}
}

for client in state.read().unwrap().master_realm.clients.iter() {
if &client.id == &query.client_id {
state.write().unwrap().master_realm.requests.push(req);
let realm_login_url = format!("/{}/login", &state.read().unwrap().master_realm.name);
return Ok(Redirect::to(&realm_login_url));
}
}

Err(StatusCode::UNAUTHORIZED)
}
36 changes: 36 additions & 0 deletions crates/core_oauth/src/handler/discovery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* Photos.network · A privacy first photo storage and sharing service for fediverse.
* Copyright (C) 2020 Photos network developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

use axum::extract::State;
use axum::Json;
use axum::{headers::Host, TypedHeader};
use openidconnect::core::CoreProviderMetadata;

use super::authorize::SharedState;

pub(crate) async fn openid_discover_handler(
State(state): State<SharedState>,
TypedHeader(host): TypedHeader<Host>,
) -> Json<CoreProviderMetadata> {
for realm in state.read().unwrap().realms.iter() {
if &realm.domain == host.hostname() {
return Json(realm.provider_metadata.clone());
}
}

Json(state.read().unwrap().master_realm.provider_metadata.clone())
}
36 changes: 36 additions & 0 deletions crates/core_oauth/src/handler/jwks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* Photos.network · A privacy first photo storage and sharing service for fediverse.
* Copyright (C) 2020 Photos network developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

use axum::extract::State;
use axum::Json;
use axum::{headers::Host, TypedHeader};
use openidconnect::core::CoreJsonWebKeySet;

use super::authorize::SharedState;

pub(crate) async fn openid_jwks_handler(
State(state): State<SharedState>,
TypedHeader(host): TypedHeader<Host>,
) -> Json<CoreJsonWebKeySet> {
for realm in state.read().unwrap().realms.iter() {
if &realm.domain == host.hostname() {
return Json(realm.jwks.clone());
}
}

Json(state.read().unwrap().master_realm.jwks.clone())
}
Loading

0 comments on commit 758dd73

Please sign in to comment.