sveltekit-auth-example 1.0.11 → 1.0.14

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.
Files changed (42) hide show
  1. package/.eslintrc.cjs +1 -1
  2. package/.prettierignore +13 -0
  3. package/.prettierrc +4 -1
  4. package/CHANGELOG.md +18 -0
  5. package/README.md +17 -20
  6. package/package.json +19 -18
  7. package/src/app.d.ts +7 -8
  8. package/src/app.html +1 -0
  9. package/src/hooks.ts +3 -13
  10. package/src/lib/auth.ts +49 -75
  11. package/src/routes/+error.svelte +6 -0
  12. package/src/routes/+layout.server.ts +8 -0
  13. package/src/routes/{__layout.svelte → +layout.svelte} +18 -14
  14. package/src/routes/{index.svelte → +page.svelte} +0 -0
  15. package/src/routes/admin/+page.server.ts +14 -0
  16. package/src/routes/admin/+page.svelte +12 -0
  17. package/src/routes/api/v1/user/+server.ts +21 -0
  18. package/src/routes/auth/[slug]/+server.ts +66 -0
  19. package/src/routes/auth/{forgot.ts → forgot/+server.ts} +5 -7
  20. package/src/routes/auth/{google.ts → google/+server.ts} +17 -21
  21. package/src/routes/auth/reset/{index.ts → +server.ts} +9 -12
  22. package/src/routes/auth/reset/{[token].svelte → [token]/+page.svelte} +4 -15
  23. package/src/routes/auth/reset/[token]/+page.ts +7 -0
  24. package/src/routes/{forgot.svelte → forgot/+page.svelte} +2 -2
  25. package/src/routes/{info.svelte → info/+page.svelte} +0 -0
  26. package/src/routes/{login.svelte → login/+page.svelte} +3 -3
  27. package/src/routes/profile/+page.server.ts +15 -0
  28. package/src/routes/{profile.svelte → profile/+page.svelte} +7 -25
  29. package/src/routes/register/+page.server.ts +10 -0
  30. package/src/routes/{register.svelte → register/+page.svelte} +6 -23
  31. package/src/routes/teachers/+page.server.ts +13 -0
  32. package/src/routes/teachers/+page.svelte +12 -0
  33. package/src/stores.ts +11 -1
  34. package/svelte.config.js +1 -3
  35. package/src/routes/__error.svelte +0 -19
  36. package/src/routes/admin.svelte +0 -40
  37. package/src/routes/api/v1/_auth.ts +0 -5
  38. package/src/routes/api/v1/admin.ts +0 -20
  39. package/src/routes/api/v1/teacher.ts +0 -20
  40. package/src/routes/api/v1/user.ts +0 -35
  41. package/src/routes/auth/[slug].ts +0 -93
  42. package/src/routes/teachers.svelte +0 -39
@@ -0,0 +1,66 @@
1
+ import { error, json } from '@sveltejs/kit'
2
+ import type { Action } from './$types'
3
+ import { query } from '../../_db'
4
+
5
+ export const POST: Action = async (event) => {
6
+ const { slug } = event.params
7
+
8
+ let result
9
+ let sql
10
+
11
+ try {
12
+ switch (slug) {
13
+ case 'logout':
14
+ if (event.locals.user) {
15
+ // if user is null, they are logged out anyway (session might have ended)
16
+ sql = `CALL delete_session($1);`
17
+ result = await query(sql, [event.locals.user.id])
18
+ }
19
+ return new Response(JSON.stringify({ message: 'Logout successful.' }), {
20
+ headers: {
21
+ 'Set-Cookie': `session=; Path=/; SameSite=Lax; HttpOnly; Expires=${new Date().toUTCString()}`
22
+ }
23
+ })
24
+ case 'login':
25
+ sql = `SELECT authenticate($1) AS "authenticationResult";`
26
+ break
27
+ case 'register':
28
+ sql = `SELECT register($1) AS "authenticationResult";`
29
+ break
30
+ default:
31
+ throw error(404, 'Invalid endpoint.')
32
+ }
33
+
34
+ // Only /auth/login and /auth/register at this point
35
+ const body = await event.request.json()
36
+
37
+ // While client checks for these to be non-null, register() in the database does not
38
+ if (slug == 'register' && (!body.email || !body.password || !body.firstName || !body.lastName))
39
+ throw error(400, 'Please supply all required fields: email, password, first and last name.')
40
+
41
+ result = await query(sql, [JSON.stringify(body)])
42
+ } catch (err) {
43
+ throw error(503, 'Could not communicate with database.')
44
+ }
45
+
46
+ const { authenticationResult }: { authenticationResult: AuthenticationResult } = result.rows[0]
47
+
48
+ if (!authenticationResult.user)
49
+ // includes when a user tries to register an existing email account with wrong password
50
+ throw error(authenticationResult.statusCode, authenticationResult.status)
51
+
52
+ // Ensures hooks.ts:handle() will not delete cookie just set
53
+ event.locals.user = authenticationResult.user
54
+
55
+ return json(
56
+ {
57
+ message: authenticationResult.status,
58
+ user: authenticationResult.user
59
+ },
60
+ {
61
+ headers: {
62
+ 'Set-Cookie': `session=${authenticationResult.sessionId}; Path=/; SameSite=Lax; HttpOnly;`
63
+ }
64
+ }
65
+ )
66
+ }
@@ -1,15 +1,15 @@
1
- import type { RequestHandler } from '@sveltejs/kit'
1
+ import type { Action } from './$types'
2
2
  import type { Secret } from 'jsonwebtoken'
