systemprompt-api 0.2.0

HTTP API server and gateway for systemprompt.io OS
Documentation
use super::{AuthorizeQuery, AuthorizeRequest, AuthorizeResponse};
use axum::Json;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Redirect};
use std::collections::HashMap;
use systemprompt_models::Config;
use systemprompt_oauth::services::templating::TemplateEngine;

pub fn convert_form_to_query(form: &AuthorizeRequest) -> AuthorizeQuery {
    AuthorizeQuery {
        response_type: form.response_type.clone(),
        client_id: form.client_id.clone(),
        redirect_uri: form.redirect_uri.clone(),
        scope: form.scope.clone(),
        state: form.state.clone(),
        code_challenge: form.code_challenge.clone(),
        code_challenge_method: form.code_challenge_method.clone(),
        response_mode: None,
        display: None,
        prompt: None,
        max_age: None,
        ui_locales: None,
        resource: form.resource.clone(),
    }
}

pub fn is_user_consent_granted(form: &AuthorizeRequest) -> bool {
    form.user_consent.as_deref() == Some("allow")
}

pub fn create_consent_denied_response(query: &AuthorizeQuery) -> axum::response::Response {
    create_error_response(
        query,
        "access_denied",
        "User denied the request",
        StatusCode::UNAUTHORIZED,
    )
}

pub fn create_error_response(
    query: &AuthorizeQuery,
    error_type: &str,
    error_description: &str,
    status_code: StatusCode,
) -> axum::response::Response {
    let state_value = query.state.as_deref().unwrap_or("");

    if let Some(redirect_uri) = &query.redirect_uri {
        let error_url = format!(
            "{}?error={}&error_description={}&state={}",
            redirect_uri,
            error_type,
            urlencoding::encode(error_description),
            state_value
        );
        return Redirect::to(&error_url).into_response();
    }

    (
        status_code,
        Json(AuthorizeResponse {
            code: None,
            state: query.state.clone(),
            error: Some(error_type.to_string()),
            error_description: Some(error_description.to_string()),
        }),
    )
        .into_response()
}

pub fn generate_webauthn_form(params: &AuthorizeQuery, resolved_scope: &str) -> String {
    let template = TemplateEngine::load_webauthn_oauth_template();
    let mut context = HashMap::new();

    let redirect_uri = params.redirect_uri.as_deref().unwrap_or("");
    let state = params.state.as_deref().unwrap_or("");
    let code_challenge = params.code_challenge.as_deref().unwrap_or("");
    let code_challenge_method = params.code_challenge_method.as_deref().unwrap_or("");
    let resource = params.resource.as_deref().unwrap_or("");
    let api_external_url = Config::get().map_or("", |c| c.api_external_url.as_str());

    context.insert("client_id", params.client_id.as_str());
    context.insert("scope", resolved_scope);
    context.insert("response_type", params.response_type.as_str());
    context.insert("redirect_uri", redirect_uri);
    context.insert("state", state);
    context.insert("code_challenge", code_challenge);
    context.insert("code_challenge_method", code_challenge_method);
    context.insert("resource", resource);
    context.insert("api_external_url", api_external_url);

    let allow_registration = Config::get().map_or(true, |c| c.allow_registration);
    let register_class = if allow_registration { "" } else { "hidden" };
    context.insert("register_class", register_class);

    TemplateEngine::render(template, context)
}