tideway 0.7.17

A batteries-included Rust web framework built on Axum for building SaaS applications quickly
Documentation
#![cfg(feature = "auth")]

use async_trait::async_trait;
use axum::{Router, routing::get};
use serde::Deserialize;
use std::sync::Arc;
use tideway::auth::{AuthProvider, AuthUser};
use tideway::testing::get as test_get;
use tideway::{App, AppContext, MessageResponse, RouteModule, TidewayError};

#[derive(Clone, Default)]
struct TestProvider;

#[derive(Clone, Deserialize)]
struct TestClaims {
    sub: String,
}

#[derive(Clone)]
struct TestUser {
    id: String,
}

#[async_trait]
impl AuthProvider for TestProvider {
    type Claims = TestClaims;
    type User = TestUser;

    async fn verify_token(&self, token: &str) -> tideway::Result<Self::Claims> {
        if token == "ok-token" {
            Ok(TestClaims {
                sub: "user-1".to_string(),
            })
        } else {
            Err(TidewayError::unauthorized("Invalid token"))
        }
    }

    async fn load_user(&self, claims: &Self::Claims) -> tideway::Result<Self::User> {
        Ok(TestUser {
            id: claims.sub.clone(),
        })
    }
}

struct ProtectedModule;

impl RouteModule for ProtectedModule {
    fn routes(&self) -> Router<AppContext> {
        Router::new().route("/me", get(me))
    }

    fn prefix(&self) -> Option<&str> {
        Some("/api")
    }
}

async fn me(AuthUser(user): AuthUser<TestProvider>) -> MessageResponse {
    MessageResponse::success(format!("hello {}", user.id))
}

#[tokio::test]
async fn test_auth_provider_from_context_is_available_to_extractors() {
    let context = AppContext::builder()
        .with_auth_provider(Arc::new(TestProvider))
        .build();

    let app = App::new()
        .with_context(context)
        .register_module(ProtectedModule)
        .into_router_with_middleware();

    test_get(app, "/api/me")
        .header("Authorization", "Bearer ok-token")
        .execute()
        .await
        .assert_ok();
}