3
3
  import jwt from 'jsonwebtoken'
4
4
  import dotenv from 'dotenv'
5
- import { query } from '../_db'
6
- import { sendMessage } from '../_send-in-blue'
5
+ import { query } from '../../_db'
6
+ import { sendMessage } from '../../_send-in-blue'
7
7
 
8
8
  dotenv.config()
9
9
  const DOMAIN = process.env.DOMAIN
10
10
  const JWT_SECRET = process.env.JWT_SECRET
11
11
 
12
- export const POST: RequestHandler = async event => {
12
+ export const POST: Action = async event => {
13
13
  const body = await event.request.json()
14
14
  const sql = `SELECT id as "userId" FROM users WHERE email = $1 LIMIT 1;`
15
15
  const { rows } = await query(sql, [body.email])
@@ -36,7 +36,5 @@ export const POST: RequestHandler = async event => {
36
36
  sendMessage(message)
37
37
  }
38
38
 
39
- return {
40
- status: 204
41
- }
39
+ return new Response(undefined, { status: 204 })
42
40
  }
@@ -1,6 +1,7 @@
1
- import type { RequestHandler } from '@sveltejs/kit'
1
+ import { error } from '@sveltejs/kit'
2
+ import type { Action } from './$types'
2
3
  import { OAuth2Client } from 'google-auth-library'
3
- import { query } from '../_db';
4
+ import { query } from '../../_db';
4
5
  import { config } from '$lib/config'
5
6
 
6
7
  // Verify JWT per https://developers.google.com/identity/gsi/web/guides/verify-google-id-token
@@ -13,7 +14,8 @@ async function getGoogleUserFromJWT(token: string): Promise<Partial<User>> {
13
14
  audience: clientId
14
15
  });
15
16
  const payload = ticket.getPayload()
16
- if (!payload) throw new Error('Google authentication did not get the expected payload')
17
+ if (!payload) throw error(500, 'Google authentication did not get the expected payload')
18
+
17
19
  return {
18
20
  firstName: payload['given_name'] || 'UnknownFirstName',
19
21
  lastName: payload['family_name'] || 'UnknownLastName',
@@ -22,7 +24,7 @@ async function getGoogleUserFromJWT(token: string): Promise<Partial<User>> {
22
24
  } catch (err) {
23
25
  let message = ''
24
26
  if (err instanceof Error) message = err.message
25
- throw new Error(`Google user could not be authenticated: ${message}`)
27
+ throw error(500,`Google user could not be authenticated: ${message}`)
26
28
  }
27
29
  }
28
30
 
@@ -35,12 +37,12 @@ async function upsertGoogleUser(user: Partial<User>): Promise<UserSession> {
35
37
  } catch (err) {
36
38
  let message = ''
37
39
  if (err instanceof Error) message = err.message
38
- throw new Error(`GMail user could not be upserted: ${message}`)
40
+ throw error(500,`Gmail user could not be upserted: ${message}`)
39
41
  }
40
42
  }
41
43
 
42
44
  // Returns local user if Google user authenticated (and authorized our app)
