sveltekit-auth-example 5.5.0 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -1
- package/README.md +12 -12
- package/db_create.sh +13 -0
- package/db_create.sql +135 -133
- package/db_schema.sql +369 -0
- package/package.json +4 -2
- package/src/hooks.server.ts +10 -7
- package/src/lib/server/email/index.ts +1 -0
- package/src/lib/server/email/mfa-code.ts +16 -0
- package/src/routes/+layout.svelte +89 -28
- package/src/routes/auth/google/+server.ts +6 -1
- package/src/routes/auth/login/+server.ts +41 -8
- package/src/routes/auth/mfa/+server.ts +60 -0
- package/src/routes/auth/register/+server.ts +4 -1
- package/src/routes/auth/reset/[token]/+page.svelte +6 -8
- package/src/routes/forgot/+page.svelte +5 -8
- package/src/routes/layout.css +3 -3
- package/src/routes/login/+page.svelte +203 -77
- package/src/routes/profile/+page.svelte +16 -12
- package/src/routes/register/+page.svelte +145 -122
|
@@ -69,8 +69,7 @@
|
|
|
69
69
|
})
|
|
70
70
|
const fromEndpoint = await res.json()
|
|
71
71
|
if (!res.ok) {
|
|
72
|
-
if (res.status == 409)
|
|
73
|
-
throw new Error('Sorry, that email address is already in use.')
|
|
72
|
+
if (res.status == 409) throw new Error('Sorry, that email address is already in use.')
|
|
74
73
|
throw new Error(fromEndpoint.message || res.statusText)
|
|
75
74
|
}
|
|
76
75
|
if (fromEndpoint.emailVerification) {
|
|
@@ -101,138 +100,162 @@
|
|
|
101
100
|
{#if emailVerificationSent}
|
|
102
101
|
<div class="tw:mx-auto tw:mt-20 tw:max-w-sm tw:space-y-4">
|
|
103
102
|
<h4>Check your email</h4>
|
|
104
|
-
<p>
|
|
103
|
+
<p>
|
|
104
|
+
We've sent a verification link to your email address. Please check your inbox (and junk
|
|
105
|
+
folder) to complete your registration.
|
|
106
|
+
</p>
|
|
105
107
|
<a href="/login" class="btn-primary tw:block tw:text-center tw:no-underline">Back to login</a>
|
|
106
108
|
</div>
|
|
107
109
|
{:else}
|
|
108
|
-
<form
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
<form
|
|
111
|
+
bind:this={formEl}
|
|
112
|
+
autocomplete="on"
|
|
113
|
+
novalidate
|
|
114
|
+
class="tw:mx-auto tw:my-8 tw:max-w-sm tw:space-y-4"
|
|
115
|
+
class:submitted
|
|
116
|
+
onsubmit={e => {
|
|
117
|
+
e.preventDefault()
|
|
118
|
+
register()
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
<h4>Register</h4>
|
|
117
122
|
<p>Welcome to our community.</p>
|
|
118
123
|
|
|
119
124
|
<div class="tw:relative tw:w-full">
|
|
120
125
|
<!-- Real Google button: invisible but receives clicks -->
|
|
121
|
-
<div id="googleButton" class="tw:
|
|
126
|
+
<div id="googleButton" class="tw:w-full tw:opacity-0"></div>
|
|
122
127
|
<!-- Visual overlay: looks good, no pointer events -->
|
|
123
|
-
<div
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
<div
|
|
129
|
+
class="tw:pointer-events-none tw:absolute tw:inset-0 tw:flex tw:items-center tw:justify-center tw:gap-3 tw:rounded tw:border tw:border-gray-300 tw:bg-white tw:text-sm tw:font-medium tw:text-gray-700 tw:dark:border-gray-600 tw:dark:bg-gray-800 tw:dark:text-gray-200"
|
|
130
|
+
>
|
|
131
|
+
<svg
|
|
132
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
133
|
+
viewBox="0 0 24 24"
|
|
134
|
+
width="18"
|
|
135
|
+
height="18"
|
|
136
|
+
aria-hidden="true"
|
|
137
|
+
>
|
|
138
|
+
<path
|
|
139
|
+
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
140
|
+
fill="#4285F4"
|
|
141
|
+
/>
|
|
142
|
+
<path
|
|
143
|
+
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
144
|
+
fill="#34A853"
|
|
145
|
+
/>
|
|
146
|
+
<path
|
|
147
|
+
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z"
|
|
148
|
+
fill="#FBBC05"
|
|
149
|
+
/>
|
|
150
|
+
<path
|
|
151
|
+
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
152
|
+
fill="#EA4335"
|
|
153
|
+
/>
|
|
129
154
|
</svg>
|
|
130
155
|
Sign in with Google
|
|
131
156
|
</div>
|
|
132
157
|
</div>
|
|
133
158
|
|
|
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
|
-
|
|
159
|
+
<label class="tw:block tw:text-sm tw:font-medium" for="email">
|
|
160
|
+
Email
|
|
161
|
+
<input
|
|
162
|
+
bind:this={focusedField}
|
|
163
|
+
type="email"
|
|
164
|
+
class="form-input-validated"
|
|
165
|
+
bind:value={user.email}
|
|
166
|
+
required
|
|
167
|
+
placeholder="Email"
|
|
168
|
+
id="email"
|
|
169
|
+
autocomplete="email"
|
|
170
|
+
/>
|
|
171
|
+
<span class="form-error">Email address required</span>
|
|
172
|
+
</label>
|
|
173
|
+
|
|
174
|
+
<label class="tw:block tw:text-sm tw:font-medium" for="password">
|
|
175
|
+
Password
|
|
176
|
+
<input
|
|
177
|
+
type="password"
|
|
178
|
+
id="password"
|
|
179
|
+
class="form-input-validated"
|
|
180
|
+
bind:value={user.password}
|
|
181
|
+
required
|
|
182
|
+
minlength="8"
|
|
183
|
+
maxlength="80"
|
|
184
|
+
pattern={passwordPattern}
|
|
185
|
+
placeholder="Password"
|
|
186
|
+
autocomplete="new-password"
|
|
187
|
+
/>
|
|
188
|
+
<span class="form-error"
|
|
189
|
+
>Must be 8+ characters with a capital letter, number, and special character</span
|
|
190
|
+
>
|
|
191
|
+
<span class="tw:text-xs tw:text-gray-500">
|
|
192
|
+
Minimum 8 characters, one capital letter, one number, one special character.
|
|
193
|
+
</span>
|
|
194
|
+
</label>
|
|
195
|
+
|
|
196
|
+
<label class="tw:block tw:text-sm tw:font-medium" for="confirmPassword">
|
|
197
|
+
Confirm password
|
|
198
|
+
<input
|
|
199
|
+
type="password"
|
|
200
|
+
id="confirmPassword"
|
|
201
|
+
class="form-input"
|
|
202
|
+
class:tw:border-red-500={passwordMismatch}
|
|
203
|
+
bind:this={confirmPassword}
|
|
204
|
+
required
|
|
205
|
+
minlength="8"
|
|
206
|
+
maxlength="80"
|
|
207
|
+
placeholder="Password (again)"
|
|
208
|
+
autocomplete="new-password"
|
|
209
|
+
/>
|
|
210
|
+
{#if passwordMismatch}
|
|
211
|
+
<span class="tw:mt-0.5 tw:text-xs tw:text-red-600">Passwords must match</span>
|
|
212
|
+
{/if}
|
|
213
|
+
</label>
|
|
214
|
+
|
|
215
|
+
<label class="tw:block tw:text-sm tw:font-medium" for="firstName">
|
|
216
|
+
First name
|
|
217
|
+
<input
|
|
218
|
+
bind:value={user.firstName}
|
|
219
|
+
class="form-input-validated"
|
|
220
|
+
id="firstName"
|
|
221
|
+
placeholder="First name"
|
|
222
|
+
required
|
|
223
|
+
autocomplete="given-name"
|
|
224
|
+
/>
|
|
225
|
+
<span class="form-error">First name required</span>
|
|
226
|
+
</label>
|
|
227
|
+
|
|
228
|
+
<label class="tw:block tw:text-sm tw:font-medium" for="lastName">
|
|
229
|
+
Last name
|
|
230
|
+
<input
|
|
231
|
+
bind:value={user.lastName}
|
|
232
|
+
class="form-input-validated"
|
|
233
|
+
id="lastName"
|
|
234
|
+
placeholder="Last name"
|
|
235
|
+
required
|
|
236
|
+
autocomplete="family-name"
|
|
237
|
+
/>
|
|
238
|
+
<span class="form-error">Last name required</span>
|
|
239
|
+
</label>
|
|
240
|
+
|
|
241
|
+
<label class="tw:block tw:text-sm tw:font-medium" for="phone">
|
|
242
|
+
Phone
|
|
243
|
+
<input
|
|
244
|
+
type="tel"
|
|
245
|
+
bind:value={user.phone}
|
|
246
|
+
id="phone"
|
|
247
|
+
class="form-input"
|
|
248
|
+
placeholder="Phone"
|
|
249
|
+
autocomplete="tel-local"
|
|
250
|
+
/>
|
|
251
|
+
</label>
|
|
252
|
+
|
|
253
|
+
{#if message}
|
|
254
|
+
<p class="tw:text-red-600">{message}</p>
|
|
185
255
|
{/if}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
bind:value={user.firstName}
|
|
192
|
-
class="form-input-validated"
|
|
193
|
-
id="firstName"
|
|
194
|
-
placeholder="First name"
|
|
195
|
-
required
|
|
196
|
-
autocomplete="given-name"
|
|
197
|
-
/>
|
|
198
|
-
<span class="form-error">First name required</span>
|
|
199
|
-
</label>
|
|
200
|
-
|
|
201
|
-
<label class="tw:block tw:text-sm tw:font-medium" for="lastName">
|
|
202
|
-
Last name
|
|
203
|
-
<input
|
|
204
|
-
bind:value={user.lastName}
|
|
205
|
-
class="form-input-validated"
|
|
206
|
-
id="lastName"
|
|
207
|
-
placeholder="Last name"
|
|
208
|
-
required
|
|
209
|
-
autocomplete="family-name"
|
|
210
|
-
/>
|
|
211
|
-
<span class="form-error">Last name required</span>
|
|
212
|
-
</label>
|
|
213
|
-
|
|
214
|
-
<label class="tw:block tw:text-sm tw:font-medium" for="phone">
|
|
215
|
-
Phone
|
|
216
|
-
<input
|
|
217
|
-
type="tel"
|
|
218
|
-
bind:value={user.phone}
|
|
219
|
-
id="phone"
|
|
220
|
-
class="form-input"
|
|
221
|
-
placeholder="Phone"
|
|
222
|
-
autocomplete="tel-local"
|
|
223
|
-
/>
|
|
224
|
-
</label>
|
|
225
|
-
|
|
226
|
-
{#if message}
|
|
227
|
-
<p class="tw:text-red-600">{message}</p>
|
|
228
|
-
{/if}
|
|
229
|
-
|
|
230
|
-
<button
|
|
231
|
-
type="submit"
|
|
232
|
-
class="btn-primary"
|
|
233
|
-
disabled={loading}
|
|
234
|
-
>
|
|
235
|
-
{loading ? 'Creating account...' : 'Register'}
|
|
236
|
-
</button>
|
|
237
|
-
</form>
|
|
256
|
+
|
|
257
|
+
<button type="submit" class="btn-primary" disabled={loading}>
|
|
258
|
+
{loading ? 'Creating account...' : 'Register'}
|
|
259
|
+
</button>
|
|
260
|
+
</form>
|
|
238
261
|
{/if}
|