forked from nexu-io/open-design
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample.html
More file actions
248 lines (228 loc) · 14 KB
/
Copy pathexample.html
File metadata and controls
248 lines (228 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Spec — Two-factor authentication for Northwind</title>
<style>
:root {
--bg: #f5f7fa;
--paper: #ffffff;
--ink: #0e1322;
--muted: #5a647a;
--line: #e2e6ee;
--line-strong: #c8cfdb;
--accent: #4a36e3;
--accent-soft: #ece8ff;
--warn: #b8741a;
--positive: #1f8a5a;
--display: 'Charter', Georgia, serif;
--body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, sans-serif;
--mono: ui-monospace, SFMono-Regular, Menlo, monospace;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--body); font-size: 14.5px; line-height: 1.6; }
.page { max-width: 1080px; margin: 28px auto; padding: 0 32px 64px; }
header.top { display: flex; justify-content: space-between; align-items: center; padding: 16px 0; border-bottom: 1px solid var(--line); margin-bottom: 28px; }
.top-left { display: flex; align-items: center; gap: 14px; }
.crumb { font-family: var(--mono); font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; }
.pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; }
.pill.draft { background: var(--accent-soft); color: var(--accent); }
.pill.dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.top-actions { display: flex; gap: 8px; font-size: 12.5px; color: var(--muted); }
.top-actions span { padding: 4px 10px; border: 1px solid var(--line); border-radius: 8px; }
h1 { font-family: var(--display); font-size: 42px; line-height: 1.06; letter-spacing: -0.015em; margin: 8px 0 8px; max-width: 22ch; font-weight: 700; }
.summary { font-size: 17px; color: var(--muted); max-width: 64ch; margin: 0 0 28px; }
.meta-row { display: flex; gap: 32px; margin: 14px 0 36px; padding: 16px 22px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; font-size: 13px; }
.meta-row span strong { display: block; font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); margin-bottom: 4px; font-weight: 500; }
section { margin-top: 40px; }
h2 { font-family: var(--display); font-size: 24px; margin: 0 0 4px; letter-spacing: -0.005em; }
h2 small { display: block; font-family: var(--body); font-size: 13px; color: var(--muted); font-weight: 400; margin-top: 4px; line-height: 1.5; letter-spacing: 0; }
/* Problem */
.problem { display: grid; grid-template-columns: 1.5fr 1fr; gap: 14px; margin-top: 14px; }
.panel { padding: 22px 24px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; }
.quote { padding: 22px 24px; background: var(--accent-soft); border-left: 3px solid var(--accent); border-radius: 6px; }
.quote .body { font-family: var(--display); font-size: 17px; line-height: 1.5; }
.quote .author { font-family: var(--mono); font-size: 11.5px; color: var(--muted); margin-top: 12px; text-transform: uppercase; letter-spacing: 0.06em; }
/* Goals */
.goals { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 14px; }
.goal-list { padding: 22px 24px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; }
.goal-list h3 { font-family: var(--display); font-size: 16px; margin: 0 0 10px; }
.goal-list h3 .tick { display: inline-flex; width: 18px; height: 18px; border-radius: 50%; align-items: center; justify-content: center; margin-right: 8px; font-size: 11px; }
.goal-list h3 .tick.yes { background: var(--positive); color: white; }
.goal-list h3 .tick.no { background: var(--line-strong); color: var(--muted); }
.goal-list ul { padding-left: 18px; margin: 0; display: flex; flex-direction: column; gap: 6px; font-size: 14px; }
/* Metrics table */
table { width: 100%; border-collapse: collapse; margin-top: 14px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
th, td { padding: 12px 18px; text-align: left; font-size: 13.5px; border-bottom: 1px solid var(--line); }
th { font-family: var(--mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); background: #f8fafd; }
tr:last-child td { border-bottom: none; }
td.target { font-family: var(--mono); color: var(--accent); font-weight: 600; }
/* Stories */
.stories { display: flex; flex-direction: column; gap: 12px; margin-top: 14px; }
.story { padding: 18px 22px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; display: grid; grid-template-columns: auto 1fr; gap: 16px; align-items: center; }
.story-num { width: 30px; height: 30px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); display: inline-flex; align-items: center; justify-content: center; font-family: var(--mono); font-weight: 600; font-size: 13px; }
.story-text { font-size: 14.5px; }
.story-text strong { color: var(--accent); }
/* Milestones */
.timeline { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-top: 14px; }
.step { padding: 18px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; position: relative; }
.step .badge { display: inline-block; padding: 3px 8px; border-radius: 999px; font-family: var(--mono); font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 8px; background: var(--accent-soft); color: var(--accent); }
.step h4 { font-family: var(--display); font-size: 15px; margin: 0 0 6px; }
.step .meta { font-family: var(--mono); font-size: 11px; color: var(--muted); margin-bottom: 8px; }
.step ul { padding-left: 16px; margin: 0; font-size: 13px; display: flex; flex-direction: column; gap: 4px; }
/* Open questions */
.questions { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.question { padding: 16px 20px; background: var(--paper); border: 1px solid var(--line); border-radius: 10px; display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: center; }
.question p { margin: 0; font-size: 14px; }
.assignee { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.avatar { width: 22px; height: 22px; border-radius: 50%; background: linear-gradient(135deg, var(--accent), #8473ff); color: white; font-size: 11px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
footer { margin-top: 60px; padding-top: 18px; border-top: 1px solid var(--line); display: flex; justify-content: space-between; font-family: var(--mono); font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
@media (max-width: 880px) {
.problem, .goals { grid-template-columns: 1fr; }
.timeline { grid-template-columns: 1fr 1fr; }
h1 { font-size: 32px; }
}
</style>
</head>
<body>
<div class="page">
<header class="top">
<div class="top-left">
<span class="crumb">Northwind / Specs / Auth</span>
<span class="pill draft"><span class="pill dot"></span>Draft v0.4</span>
</div>
<div class="top-actions">
<span>Owner · Devon Park</span>
<span>Updated · 22 Oct 2025</span>
<span>Reviewers · 4</span>
</div>
</header>
<h1>Two-factor authentication for the Northwind app.</h1>
<p class="summary">Add TOTP and security-key second factors to the Northwind login flow so enterprise customers can meet their internal controls and we can move from "considered" to "approved" on three pending deals.</p>
<div class="meta-row">
<span><strong>Squad</strong>Identity Platform</span>
<span><strong>Engineering lead</strong>Priya Banerjee</span>
<span><strong>Design lead</strong>Sasha Lin</span>
<span><strong>Target launch</strong>End Q4 (Dec 18)</span>
<span><strong>Effort</strong>~6 eng-weeks</span>
</div>
<section>
<h2>Problem<small>What hurts today, and for whom.</small></h2>
<div class="problem">
<div class="panel">
<p>Three of the last six enterprise security reviews flagged the absence of a second factor as a blocker. Today, password is the only thing standing between a phished credential and a workspace full of customer data — for tenants under SOC 2 Type II expectations that's not just a perception problem, it's a control-plane gap.</p>
<p>It also affects internal staff: every engineer with prod access is on the same auth surface as a marketing-team viewer. We rely on policy, not posture.</p>
</div>
<div class="quote">
<div class="body">"We love the product, but the absence of TOTP came up in two of three security reviews. Add it and we can sign."</div>
<div class="author">— Maya Reddy · CTO, Pioneer Robotics</div>
</div>
</div>
</section>
<section>
<h2>Goals & non-goals<small>What this spec ships, and what we're explicitly leaving for later.</small></h2>
<div class="goals">
<div class="goal-list">
<h3><span class="tick yes">✓</span>Goals</h3>
<ul>
<li>TOTP support (Authy, 1Password, Google Authenticator) for all paid plans.</li>
<li>Security key support (WebAuthn) for Enterprise plans.</li>
<li>Workspace-level enforcement: admin can require 2FA for all members.</li>
<li>Recovery codes — printable, downloadable, regeneratable.</li>
<li>Audit log entries for setup, change, and removal events.</li>
</ul>
</div>
<div class="goal-list">
<h3><span class="tick no">×</span>Non-goals</h3>
<ul>
<li>SMS as a second factor (NIST deprecated; not adding).</li>
<li>SSO replacement — SAML stays a separate workstream.</li>
<li>Per-action step-up (future spec, owned by Identity).</li>
<li>Custom 2FA brand voice for whitelabel deployments.</li>
</ul>
</div>
</div>
</section>
<section>
<h2>Success metrics<small>We'll judge this launch on the three numbers below at the 30 / 60 / 90 day marks.</small></h2>
<table>
<thead><tr><th>Metric</th><th>Baseline</th><th>Target (90d)</th><th>How we measure</th></tr></thead>
<tbody>
<tr><td>Enterprise deals unblocked by 2FA gap</td><td>0 of 3</td><td class="target">3 of 3</td><td>Sales motion notes + signed contract count</td></tr>
<tr><td>Member 2FA adoption (paid workspaces)</td><td>n/a</td><td class="target">≥ 60%</td><td>auth.factor_enrolled events / DAU</td></tr>
<tr><td>Account takeover incidents (rolling 30d)</td><td>4 last quarter</td><td class="target">≤ 1</td><td>Security incident tracker (SEV-3+)</td></tr>
<tr><td>Support load from 2FA recovery</td><td>n/a</td><td class="target">< 1.5% of tickets</td><td>Tagged "auth-2fa" in Zendesk</td></tr>
</tbody>
</table>
</section>
<section>
<h2>User stories<small>Three personas, three motions.</small></h2>
<div class="stories">
<div class="story">
<div class="story-num">1</div>
<div class="story-text">As a <strong>workspace admin</strong>, I want to require 2FA for everyone in my workspace, so that I can pass our annual SOC 2 control review.</div>
</div>
<div class="story">
<div class="story-num">2</div>
<div class="story-text">As a <strong>day-to-day member</strong>, I want to enroll a TOTP app in under two minutes, so that I'm not pulled out of work to reconfigure auth.</div>
</div>
<div class="story">
<div class="story-num">3</div>
<div class="story-text">As a <strong>support engineer</strong>, I want a clear path to help locked-out users without bypassing their second factor, so that we don't undo the security we just added.</div>
</div>
</div>
</section>
<section>
<h2>Rollout milestones<small>Four phases. Each phase ships behind a flag.</small></h2>
<div class="timeline">
<div class="step">
<span class="badge">M1 · Nov 4</span>
<h4>TOTP enrollment</h4>
<div class="meta">2 eng-weeks</div>
<ul><li>Settings page UI</li><li>Recovery codes</li><li>Audit log entries</li></ul>
</div>
<div class="step">
<span class="badge">M2 · Nov 18</span>
<h4>Login flow</h4>
<div class="meta">1.5 eng-weeks</div>
<ul><li>Challenge step in login</li><li>Trusted-device cookie</li><li>Rate limiting</li></ul>
</div>
<div class="step">
<span class="badge">M3 · Dec 2</span>
<h4>WebAuthn + admin enforcement</h4>
<div class="meta">2 eng-weeks</div>
<ul><li>Security keys (Enterprise)</li><li>Workspace policy</li><li>Member nag prompt</li></ul>
</div>
<div class="step">
<span class="badge">M4 · Dec 18</span>
<h4>GA + comms</h4>
<div class="meta">0.5 eng-weeks</div>
<ul><li>Changelog + email</li><li>Help center articles</li><li>Sales enablement</li></ul>
</div>
</div>
</section>
<section>
<h2>Open questions<small>Assigned. We need answers by Friday Oct 31 to keep the date.</small></h2>
<div class="questions">
<div class="question">
<p>Should we let members choose between TOTP and security keys, or pick the strongest available factor for them?</p>
<span class="assignee"><span class="avatar">DP</span>Devon Park · Oct 28</span>
</div>
<div class="question">
<p>Trusted-device cookie lifetime: 7 days, 30 days, or admin-configurable?</p>
<span class="assignee"><span class="avatar">PB</span>Priya Banerjee · Oct 29</span>
</div>
<div class="question">
<p>Do we surface a member's 2FA status in the admin user list, or only in the audit log?</p>
<span class="assignee"><span class="avatar">SL</span>Sasha Lin · Oct 30</span>
</div>
</div>
</section>
<footer>
<span>Northwind Identity Platform · spec-2fa</span>
<span>v0.4 · 22 October 2025</span>
</footer>
</div>
</body>
</html>