43
- export const POST: RequestHandler = async event => {
45
+ export const POST: Action = async event => {
44
46
  try {
45
47
  const { token } = await event.request.json()
46
48
  const user = await getGoogleUserFromJWT(token)
@@ -49,24 +51,18 @@ export const POST: RequestHandler = async event => {
49
51
  // Prevent hooks.ts's handler() from deleting cookie thinking no one has authenticated
50
52
  event.locals.user = userSession.user
51
53
 
52
- return {
53
- status: 200,
54
- headers: { // database expires sessions in 2 hours
55
- 'Set-Cookie': `session=${userSession.id}; Path=/; SameSite=Lax; HttpOnly;`
56
- },
57
- body: {
58
- message: 'Successful Google Sign-In.',
59
- user: userSession.user
54
+ return new Response(JSON.stringify({
55
+ message: 'Successful Google Sign-In.',
56
+ user: userSession.user
57
+ }), {
58
+ headers: {
59
+ 'Set-Cookie': `session=${userSession.id}; Path=/; SameSite=Lax; HttpOnly;`}
60
60
  }
61
- }
61
+ )
62
+
62
63
  } catch (err) {
63
64
  let message = ''
64
65
  if (err instanceof Error) message = err.message
65
- return { // session cookie deleted by hooks.js handle()
66
- status: 401,
67
- body: {
68
- message: message
69
- }
70
- }
66
+ throw error(401, message)
71
67
  }
72
68
  }
@@ -1,3 +1,4 @@
1
+ import { json as json$1 } from '@sveltejs/kit';
1
2
  import dotenv from 'dotenv'
2
3
  import type { RequestHandler } from '@sveltejs/kit'
3
4
  import type { JwtPayload } from 'jsonwebtoken'
@@ -21,19 +22,15 @@ export const PUT: RequestHandler = async event => {
21
22
  const sql = `CALL reset_password($1, $2);`
22
23
  await query(sql, [userId, password])
23
24
 
24
- return {
25
- status: 200,
26
- body: {
27
- message: 'Password successfully reset.'
28
- }
29
- }
25
+ return json$1({
26
+ message: 'Password successfully reset.'
27
+ })
30
28
  } catch (error) {
31
29
  // Technically, I should check error.message to make sure it's not a DB issue
32
- return {
33
- status: 403,
34
- body: {
35
- message: 'Password reset token expired.'
36
- }
37
- }
30
+ return json$1({
31
+ message: 'Password reset token expired.'
32
+ }, {
33
+ status: 403
34
+ })
38
35
  }
39
36
  }
@@ -1,22 +1,11 @@
1
- <script context="module" lang="ts">
2
- import type { Load } from '@sveltejs/kit'
3
-
4
- export const load: Load = async event => {
5
- return {
6
- props: {
7
- token: event.params.token
8
- }
9
- }
10
- }
11
- </script>
12
-
13
1
  <script lang="ts">
2
+ import type { PageData } from './$types'
14
3
  import { onMount } from 'svelte'
15
4
  import { goto } from '$app/navigation'
16
- import { toast } from '../../../stores'
5
+ import { toast } from '../../../../stores'
17
6
  import { focusOnFirstError } from '$lib/focus'
18
7
 
19
- export let token: string
8
+ export let data: PageData
20
9
 
21
10
  let focusedField: HTMLInputElement
22
11
  let password: string
@@ -49,7 +38,7 @@
49
38
  'Content-Type': 'application/json'
50
39
  },
51
40
  body: JSON.stringify({
52
- token,
41
+ token: data.token,
53
42
  password
54
43
  })
55
44
  })
@@ -0,0 +1,7 @@
1
+ import type { PageLoad } from './$types'
2
+
3
+ export const load: PageLoad = async event => {
4
+ return {
5
+ token: event.params.token
6
+ }
7
+ }
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte'
3
3
  import { goto } from '$app/navigation'
4
- import { toast } from '../stores'
4
+ import { toast } from '../../stores'
5
5
  import { focusOnFirstError } from '$lib/focus'
6
6
 
7
7
  let focusedField: HTMLInputElement
@@ -18,7 +18,7 @@
18
18
 
