sveltekit-auth-example 2.1.0 → 5.0.1
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 +12 -0
- package/README.md +1 -1
- package/eslint.config.js +1 -1
- package/package.json +16 -16
- package/src/routes/+error.svelte +1 -1
- package/src/routes/+layout.svelte +13 -5
- package/src/routes/admin/+page.svelte +5 -1
- package/src/routes/auth/reset/[token]/+page.svelte +13 -9
- package/src/routes/forgot/+page.svelte +5 -5
- package/src/routes/login/+page.svelte +6 -6
- package/src/routes/profile/+page.svelte +13 -9
- package/src/routes/register/+page.svelte +9 -9
- package/src/routes/teachers/+page.svelte +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,29 +2,41 @@
|
|
|
2
2
|
|
|
3
3
|
- Add password complexity checking on /register and /profile pages (only checks for length currently despite what the pages say)
|
|
4
4
|
|
|
5
|
+
# 5.0.1
|
|
6
|
+
- Migrate to Svelte 5 runes mode
|
|
7
|
+
- Format with prettier
|
|
8
|
+
- Bump dependencies
|
|
9
|
+
|
|
5
10
|
# 2.1.0
|
|
11
|
+
|
|
6
12
|
- Bump svelte, @sveltejs/kit, @sveltejs/adapter-node, @sveltejs/vite-plugin-svelte, @sendgrid/mail, pg, google-auth-library, @eslint/js, yarn, @types/google.accounts, @types/jsonwebtoken, @types/pg, eslint, eslint-plugin-svelte, globals, prettier-plugin-svelte, sass, svelte-check, tslib, typescript, typescript-eslint, vite, vitest
|
|
7
13
|
- Change Typescript casting to use "as" since the Svelte 5 has an issue with angle brackets style
|
|
8
14
|
|
|
9
15
|
# 2.0.8
|
|
16
|
+
|
|
10
17
|
- Bump svelte, @sveltejs/kit, @sveltejs/vite-plugin-svelte, google-auth-library and other devDependencies
|
|
11
18
|
|
|
12
19
|
# 2.0.7
|
|
20
|
+
|
|
13
21
|
- Bump pg, google-auth-library, svelte, svelte-check, yarn, typescript-eslint, @types/pg, eslint, prettier, prettier-plugin-svelte, sass, tslib, typescript, vite, vitest
|
|
14
22
|
- Install typescript-eslint, @eslint/js, globals, eslint-plugin-prettier
|
|
15
23
|
- Change eslint config to flat
|
|
16
24
|
|
|
17
25
|
# 2.0.6
|
|
26
|
+
|
|
18
27
|
- Bump google-auth-library, @sendgrid/mail, pg, @sveltejs/kit, @sveltejs/vite-plugin-svelte, @types/pg, @typescript-eslint, eslint, eslint-config-prettier, prettier-plugin-svelte, sass, svelte, svelte-check, typescript, vite, vitest
|
|
19
28
|
|
|
20
29
|
# 2.0.5
|
|
30
|
+
|
|
21
31
|
- Bump @sveltejs/kit, @types/pg, @typescript-eslint, google-auth-library, sass, svelte-check, typescript, vite, vitest
|
|
22
32
|
|
|
23
33
|
# 2.0.4
|
|
34
|
+
|
|
24
35
|
- Tested with latest Sveltekit and google-auth-library
|
|
25
36
|
- Bump @sendgrid/mail, sveltekit, adapter-node, @types, @typescript-eslint, google-auth-library, prettier, prettier-plugin-svelte, sass, svelte, svelte-check, typescript, vite, vitest, yarn
|
|
26
37
|
|
|
27
38
|
# 2.0.3
|
|
39
|
+
|
|
28
40
|
- Move to eslint's new eslint.config.js
|
|
29
41
|
- Convert vite.config to TypeScript
|
|
30
42
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SvelteKit Authentication and Authorization Example
|
|
2
2
|
|
|
3
|
-
**Updated for SvelteKit 2.
|
|
3
|
+
**Updated for Svelte 5 and SvelteKit 2.8.1**
|
|
4
4
|
|
|
5
5
|
This is an example of how to register, authenticate, and update users and limit their access to
|
|
6
6
|
areas of the website by role (admin, teacher, student). It includes profile management and password resets via SendGrid.
|
package/eslint.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sveltekit-auth-example",
|
|
3
3
|
"description": "SvelteKit Authentication Example",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.1",
|
|
5
5
|
"author": "Nate Stuyvesant",
|
|
6
6
|
"license": "https://github.com/nstuyvesant/sveltekit-auth-example/blob/master/LICENSE",
|
|
7
7
|
"repository": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"format": "prettier --write . --ignore-path ./.eslintignore"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
|
-
"node": "^18.
|
|
34
|
+
"node": "^20.18.0"
|
|
35
35
|
},
|
|
36
36
|
"type": "module",
|
|
37
37
|
"dependencies": {
|
|
@@ -39,32 +39,32 @@
|
|
|
39
39
|
"pg": "^8.13.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@eslint/js": "^9.
|
|
42
|
+
"@eslint/js": "^9.15.0",
|
|
43
43
|
"@sveltejs/adapter-node": "^5.2.9",
|
|
44
|
-
"@sveltejs/kit": "^2.
|
|
45
|
-
"@sveltejs/vite-plugin-svelte": "^4.0.
|
|
44
|
+
"@sveltejs/kit": "^2.8.1",
|
|
45
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.1",
|
|
46
46
|
"@types/bootstrap": "5.2.10",
|
|
47
47
|
"@types/google.accounts": "^0.0.15",
|
|
48
48
|
"@types/jsonwebtoken": "^9.0.7",
|
|
49
49
|
"@types/pg": "^8.11.10",
|
|
50
50
|
"bootstrap": "^5.3.3",
|
|
51
|
-
"eslint": "^9.
|
|
51
|
+
"eslint": "^9.15.0",
|
|
52
52
|
"eslint-config-prettier": "^9.1.0",
|
|
53
53
|
"eslint-plugin-prettier": "^5.2.1",
|
|
54
54
|
"eslint-plugin-svelte": "^2.46.0",
|
|
55
|
-
"globals": "^15.
|
|
56
|
-
"google-auth-library": "^9.
|
|
55
|
+
"globals": "^15.12.0",
|
|
56
|
+
"google-auth-library": "^9.15.0",
|
|
57
57
|
"jsonwebtoken": "^9.0.2",
|
|
58
58
|
"prettier": "^3.3.3",
|
|
59
|
-
"prettier-plugin-svelte": "^3.2.
|
|
60
|
-
"sass": "^1.
|
|
61
|
-
"svelte": "^5.
|
|
62
|
-
"svelte-check": "^4.0.
|
|
63
|
-
"tslib": "^2.8.
|
|
59
|
+
"prettier-plugin-svelte": "^3.2.8",
|
|
60
|
+
"sass": "^1.81.0",
|
|
61
|
+
"svelte": "^5.2.4",
|
|
62
|
+
"svelte-check": "^4.0.9",
|
|
63
|
+
"tslib": "^2.8.1",
|
|
64
64
|
"typescript": "^5.6.3",
|
|
65
|
-
"typescript-eslint": "^8.
|
|
66
|
-
"vite": "^5.4.
|
|
67
|
-
"vitest": "^2.1.
|
|
65
|
+
"typescript-eslint": "^8.15.0",
|
|
66
|
+
"vite": "^5.4.11",
|
|
67
|
+
"vitest": "^2.1.5"
|
|
68
68
|
},
|
|
69
69
|
"packageManager": "yarn@4.5.1",
|
|
70
70
|
"prettier": "./prettier.config.mjs"
|
package/src/routes/+error.svelte
CHANGED
|
@@ -7,7 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
import 'bootstrap/scss/bootstrap.scss' // preferred way to load Bootstrap SCSS for hot module reloading
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
interface Props {
|
|
11
|
+
data: LayoutServerData
|
|
12
|
+
children?: import('svelte').Snippet
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { data, children }: Props = $props()
|
|
11
16
|
|
|
12
17
|
// If returning from different website, runs once (as it's an SPA) to restore user session if session cookie is still valid
|
|
13
18
|
const { user } = data
|
|
@@ -34,7 +39,8 @@
|
|
|
34
39
|
if (!$loginSession) google.accounts.id.prompt()
|
|
35
40
|
})
|
|
36
41
|
|
|
37
|
-
async function logout() {
|
|
42
|
+
async function logout(event: MouseEvent) {
|
|
43
|
+
event.preventDefault()
|
|
38
44
|
// Request server delete httpOnly cookie called loginSession
|
|
39
45
|
const url = '/auth/logout'
|
|
40
46
|
const res = await fetch(url, {
|
|
@@ -54,7 +60,9 @@
|
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
$effect(() => {
|
|
64
|
+
openToast($toast.isOpen)
|
|
65
|
+
})
|
|
58
66
|
</script>
|
|
59
67
|
|
|
60
68
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
|
@@ -117,7 +125,7 @@
|
|
|
117
125
|
</li>
|
|
118
126
|
<li>
|
|
119
127
|
<a
|
|
120
|
-
|
|
128
|
+
onclick={logout}
|
|
121
129
|
class="dropdown-item"
|
|
122
130
|
class:d-none={!$loginSession || $loginSession.id === 0}
|
|
123
131
|
href={'#'}>Logout</a
|
|
@@ -136,7 +144,7 @@
|
|
|
136
144
|
</nav>
|
|
137
145
|
|
|
138
146
|
<main class="container">
|
|
139
|
-
|
|
147
|
+
{@render children?.()}
|
|
140
148
|
|
|
141
149
|
<div
|
|
142
150
|
id="authToast"
|
|
@@ -5,20 +5,24 @@
|
|
|
5
5
|
import { toast } from '../../../../stores'
|
|
6
6
|
import { focusOnFirstError } from '$lib/focus'
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
interface Props {
|
|
9
|
+
data: PageData
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let { data }: Props = $props()
|
|
9
13
|
|
|
10
|
-
let focusedField: HTMLInputElement
|
|
11
|
-
let password
|
|
12
|
-
let confirmPassword: HTMLInputElement
|
|
13
|
-
let message
|
|
14
|
+
let focusedField: HTMLInputElement | undefined = $state()
|
|
15
|
+
let password = $state('')
|
|
16
|
+
let confirmPassword: HTMLInputElement | undefined = $state()
|
|
17
|
+
let message = $state('')
|
|
14
18
|
|
|
15
19
|
onMount(() => {
|
|
16
|
-
focusedField
|
|
20
|
+
focusedField?.focus()
|
|
17
21
|
})
|
|
18
22
|
|
|
19
23
|
const passwordMatch = () => {
|
|
20
24
|
if (!password) password = ''
|
|
21
|
-
return password == confirmPassword
|
|
25
|
+
return password == confirmPassword?.value
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
const resetPassword = async () => {
|
|
@@ -26,7 +30,7 @@
|
|
|
26
30
|
const form = document.getElementById('reset') as HTMLFormElement
|
|
27
31
|
|
|
28
32
|
if (!passwordMatch()) {
|
|
29
|
-
confirmPassword
|
|
33
|
+
confirmPassword?.classList.add('is-invalid')
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
if (form.checkValidity()) {
|
|
@@ -111,7 +115,7 @@
|
|
|
111
115
|
<p class="text-danger">{message}</p>
|
|
112
116
|
{/if}
|
|
113
117
|
<div class="d-grid gap-2">
|
|
114
|
-
<button
|
|
118
|
+
<button onclick={resetPassword} type="button" class="btn btn-primary btn-lg"
|
|
115
119
|
>Send Email</button
|
|
116
120
|
>
|
|
117
121
|
</div>
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
import { toast } from '../../stores'
|
|
5
5
|
import { focusOnFirstError } from '$lib/focus'
|
|
6
6
|
|
|
7
|
-
let focusedField: HTMLInputElement
|
|
8
|
-
let email: string
|
|
9
|
-
let message: string
|
|
7
|
+
let focusedField: HTMLInputElement | undefined = $state()
|
|
8
|
+
let email: string = $state('')
|
|
9
|
+
let message: string = $state('')
|
|
10
10
|
|
|
11
11
|
onMount(() => {
|
|
12
|
-
focusedField
|
|
12
|
+
focusedField?.focus()
|
|
13
13
|
})
|
|
14
14
|
|
|
15
15
|
const sendPasswordReset = async () => {
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
<p class="text-danger">{message}</p>
|
|
73
73
|
{/if}
|
|
74
74
|
<div class="d-grid gap-2">
|
|
75
|
-
<button
|
|
75
|
+
<button onclick={sendPasswordReset} type="button" class="btn btn-primary btn-lg"
|
|
76
76
|
>Send Email</button
|
|
77
77
|
>
|
|
78
78
|
</div>
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
import { focusOnFirstError } from '$lib/focus'
|
|
7
7
|
import { initializeGoogleAccounts, renderGoogleButton } from '$lib/google'
|
|
8
8
|
|
|
9
|
-
let focusedField: HTMLInputElement
|
|
10
|
-
let message
|
|
11
|
-
const credentials: Credentials = {
|
|
9
|
+
let focusedField: HTMLInputElement | undefined = $state()
|
|
10
|
+
let message = $state('')
|
|
11
|
+
const credentials: Credentials = $state({
|
|
12
12
|
email: '',
|
|
13
13
|
password: ''
|
|
14
|
-
}
|
|
14
|
+
})
|
|
15
15
|
|
|
16
16
|
async function login() {
|
|
17
17
|
message = ''
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
initializeGoogleAccounts()
|
|
37
37
|
renderGoogleButton()
|
|
38
38
|
|
|
39
|
-
focusedField
|
|
39
|
+
focusedField?.focus()
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
async function loginLocal(credentials: Credentials) {
|
|
@@ -135,7 +135,7 @@
|
|
|
135
135
|
<p class="text-danger">{message}</p>
|
|
136
136
|
{/if}
|
|
137
137
|
<div class="d-grid gap-2">
|
|
138
|
-
<button
|
|
138
|
+
<button onclick={login} type="button" class="btn btn-primary btn-lg">Sign In</button>
|
|
139
139
|
</div>
|
|
140
140
|
</form>
|
|
141
141
|
</div>
|
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
import { focusOnFirstError } from '$lib/focus'
|
|
5
5
|
import { loginSession } from '../../stores'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
interface Props {
|
|
8
|
+
data: PageData
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { data }: Props = $props()
|
|
12
|
+
const { user }: { user: User } = $state(data)
|
|
9
13
|
|
|
10
|
-
let focusedField: HTMLInputElement
|
|
11
|
-
let message
|
|
12
|
-
let confirmPassword: HTMLInputElement
|
|
14
|
+
let focusedField: HTMLInputElement | undefined = $state()
|
|
15
|
+
let message = $state('')
|
|
16
|
+
let confirmPassword: HTMLInputElement | undefined = $state()
|
|
13
17
|
|
|
14
18
|
onMount(() => {
|
|
15
|
-
focusedField
|
|
19
|
+
focusedField?.focus()
|
|
16
20
|
})
|
|
17
21
|
|
|
18
22
|
async function update() {
|
|
@@ -20,7 +24,7 @@
|
|
|
20
24
|
const form = document.getElementById('profile') as HTMLFormElement
|
|
21
25
|
|
|
22
26
|
if (!user?.email?.includes('gmail.com') && !passwordMatch()) {
|
|
23
|
-
confirmPassword
|
|
27
|
+
confirmPassword?.classList.add('is-invalid')
|
|
24
28
|
return
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -44,7 +48,7 @@
|
|
|
44
48
|
|
|
45
49
|
const passwordMatch = () => {
|
|
46
50
|
if (!user.password) user.password = ''
|
|
47
|
-
return user.password == confirmPassword
|
|
51
|
+
return user.password == confirmPassword?.value
|
|
48
52
|
}
|
|
49
53
|
</script>
|
|
50
54
|
|
|
@@ -150,7 +154,7 @@
|
|
|
150
154
|
<p>{message}</p>
|
|
151
155
|
{/if}
|
|
152
156
|
|
|
153
|
-
<button type="button"
|
|
157
|
+
<button onclick={update} type="button" class="btn btn-primary btn-lg">Update</button>
|
|
154
158
|
</form>
|
|
155
159
|
</div>
|
|
156
160
|
</div>
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
import { focusOnFirstError } from '$lib/focus'
|
|
6
6
|
import { initializeGoogleAccounts, renderGoogleButton } from '$lib/google'
|
|
7
7
|
|
|
8
|
-
let focusedField: HTMLInputElement
|
|
8
|
+
let focusedField: HTMLInputElement | undefined = $state()
|
|
9
9
|
|
|
10
|
-
let user: User = {
|
|
10
|
+
let user: User = $state({
|
|
11
11
|
id: 0,
|
|
12
12
|
role: 'student',
|
|
13
13
|
firstName: '',
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
password: '',
|
|
16
16
|
email: '',
|
|
17
17
|
phone: ''
|
|
18
|
-
}
|
|
19
|
-
let confirmPassword: HTMLInputElement
|
|
20
|
-
let message
|
|
18
|
+
})
|
|
19
|
+
let confirmPassword: HTMLInputElement | undefined = $state()
|
|
20
|
+
let message = $state('')
|
|
21
21
|
|
|
22
22
|
async function register() {
|
|
23
23
|
const form = document.getElementById('register') as HTMLFormElement
|
|
24
24
|
message = ''
|
|
25
25
|
|
|
26
26
|
if (!passwordMatch()) {
|
|
27
|
-
confirmPassword
|
|
27
|
+
confirmPassword?.classList.add('is-invalid')
|
|
28
28
|
return
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
initializeGoogleAccounts()
|
|
48
48
|
renderGoogleButton()
|
|
49
49
|
|
|
50
|
-
focusedField
|
|
50
|
+
focusedField?.focus()
|
|
51
51
|
})
|
|
52
52
|
|
|
53
53
|
async function registerLocal(user: User) {
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
const passwordMatch = () => {
|
|
82
82
|
if (!user) return false // placate TypeScript
|
|
83
83
|
if (!user.password) user.password = ''
|
|
84
|
-
return user.password == confirmPassword
|
|
84
|
+
return user.password == confirmPassword?.value
|
|
85
85
|
}
|
|
86
86
|
</script>
|
|
87
87
|
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
<p class="text-danger">{message}</p>
|
|
191
191
|
{/if}
|
|
192
192
|
|
|
193
|
-
<button type="button"
|
|
193
|
+
<button onclick={register} type="button" class="btn btn-primary btn-lg">Register</button>
|
|
194
194
|
</form>
|
|
195
195
|
{/if}
|
|
196
196
|
</div>
|