sveltekit-auth-example 1.0.22 → 1.0.24

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,6 +1,13 @@
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.24
5
+ * Bump dependencies
6
+
7
+ # 1.0.23
8
+ * Restructured server-side libraries to $lib/server based on https://github.com/sveltejs/kit/pull/6623
9
+ * General cleanup
10
+
4
11
  # 1.0.22
5
12
  * Move google-auth-library and jsonwebtoken to devDependencies from dependencies and other cleanup to package.json
6
13
 
package/README.md CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  This is an example of how to register, authenticate, and update users and limit their access to
4
4
  areas of the website by role (admin, teacher, student). As almost every recent release of SvelteKit introduced breaking changes, this project attempts to
5
- maintain compatibility with the latest release.
5
+ maintain compatibility with the latest release and leverage new APIs.
6
6
 
7
7
  It's a Single Page App (SPA) built with SvelteKit and a PostgreSQL database back-end. Code is TypeScript and the website is styled using Bootstrap. PostgreSQL functions handle password hashing and UUID generation for the session ID. Unlike most authentication examples, this SPA does not use callbacks that redirect back to the site (causing the website to be reloaded with a visual flash).
8
8
 
9
+ The project includes a Content Security Policy (CSP) in svelte.config.js.
10
+
9
11
  The website supports two types of authentication:
10
12
  1. **Local accounts** via username (email) and password
11
13
  - The login form (/src/routes/login/+page.svelte) sends the login info as JSON to endpoint /auth/login
@@ -25,7 +27,7 @@ The website supports two types of authentication:
25
27
 
