diff --git a/dashboard/src/guest.rs b/dashboard/src/guest.rs index e09c557ce23f65c214139b2fb38dbfc072b34830..745eb2b1ea150401da3ad94880dd136477602803 100644 --- a/dashboard/src/guest.rs +++ b/dashboard/src/guest.rs @@ -148,7 +148,10 @@ fn signed_claims( let header: Value = serde_json::from_slice(&URL_SAFE_NO_PAD.decode(parts[0]).map_err(|_| reject())?) .map_err(|_| reject())?; - if header["alg"] != "ES256" + if header + .get("kid") + .is_some_and(|id| id.as_str().is_none_or(str::is_empty)) + || header["alg"] != "ES256" || header.get("crit").is_some() || header.get("jku").is_some() || header.get("jwk").is_some() @@ -158,8 +161,9 @@ fn signed_claims( let matching: Vec<_> = array(&keys["keys"]) .iter() .filter(|key| { - key["kid"].is_string() - && key["kid"] == header["kid"] + header + .get("kid") + .is_none_or(|id| key.get("kid") == Some(id)) && key["kty"] == "EC" && key["crv"] == "P-256" && key.get("alg").is_none_or(|v| v == "ES256") @@ -544,17 +548,23 @@ async fn handle(app: &App, request: Request) -> Result { ], ) .into_response()), - Err(_) => Ok(( - StatusCode::FOUND, - [ - ( - "location", - format!("/sign-in?next={}&guest_error=1", encoded(&next)), - ), - ("set-cookie", auth::set_cookie(COOKIE, "", 0)), - ], - ) - .into_response()), + Err(error) => { + eprintln!( + "guest callback {provider}: {}: {}", + error.status, error.message + ); + Ok(( + StatusCode::FOUND, + [ + ( + "location", + format!("/sign-in?next={}&guest_error=1", encoded(&next)), + ), + ("set-cookie", auth::set_cookie(COOKIE, "", 0)), + ], + ) + .into_response()) + } } } @@ -585,6 +595,37 @@ mod tests { let claims = signed_claims(token, &vector["keys"], "fixture", "fixture-nonce", true).unwrap(); assert_eq!(claims["sub"], "external-123"); + let unnamed = &vector["withoutKid"]; + let unnamed_token = string(&unnamed["token"]); + assert_eq!( + signed_claims( + unnamed_token, + &unnamed["keys"], + "fixture", + "fixture-nonce", + true + ) + .unwrap()["sub"], + "external-123" + ); + let mut ambiguous = unnamed["keys"].clone(); + ambiguous["keys"] + .as_array_mut() + .unwrap() + .push(unnamed["keys"]["keys"][0].clone()); + assert!( + signed_claims(unnamed_token, &ambiguous, "fixture", "fixture-nonce", true).is_err() + ); + assert!( + signed_claims( + unnamed_token, + &vector["keys"], + "fixture", + "fixture-nonce", + true + ) + .is_err() + ); for (name, token) in vector["invalid"].as_object().unwrap() { assert!( signed_claims( diff --git a/dashboard/tests/guest-jwt.json b/dashboard/tests/guest-jwt.json index ffac1183599b864b77c595a56ccac68cc6803060..64b8fb4758cdcacc6b002791824c4e388f471500 100644 --- a/dashboard/tests/guest-jwt.json +++ b/dashboard/tests/guest-jwt.json @@ -22,5 +22,18 @@ "subject": "eyJhbGciOiJFUzI1NiIsImtpZCI6InRlc3Qta2V5In0.eyJpc3MiOiJodHRwczovL2lkZW50aXR5LmFzdGhlbm8uc29mdHdhcmUiLCJzdWIiOiIiLCJhdWQiOiJmaXh0dXJlIiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjQxMDI0NDQ4MDAsIm5vbmNlIjoiZml4dHVyZS1ub25jZSJ9.LvYyzvg0HAzhAWKo76lWyPktuoxSdmXySfXjiIWea40O0fdqGa-Du73lHzTLUJ3BDS5yWd5gF3doHbYrhbL4TA", "authorizedParty": "eyJhbGciOiJFUzI1NiIsImtpZCI6InRlc3Qta2V5In0.eyJpc3MiOiJodHRwczovL2lkZW50aXR5LmFzdGhlbm8uc29mdHdhcmUiLCJzdWIiOiJleHRlcm5hbC0xMjMiLCJhdWQiOiJmaXh0dXJlIiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjQxMDI0NDQ4MDAsIm5vbmNlIjoiZml4dHVyZS1ub25jZSIsImF6cCI6Im90aGVyIn0.W8C8sSVCpLn32JAZ6VKoJyUV9Ew_-eBsatAtynsnzJpj8l28sKv_bo8lGPq5Ef3HTxiqEGtgdWRR1wO92k_-Tw", "multipleAudience": "eyJhbGciOiJFUzI1NiIsImtpZCI6InRlc3Qta2V5In0.eyJpc3MiOiJodHRwczovL2lkZW50aXR5LmFzdGhlbm8uc29mdHdhcmUiLCJzdWIiOiJleHRlcm5hbC0xMjMiLCJhdWQiOlsiZml4dHVyZSIsIm90aGVyIl0sImlhdCI6MTcwMDAwMDAwMCwiZXhwIjo0MTAyNDQ0ODAwLCJub25jZSI6ImZpeHR1cmUtbm9uY2UifQ.D3HM97jMyMnqatXGcjDRopQIYe-ZKjzMuvVNt5dkqFW-tqXKXJlNXGd1hfpCc8XIdK4pL_iRaYwIbnMnVfZoQA" + }, + "withoutKid": { + "token": "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJodHRwczovL2lkZW50aXR5LmFzdGhlbm8uc29mdHdhcmUiLCJzdWIiOiJleHRlcm5hbC0xMjMiLCJhdWQiOiJmaXh0dXJlIiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjQxMDI0NDQ4MDAsIm5vbmNlIjoiZml4dHVyZS1ub25jZSJ9.EOWeGwhkb5C0H0844bjW3hfieiTusaMAnGsrXjR9xZsVA59uy-zggNrWBQWf6v_eyb2aWZDNwMDcVo240GPyGQ", + "keys": { + "keys": [ + { + "kty": "EC", + "crv": "P-256", + "x": "CGn5dXeweSkJttck59RKlf4xg3en2nz1wepSXpOywvU", + "y": "poKWPsMzUkytFzQmVtH7ps0sajMsZrCuYbeKAoOCw60" + } + ] + } } } diff --git a/dashboard/web/pages/SignIn.css b/dashboard/web/pages/SignIn.css index 02663f3d794840d8c5c386c6ef90e13654dc2c5a..dae08c77e2b8e98f2099a37003e827feac483303 100644 --- a/dashboard/web/pages/SignIn.css +++ b/dashboard/web/pages/SignIn.css @@ -9,5 +9,5 @@ body { font: 16px "Name Sans", sans-serif; } .pf-v5-c-login__main .checkbox label:has(:focus-visible) { outline: 2px solid var(--primary); } .setup-intro { margin-bottom: 1rem; text-align: center; } .guest-providers { margin-top: 1.5rem; } -.guest-providers p { text-align: center; } +.guest-providers a { display: flex; align-items: center; justify-content: center; text-decoration: none; } .guest-providers a + a { margin-top: .75rem; } diff --git a/dashboard/web/pages/SignIn.tsx b/dashboard/web/pages/SignIn.tsx index 2cec0cca9f3e9fdeab1aadd0c2ab006177973250..b9d150109da962c3169cb186cd1afd0dd4d71932 100644 --- a/dashboard/web/pages/SignIn.tsx +++ b/dashboard/web/pages/SignIn.tsx @@ -110,8 +110,7 @@ export function SignIn() { { event.preventDefault(); void passkey(); }}>sign in with passkey
-

sign in as a Shale guest

- {(provider) => continue with {provider.name}} + {(provider) => continue with {provider.name}}