Add basic demo SPA to display claims
This commit is contained in:
parent
15763cd0bb
commit
0f110d70b4
@ -1,2 +1,3 @@
|
|||||||
.github
|
.github
|
||||||
example
|
example
|
||||||
|
target
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
/target
|
**/target
|
||||||
/static/build
|
/static/build
|
||||||
wrangler.toml
|
**/wrangler.toml
|
||||||
|
**/node_module
|
||||||
|
**/dist
|
||||||
|
3
example/demo/.env
Normal file
3
example/demo/.env
Normal file
@ -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"
|
17
example/demo/Cargo.toml
Normal file
17
example/demo/Cargo.toml
Normal file
@ -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"] }
|
30
example/demo/README.md
Normal file
30
example/demo/README.md
Normal file
@ -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
|
||||||
|
```
|
10
example/demo/index.html
Normal file
10
example/demo/index.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
122
example/demo/src/main.rs
Normal file
122
example/demo/src/main.rs
Normal file
@ -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! {
|
||||||
|
<>
|
||||||
|
<form action="https://oidc.login.xyz/authorize">
|
||||||
|
<input type="hidden" name="client_id" value={ CLIENT_ID.clone() } />
|
||||||
|
<input type="hidden" name="response_type" value="code" />
|
||||||
|
<input type="hidden" name="nonce" value="nonce" />
|
||||||
|
<input type="hidden" name="scope" value="openid profile" />
|
||||||
|
<input type="hidden" name="state" value="state" />
|
||||||
|
<input type="hidden" name="redirect_uri" value={ REDIRECT_URI.to_string() } />
|
||||||
|
<input type="submit" value="Sign-In with Ethereum using OpenID Connect" />
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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! {
|
||||||
|
<>
|
||||||
|
<p>{ (*claims).clone() }</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn switch(routes: &Route) -> Html {
|
||||||
|
match routes {
|
||||||
|
Route::Home => html! { <SIWE /> },
|
||||||
|
Route::OIDCCallback => html! { <Callback /> },
|
||||||
|
Route::NotFound => html! { <Redirect<Route> to={Route::Home}/> },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component(App)]
|
||||||
|
fn app() -> Html {
|
||||||
|
html! {
|
||||||
|
<BrowserRouter>
|
||||||
|
<Switch<Route> render={Switch::render(switch)} />
|
||||||
|
</BrowserRouter>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
yew::start_app::<App>();
|
||||||
|
}
|
3
example/demo/workers-site/.gitignore
vendored
Normal file
3
example/demo/workers-site/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
worker
|
39
example/demo/workers-site/index.js
Normal file
39
example/demo/workers-site/index.js
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
10
example/demo/workers-site/package.json
Normal file
10
example/demo/workers-site/package.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
19
example/demo/wrangler_example.toml
Normal file
19
example/demo/wrangler_example.toml
Normal file
@ -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"
|
Loading…
Reference in New Issue
Block a user