diff --git a/.dockerignore b/.dockerignore index f2c136e..8a3a6c4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,3 @@ .github example +target diff --git a/.gitignore b/.gitignore index 2517629..ff6ab22 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ -/target +**/target /static/build -wrangler.toml +**/wrangler.toml +**/node_module +**/dist diff --git a/example/demo/.env b/example/demo/.env new file mode 100644 index 0000000..94c1917 --- /dev/null +++ b/example/demo/.env @@ -0,0 +1,3 @@ +CLIENT_ID="8f169184-8815-457c-a32f-85b39fbcfca7" +CLIENT_SECRET="430d8ade-d2d7-48c1-9c68-b97fcd73967d" +REDIRECT_URI="https://demo-oidc.login.xyz/callback" diff --git a/example/demo/Cargo.toml b/example/demo/Cargo.toml new file mode 100644 index 0000000..e9bfb14 --- /dev/null +++ b/example/demo/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "demo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +yew = "0.19.3" +yew-router = "0.16.0" +url = "2.2.2" +lazy_static = "1.4.0" +serde = "1.0.136" +openidconnect = {git = "https://github.com/sbihel/openidconnect-rs", branch = "main", default-features = false, features = ["reqwest", "rustls-tls", "rustcrypto"]} +wasm-bindgen-futures = "0.4.29" +serde_json = "1.0.78" +chrono = { version = "0.4.19", features = ["wasmbind"] } diff --git a/example/demo/README.md b/example/demo/README.md new file mode 100644 index 0000000..8145c00 --- /dev/null +++ b/example/demo/README.md @@ -0,0 +1,30 @@ +# Demo Single Page Application for the OIDC IdP + +This demo's purpose is to display the claims that are shared with Relying +Parties. It is currently deployed at https://demo-oidc.login.xyz. + +## Dependencies + +```sh +$ cargo install trunk +$ rustup target add wasm32-unknown-unknown +``` + +## Development + +```sh +trunk serve --open +``` + +## Deploy + +```sh +cp wrangler_example.toml wrangler.toml +``` +And fill in `account_id` and `zone_id`. + +```sh +$ source .env +$ trunk build +$ wrangler publish +``` diff --git a/example/demo/index.html b/example/demo/index.html new file mode 100644 index 0000000..e634ace --- /dev/null +++ b/example/demo/index.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/example/demo/src/main.rs b/example/demo/src/main.rs new file mode 100644 index 0000000..60e5815 --- /dev/null +++ b/example/demo/src/main.rs @@ -0,0 +1,122 @@ +use openidconnect::{ + core::{CoreAuthenticationFlow, CoreClient, CoreProviderMetadata}, + reqwest::async_http_client, + AuthorizationCode, ClientId, ClientSecret, CsrfToken, IssuerUrl, Nonce, Scope, TokenResponse, +}; +use serde::Deserialize; +use url::Url; +use wasm_bindgen_futures::spawn_local; +use yew::prelude::*; +use yew_router::{hooks::use_location, prelude::*}; + +lazy_static::lazy_static! { +static ref CLIENT_ID: String = option_env!("CLIENT_ID").unwrap_or("fb24a7d9-6db9-476b-93c4-e8562e750250").to_string(); +static ref CLIENT_SECRET: String = option_env!("CLIENT_SECRET").unwrap_or("6aae6334-148f-464c-a4bf-a204e62e197c").to_string(); +static ref REDIRECT_URI: Url = Url::parse(option_env!("REDIRECT_URI").unwrap_or("http://localhost:8080/callback")).unwrap(); +} + +#[derive(Clone, Routable, PartialEq)] +enum Route { + #[at("/")] + Home, + #[at("/callback")] + OIDCCallback, + #[not_found] + #[at("/404")] + NotFound, +} + +#[function_component(SIWE)] +pub fn siwe() -> Html { + html! { + <> +
+ + + + + + + +
+ + } +} + +#[derive(Deserialize)] +struct CallbackParams { + code: String, + _state: String, +} + +#[function_component(Callback)] +pub fn callback() -> Html { + let location = use_location().unwrap(); + let params: CallbackParams = location.query().unwrap(); + + let claims = use_state(String::default); + let claims2 = claims.clone(); + spawn_local(async move { + let provider_metadata = CoreProviderMetadata::discover_async( + IssuerUrl::new("https://oidc.login.xyz/".to_string()).unwrap(), + async_http_client, + ) + .await + .unwrap(); + let client = CoreClient::from_provider_metadata( + provider_metadata, + ClientId::new(CLIENT_ID.to_string()), + Some(ClientSecret::new(CLIENT_SECRET.to_string())), + ); + let (_auth_url, _csrf_token, nonce) = client + .authorize_url( + CoreAuthenticationFlow::AuthorizationCode, + CsrfToken::new_random, + || Nonce::new("nonce".to_string()), + ) + .add_scope(Scope::new("openid".to_string())) + .add_scope(Scope::new("profile".to_string())) + .url(); + let token_response = client + .exchange_code(AuthorizationCode::new(params.code)) + .request_async(async_http_client) + .await + .unwrap(); + let id_token = token_response.id_token().unwrap(); + claims2.set( + serde_json::to_string( + id_token + .claims(&client.id_token_verifier(), &nonce) + .unwrap(), + ) + .unwrap(), + ); + }); + + html! { + <> +

