1 个不稳定版本
0.2.0 | 2021 年 2 月 17 日 |
---|
#32 in #cloudflare-workers
50KB
822 行
wasm-service-oauth
使用 Cloudflare Workers 配合 OAuth
以下示例使用 Github 测试过。经过少量修改后,应与其他 OAuth 提供商兼容。
设置
配置参数通过一个 OAuthConfig
结构传递给服务进行初始化。以下示例在环境中设置了密钥参数,因此它们不是编译的 wasm 二进制文件的一部分。
- 创建一个包含 json 数据的环境变量
env_json
,该数据将被工作者解析。
在 wrangler.toml 的底部添加以下内容。
[vars]
env_json= """{
"oauth": {
"app_url": "https://app.example.com/",
"authorized_users": [ "gituser" ],
"client_id" : "0000",
"client_secret": "0000",
"state_secret": "0000",
"cors_origins": [ "https://app.example.com", "https://127.0.0.1:3000" ],
"logged_out_app_url": "https://app.example.com/",
"session_path_prefix": "/private/",
"session_secret": "0000"
}
}
-
app_url
: 应用的基本 URL -
client_id
、client_secret
:github API ID 和密钥 -
state_secret
、session_secret
:32 位加密密钥,以 64 个十六进制数字表示。在 Unix 上创建这些密钥的一种方法head--bytes32 /dev/urandom|hexdump-ve'1/1 "%.2x"' &&echo
-
logged_out_app_url
:用户登出后被重定向到的位置 -
session_path_prefix
:以该 URL 开头的任何 URL 都会设置其会话cookie
- 按以下方式更新您的服务程序
#[wasm_bindgen]
extern "C" {
static env_json: String;
}
pub async fn main_entry(req: Jsvalue) -> Result<Jsvalue,JsValue> {
// ...
let environ_config = env_json.as_str();
let settings = load(environ_config).map_err(|e| JsValue::from_str(&e))?;
// ...
let oauth_config = build_oauth_config(&settings.oauth)?;
let oauth_handler = OAuthHandler::init(oauth_config)
.map_err(|e| JsValue::from(&format!("OAuthHandler init error: {}", e.to_string())))?;
wasm_service::service_request(
req,
ServiceConfig {
logger,
handlers: vec![
Box::new(MyHandler(oauth_handler))
],
..Default::default()
}
).await
}
fn build_oauth_config(env: &Oauth) -> Result<OAuthConfig, JsValue> {
let allow = wasm_service_oauth::UserAllowList {
allowed_users: env.authorized_users.clone(),
login_failed_url: "/login-failed".into(),
};
let config = OAuthConfig {
app_url: env.app_url.to_string(),
logged_out_app_url: env.logged_out_app_url.to_string(),
authorize_url_path: "/authorize".to_string(),
code_url_path: "/code".to_string(),
login_failed_url_path: "/login-failed".to_string(),
logout_url_path: "/logout".to_string(),
auth_checker: Box::new(allow),
client_id: env.client_id.to_string(),
client_secret: env.client_secret.to_string(),
state_secret: key_from_hex(&env.state_secret, 32).map_err(JsValue::from)?,
session_secret: key_from_hex(&env.session_secret, 32).map_err(JsValue::from)?,
session_cookie_path_prefix: env.session_path_prefix.to_string(),
cors_origins: env.cors_origins.clone(), // .iter().map(|v| v.as_ref()).collect(),
..Default::default()
};
Ok(config)
}
/// load config from environment
pub(crate) fn load(json: &str) -> Result<Config, String> {
//let var = std::env::var("env_json")
// .map_err(|_| Error::Environment("Missing env_json".to_string()))?;
let conf = serde_json::from_str(json).map_err(|e| e.to_string())?;
Ok(conf)
}
#[derive(Debug, Deserialize)]
pub struct Config {
pub oauth: Oauth,
}
#[derive(Debug, Deserialize)]
pub struct Oauth {
pub client_id: String,
pub client_secret: String,
pub state_secret: String,
pub session_secret: String,
pub session_path_prefix: String,
pub app_url: String,
pub logged_out_app_url: String,
pub cors_origins: Vec<String>,
pub authorized_users: Vec<String>,
}
- 按以下方式更新处理函数
async fn handle(&self, req: &Request, mut ctx: &mut Context) -> Result<(), HandlerReturn> {
// urls beginning with session_path_prefix require authentication
if req.url().path().starts_with("/private/") {
let _session = self.oauth_handler.verify_auth_user(req, &mut ctx)?;
// user is authenticated!!
// ...
} else {
// handle urls not requiring authentication
// ...
}
// let oauth handler process its urls
if ctx.response().is_unset() {
if self.oauth_handler.would_handle(&req) {
// handle oauth processing for /code, /authorize, /login-failed, etc.
self.oauth_handler.handle(req, &mut ctx).await?;
}
}
Ok(())
}
依赖项
~12–32MB
~567K SLoC