Files
Gondulf/src/gondulf/templates/verify_code.html
Phil Skentelbery 8dddc73826 fix(security): require domain verification before authorization
CRITICAL SECURITY FIX: The authorization endpoint was bypassing domain
verification entirely, allowing anyone to authenticate as any domain.

Changes:
- Add domain verification check in GET /authorize before showing consent
- Add POST /authorize/verify-code endpoint for code validation
- Add verify_code.html and verification_error.html templates
- Add check_domain_verified() and store_verified_domain() functions
- Preserve OAuth parameters through verification flow

Flow for unverified domains:
1. GET /authorize -> Check DB for verified domain
2. If not verified: start 2FA (DNS + email) -> show code entry form
3. POST /authorize/verify-code -> validate code -> store verified
4. Show consent page
5. POST /authorize/consent -> issue authorization code

Verified domains skip directly to consent page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-22 12:45:59 -07:00

52 lines
1.9 KiB
HTML

{% extends "base.html" %}
{% block title %}Verify Your Identity - Gondulf{% endblock %}
{% block content %}
<h1>Verify Your Identity</h1>
<p>To sign in as <strong>{{ domain }}</strong>, please enter the verification code sent to <strong>{{ masked_email }}</strong>.</p>
{% if error %}
<div class="error">
<p>{{ error }}</p>
</div>
{% endif %}
<form method="POST" action="/authorize/verify-code">
<!-- Pass through authorization parameters -->
<input type="hidden" name="domain" value="{{ domain }}">
<input type="hidden" name="client_id" value="{{ client_id }}">
<input type="hidden" name="redirect_uri" value="{{ redirect_uri }}">
<input type="hidden" name="response_type" value="{{ response_type }}">
<input type="hidden" name="state" value="{{ state }}">
<input type="hidden" name="code_challenge" value="{{ code_challenge }}">
<input type="hidden" name="code_challenge_method" value="{{ code_challenge_method }}">
<input type="hidden" name="scope" value="{{ scope }}">
<input type="hidden" name="me" value="{{ me }}">
<div class="form-group">
<label for="code">Verification Code:</label>
<input type="text"
id="code"
name="code"
placeholder="000000"
maxlength="6"
pattern="[0-9]{6}"
inputmode="numeric"
autocomplete="one-time-code"
required
autofocus>
</div>
<button type="submit">Verify</button>
</form>
<p class="help-text">
Did not receive a code? Check your spam folder.
<a href="/authorize?client_id={{ client_id }}&redirect_uri={{ redirect_uri }}&response_type={{ response_type }}&state={{ state }}&code_challenge={{ code_challenge }}&code_challenge_method={{ code_challenge_method }}&scope={{ scope }}&me={{ me }}">
Request a new code
</a>
</p>
{% endblock %}