sveltekit-auth-example 2.0.0 → 2.0.2
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/.eslintrc.cjs +19 -7
- package/.prettierignore +1 -0
- package/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +164 -98
- package/README.md +6 -1
- package/package.json +67 -66
- package/prettier.config.mjs +15 -0
- package/src/app.d.ts +31 -29
- package/src/app.html +8 -3
- package/src/hooks.server.ts +15 -15
- package/src/lib/focus.ts +6 -6
- package/src/lib/google.ts +48 -49
- package/src/lib/server/db.ts +7 -6
- package/src/lib/server/sendgrid.ts +11 -11
- package/src/routes/+error.svelte +1 -1
- package/src/routes/+layout.server.ts +5 -5
- package/src/routes/+layout.svelte +133 -100
- package/src/routes/admin/+page.server.ts +1 -1
- package/src/routes/admin/+page.svelte +2 -2
- package/src/routes/api/v1/user/+server.ts +13 -14
- package/src/routes/auth/[slug]/+server.ts +11 -6
- package/src/routes/auth/forgot/+server.ts +23 -23
- package/src/routes/auth/google/+server.ts +44 -45
- package/src/routes/auth/reset/+server.ts +1 -1
- package/src/routes/auth/reset/[token]/+page.svelte +117 -95
- package/src/routes/auth/reset/[token]/+page.ts +4 -4
- package/src/routes/forgot/+page.svelte +74 -63
- package/src/routes/info/+page.svelte +1 -1
- package/src/routes/login/+page.svelte +140 -120
- package/src/routes/profile/+page.server.ts +9 -9
- package/src/routes/profile/+page.svelte +142 -88
- package/src/routes/register/+page.server.ts +3 -2
- package/src/routes/register/+page.svelte +159 -104
- package/src/routes/teachers/+page.server.ts +5 -5
- package/src/routes/teachers/+page.svelte +2 -2
- package/src/stores.ts +1 -1
- package/svelte.config.js +1 -1
- package/.prettierrc +0 -9
|
@@ -1,57 +1,56 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { onMount } from 'svelte'
|
|
3
|
+
import { goto } from '$app/navigation'
|
|
4
|
+
import { loginSession } from '../../stores'
|
|
5
|
+
import { focusOnFirstError } from '$lib/focus'
|
|
6
|
+
import { initializeGoogleAccounts, renderGoogleButton } from '$lib/google'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
let focusedField: HTMLInputElement
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
let user: User = {
|
|
11
|
+
id: 0,
|
|
12
|
+
role: 'student',
|
|
13
|
+
firstName: '',
|
|
14
|
+
lastName: '',
|
|
15
|
+
password: '',
|
|
16
|
+
email: '',
|
|
17
|
+
phone: ''
|
|
18
|
+
}
|
|
19
|
+
let confirmPassword: HTMLInputElement
|
|
20
|
+
let message: string
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
async function register() {
|
|
23
|
+
const form = <HTMLFormElement>document.getElementById('register')
|
|
24
|
+
message = ''
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
if (!passwordMatch()) {
|
|
27
|
+
confirmPassword.classList.add('is-invalid')
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
31
|
+
if (form.checkValidity()) {
|
|
32
|
+
try {
|
|
33
|
+
await registerLocal(user)
|
|
34
|
+
} catch (err) {
|
|
35
|
+
if (err instanceof Error) {
|
|
36
|
+
message = err.message
|
|
37
|
+
console.log('Login error', message)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
form.classList.add('was-validated')
|
|
42
|
+
focusOnFirstError(form)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
46
45
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
onMount(() => {
|
|
47
|
+
initializeGoogleAccounts()
|
|
48
|
+
renderGoogleButton()
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
focusedField.focus()
|
|
51
|
+
})
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
async function registerLocal(user: User) {
|
|
55
54
|
try {
|
|
56
55
|
const res = await fetch('/auth/register', {
|
|
57
56
|
method: 'POST',
|
|
@@ -79,71 +78,127 @@
|
|
|
79
78
|
}
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
const passwordMatch = () => {
|
|
82
|
+
if (!user) return false // placate TypeScript
|
|
83
|
+
if (!user.password) user.password = ''
|
|
84
|
+
return user.password == confirmPassword.value
|
|
85
|
+
}
|
|
87
86
|
</script>
|
|
88
87
|
|
|
89
88
|
<svelte:head>
|
|
90
|
-
|
|
89
|
+
<title>Register</title>
|
|
91
90
|
</svelte:head>
|
|
92
91
|
|
|
93
92
|
<div class="d-flex justify-content-center my-3">
|
|
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
|
-
|
|
93
|
+
<div class="card login">
|
|
94
|
+
<div class="card-body">
|
|
95
|
+
<h4><strong>Register</strong></h4>
|
|
96
|
+
<p>Welcome to our community.</p>
|
|
97
|
+
{#if user}
|
|
98
|
+
<form id="register" autocomplete="on" novalidate class="mt-3">
|
|
99
|
+
<div class="mb-3">
|
|
100
|
+
<div id="googleButton"></div>
|
|
101
|
+
</div>
|
|
102
|
+
<div class="mb-3">
|
|
103
|
+
<label class="form-label" for="email">Email</label>
|
|
104
|
+
<input
|
|
105
|
+
bind:this={focusedField}
|
|
106
|
+
type="email"
|
|
107
|
+
class="form-control"
|
|
108
|
+
bind:value={user.email}
|
|
109
|
+
required
|
|
110
|
+
placeholder="Email"
|
|
111
|
+
id="email"
|
|
112
|
+
autocomplete="email"
|
|
113
|
+
/>
|
|
114
|
+
<div class="invalid-feedback">Email address required</div>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="mb-3">
|
|
117
|
+
<label class="form-label" for="password">Password</label>
|
|
118
|
+
<input
|
|
119
|
+
type="password"
|
|
120
|
+
id="password"
|
|
121
|
+
class="form-control"
|
|
122
|
+
bind:value={user.password}
|
|
123
|
+
required
|
|
124
|
+
minlength="8"
|
|
125
|
+
maxlength="80"
|
|
126
|
+
placeholder="Password"
|
|
127
|
+
autocomplete="new-password"
|
|
128
|
+
/>
|
|
129
|
+
<div class="invalid-feedback">Password with 8 chars or more required</div>
|
|
130
|
+
<div class="form-text">
|
|
131
|
+
Password minimum length 8, must have one capital letter, 1 number, and one unique
|
|
132
|
+
character.
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="mb-3">
|
|
136
|
+
<label class="form-label" for="password">Confirm password</label>
|
|
137
|
+
<input
|
|
138
|
+
type="password"
|
|
139
|
+
id="password"
|
|
140
|
+
class="form-control"
|
|
141
|
+
bind:this={confirmPassword}
|
|
142
|
+
required
|
|
143
|
+
minlength="8"
|
|
144
|
+
maxlength="80"
|
|
145
|
+
placeholder="Password (again)"
|
|
146
|
+
autocomplete="new-password"
|
|
147
|
+
/>
|
|
148
|
+
<div class="form-text">
|
|
149
|
+
Password minimum length 8, must have one capital letter, 1 number, and one unique
|
|
150
|
+
character.
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
<div class="mb-3">
|
|
154
|
+
<label class="form-label" for="firstName">First name</label>
|
|
155
|
+
<input
|
|
156
|
+
bind:value={user.firstName}
|
|
157
|
+
class="form-control"
|
|
158
|
+
id="firstName"
|
|
159
|
+
placeholder="First name"
|
|
160
|
+
required
|
|
161
|
+
autocomplete="given-name"
|
|
162
|
+
/>
|
|
163
|
+
<div class="invalid-feedback">First name required</div>
|
|
164
|
+
</div>
|
|
165
|
+
<div class="mb-3">
|
|
166
|
+
<label class="form-label" for="lastName">Last name</label>
|
|
167
|
+
<input
|
|
168
|
+
bind:value={user.lastName}
|
|
169
|
+
class="form-control"
|
|
170
|
+
id="lastName"
|
|
171
|
+
placeholder="Last name"
|
|
172
|
+
required
|
|
173
|
+
autocomplete="family-name"
|
|
174
|
+
/>
|
|
175
|
+
<div class="invalid-feedback">Last name required</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="mb-3">
|
|
178
|
+
<label class="form-label" for="phone">Phone</label>
|
|
179
|
+
<input
|
|
180
|
+
type="tel"
|
|
181
|
+
bind:value={user.phone}
|
|
182
|
+
id="phone"
|
|
183
|
+
class="form-control"
|
|
184
|
+
placeholder="Phone"
|
|
185
|
+
autocomplete="tel-local"
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
{#if message}
|
|
190
|
+
<p class="text-danger">{message}</p>
|
|
191
|
+
{/if}
|
|
192
|
+
|
|
193
|
+
<button type="button" on:click={register} class="btn btn-primary btn-lg">Register</button>
|
|
194
|
+
</form>
|
|
195
|
+
{/if}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
143
198
|
</div>
|
|
144
199
|
|
|
145
200
|
<style lang="scss">
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
</style>
|
|
201
|
+
.card-body {
|
|
202
|
+
width: 25rem;
|
|
203
|
+
}
|
|
204
|
+
</style>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { redirect } from '@sveltejs/kit'
|
|
2
2
|
import type { PageServerLoad } from './$types'
|
|
3
3
|
|
|
4
|
-
export const load: PageServerLoad = async ({locals}) => {
|
|
4
|
+
export const load: PageServerLoad = async ({ locals }) => {
|
|
5
5
|
const { user } = locals
|
|
6
6
|
const authorized = ['admin', 'teacher']
|
|
7
7
|
if (!user || !authorized.includes(user.role)) {
|
|
8
|
-
redirect(302, '/login?referrer=/teachers')
|
|
8
|
+
redirect(302, '/login?referrer=/teachers')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
return {
|
|
12
|
+
message: 'Teachers or Admin-only content from server.'
|
|
13
|
+
}
|
|
14
14
|
}
|
package/src/stores.ts
CHANGED
|
@@ -8,6 +8,6 @@ export const toast = writable({
|
|
|
8
8
|
|
|
9
9
|
// While server determines whether the user is logged in by examining RequestEvent.locals.user, the
|
|
10
10
|
// loginSession is updated so all parts of the SPA client-side see the user and role.
|
|
11
|
-
export const loginSession = <Writable<User>>
|
|
11
|
+
export const loginSession = <Writable<User>>writable(undefined)
|
|
12
12
|
|
|
13
13
|
export const googleInitialized = writable(false)
|
package/svelte.config.js
CHANGED