26
28
  > There is some overhead to checking the user session in a database each time versus using a JWT; however, validating each request avoids problems discussed in [this article](https://redis.com/blog/json-web-tokens-jwt-are-dangerous-for-user-sessions/) and [this one](https://scotch.io/bar-talk/why-jwts-suck-as-session-tokens). For a high-volume website, I would use Redis or the equivalent.
27
29
 
28
- The forgot password functionality uses [**SendInBlue**](https://www.sendinblue.com) to send the email. You would need to have a **SendInBlue** account and set three environmental variables. Email sending is in /src/routes/auth/forgot.ts. This code could easily be replaced by nodemailer or something similar. Note: I have no affliation with **SendInBlue** (just happen to be familiar with their API because of another project).
30
+ The forgot password / password reset functionality uses a JWT and [**SendInBlue**](https://www.sendinblue.com) to send the email. You would need to have a **SendInBlue** account and set three environmental variables. Email sending is in /src/routes/auth/forgot.ts. This code could easily be replaced by nodemailer or something similar. Note: I have no affliation with **SendInBlue** (used their API because on another project).
29
31
 
30
32
  ## Prerequisites
31
33
  - PostgreSQL 14.5 or higher
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.22",
4
+ "version": "1.0.24",
5
5
  "private": false,
6
6
  "author": "Nate Stuyvesant",
7
7
  "license": "https://github.com/nstuyvesant/sveltekit-auth-example/blob/master/LICENSE",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "engines": {
35
35
  "node": "~18.9.0",
36
- "npm": "^8.19.1"
36
+ "npm": "^8.19.2"
37
37
  },
38
38
  "type": "module",
39
39
  "dependencies": {
@@ -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.36.1",
50
- "@typescript-eslint/parser": "^5.36.1",
49
+ "@typescript-eslint/eslint-plugin": "^5.37.0",
50
+ "@typescript-eslint/parser": "^5.37.0",
51
51
  "bootstrap": "^5.2.1",
52
- "eslint": "^8.23.0",
52
+ "eslint": "^8.23.1",
53
53
  "eslint-config-prettier": "^8.5.0",
54
54
  "eslint-plugin-svelte3": "^4.0.0",
55
55
  "google-auth-library": "^8.5.1",
@@ -62,6 +62,6 @@
62
62
  "svelte-preprocess": "^4.10.7",
63
63
  "tslib": "^2.4.0",
64
64
  "typescript": "^4.8.3",
65
- "vite": "^3.1.0"
65
+ "vite": "^3.1.2"
66
66
  }
67
67
  }
package/src/app.d.ts CHANGED
@@ -13,7 +13,7 @@ declare namespace App {
13
13
 
14
14
  // interface Platform {}
15
15
 
16
- interface PrivateEnv { // $env/dynamic/private
16
+ interface PrivateEnv { // $env/static/private
17
17
  DATABASE_URL: string
18
18
  DOMAIN: string
19
19
  JWT_SECRET: string
@@ -23,7 +23,7 @@ declare namespace App {
23
23
  SEND_IN_BLUE_ADMINS: string
24
24
  }
25
25
 
26
- interface PublicEnv { // $env/dynamic/public
26
+ interface PublicEnv { // $env/static/public
27
27
  PUBLIC_GOOGLE_CLIENT_ID: string
28
28
  }
29
29
  }
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="/favicon.png" />
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 src="https://accounts.google.com/gsi/client" async defer></script>
8
8
  %sveltekit.head%
@@ -1,5 +1,5 @@
1
1
  import type { Handle, RequestEvent } from '@sveltejs/kit'
2
- import { query } from './routes/_db'
2
+ import { query } from '$lib/server/db'
3
3
 
4
4
  // Attach authorization to each server request (role may have changed)
5
5
  async function attachUserToRequestEvent(sessionId: string, event: RequestEvent) {
package/src/lib/auth.ts CHANGED
@@ -62,7 +62,7 @@ export default function useAuth(
62
62
  try {
63
63
  const res = await fetch('/auth/register', {
64
64
  method: 'POST',
65
- body: JSON.stringify(user), // server needs to ignore user.role and always set it to 'student'
65
+ body: JSON.stringify(user), // server ignores user.role - always set it to 'student' (lowest priv)
66
66
  headers: {
67
67
  'Content-Type': 'application/json'
68
68
  }
File without changes
@@ -1,8 +1,7 @@
1
1
  import type { LayoutServerLoad } from './$types'
2
2
 
3
- export const load: LayoutServerLoad = (event) => {
4
- const locals = event.locals
5
- const { user }: { user: User } = locals // locals.user set by hooks.ts/handle(), undefined if not logged in
3
+ export const load: LayoutServerLoad = ({ locals }) => {
4
+ const { user } = locals // locals.user set by hooks.server.ts/handle(), undefined if not logged in
6
5
  return {
7
6
  user
8
7
  }
@@ -1,7 +1,7 @@
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']
7
7
  if (!user || !authorized.includes(user.role)) {
@@ -9,6 +9,6 @@ export const load: PageServerLoad = async ({locals})=> {
9
9
  }
10
10
 
11
11
  return {
12
- message: 'Admin-only content from endpoint.'
13
- }
12
+ message: 'Admin-only content from server.'
13
+ }
14
14
  }
@@ -1,6 +1,6 @@
1
- import { error, json} from '@sveltejs/kit'
1
+ import { error, json } from '@sveltejs/kit'
2
2
  import type { RequestHandler } from './$types'
3
- import { query } from '../../../_db'
3
+ import { query } from '$lib/server/db'
4
4
 
5
5
  export const PUT: RequestHandler = async event => {
6
6
  const { user } = event.locals
@@ -1,6 +1,6 @@
1
1
  import { error, json } from '@sveltejs/kit'
2
2
  import type { RequestHandler } from './$types'
3
- import { query } from '../../_db'
3
+ import { query } from '$lib/server/db'
4
4
 
5
5
  export const POST: RequestHandler = async (event) => {
6
6
  const { slug } = event.params
@@ -11,8 +11,7 @@ export const POST: RequestHandler = async (event) => {
11
11
  try {
12
12
  switch (slug) {
13
13
  case 'logout':
14
- if (event.locals.user) {
15
- // if user is null, they are logged out anyway (session might have ended)
14
+ if (event.locals.user) { // else they are logged out / session ended
16
15
  sql = `CALL delete_session($1);`
17
16
  result = await query(sql, [event.locals.user.id])
18
17
  }
@@ -50,7 +49,7 @@ export const POST: RequestHandler = async (event) => {
50
49
  // includes when a user tries to register an existing email account with wrong password
51
50
  throw error(authenticationResult.statusCode, authenticationResult.status)
52
51
 
53
- // Ensures hooks.ts:handle() will not delete cookie just set
52
+ // Ensures hooks.server.ts:handle() will not delete session cookie
54
53
  event.locals.user = authenticationResult.user
55
54
 
56
55
  return json(
@@ -2,8 +2,8 @@ import type { RequestHandler } from './$types'
2
2
  import { JWT_SECRET, DOMAIN } from '$env/static/private'
3
3
  import type { Secret } from 'jsonwebtoken'
4
4
  import jwt from 'jsonwebtoken'
5
- import { query } from '../../_db'
6
- import { sendMessage } from '../../_send-in-blue'
5
+ import { query } from '$lib/server/db'
6
+ import { sendMessage } from '$lib/server/send-in-blue'
7
7
 
8
8
  export const POST: RequestHandler = async event => {
9
9
  const body = await event.request.json()
@@ -20,7 +20,6 @@ export const POST: RequestHandler = async event => {
20
20
 
21
21
  // Email URL with token to user
22
22
  const message: Message = {
23
- // sender: JSON.parse(<string> VITE_EMAIL_FROM),
24
23
  to: [{ email: body.email }],
25
24
  subject: 'Password reset',
26
25
  tags: ['account'],
@@ -1,7 +1,7 @@
1
1
  import { error, json } from '@sveltejs/kit'
2
2
  import type { RequestHandler } from './$types'
3
3
  import { OAuth2Client } from 'google-auth-library'
4
- import { query } from '../../_db'
4
+ import { query } from '$lib/server/db'
5
5
  import { PUBLIC_GOOGLE_CLIENT_ID } from '$env/static/public'
6
6
 
7
7
  // Verify JWT per https://developers.google.com/identity/gsi/web/guides/verify-google-id-token
@@ -47,7 +47,7 @@ export const POST: RequestHandler = async event => {
47
47
  const user = await getGoogleUserFromJWT(token)
48
48
  const userSession = await upsertGoogleUser(user)
49
49
 
50
- // Prevent hooks.ts's handler() from deleting cookie thinking no one has authenticated
50
+ // Prevent hooks.server.ts's handler() from deleting cookie thinking no one has authenticated
51
51
  event.locals.user = userSession.user
52
52
 
53
53
  return json({
@@ -2,8 +2,8 @@ import { json } from '@sveltejs/kit'
2
2
  import type { RequestHandler } from './$types'
3
3
  import type { JwtPayload } from 'jsonwebtoken'
4
4
  import jwt from 'jsonwebtoken'
5
- import { query } from '../../_db'
6
5
  import { JWT_SECRET } from '$env/static/private'
6
+ import { query } from '$lib/server/db'
7
7
 
8
8
  export const PUT: RequestHandler = async (event) => {
9
9
  const body = await event.request.json()
@@ -52,7 +52,6 @@
52
52
  initializeSignInWithGoogle('googleButton')
53
53
  })
54
54
 
55
-
56
55
  const passwordMatch = () => {
57
56
  if (!user) return false // placate TypeScript
58
57
  if (!user.password) user.password = ''
@@ -9,6 +9,6 @@ export const load: PageServerLoad = async ({locals}) => {
9
9
  }
10
10
 
11
11
  return {
12
- message: 'Teachers or Admin-only content from endpoint.'
12
+ message: 'Teachers or Admin-only content from server.'
13
13
  }
14
14
  }