feat: Styling for OIDC-related pages

This commit is contained in:
Kumi 2025-06-27 16:35:33 +02:00
parent 925a4158ac
commit e75b47fa8d
Signed by: kumi
GPG key ID: ECBCC9082395383F
7 changed files with 218 additions and 15 deletions

View file

@ -1,3 +1,2 @@
migrations/
.ruff_cache/
admin/

23
duckpond/core/claims.py Normal file
View file

@ -0,0 +1,23 @@
from django.utils.translation import gettext_lazy as _
from oidc_provider.lib.claims import ScopeClaims
class DuckpondClaims(ScopeClaims):
info_membership = (
_("Membership"),
_("Information about your membership."),
)
def scope_membership(self):
dic = {
"tier_id": self.user.membership.tier.id if self.user.membership else None,
"tier": self.user.membership.tier.name if self.user.membership else None,
"status": self.user.membership.is_active if self.user.membership else None,
}
return dic
info_profile = (
_("Profile"),
_("Access to some basic profile information, including your preferred name."),
)

View file

@ -0,0 +1,78 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}
{% trans "Authorize" %}
{% endblock %}
{% block content %}
<div class="auth-form">
<h2>{% trans "Authorization Request" %}</h2>
<div class="client-info">
<p>
<strong>{{ client.name }}</strong> {% trans "is requesting access to your account." %}
</p>
</div>
{% if scopes %}
<div class="scopes-section">
<h3>{% trans "This application will be able to:" %}</h3>
<ul class="scopes-list">
{% for scope in scopes %}
<li>
<i class="fas fa-check"></i>
{{ scope.description }}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<form method="post" action="{% url 'oidc_provider:authorize' %}">
{% csrf_token %}
{{ hidden_inputs }}
<div class="form-actions">
<button type="submit"
name="allow"
value="Authorize"
class="button button-primary">{% trans "Authorize" %}</button>
<button type="submit" class="button button-outline">{% trans "Cancel" %}</button>
</div>
</form>
</div>
<style>
.client-info {
background-color: var(--color-gray-light);
padding: var(--spacing-3);
border-radius: var(--border-radius);
margin-bottom: var(--spacing-3);
}
.scopes-section {
margin-bottom: var(--spacing-3);
}
.scopes-list {
list-style: none;
padding: 0;
margin: var(--spacing-2) 0;
}
.scopes-list li {
padding: var(--spacing-2);
background-color: var(--color-gray-light);
margin-bottom: var(--spacing-1);
border-radius: var(--border-radius-sm);
display: flex;
align-items: center;
}
.scopes-list i {
color: var(--color-success);
margin-right: var(--spacing-2);
}
.form-actions {
display: flex;
gap: var(--spacing-2);
justify-content: center;
margin-top: var(--spacing-4);
}
</style>
{% endblock %}

View file

@ -0,0 +1,55 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}
{% trans "Logout" %}
{% endblock %}
{% block content %}
<div class="auth-form">
<div class="logout-icon">
<i class="fas fa-sign-out-alt"></i>
</div>
<h2>{% trans "Logout" %}</h2>
<p>{% trans "Are you sure you want to log out?" %}</p>
{% if client %}
<p class="client-info">
{% blocktrans with client_name=client.name %}
You will be logged out from {{ client_name }}.
{% endblocktrans %}
</p>
{% endif %}
<form method="post" action="{% url 'oidc_provider:end-session' %}">
{% csrf_token %}
<input type="hidden" name="id_token_hint" value="{{ id_token_hint }}">
<input type="hidden"
name="post_logout_redirect_uri"
value="{{ post_logout_redirect_uri }}">
<input type="hidden" name="state" value="{{ state }}">
<div class="form-actions">
<button type="submit" class="button button-primary">{% trans "Logout" %}</button>
<a href="{% url 'dashboard' %}" class="button button-outline">{% trans "Cancel" %}</a>
</div>
</form>
</div>
<style>
.logout-icon {
font-size: 3rem;
color: var(--color-primary);
margin-bottom: var(--spacing-3);
text-align: center;
}
.client-info {
background-color: var(--color-gray-light);
padding: var(--spacing-2);
border-radius: var(--border-radius-sm);
margin: var(--spacing-3) 0;
}
.form-actions {
display: flex;
gap: var(--spacing-2);
justify-content: center;
margin-top: var(--spacing-4);
}
</style>
{% endblock %}

View file

@ -0,0 +1,59 @@
{% extends "base.html" %}
{% load i18n %}
{% block title %}
{% trans "Error" %}
{% endblock %}
{% block content %}
<div class="error-page">
<div class="error-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<h2>{% trans "Authentication Error" %}</h2>
<div class="error-details">
{% if error %}
<p class="error-code">
<strong>{% trans "Error:" %}</strong> {{ error }}
</p>
{% endif %}
{% if error_description %}<p class="error-description">{{ error_description }}</p>{% endif %}
</div>
<div class="error-actions">
<a href="{% url 'dashboard' %}" class="button button-primary">{% trans "Return to Dashboard" %}</a>
</div>
</div>
<style>
.error-page {
max-width: 600px;
margin: 0 auto;
text-align: center;
padding: var(--spacing-4);
}
.error-icon {
font-size: 4rem;
color: var(--color-danger);
margin-bottom: var(--spacing-3);
}
.error-details {
background-color: var(--color-gray-light);
padding: var(--spacing-3);
border-radius: var(--border-radius);
margin: var(--spacing-3) 0;
text-align: left;
}
.error-code {
color: var(--color-danger);
margin-bottom: var(--spacing-2);
}
.error-description {
color: var(--color-gray-dark);
}
.error-actions {
margin-top: var(--spacing-4);
}
</style>
{% endblock %}

View file

@ -41,21 +41,9 @@ def userinfo(claims, user):
Custom function for OIDC userinfo endpoint
"""
claims["name"] = user.get_preferred_name()
claims["given_name"] = user.first_name
claims["family_name"] = user.last_name
claims["email"] = user.email
claims["email_verified"] = user.email_verified
# Add membership information if available
try:
membership = user.membership
claims["membership"] = {
"tier": membership.tier.name,
"active": membership.is_active,
"expiry_date": membership.expiry_date.isoformat(),
}
except:
pass
claims["address"]["formatted"] = user.address
return claims

View file

@ -37,7 +37,6 @@ INSTALLED_APPS = [
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"oidc_provider",
"mjml",
"dbsettings",
"duckpond.core",
@ -45,6 +44,7 @@ INSTALLED_APPS = [
"duckpond.member_area",
"duckpond.payments",
"django.contrib.admin",
"oidc_provider",
]
MIDDLEWARE = [
@ -182,6 +182,7 @@ LOGIN_URL = "/login/"
LOGOUT_REDIRECT_URL = "/"
OIDC_USERINFO = "duckpond.core.views.userinfo"
OIDC_EXTRA_SCOPE_CLAIMS = 'duckpond.core.claims.DuckpondClaims'
# Email settings