sveltekit-auth-example 1.0.28 → 1.0.30

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 CHANGED
@@ -1,7 +1,16 @@
1
1
  # Backlog
2
2
  * Add password complexity checking on /register and /profile pages (only checks for length currently despite what the pages say)
3
3
 
4
- # 1.0.27
4
+ # 1.0.30
5
+ * Fixed bug where opening /login or /register would fail to render Sign in With Google button (onMount in +layout.svelte loads after children's onMount)
6
+ * Fix bad path for favicon
7
+ * Update dependencies
8
+
9
+ # 1.0.29
10
+ * Fixed bug in hooks.server.ts - new version of SvelteKit complains about modifying cookie after `const response = await resolve(event)` so moved it up two lines.
11
+ * Update dependencies
12
+
13
+ # 1.0.28
5
14
  * Update dependencies
6
15
 
7
16
  # 1.0.27
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sveltekit-auth-example",
3
3
  "description": "SvelteKit Authentication Example",
4
- "version": "1.0.28",
4
+ "version": "1.0.30",
5
5
  "private": false,
6
6
  "author": "Nate Stuyvesant",
7
7
  "license": "https://github.com/nstuyvesant/sveltekit-auth-example/blob/master/LICENSE",
@@ -46,10 +46,10 @@
46
46
  "@types/google.accounts": "0.0.2",
47
47
  "@types/jsonwebtoken": "^8.5.9",
48
48
  "@types/pg": "^8.6.5",
49
- "@typescript-eslint/eslint-plugin": "^5.40.1",
50
- "@typescript-eslint/parser": "^5.40.1",
49
+ "@typescript-eslint/eslint-plugin": "^5.42.0",
50
+ "@typescript-eslint/parser": "^5.42.0",
51
51
  "bootstrap": "^5.2.2",
52
- "eslint": "^8.25.0",
52
+ "eslint": "^8.26.0",
53
53
  "eslint-config-prettier": "^8.5.0",
54
54
  "eslint-plugin-svelte3": "^4.0.0",
55
55
  "google-auth-library": "^8.6.0",
@@ -60,8 +60,8 @@
60
60
  "svelte": "^3.52.0",
61
61
  "svelte-check": "^2.9.2",
62
62
  "svelte-preprocess": "^4.10.7",
63
- "tslib": "^2.4.0",
63
+ "tslib": "^2.4.1",
64
64
  "typescript": "^4.8.4",
65
- "vite": "^3.1.8"
65
+ "vite": "^3.2.2"
66
66
  }
67
67
  }
package/src/app.html CHANGED
@@ -2,7 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
- <link rel="icon" href="%sveltekit.assets%//favicon.png" sizes="any" />
5
+ <link rel="icon" href="%sveltekit.assets%/favicon.png" sizes="any" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
7
  <script nonce="%sveltekit.nonce%" src="https://accounts.google.com/gsi/client" async defer></script>
8
8
  %sveltekit.head%
@@ -21,10 +21,11 @@ export const handle: Handle = async ({ event, resolve }) => {
21
21
  await attachUserToRequestEvent(sessionId, event)
22
22
  }
23
23
 
24
+ if (!event.locals.user) cookies.delete('session')
25
+
24
26
  const response = await resolve(event)
25
27
 
26
28
  // after endpoint or page is called
27
- if (!event.locals.user) cookies.delete('session')
28
29
 
29
30
  return response
30
31
  }
@@ -0,0 +1,63 @@
1
+ import type { Page } from '@sveltejs/kit'
2
+ import { page } from '$app/stores'
3
+ import { goto } from '$app/navigation'
4
+ import { PUBLIC_GOOGLE_CLIENT_ID } from '$env/static/public'
5
+ import { googleInitialized, loginSession } from '../stores'
6
+ import type { Readable } from 'svelte/store'
7
+
8
+ export function renderGoogleButton() {
9
+ const btn = document.getElementById('googleButton')
10
+ if (btn) {
11
+ google.accounts.id.renderButton(btn, {
12
+ type: 'standard',
13
+ theme: 'filled_blue',
14
+ size: 'large',
15
+ width: '367'
16
+ })
17
+ }
18
+ }
19
+
20
+ export function initializeGoogleAccounts() {
21
+ let initialized = false
22
+ const unsubscribe = googleInitialized.subscribe(value => {
23
+ initialized = value;
24
+ })
25
+
26
+ if (!initialized) {
27
+ window.google.accounts.id.initialize({
28
+ client_id: PUBLIC_GOOGLE_CLIENT_ID,
29
+ callback: googleCallback
30
+ })
31
+
32
+ googleInitialized.set(true)
33
+ }
34
+ unsubscribe()
35
+
36
+ async function googleCallback(response: google.accounts.id.CredentialResponse) {
37
+ const res = await fetch('/auth/google', {
38
+ method: 'POST',
39
+ headers: {
40
+ 'Content-Type': 'application/json'
41
+ },
42
+ body: JSON.stringify({ token: response.credential })
43
+ })
44
+
45
+ if (res.ok) {
46
+ const fromEndpoint = await res.json()
47
+ loginSession.set(fromEndpoint.user) // update loginSession store
48
+ const { role } = fromEndpoint.user
49
+
50
+ let referrer
51
+ const unsubscribe = page.subscribe(p => {
52
+ referrer = p.url.searchParams.get('referrer');
53
+ })
54
+ unsubscribe()
55
+
56
+ if (referrer) return goto(referrer)
57
+ if (role === 'teacher') return goto('/teachers')
58
+ if (role === 'admin') return goto('/admin')
59
+ if (location.pathname === '/login') goto('/') // logged in so go home
60
+ }
61
+ }
62
+ }
63
+
@@ -4,7 +4,8 @@
4
4
  import { goto, beforeNavigate } from '$app/navigation'
