Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/stevelr/wasm-service-oauth

Use OAuth with Cloudflare Workers
https://github.com/stevelr/wasm-service-oauth

oauth oauth2 wasm wasm-service workers

Last synced: about 2 months ago
JSON representation

Use OAuth with Cloudflare Workers

Awesome Lists containing this project

README

        

# wasm-service-oauth

Use OAuth with Cloudflare Workers

Examples below have been tested with Github. It should work with other oauth providers with minor changes.

## Setup

The configuration parameters are passed in an `OAuthConfig` struct to initialize the service.
The example below sets the secret parameters in the environment, so they aren't part of the compiled wasm binary.

1. Create an environment variable `env_json` containing json data, which will be parsed
by the worker.

At the bottom of wrangler.toml, add the following.

```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", "http://localhost:3000" ],
"logged_out_app_url": "https://app.example.com/",
"session_path_prefix": "/private/",
"session_secret": "0000"
}
}
```

- `app_url` : base url for your app
- `client_id`, `client_secret`: github api id and secret
- `state_secret`, `session_secret`: 32-bit encryption keys
as 64 hex digits. One way to create these on unix:

```head --bytes 32 /dev/urandom | hexdump -ve '1/1 "%.2x"' && echo```

- `logged_out_app_url`: where user will be redirected after logout
- `session_path_prefix`: any url beginning with this will have its session cookie set

2. Update your service program as follows

```rust2018
#[wasm_bindgen]
extern "C" {
static env_json: String;
}

pub async fn main_entry(req: Jsvalue) -> Result {
// ...
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 {
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 {
//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,
pub authorized_users: Vec,
}
```

3. Update the handler function as follows
```rust2018
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(())
}
```