Uses Oslo for password hashing (#1782)

Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
This commit is contained in:
Mihovil Ilakovac 2024-02-20 17:46:54 +01:00 committed by GitHub
parent edb3cbd61f
commit 55ede5684b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 1493 additions and 97 deletions

View File

@ -1,17 +1,16 @@
import SecurePassword from 'secure-password'
import { Argon2id } from 'oslo/password'
const SP = new SecurePassword()
const argon2id = new Argon2id()
// PRIVATE API
export const hashPassword = async (password: string): Promise<string> => {
const hashedPwdBuffer = await SP.hash(Buffer.from(password))
return hashedPwdBuffer.toString("base64")
return argon2id.hash(password)
}
// PRIVATE API
export const verifyPassword = async (hashedPassword: string, password: string): Promise<void> => {
const result = await SP.verify(Buffer.from(password), Buffer.from(hashedPassword, "base64"))
if (result !== SecurePassword.VALID) {
const isValidPassword = await argon2id.verify(hashedPassword, password)
if (!isValidPassword) {
throw new Error('Invalid password.')
}
}

View File

@ -207,7 +207,7 @@
"file",
"../out/sdk/wasp/package.json"
],
"8c4e7254985043dbada72ea8caa14dc82e154e5d1d3384677b7986f9ba8ef833"
"6691a48b86fa7e4133f01e647534c32c378755b9c4f266853fdcdc61907e711c"
],
[
[

View File

@ -1 +1 @@
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"secure-password","version":"^4.0.0"},{"name":"sodium-native","version":"3.3.0"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}

View File

@ -17,8 +17,6 @@
"react": "^18.2.0",
"react-hook-form": "^7.45.4",
"react-router-dom": "^5.3.3",
"secure-password": "^4.0.0",
"sodium-native": "3.3.0",
"superjson": "^1.12.2",
"uuid": "^9.0.0",
"vitest": "^1.2.1"

View File

@ -207,7 +207,7 @@
"file",
"../out/sdk/wasp/package.json"
],
"8c4e7254985043dbada72ea8caa14dc82e154e5d1d3384677b7986f9ba8ef833"
"6691a48b86fa7e4133f01e647534c32c378755b9c4f266853fdcdc61907e711c"
],
[
[

View File

@ -1 +1 @@
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"secure-password","version":"^4.0.0"},{"name":"sodium-native","version":"3.3.0"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}

View File

@ -17,8 +17,6 @@
"react": "^18.2.0",
"react-hook-form": "^7.45.4",
"react-router-dom": "^5.3.3",
"secure-password": "^4.0.0",
"sodium-native": "3.3.0",
"superjson": "^1.12.2",
"uuid": "^9.0.0",
"vitest": "^1.2.1"

View File

@ -123,7 +123,7 @@
"file",
"../out/sdk/wasp/auth/password.ts"
],
"dfcf3e3fe8772049f3fb90fef0da794a759f990a4fef400ce08f9dbd107efa38"
"1be6716ab375f1ac492e2ed65e6343990770397946cadef292e92eaa3b3ceefc"
],
[
[
@ -480,7 +480,7 @@
"file",
"../out/sdk/wasp/package.json"
],
"5d72459af3a4e005f73ab8d7fa818b1e8e9225dfdf650809e64e620a22af7e43"
"b53c678e00bdf4457cbb1f957482203dad88eb527fcd2d81e87d5f49004e78e1"
],
[
[

View File

@ -1 +1 @@
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"secure-password","version":"^4.0.0"},{"name":"sodium-native","version":"3.3.0"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@stitches/react","version":"^1.2.8"},{"name":"lucia","version":"^3.0.0-beta.14"},{"name":"@lucia-auth/adapter-prisma","version":"^4.0.0-beta.9"},{"name":"@sendgrid/mail","version":"^7.7.0"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"passport","version":"0.6.0"},{"name":"passport-google-oauth20","version":"2.0.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@stitches/react","version":"^1.2.8"},{"name":"lucia","version":"^3.0.1"},{"name":"@lucia-auth/adapter-prisma","version":"^4.0.0"},{"name":"oslo","version":"^1.1.2"},{"name":"@sendgrid/mail","version":"^7.7.0"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"passport","version":"0.6.0"},{"name":"passport-google-oauth20","version":"2.0.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}

View File

@ -1,17 +1,16 @@
import SecurePassword from 'secure-password'
import { Argon2id } from 'oslo/password'
const SP = new SecurePassword()
const argon2id = new Argon2id()
// PRIVATE API
export const hashPassword = async (password: string): Promise<string> => {
const hashedPwdBuffer = await SP.hash(Buffer.from(password))
return hashedPwdBuffer.toString("base64")
return argon2id.hash(password)
}
// PRIVATE API
export const verifyPassword = async (hashedPassword: string, password: string): Promise<void> => {
const result = await SP.verify(Buffer.from(password), Buffer.from(hashedPassword, "base64"))
if (result !== SecurePassword.VALID) {
const isValidPassword = await argon2id.verify(hashedPassword, password)
if (!isValidPassword) {
throw new Error('Invalid password.')
}
}

View File

@ -1,14 +1,13 @@
import SecurePassword from 'secure-password';
const SP = new SecurePassword();
import { Argon2id } from 'oslo/password';
const argon2id = new Argon2id();
// PRIVATE API
export const hashPassword = async (password) => {
const hashedPwdBuffer = await SP.hash(Buffer.from(password));
return hashedPwdBuffer.toString("base64");
return argon2id.hash(password);
};
// PRIVATE API
export const verifyPassword = async (hashedPassword, password) => {
const result = await SP.verify(Buffer.from(password), Buffer.from(hashedPassword, "base64"));
if (result !== SecurePassword.VALID) {
const isValidPassword = await argon2id.verify(hashedPassword, password);
if (!isValidPassword) {
throw new Error('Invalid password.');
}
};

View File

@ -1 +1 @@
{"version":3,"file":"password.js","sourceRoot":"","sources":["../../auth/password.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,iBAAiB,CAAA;AAE5C,MAAM,EAAE,GAAG,IAAI,cAAc,EAAE,CAAA;AAE/B,cAAc;AACd,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,QAAgB,EAAmB,EAAE;IACtE,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC5D,OAAO,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;AAC3C,CAAC,CAAA;AAED,cAAc;AACd,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,cAAsB,EAAE,QAAgB,EAAiB,EAAE;IAC9F,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC5F,IAAI,MAAM,KAAK,cAAc,CAAC,KAAK,EAAE;QACnC,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;KACrC;AACH,CAAC,CAAA"}
{"version":3,"file":"password.js","sourceRoot":"","sources":["../../auth/password.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAA;AAE/B,cAAc;AACd,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,QAAgB,EAAmB,EAAE;IACtE,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AAChC,CAAC,CAAA;AAED,cAAc;AACd,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,cAAsB,EAAE,QAAgB,EAAiB,EAAE;IAC9F,MAAM,eAAe,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAA;IACvE,IAAI,CAAC,eAAe,EAAE;QACpB,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAA;KACrC;AACH,CAAC,CAAA"}

View File

@ -1,6 +1,6 @@
{
"dependencies": {
"@lucia-auth/adapter-prisma": "^4.0.0-beta.9",
"@lucia-auth/adapter-prisma": "^4.0.0",
"@prisma/client": "4.16.2",
"@sendgrid/mail": "^7.7.0",
"@stitches/react": "^1.2.8",
@ -14,16 +14,15 @@
"jsdom": "^21.1.1",
"jsonwebtoken": "^8.5.1",
"lodash.merge": "^4.6.2",
"lucia": "^3.0.0-beta.14",
"lucia": "^3.0.1",
"mitt": "3.0.0",
"msw": "^1.1.0",
"oslo": "^1.1.2",
"pg-boss": "^8.4.2",
"prisma": "4.16.2",
"react": "^18.2.0",
"react-hook-form": "^7.45.4",
"react-router-dom": "^5.3.3",
"secure-password": "^4.0.0",
"sodium-native": "3.3.0",
"superjson": "^1.12.2",
"uuid": "^9.0.0",
"vitest": "^1.2.1"

View File

@ -214,7 +214,7 @@
"file",
"../out/sdk/wasp/package.json"
],
"2088c007790071632d6fffacfe8fa531adc674ffbfcd8f9da8627318dff6707c"
"7da143b79de48120d1d0ff96aa813f59ef44582c375cb3c624467cb9afa7223b"
],
[
[

View File

@ -1 +1 @@
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"secure-password","version":"^4.0.0"},{"name":"sodium-native","version":"3.3.0"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"},{"name":"pg-boss","version":"^8.4.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}

View File

@ -18,8 +18,6 @@
"react": "^18.2.0",
"react-hook-form": "^7.45.4",
"react-router-dom": "^5.3.3",
"secure-password": "^4.0.0",
"sodium-native": "3.3.0",
"superjson": "^1.12.2",
"uuid": "^9.0.0",
"vitest": "^1.2.1"

View File

@ -207,7 +207,7 @@
"file",
"../out/sdk/wasp/package.json"
],
"8c4e7254985043dbada72ea8caa14dc82e154e5d1d3384677b7986f9ba8ef833"
"6691a48b86fa7e4133f01e647534c32c378755b9c4f266853fdcdc61907e711c"
],
[
[

View File

@ -1 +1 @@
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"secure-password","version":"^4.0.0"},{"name":"sodium-native","version":"3.3.0"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}
{"_waspSdkNpmDeps":{"dependencies":[{"name":"@prisma/client","version":"4.16.2"},{"name":"prisma","version":"4.16.2"},{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"express","version":"~4.18.1"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"mitt","version":"3.0.0"},{"name":"react","version":"^18.2.0"},{"name":"lodash.merge","version":"^4.6.2"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"superjson","version":"^1.12.2"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"uuid","version":"^9.0.0"},{"name":"vitest","version":"^1.2.1"},{"name":"@vitest/ui","version":"^1.2.1"},{"name":"jsdom","version":"^21.1.1"},{"name":"@testing-library/react","version":"^14.1.2"},{"name":"@testing-library/jest-dom","version":"^6.3.0"},{"name":"msw","version":"^1.1.0"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"}]},"_userNpmDeps":{"userDependencies":[{"name":"react","version":"^18.2.0"},{"name":"wasp","version":"file:.wasp/out/sdk/wasp"}],"userDevDependencies":[{"name":"@types/react","version":"^18.0.37"},{"name":"prisma","version":"4.16.2"},{"name":"typescript","version":"^5.1.0"},{"name":"vite","version":"^4.3.9"}]},"_waspFrameworkNpmDeps":{"npmDepsForWebApp":{"dependencies":[{"name":"@tanstack/react-query","version":"^4.29.0"},{"name":"axios","version":"^1.4.0"},{"name":"mitt","version":"3.0.0"},{"name":"react-dom","version":"^18.2.0"},{"name":"react-hook-form","version":"^7.45.4"},{"name":"react-router-dom","version":"^5.3.3"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/vite-react","version":"^2.0.0"},{"name":"@types/react-dom","version":"^18.0.11"},{"name":"@types/react-router-dom","version":"^5.3.3"},{"name":"@vitejs/plugin-react","version":"^4.2.1"},{"name":"dotenv","version":"^16.0.3"}]},"npmDepsForServer":{"dependencies":[{"name":"cookie-parser","version":"~1.4.6"},{"name":"cors","version":"^2.8.5"},{"name":"dotenv","version":"16.0.2"},{"name":"express","version":"~4.18.1"},{"name":"helmet","version":"^6.0.0"},{"name":"jsonwebtoken","version":"^8.5.1"},{"name":"morgan","version":"~1.10.0"},{"name":"rate-limiter-flexible","version":"^2.4.1"},{"name":"superjson","version":"^1.12.2"}],"devDependencies":[{"name":"@tsconfig/node18","version":"latest"},{"name":"@types/cors","version":"^2.8.5"},{"name":"@types/express","version":"^4.17.13"},{"name":"@types/express-serve-static-core","version":"^4.17.13"},{"name":"@types/node","version":"^18.0.0"},{"name":"nodemon","version":"^2.0.19"},{"name":"rollup","version":"^4.9.6"},{"name":"rollup-plugin-esbuild","version":"^6.1.1"},{"name":"standard","version":"^17.0.0"}]}}}

View File

@ -17,8 +17,6 @@
"react": "^18.2.0",
"react-hook-form": "^7.45.4",
"react-router-dom": "^5.3.3",
"secure-password": "^4.0.0",
"sodium-native": "3.3.0",
"superjson": "^1.12.2",
"uuid": "^9.0.0",
"vitest": "^1.2.1"

File diff suppressed because it is too large Load Diff

View File

@ -211,21 +211,6 @@ npmDepsForSdk spec =
("lodash.merge", "^4.6.2"),
("react-router-dom", "^5.3.3"),
("react-hook-form", "^7.45.4"),
("secure-password", "^4.0.0"),
-- Secure-password 4.0.0. defaults to using sodium-native 3.4.1.,
-- which segfaults on on Alpine:
-- https://github.com/sodium-friends/sodium-native/issues/160
--
-- Before 0.12.0 (i.e., restructuring), we made Wasp use
-- sodium-native 3.3.0. with package.json overrides in our web-app/package.json:
-- https://github.com/wasp-lang/wasp/pull/729
--
-- Because our code that uses secure-password now lives in the SDK,
-- and NPM apparently ignores package.json overrides for
-- dependencies (no reference, found out by trying it out), the only
-- way to force NPM to install sodium-native 3.3.0 is by listing it
-- as a direct dependency.
("sodium-native", "3.3.0"),
("superjson", "^1.12.2"),
("@types/express-serve-static-core", "^4.17.13")
]

View File

@ -91,6 +91,7 @@ depsRequiredByAuth spec = maybe [] (const authDeps) maybeAuth
maybeAuth = AS.App.auth $ snd $ getApp spec
authDeps =
AS.Dependency.fromList
[ ("lucia", "^3.0.0-beta.14"),
("@lucia-auth/adapter-prisma", "^4.0.0-beta.9")
[ ("lucia", "^3.0.1"),
("@lucia-auth/adapter-prisma", "^4.0.0"),
("oslo", "^1.1.2")
]

View File

@ -56,7 +56,7 @@ The main differences are:
- The server/client code separation is no longer necessary. You can now organize
your code however you want, as long as it's inside the `src` directory.
- All external imports in your Wasp file must have paths starting with `@src` (e.g., `import foo from '@src/bar.js')
- All external imports in your Wasp file must have paths starting with `@src` (e.g., `import foo from '@src/bar.js'`)
where `@src` refers to the `src` directory in your project root. The paths can
no longer start with `@server` or `@client`.
- Your project now features a top-level `public` dir. Wasp will publicly serve
@ -438,6 +438,322 @@ The migration functions provided below are written with the typical use cases in
##### Username & Password
:::caution Users will need to migrate their password
There is a breaking change between the old and the new auth in the way the password is hashed. This means that users will need to migrate their password after the migration, as the old password will no longer work.
Since the only way users using username and password as a login method can verify their identity is by providing both their username and password (there is no email or any other info, unless you asked for it and stored it explicitly), we need to provide them a way to exchange their old password for a new password. One way to handle this is to inform them about the need to migrate their password (on the login page) and provide a custom page to migrate the password.
:::
<details>
<summary>
Steps to create a custom page for migrating the password
</summary>
1. You will need to install the `secure-password` and `sodium-native` packages to use the old hashing algorithm:
```bash
npm install secure-password@4.0.0 sodium-native@3.3.0 --save-exact
```
Make sure to save the exact versions of the packages.
2. Then you'll need to create a new page in your app where users can migrate their password. You can use the following code as a starting point:
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```jsx title="src/pages/MigratePasswordPage.jsx"
import {
FormItemGroup,
FormLabel,
FormInput,
FormError,
} from "wasp/client/auth";
import { useForm } from "react-hook-form";
import { migratePassword } from "wasp/client/operations";
import { useState } from "react";
export function MigratePasswordPage() {
const [successMessage, setSuccessMessage] = useState(null);
const [errorMessage, setErrorMessage] = useState(null);
const form = useForm({
defaultValues: {
username: "",
password: "",
},
});
const onSubmit = form.handleSubmit(async (data) => {
try {
const result = await migratePassword(data);
setSuccessMessage(result.message);
} catch (e) {
console.error(e);
if (e instanceof Error) {
setErrorMessage(e.message);
}
}
});
return (
<div style={{
maxWidth: "400px",
margin: "auto",
}}>
<h1>Migrate your password</h1>
<p>
If you have an account on the old version of the website, you can
migrate your password to the new version.
</p>
{successMessage && <div>{successMessage}</div>}
{errorMessage && <FormError>{errorMessage}</FormError>}
<form onSubmit={onSubmit}>
<FormItemGroup>
<FormLabel>Username</FormLabel>
<FormInput
{...form.register("username", {
required: "Username is required",
})}
/>
<FormError>{form.formState.errors.username?.message}</FormError>
</FormItemGroup>
<FormItemGroup>
<FormLabel>Password</FormLabel>
<FormInput
{...form.register("password", {
required: "Password is required",
})}
type="password"
/>
<FormError>{form.formState.errors.password?.message}</FormError>
</FormItemGroup>
<button type="submit">Migrate password</button>
</form>
</div>
);
}
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```tsx title="src/pages/MigratePasswordPage.tsx"
import {
FormItemGroup,
FormLabel,
FormInput,
FormError,
} from "wasp/client/auth";
import { useForm } from "react-hook-form";
import { migratePassword } from "wasp/client/operations";
import { useState } from "react";
export function MigratePasswordPage() {
const [successMessage, setSuccessMessage] = useState<string | null>(null);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const form = useForm<{
username: string;
password: string;
}>();
const onSubmit = form.handleSubmit(async (data) => {
try {
const result = await migratePassword(data);
setSuccessMessage(result.message);
} catch (e: unknown) {
console.error(e);
if (e instanceof Error) {
setErrorMessage(e.message);
}
}
});
return (
<div style={{
maxWidth: "400px",
margin: "auto",
}}>
<h1>Migrate your password</h1>
<p>
If you have an account on the old version of the website, you can
migrate your password to the new version.
</p>
{successMessage && <div>{successMessage}</div>}
{errorMessage && <FormError>{errorMessage}</FormError>}
<form onSubmit={onSubmit}>
<FormItemGroup>
<FormLabel>Username</FormLabel>
<FormInput
{...form.register("username", {
required: "Username is required",
})}
/>
<FormError>{form.formState.errors.username?.message}</FormError>
</FormItemGroup>
<FormItemGroup>
<FormLabel>Password</FormLabel>
<FormInput
{...form.register("password", {
required: "Password is required",
})}
type="password"
/>
<FormError>{form.formState.errors.password?.message}</FormError>
</FormItemGroup>
<button type="submit">Migrate password</button>
</form>
</div>
);
}
```
</TabItem>
</Tabs>
3. Finally, you will need to create a new operation in your app to handle the password migration. You can use the following code as a starting point:
<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
```wasp title="main.wasp"
action migratePassword {
fn: import { migratePassword } from "@src/auth",
entities: []
}
```
```js title="src/auth.js"
import SecurePassword from "secure-password";
import { HttpError } from "wasp/server";
import {
createProviderId,
deserializeAndSanitizeProviderData,
findAuthIdentity,
updateAuthIdentityProviderData,
} from "wasp/server/auth";
export const migratePassword = async ({ password, username }, _context) => {
const providerId = createProviderId("username", username);
const authIdentity = await findAuthIdentity(providerId);
if (!authIdentity) {
throw new HttpError(400, "Something went wrong");
}
const providerData =
deserializeAndSanitizeProviderData < "username" > authIdentity.providerData;
try {
const SP = new SecurePassword();
// This will verify the password using the old algorithm
const result = await SP.verify(
Buffer.from(password),
Buffer.from(providerData.hashedPassword, "base64")
);
if (result !== SecurePassword.VALID) {
throw new HttpError(400, "Something went wrong");
}
// This will hash the password using the new algorithm and update the
// provider data in the database.
(await updateAuthIdentityProviderData) <
"username" >
(providerId,
providerData,
{
hashedPassword: password,
});
} catch (e) {
throw new HttpError(400, "Something went wrong");
}
return {
message: "Password migrated successfully.",
};
};
```
</TabItem>
<TabItem value="ts" label="TypeScript">
```wasp title="main.wasp"
action migratePassword {
fn: import { migratePassword } from "@src/auth",
entities: []
}
```
```ts title="src/auth.ts"
import SecurePassword from "secure-password";
import { HttpError } from "wasp/server";
import {
createProviderId,
deserializeAndSanitizeProviderData,
findAuthIdentity,
updateAuthIdentityProviderData,
} from "wasp/server/auth";
import { MigratePassword } from "wasp/server/operations";
type MigratePasswordInput = {
username: string;
password: string;
};
type MigratePasswordOutput = {
message: string;
};
export const migratePassword: MigratePassword<
MigratePasswordInput,
MigratePasswordOutput
> = async ({ password, username }, _context) => {
const providerId = createProviderId("username", username);
const authIdentity = await findAuthIdentity(providerId);
if (!authIdentity) {
throw new HttpError(400, "Something went wrong");
}
const providerData = deserializeAndSanitizeProviderData<"username">(
authIdentity.providerData
);
try {
const SP = new SecurePassword();
// This will verify the password using the old algorithm
const result = await SP.verify(
Buffer.from(password),
Buffer.from(providerData.hashedPassword, "base64")
);
if (result !== SecurePassword.VALID) {
throw new HttpError(400, "Something went wrong");
}
// This will hash the password using the new algorithm and update the
// provider data in the database.
await updateAuthIdentityProviderData<"username">(providerId, providerData, {
hashedPassword: password,
});
} catch (e) {
throw new HttpError(400, "Something went wrong");
}
return {
message: "Password migrated successfully.",
};
};
```
</TabItem>
</Tabs>
</details>
```ts title="src/migrateToNewAuth.ts"
import { PrismaClient } from "@prisma/client";
import { ProviderName, UsernameProviderData } from "wasp/server/auth";
@ -488,6 +804,14 @@ export async function migrateUsernameAuth(prismaClient: PrismaClient) {
##### Email
:::caution Users will need to reset their password
There is a breaking change between the old and the new auth in the way the password is hashed. This means that users will need to reset their password after the migration, as the old password will no longer work.
It would be best to notify your users about this change and put a notice on your login page to **request a password reset**.
:::
```ts title="src/migrateToNewAuth.ts"
import { PrismaClient } from "@prisma/client";
import { EmailProviderData, ProviderName } from "wasp/server/auth";