Skip to content

Commit 08519ea

Browse files
committed
refactor(sso): update ui, css theming, and login session expiry indicator
- Move sso-login css to a file - Add session expire message - Update GraphQL `UserMeType` to expose `loginExpire` field - Add test
1 parent fbb1b88 commit 08519ea

8 files changed

Lines changed: 281 additions & 87 deletions

File tree

apps/common/templates/common/sign_in.html

Lines changed: 67 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,82 @@
1+
{% load static %}
2+
13
<!DOCTYPE html>
24
<html lang="en">
35
<head>
4-
<meta charset="UTF-8">
5-
<title>Login Page</title>
6-
<script src="//accounts.google.com/gsi/client" async></script>
7-
<style>
8-
body {
9-
font-family: Arial, sans-serif;
10-
background-color: #121212;
11-
display: flex;
12-
justify-content: center;
13-
align-items: center;
14-
height: 100vh;
15-
margin: 0;
16-
}
17-
18-
.container {
19-
background-color: white;
20-
padding: 20px;
21-
border-radius: 10px;
22-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
23-
text-align: center;
24-
}
25-
26-
img {
27-
border-radius: 50%;
28-
width: 100px;
29-
height: 100px;
30-
object-fit: cover;
31-
margin-top: 10px;
32-
}
33-
34-
p {
35-
color: #333;
36-
margin: 10px 0;
37-
}
38-
39-
a {
40-
color: #007bff;
41-
text-decoration: none;
42-
font-weight: bold;
43-
}
44-
45-
a:hover {
46-
text-decoration: underline;
47-
}
48-
49-
.g_id_signin {
50-
margin-top: 20px;
51-
}
52-
</style>
6+
<meta charset="UTF-8">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
<title>Login Page</title>
9+
10+
<link rel="stylesheet" type="text/css" href="{% static "css/sso-login.css" %}"/>
11+
12+
<script src="//accounts.google.com/gsi/client" async></script>
13+
<!-- <script src="{% static "js/autoreload-dev.js" %}" async></script> -->
5314

5415
</head>
5516
<body>
5617

5718
<div class="container">
19+
{% if request.user.is_authenticated %}
20+
<h1>Timur Backend</h1>
21+
<p>
22+
Welcome, {{ request.user.display_name }} 🙂
23+
<br />
24+
Your email is <strong>{{ request.user.email }}</strong>
25+
</p>
26+
27+
<div class="card-container">
28+
<a href="{{APP_FRONTEND_HOST}}" class="card">
29+
Timur App
30+
</a>
31+
32+
{% if request.user.is_staff %}
33+
<a href="{{APP_DOMAIN}}/admin" class="card">
34+
Admin Panel
35+
</a>
36+
{% endif %}
37+
38+
<a href="{% url 'account_logout' %}" class="danger-card card">
39+
Logout
40+
</a>
41+
</div>
42+
43+
{% else %}
44+
<h1>Welcome Back 🙂 to Timur</h1>
45+
<p>Click below to sign in with Google</p>
46+
{% endif %}
47+
48+
{% if login_expire_in_days %}
49+
<p class="note {% if login_expire_in_days < login_expire_threshold %}warning{% endif %}">
50+
Login session is valid for {{ login_expire_in_days }} day{{ login_expire_in_days|pluralize }}.
51+
</p>
52+
{% endif %}
5853

