80 lines
2.4 KiB
Python
80 lines
2.4 KiB
Python
"""Minimal inline HTML. No template engine — just f-strings — to keep the
|
|
reference single-purpose and dependency-light. Output is intentionally plain.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import html
|
|
import json
|
|
from typing import Any
|
|
|
|
_PAGE = """<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Porchlight RP Reference</title>
|
|
<style>
|
|
body {{ font-family: system-ui, sans-serif; max-width: 52rem; margin: 2rem auto;
|
|
padding: 0 1rem; line-height: 1.5; }}
|
|
h1 {{ font-size: 1.4rem; }}
|
|
pre {{ background: #f4f4f5; padding: 1rem; border-radius: .5rem; overflow:auto; }}
|
|
.btn {{ display: inline-block; padding: .5rem 1rem; border-radius: .4rem;
|
|
background: #1f2937; color: #fff; text-decoration: none; border: 0;
|
|
font: inherit; cursor: pointer; }}
|
|
.btn.secondary {{ background: #e5e7eb; color: #111; }}
|
|
.row {{ display: flex; gap: .5rem; flex-wrap: wrap; margin: 1rem 0; }}
|
|
.err {{ background: #fee2e2; padding: 1rem; border-radius: .5rem; }}
|
|
section {{ margin: 1.5rem 0; }}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
{body}
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
|
|
def _block(title: str, obj: Any) -> str:
|
|
pretty = html.escape(json.dumps(obj, indent=2, sort_keys=True, default=str))
|
|
return f"<section><h2>{html.escape(title)}</h2><pre>{pretty}</pre></section>"
|
|
|
|
|
|
def home_page() -> str:
|
|
body = """
|
|
<h1>Porchlight OIDC RP — reference</h1>
|
|
<p>A hand-rolled Relying Party. Click below to start the Authorization Code
|
|
flow (with PKCE) against your porchlight provider.</p>
|
|
<div class="row">
|
|
<a class="btn" href="/login">Login with Porchlight</a>
|
|
</div>
|
|
"""
|
|
return _PAGE.format(body=body)
|
|
|
|
|
|
def result_page(
|
|
*,
|
|
id_claims: dict[str, Any],
|
|
userinfo: dict[str, Any],
|
|
tokens: dict[str, Any],
|
|
) -> str:
|
|
body = f"""
|
|
<h1>Logged in</h1>
|
|
<div class="row">
|
|
<form method="post" action="/refresh"><button class="btn">Refresh tokens</button></form>
|
|
<form method="post" action="/logout"><button class="btn secondary">Log out</button></form>
|
|
</div>
|
|
{_block("ID token claims (verified)", id_claims)}
|
|
{_block("UserInfo response", userinfo)}
|
|
{_block("Raw token response", tokens)}
|
|
"""
|
|
return _PAGE.format(body=body)
|
|
|
|
|
|
def error_page(message: str) -> str:
|
|
body = f"""
|
|
<h1>Something went wrong</h1>
|
|
<div class="err"><pre>{html.escape(message)}</pre></div>
|
|
<div class="row"><a class="btn secondary" href="/">Back home</a></div>
|
|
"""
|
|
return _PAGE.format(body=body)
|