5
5
  import { page } from '$app/stores'
6
6
  import { loginSession, toast } from '../stores'
7
- import { PUBLIC_GOOGLE_CLIENT_ID } from '$env/static/public'
7
+ import { initializeGoogleAccounts } from '$lib/google'
8
+
8
9
  import 'bootstrap/scss/bootstrap.scss' // preferred way to load Bootstrap SCSS for hot module reloading
9
10
 
10
11
  export let data: LayoutServerData
@@ -25,30 +26,16 @@
25
26
  })
26
27
 
27
28
  onMount(async () => {
29
+ initializeGoogleAccounts()
30
+
28
31
  await import('bootstrap/js/dist/collapse') // lots of ways to load Bootstrap but prefer this approach to avoid SSR issues
29
32
  await import('bootstrap/js/dist/dropdown')
30
33
  Toast = (await import('bootstrap/js/dist/toast')).default
31
- window.google.accounts.id.initialize({
32
- client_id: PUBLIC_GOOGLE_CLIENT_ID,
33
- callback: googleCallback
34
- })
35
34
 
36
- if (!$loginSession) window.google.accounts.id.prompt()
35
+ if (!$loginSession) google.accounts.id.prompt()
37
36
  })
38
37
 
39
- async function logout() {
40
- // Request server delete httpOnly cookie called loginSession
41
- const url = '/auth/logout'
42
- const res = await fetch(url, {
43
- method: 'POST'
44
- })
45
- if (res.ok) {
46
- loginSession.set(undefined) // delete loginSession.user from
47
- goto('/login')
48
- } else console.error(`Logout not successful: ${res.statusText} (${res.status})`)
49
- }
50
-
51
- async function googleCallback(response: GoogleCredentialResponse) {
38
+ async function googleCallback(response: google.accounts.id.CredentialResponse) {
52
39
  const res = await fetch('/auth/google', {
53
40
  method: 'POST',
54
41
  headers: {
@@ -69,6 +56,18 @@
69
56
  }
70
57
  }
71
58
 
59
+ async function logout() {
60
+ // Request server delete httpOnly cookie called loginSession
61
+ const url = '/auth/logout'
62
+ const res = await fetch(url, {
63
+ method: 'POST'
64
+ })
65
+ if (res.ok) {
66
+ loginSession.set(undefined) // delete loginSession.user from
67
+ goto('/login')
68
+ } else console.error(`Logout not successful: ${res.statusText} (${res.status})`)
69
+ }
70
+
72
71
  const openToast = (open: boolean) => {
73
72
  if (open) {
74
73
  const toastDiv = <HTMLDivElement> document.getElementById('authToast')
@@ -4,6 +4,7 @@
4
4
  import { page } from '$app/stores'
5
5
  import { loginSession } from '../../stores'
6
6
  import { focusOnFirstError } from '$lib/focus'
7
+ import { initializeGoogleAccounts, renderGoogleButton } from '$lib/google'
7
8
 
8
9
  let focusedField: HTMLInputElement
9
10
  let message: string
@@ -31,12 +32,10 @@
31
32
  }
32
33
  }
33
34
 
34
- onMount(async() => {
35
- window.google.accounts.id.renderButton(document.getElementById('googleButton'), {
36
- theme: 'filled_blue',
37
- size: 'large',
38
- width: '367'
39
- })
35
+ onMount(() => {
36
+ initializeGoogleAccounts()
37
+ renderGoogleButton()
38
+
40
39
  focusedField.focus()
41
40
  })
42
41
 
@@ -1,8 +1,10 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte'
3
3
  import { goto } from '$app/navigation'
4
+ import { page } from '$app/stores'
4
5
  import { loginSession } from '../../stores'
5
6
  import { focusOnFirstError } from '$lib/focus'
7
+ import { initializeGoogleAccounts, renderGoogleButton } from '$lib/google'
6
8
 
7
9
  let focusedField: HTMLInputElement
8
10
 
@@ -44,12 +46,10 @@
44
46
  }
45
47
 
46
48
  onMount(() => {
49
+ initializeGoogleAccounts()
50
+ renderGoogleButton()
51
+
47
52
  focusedField.focus()
48
- window.google.accounts.id.renderButton(document.getElementById('googleButton'), {
49
- theme: 'filled_blue',
50
- size: 'large',
51
- width: '367'
52
- })
53
53
  })
54
54
 
55
55
  async function registerLocal(user: User) {
package/src/stores.ts CHANGED
@@ -9,3 +9,5 @@ export const toast = writable({
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
11
  export const loginSession = <Writable<User>> writable(undefined)
12
+
13
+ export const googleInitialized = writable(false)