59-
{% if request.user.is_authenticated %}
60-
<div>
61-
<p>Hi {{ request.user.display_name }} 🙂</p>
62-
<p>Your email is {{ request.user.email }}</p>
63-
</br> Go to the <a href="{{APP_FRONTEND_HOST}}">Timur App</a>
64-
{% if request.user.is_staff %}
65-
</br> Go to the <a href="{{APP_DOMAIN}}/admin">Admin Panel</a>
66-
{% endif %}
67-
</br> <a href="{% url 'account_logout' %}">Logout</a>
54+
{% if not request.user.is_authenticated or login_expire_in_days < login_expire_threshold %}
55+
56+
<a href="{{APP_DOMAIN}}/admin" class="card">
57+
Sign in with Admin Panel
58+
</a>
59+
60+
<!-- Reference: https://developers.google.com/identity/gsi/web/reference/html-reference --!>
61+
<div id="g_id_onload"
62+
data-client_id="{{ GOOGLE_OAUTH_CLIENT_ID }}"
63+
data-context="use"
64+
data-ux_mode="popup"
65+
data-login_uri="{% url 'google_login_by_token' %}"
66+
data-auto_prompt="false">
6867
</div>
69-
{% else %}
70-
<div>
71-
<p>Hi there 🙂 </p>
72-
<p>Click below to sign in with Google</p>
73-
74-
<div id="g_id_onload"
75-
data-client_id="{{ GOOGLE_OAUTH_CLIENT_ID }}"
76-
data-context="use"
77-
data-ux_mode="popup"
78-
data-login_uri="{% url 'google_login_by_token' %}"
79-
data-auto_prompt="false">
80-
</div>
81-
82-
<div class="g_id_signin"
83-
data-type="standard"
84-
data-shape="rectangular"
85-
data-theme="filled_black"
86-
data-text="continue_with"
87-
data-size="large"
88-
data-logo_alignment="left">
89-
</div>
90-
91-
</br> Login using <a href="{{APP_DOMAIN}}/admin">admin panel</a> instead?
92-
<p>{{ APP_ENVIRONMENT }}</p>
9368

69+
<div class="g_id_signin"
70+
data-type="standard"
71+
data-shape="rectangular"
72+
data-theme="filled_black"
73+
data-text="signin_with"
74+
data-size="large"
75+
data-logo_alignment="left">
9476
</div>
95-
{% endif %}
77+
78+
<p class="note">{{ APP_ENVIRONMENT }}</p>
79+
{% endif %}
9680
</div>
9781

9882
</body>

apps/common/views.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
1+
import datetime
2+
import typing
3+
4+
from django.contrib.sessions.models import Session
15
from django.shortcuts import render
6+
from django.utils import timezone
27
from django.views.decorators.csrf import csrf_exempt
38

49
from main import config
510

11+
if typing.TYPE_CHECKING:
12+
from django.http import HttpRequest
13+
14+
15+
def get_login_expire(request: "HttpRequest") -> datetime.datetime | None:
16+
session_model_class = getattr(request.session, "model", None)
17+
if session_model_class == Session:
18+
session_key = request.session.session_key
19+
if session := Session.objects.filter(session_key=session_key).first():
20+
return session.expire_date
21+
return None
22+
623

724
@csrf_exempt
8-
def dev_sign_in(request):
25+
def sso_sign_in(request):
926
"""
1027
For server-side development only, Used to simulate frontend sign_in
1128
"""
1229
assert config.GOOGLE_SSO_ENABLED
1330
assert config.SOCIALACCOUNT_PROVIDERS
31+
32+
login_expire_in_days = None
33+
if login_expire := get_login_expire(request):
34+
login_expire_in_days = (login_expire - timezone.now()).days
35+
1436
return render(
1537
request,
1638
"common/sign_in.html",
1739
context=dict(
1840
GOOGLE_OAUTH_CLIENT_ID=config.SOCIALACCOUNT_PROVIDERS["google"]["APP"]["client_id"],
41+
login_expire_threshold=4,
42+
login_expire_in_days=login_expire_in_days,
1943
),
2044
)

