#provider #axum #oauth2 #oauth #user #url

oauth-axum

使用Axum的OAuth2授权代码流

3个版本

0.1.2 2024年5月10日
0.1.1 2024年5月6日
0.1.0 2024年4月29日

308身份验证

每月48次下载

LGPL-3.0-or-later

20KB
218

oauth-axum

这个crate是oauth2库的包装器,但它已经完成了所有提供者的配置,使得在您的Axum项目中实现它变得很容易。目的是添加这个列表中的所有提供者: https://en.wikipedia.org/wiki/List_of_OAuth_providers 那些提供了oauth2。

用法

要使用它,非常简单。只需创建某个提供者的新实例

  • CustomProvider
  • GithubProvider
  • DiscordProvider
  • TwitterProvider
  • GoogleProvider
  • MicrosoftProvider
  • FacebookProvider
  • SpotifyProvider

在您的项目中,将其传递给 new 函数

  • client_id: 在您的提供者中创建的应用的唯一ID

  • secret_id: 您提供者中的应用中的秘密令牌,此令牌需要从用户那里隐藏

  • redirect_url: 您的后端将接受的来自提供者的URL

    如果您使用的是 CustomProvider,则需要传递

  • auth_url: 用来获取您的应用访问用户账户权限的提供者URL

  • token_url: 用来生成授权令牌的URL

此项目的结构分为两个步骤

1. 生成URL

此步骤将创建一个URL,将用户重定向到提供者以执行您的应用访问用户信息的授权。

此URL具有以下格式(GitHub示例): https://github.com/login/oauth/authorize?response_type=code&client_id={CLIENT_ID}&state={RANDOM_STATE}&code_challenge={RANDOM_STATE}&code_challenge_method=S256&redirect_uri={REDIRECT_URL}&scope={SCOPES}

此步骤很重要,因为它将生成验证器字段,需要将其保存到某个地方(内存、数据库...),与状态字段一起保存。状态将是您在第二步中获取验证器的ID。

2. 回调URL

用户接受提供者的授权后,它将重定向用户到您在提供者配置中添加的特定URL redirect_url,并且重要的是要记住,在oauth-axum参数中应设置相同的URL,如果不是相同的URL,将会发生错误。此重定向将包含两个查询参数,CODE和STATE,我们需要从code和verifier字段生成一个令牌,这也是为什么在第一步中需要将验证器和状态一起保存。之后,您将拥有一个令牌来访问提供者的API。

示例

此方法适用于将在Axum的唯一实例中运行的小型项目。它将状态和验证器保存在内存中,可以在回调URL调用中访问。

mod utils;
use std::sync::Arc;

use axum::extract::Query;
use axum::Router;
use axum::{routing::get, Extension};
use oauth_axum::providers::twitter::TwitterProvider;
use oauth_axum::{CustomProvider, OAuthClient};

use crate::utils::memory_db_util::AxumState;

#[derive(Clone, serde::Deserialize)]
pub struct QueryAxumCallback {
    pub code: String,
    pub state: String,
}

#[tokio::main]
async fn main() {
    dotenv::from_filename("examples/.env").ok();
    println!("Starting server...");

    let state = Arc::new(AxumState::new());
    let app = Router::new()
        .route("/", get(create_url))
        .route("/api/v1/twitter/callback", get(callback))
        .layer(Extension(state.clone()));

    println!("🚀 Server started successfully");
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap();
}

fn get_client() -> CustomProvider {
    TwitterProvider::new(
        std::env::var("TWITTER_CLIENT_ID").expect("TWITTER_CLIENT_ID must be set"),
        std::env::var("TWITTER_SECRET").expect("TWITTER_SECRET must be set"),
        "https://127.0.0.1:3000/api/v1/twitter/callback".to_string(),
    )
}

pub async fn create_url(Extension(state): Extension<Arc<AxumState>>) -> String {
    let state_oauth = get_client()
        .generate_url(
            Vec::from(["users.read".to_string()]),
            |state_e| async move {
                //SAVE THE DATA IN THE DB OR MEMORY
                //state should be your ID
                state.set(state_e.state, state_e.verifier);
            },
        )
        .await
        .unwrap()
        .state
        .unwrap();

    state_oauth.url_generated.unwrap()
}

pub async fn callback(
    Extension(state): Extension<Arc<AxumState>>,
    Query(queries): Query<QueryAxumCallback>,
) -> String {
    println!("{:?}", state.clone().get_all_items());
    // GET DATA FROM DB OR MEMORY
    // get data using state as ID
    let item = state.get(queries.state.clone());
    get_client()
        .generate_token(queries.code, item.unwrap())
        .await
}

开发下一步

  • 添加所有测试
  • 添加更多提供者

依赖项

~8–21MB
~320K SLoC