feat: oauth

This commit is contained in:
nullishamy 2025-05-01 23:23:29 +01:00
parent 4167a2f8bb
commit f7f57e92e3
Signed by: amy
SSH key fingerprint: SHA256:WmV0uk6WgAQvDJlM8Ld4mFPHZo02CLXXP5VkwQ5xtyk
4 changed files with 108 additions and 12 deletions

View file

@ -27,8 +27,8 @@ pub async fn new_app(
VALUES (?1, ?2, ?3)
"#,
app.client_name,
app.scopes,
secret
secret,
app.scopes
)
.execute(&mut **db)
.await

View file

@ -1,23 +1,41 @@
use crate::Db;
use askama::Template;
use tracing::error;
use rocket::{
FromForm,
form::Form,
get, post,
response::Redirect,
response::status::BadRequest,
response::content::RawHtml,
serde::{Deserialize, Serialize, json::Json},
};
use rocket_db_pools::Connection;
#[get("/oauth/authorize?<client_id>&<scope>&<redirect_uri>&<response_type>")]
pub async fn authorize(
client_id: &str,
scope: &str,
redirect_uri: &str,
response_type: &str,
struct AuthorizeClient {
id: String
}
#[derive(Template)]
#[template(path = "authorize.html")]
struct AuthorizeTemplate {
client: AuthorizeClient,
scopes: Vec<String>,
scope_raw: String,
redirect_uri: String,
user_id: String
}
#[post("/oauth/accept?<id>&<client_id>&<scope>")]
pub async fn accept(
mut db: Connection<Db>,
) -> Redirect {
// For now, we will always authorize the request and assign it to an admin user
let user_id = "9b9d497b-2731-435f-a929-e609ca69dac9";
id: &str,
client_id: &str,
scope: &str
) -> RawHtml<String> {
let user_id = id;
let code = main::gen_token(15);
// This will act as a token for the user, but we will in future say that it expires very shortly
@ -52,7 +70,38 @@ pub async fn authorize(
.await
.unwrap();
Redirect::temporary(format!("{}?code={}", redirect_uri, code))
// HACK: Until we are storing oauth stuff more properly we will hardcode phanpy
RawHtml(format!(r#"
<script>window.location.href="{}{}"</script>
"#, "https://phanpy.social?code=", code))
}
#[get("/oauth/authorize?<client_id>&<scope>&<redirect_uri>&<response_type>")]
pub async fn authorize(
client_id: &str,
scope: &str,
redirect_uri: &str,
response_type: &str,
mut db: Connection<Db>,
) -> Result<RawHtml<String>, BadRequest<String>> {
if response_type != "code" {
error!("unknown response type {}", response_type);
return Err(
BadRequest(format!("unknown response type {}", response_type))
)
}
let tmpl = AuthorizeTemplate {
client: AuthorizeClient {
id: client_id.to_string()
},
scope_raw: scope.to_string(),
scopes: scope.split(" ").map(|s| s.to_string()).collect(),
redirect_uri: redirect_uri.to_string(),
user_id: "9b9d497b-2731-435f-a929-e609ca69dac9".to_string()
};
Ok(RawHtml(tmpl.render().unwrap()))
}
#[derive(Serialize, Deserialize, Debug)]

View file

@ -140,6 +140,7 @@ pub fn launch(cfg: Config) -> Rocket<Build> {
user::following,
user::post,
oauth::authorize,
oauth::accept,
oauth::new_token,
cors::options_req,
activity_endpoint,

View file

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<base href="https://ferri.amy.mov/admin/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Ferri Test</title>
<link rel="stylesheet" href="https://raw.githubusercontent.com/tailwindlabs/tailwindcss/dbc8023a08964f513c20796e170cb91ce891df3f/packages/tailwindcss/preflight.css">
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<style>
main {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
max-width: 80%;
margin: auto;
}
</style>
</head>
<body>
<main>
<h1>Authorization request</h1>
<span>
App '{{ client.id }}' would like to access the following scopes in your account
</span>
<ul>
{% for scope in scopes %}
<li>{{ scope }}</li>
{% endfor %}
</ul>
<button
hx-post="/oauth/accept?id={{ user_id }}&client_id={{ client.id }}&scope={{ scope_raw }}"
>
Accept and return to {{ redirect_uri }}?
</button>
</main>
</body>
</html>