19
19
  if (form.checkValidity()) {
20
20
  if (email.toLowerCase().includes('gmail.com')) {
21
- return message = 'GMail passwords must be reset on Manage Your Google Account.'
21
+ return message = 'Gmail passwords must be reset on Manage Your Google Account.'
22
22
  }
23
23
  const url = `/auth/forgot`
24
24
  const res = await fetch(url, {
File without changes
@@ -1,11 +1,12 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte'
3
3
  import { goto } from '$app/navigation'
4
- import { page, session } from '$app/stores'
4
+ import { page } from '$app/stores'
5
+ import { loginSession } from '../../stores'
5
6
  import useAuth from '$lib/auth'
6
7
  import { focusOnFirstError } from '$lib/focus'
7
8
 
8
- const { loadScript, initializeSignInWithGoogle, loginLocal } = useAuth(page, session, goto)
9
+ const { initializeSignInWithGoogle, loginLocal } = useAuth(page, loginSession, goto)
9
10
 
10
11
  let focusedField: HTMLInputElement
11
12
  let message: string
@@ -34,7 +35,6 @@
34
35
  }
35
36
 
36
37
  onMount(async() => {
37
- await loadScript() // probably cached
38
38
  initializeSignInWithGoogle('googleButton')
39
39
  focusedField.focus()
40
40
  })
@@ -0,0 +1,15 @@
1
+ import { redirect } from '@sveltejs/kit';
2
+ import type { PageServerLoad } from './$types'
3
+
4
+ export const load: PageServerLoad = async ({ locals }) => {
5
+ const { user } = locals // populated by /src/hooks.ts
6
+
7
+ const authorized = ['admin', 'teacher', 'student'] // must be logged-in
8
+ if (user && !authorized.includes(user.role)) {
9
+ throw redirect(302, '/login?referrer=/profile')
10
+ }
11
+
12
+ return {
13
+ user
14
+ }
15
+ }
@@ -1,33 +1,15 @@
1
- <script context="module" lang="ts">
2
- import type { Load } from '@sveltejs/kit'
3
-
4
- export const load: Load = ({ session }) => {
5
- const authorized = ['admin', 'teacher', 'student'] // must be logged-in
6
- if (session.user && !authorized.includes(session.user.role)) {
7
- return {
8
- status: 302,
9
- redirect: '/login?referrer=/profile'
10
- }
11
- }
12
-
13
- return {
14
- props: {
15
- // Clone session.user so unsaved changes are NOT retained
16
- user: JSON.parse(JSON.stringify(session.user))
17
- }
18
- }
19
- }
20
- </script>
21
-
22
1
  <script lang="ts">
2
+ import type { PageData } from './$types'
23
3
  import { onMount } from 'svelte'
24
- import { session } from '$app/stores'
25
- import { focusOnFirstError } from '$lib/focus';
4
+ import { focusOnFirstError } from '$lib/focus'
5
+ import { loginSession } from '../../stores'
6
+
7
+ export let data: PageData
8
+ const { user } : {user : User } = data
26
9
 
27
10
  let focusedField: HTMLInputElement
28
11
  let message: string
29
12
  let confirmPassword: HTMLInputElement
30
- export let user: User
31
13
 
32
14
  onMount(() => {
33
15
  focusedField.focus()
@@ -43,7 +25,6 @@
43
25
  }
44
26
 
45
27
  if (form.checkValidity()) {
46
- $session.user = JSON.parse(JSON.stringify(user)) // deep clone
47
28
  const url = '/api/v1/user'
48
29
  const res = await fetch(url, {
49
30
  method: 'PUT',
@@ -54,6 +35,7 @@
54
35
  })
55
36
  const reply = await res.json()
56
37
  message = reply.message
38
+ $loginSession = JSON.parse(JSON.stringify(user)) // update loginSession store
57
39
  } else {
58
40
  form.classList.add('was-validated')
59
41
  focusOnFirstError(form)
@@ -0,0 +1,10 @@
1
+ import { redirect } from '@sveltejs/kit';
2
+ import type { PageServerLoad } from './$types'
3
+
4
+ export const load: PageServerLoad = ({ locals }) => {
5
+ const { user } = locals
6
+ if (user) { // Redirect to home if user is logged in already
7
+ throw redirect(302, '/');
8
+ }
9
+ return {}
10
+ }
@@ -1,25 +1,12 @@
1
- <script context="module" lang="ts">
2
- import type { Load } from '@sveltejs/kit'
3
-
4
- export const load: Load = ({ session }) => {
5
- if (session.user) { // Do not display if user is logged in
6
- return {
7
- status: 302,
8
- redirect: '/'
9
- }
10
- }
11
- return {}
12
- }
13
- </script>
14
-
15
1
  <script lang="ts">
16
2
  import { onMount } from 'svelte'
17
3
  import { goto } from '$app/navigation'
18
- import { page, session } from '$app/stores'
4
+ import { page } from '$app/stores'
5
+ import { loginSession } from '../../stores'
19
6
  import useAuth from '$lib/auth'
20
7
  import { focusOnFirstError } from '$lib/focus'
21
8
 
22
- const { initializeSignInWithGoogle, registerLocal } = useAuth(page, session, goto)
9
+ const { initializeSignInWithGoogle, registerLocal } = useAuth(page, loginSession, goto)
23
10
 
24
11
  let focusedField: HTMLInputElement
25
12
 
@@ -50,7 +37,7 @@
50
37
  } catch (err) {
51
38
  if (err instanceof Error) {
52
39
  message = err.message
53
- console.error('Login error', message)
40
+ console.log('Login error', message)
54
41
  }
55
42
  }
56
43
  } else {
@@ -62,11 +49,7 @@
62
49
 
63
50
  onMount(() => {
64
51
  focusedField.focus()
65
- initializeSignInWithGoogle()
66
- window.google.accounts.id.renderButton(
67
- document.getElementById('googleButton'),
68
- { theme: 'filled_blue', size: 'large', width: '367' } // customization attributes
69
- )
52
+ initializeSignInWithGoogle('googleButton')
70
53
  })
71
54
 
72
55
 
@@ -121,7 +104,7 @@
121
104
  </div>
122
105
 
123
106
  {#if message}
124
- <p>{message}</p>
107
+ <p class="text-danger">{message}</p>
125
108
  {/if}
126
109
 
127
110
  <button type="button" on:click={register} class="btn btn-primary btn-lg">Register</button>
@@ -0,0 +1,13 @@
1
+ import { redirect } from '@sveltejs/kit'
2
+ import type { PageServerLoad } from './$types'
3
+
4
+ export const load: PageServerLoad = async ({locals}) => {
5
+ const authorized = ['admin', 'teacher']
6
+ if (!locals.user || !authorized.includes(locals.user.role)) {
7
+ throw redirect(302, '/login?referrer=/teachers')
8
+ }
9
+
10
+ return {
11
+ message: 'Teachers or Admin-only content from endpoint.'
12
+ }
13
+ }
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import type { PageData } from './$types'
3
+ export let data: PageData
4
+ </script>
5
+
6
+ <svelte:head>
7
+ <title>Teachers</title>
8
+ </svelte:head>
9
+
10
+ <h1>Teachers</h1>
11
+ <h4>Teacher Or Admin Role</h4>
12
+ <p>{data.message}</p>
package/src/stores.ts CHANGED
@@ -4,4 +4,14 @@ export const toast = writable({
4
4
  title: '',
5
5
  body: '',
6
6
  isOpen: false
7
- })
7
+ })
8
+
9
+ // While server determines whether the user is logged in by examining RequestEvent.locals.user, the
10
+ // loginSession is updated so all parts of the SPA client-side see the user and role.
11
+
12
+ export const defaultUser: User = {
13
+ id: 0, // the not-logged-in user id
14
+ role: 'student' // default role for users who are not logged in
15
+ }
16
+
17
+ export const loginSession = writable(defaultUser)
package/svelte.config.js CHANGED
@@ -5,7 +5,6 @@ const production = process.env.NODE_ENV === 'production'
5
5
 
6
6
  const baseCsp = [
7
7
  'self',
8
- // 'strict-dynamic', // issues with datepicker on classes, add to calendar scripts
9
8
  'https://www.gstatic.com/recaptcha/', // recaptcha
10
9
  'https://accounts.google.com/gsi/', // sign-in w/google
11
10
  'https://www.google.com/recaptcha/', // recapatcha
@@ -30,8 +29,7 @@ const config = {
30
29
  'img-src': ['data:', 'blob:', ...baseCsp],
31
30
  'style-src': ['unsafe-inline', ...baseCsp],
32
31
  'object-src': ['none'],
33
- 'base-uri': ['self'],
34
- // 'require-trusted-types-for': ["'script'"] // will require effort to get this working
32
+ 'base-uri': ['self']
35
33
  }
36
34
  }
37
35
  }
@@ -1,19 +0,0 @@
1
- <script lang="ts" context="module">
2
- import type { Load } from '@sveltejs/kit'
3
-
4
- export const load: Load = ({ error, status }) => {
5
- const { message } = <Error> error
6
- return {
7
- props: {
8
- errorMessage: `${status}: ${message}`
9
- }
10
- }
11
- }
12
- </script>
13
-
14
- <script lang="ts">
15
- export let errorMessage = ''
16
- </script>
17
-
18
- <h1>Error</h1>
19
- <h4>{errorMessage}</h4>
@@ -1,40 +0,0 @@
1
- <script context="module" lang="ts">
2
- import type { Load } from '@sveltejs/kit'
3
-
4
- export const load: Load = async ({ fetch, session }) => {
5
- const authorized = ['admin']
6
- if (session.user && !authorized.includes(session.user.role)) {
7
- return {
8
- status: 302,
9
- redirect: '/login?referrer=/admin'
10
- }
11
- }
12
-
13
- const url = '/api/v1/admin'
14
- const res = await fetch(url, {
15
- method: 'GET',
16
- headers: { 'Content-Type': 'application/json' }
17
- })
18
-
19
- // if !res.ok, error is returned as message
20
- const { message } = await res.json()
21
- return {
22
- props: {
23
- message
24
- }
25
- }
26
-
27
- }
28
- </script>
29
-
30
- <script lang="ts">
31
- export let message = ''
32
- </script>
33
-
34
- <svelte:head>
35
- <title>Administration</title>
36
- </svelte:head>
37
-
38
- <h1>Admin</h1>
39
- <h4>Admin Role</h4>
40
- <p>{message}</p>
@@ -1,5 +0,0 @@
1
- import type { RequestEvent } from "@sveltejs/kit"
2
-
3
- export function requestAuthorized(event: RequestEvent, roles: string[]): boolean {
4
- return !!event.locals.user && roles.includes(event.locals.user.role)
5
- }
@@ -1,20 +0,0 @@
1
- import type { RequestHandler } from '@sveltejs/kit'
2
- import { requestAuthorized } from './_auth'
3
-
4
- export const GET: RequestHandler = async event => {
5
- if (!requestAuthorized(event, ['admin'])) {
6
- return {
7
- status: 401,
8
- body: {
9
- message: 'Unauthorized - admin role required.'
10
- }
11
- }
12
- }
13
-
14
- return {
15
- status: 200,
16
- body: {
17
- message: 'Admin-only content from endpoint.'
18
- }
19
- }
20
- }
@@ -1,20 +0,0 @@
1
- import type { RequestHandler } from '@sveltejs/kit'
2
- import { requestAuthorized } from './_auth'
3
-
4
- export const GET: RequestHandler = async event=> {
5
- if (!requestAuthorized(event, ['admin', 'teacher'])) {
6
- return {
7
- status: 401,
8
- body: {
9
- message: 'Unauthorized - admin or teacher role required.'
10
- }
11
- }
12
- }
13
-
14
- return {
15
- status: 200,
16
- body: {
17
- message: 'Teachers or Admin-only content from endpoint.'
18
- }
19
- }
20
- }
@@ -1,35 +0,0 @@
1
- import type { RequestHandler } from '@sveltejs/kit'
2
- import { query } from '../../_db'
3
- import { requestAuthorized } from './_auth'
4
-
5
- export const PUT: RequestHandler = async event => {
6
- if (!requestAuthorized(event, ['admin', 'teacher', 'student'])) {
7
- return {
8
- status: 401,
9
- body: {
10
- message: 'Unauthorized - must be logged-in.'
11
- }
12
- }
13
- }
14
-
15
- const sql = `CALL update_user($1, $2);`
16
- try {
17
- // Only permit update of the authenticated user
18
- const body = await event.request.json()
19
- await query(sql, [event.locals.user.id, JSON.stringify(body)])
20
- } catch (error) {
21
- return {
22
- status: 503,
23
- body: {
24
- message: 'Could not communicate with database.'
25
- }
26
- }
27
- }
28
-
29
- return {
30
- status: 200,
31
- body: {
32
- message: 'Successfully updated user profile.'
33
- }
34
- }
35
- }