apps/static/css/sso-login.css

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/* General body styles */
2+
:root {
3+
--color-background: #d7d7d7;
4+
--color-foreground: #ffffff;
5+
--color-text: #333333;
6+
--color-text-warning: #ffcc00;
7+
8+
--color-background-card: #e9e9e9;
9+
--color-background-card-danger: #f9d9d9;
10+
11+
--font-size-sm: 12px;
12+
--font-size-md: 14px;
13+
--font-size-lg: 16px;
14+
--font-size-xl: 20px;
15+
--font-size-xxl: 24px;
16+
17+
--spacing-xs: 4px;
18+
--spacing-sm: 6px;
19+
--spacing-md: 12px;
20+
--spacing-lg: 16px;
21+
--spacing-xl: 24px;
22+
23+
--radius-md: 12px;
24+
--radius-lg: 16px;
25+
}
26+
27+
@media (prefers-color-scheme: dark) {
28+
:root {
29+
--color-background: #1e1e1e;
30+
--color-foreground: #2a2a2a;
31+
--color-text: #f0f0f0;
32+
--color-text-warning: #ffcc00;
33+
34+
--color-background-card: #2f2f2f;
35+
--color-background-card-danger: #552222;
36+
}
37+
}
38+
39+
html {
40+
margin: 0;
41+
padding: 0;
42+
}
43+
44+
body {
45+
margin: 0;
46+
font-family: 'Roboto', sans-serif;
47+
background-color: var(--color-background); /* Default light background */
48+
display: flex;
49+
justify-content: center;
50+
align-items: center;
51+
height: 100vh;
52+
color: var(--color-text); /* Default dark text */
53+
transition: background-color 0.3s ease, color 0.3s ease;
54+
}
55+
56+
/* Light theme adjustments */
57+
58+
.container {
59+
background-color: var(--color-foreground);
60+
display: flex;
61+
flex-direction: column;
62+
gap: var(--spacing-md);
63+
padding: var(--spacing-xl);
64+
margin: var(--spacing-lg);
65+
border-radius: var(--radius-lg);
66+
text-align: center;
67+
width: 100%;
68+
69+
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
70+
max-width: 380px;
71+
transition: all 0.3s ease-in-out;
72+
}
73+
74+
.container:hover {
75+
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.2);
76+
}
77+
78+
.card-container {
79+
display: flex;
80+
flex-direction: column;
81+
gap: var(--spacing-md);
82+
}
83+
84+
.card {
85+
background: var(--color-background-card);
86+
border-radius: var(--radius-md);
87+
display: flex;
88+
justify-content: center;
89+
align-items: center;
90+
text-align: center;
91+
padding: var(--spacing-md);
92+
93+
transition: box-shadow 0.3s ease-in-out, transform 0.3s ease-in-out;
94+
}
95+
96+
.card:hover {
97+
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
98+
transform: translateY(-4px);
99+
}
100+
101+
.card a {
102+
font-size: var(--font-size-md);
103+
text-align: center;
104+
}
105+
106+
.danger-card, .card a {
107+
background-color: var(--color-background-card-danger);
108+
}
109+
110+
.note {
111+
font-size: var(--font-size-md);
112+
}
113+
114+
.warning {
115+
color: var(--color-text-warning);
116+
font-weight: bold;
117+
font-size: var(--font-size-md);
118+
}
119+
120+
/* Google login */
121+
122+
.g_id_signin {
123+
transition: transform 0.3s ease-in-out;
124+
}
125+
126+
.g_id_signin div {
127+
display: flex;
128+
flex-direction: column;
129+
justify-content: center;
130+
align-items: center;
131+
}
132+
133+
.g_id_signin:hover {
134+
transform: translateY(-4px);
135+
}
136+
137+
/* Global override for anchor .card (applies to both themes) */
138+
img {
139+
width: 80px;
140+
height: 80px;
141+
border-radius: 50%;
142+
object-fit: cover;
143+
margin-bottom: 15px;
144+
transition: transform 0.3s ease-in-out;
145+
}
146+
147+
img:hover {
148+
transform: scale(1.1);
149+
}
150+
151+
h1 {
152+
font-size: var(--font-size-xxl);
153+
margin: 0;
154+
font-weight: 600;
155+
}
156+
157+
p {
158+
font-size: var(--font-size-lg);
159+
}
160+
161+
a {
162+
color: var(--color-text);
163+
text-decoration: none;
164+
transition: color 0.3s;
165+
}
166+
167+
a:hover {
168+
color: var(--color-text);
169+
text-decoration: none;
170+
}

apps/static/js/autoreload-dev.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
window.setInterval(
2+
() => {
3+
window.location.reload();
4+
},
5+
1000,
6+
);

0 commit comments

Comments
 (0)