{ (*claims).clone() }

+ + } +} + +fn switch(routes: &Route) -> Html { + match routes { + Route::Home => html! { }, + Route::OIDCCallback => html! { }, + Route::NotFound => html! { to={Route::Home}/> }, + } +} + +#[function_component(App)] +fn app() -> Html { + html! { + + render={Switch::render(switch)} /> + + } +} + +fn main() { + yew::start_app::(); +} diff --git a/example/demo/workers-site/.gitignore b/example/demo/workers-site/.gitignore new file mode 100644 index 0000000..7915249 --- /dev/null +++ b/example/demo/workers-site/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +worker diff --git a/example/demo/workers-site/index.js b/example/demo/workers-site/index.js new file mode 100644 index 0000000..8cadca6 --- /dev/null +++ b/example/demo/workers-site/index.js @@ -0,0 +1,39 @@ +import { getAssetFromKV, mapRequestToAsset } from '@cloudflare/kv-asset-handler' + +const DEBUG = false + +addEventListener('fetch', event => { + event.respondWith(handleEvent(event)) +}) + +async function handleEvent(event) { + let options = {} + options.mapRequestToAsset = spaRouting() + options.cacheControl = { + bypassCache: DEBUG, + } + + try { + const page = await getAssetFromKV(event, options) + const response = new Response(page.body, page) + response.headers.set('X-XSS-Protection', '1; mode=block') + response.headers.set('X-Content-Type-Options', 'nosniff') + response.headers.set('X-Frame-Options', 'DENY') + response.headers.set('Referrer-Policy', 'unsafe-url') + response.headers.set('Feature-Policy', 'none') + return response + } catch (e) { + return new Response(e.message || e.toString(), { status: 500 }) + } +} + +function spaRouting() { + return request => { + let defaultAssetKey = mapRequestToAsset(request) + let url = new URL(defaultAssetKey.url) + if (url.pathname.includes(".html")) { + url.pathname = "/index.html" + } + return new Request(url.toString(), defaultAssetKey) + } +} diff --git a/example/demo/workers-site/package.json b/example/demo/workers-site/package.json new file mode 100644 index 0000000..2b4250e --- /dev/null +++ b/example/demo/workers-site/package.json @@ -0,0 +1,10 @@ +{ + "private": true, + "version": "1.0.0", + "description": "A template for kick starting a Cloudflare Workers project", + "main": "index.js", + "license": "MIT", + "dependencies": { + "@cloudflare/kv-asset-handler": "~0.1.2" + } +} diff --git a/example/demo/wrangler_example.toml b/example/demo/wrangler_example.toml new file mode 100644 index 0000000..d43c774 --- /dev/null +++ b/example/demo/wrangler_example.toml @@ -0,0 +1,19 @@ +name = "siwe-oidc-demo" +type = "webpack" +account_id = "" + +workers_dev = false + +zone_id = "" +routes = ["demo-oidc.login.xyz/*"] + +compatibility_date = "2022-02-10" + +[build] +command = "cargo install -q trunk && trunk build" + +[build.upload] +format = "service-worker" + +[site] +bucket = "./dist"