auth.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import { Database } from "bun:sqlite";
  2. const authDB = new Database("./data/auth.sqlite");
  3. authDB.run(`create table if not exists verifications (
  4. email text not null primary key,
  5. token text not null,
  6. code integer not null,
  7. timestamp datetime default current_timestamp
  8. );`)
  9. authDB.run(`create table if not exists users (
  10. email text not null primary key,
  11. name text,
  12. timestamp datetime default current_timestamp
  13. );`)
  14. authDB.run(`create table if not exists sessions (
  15. id text not null primary key,
  16. secretHash text not null,
  17. email text not null,
  18. timestamp datetime default current_timestamp,
  19. lastAccessed datetime default current_timestamp
  20. );`)
  21. import nodemailer from "nodemailer"
  22. import generateSecureRandomString from "./generateSecureRandomString";
  23. import generateSecureCode from "./generateSecureCode";
  24. import hashSecret from "./hashSecret";
  25. import Meteostanice from "./meteostanice";
  26. const transporter = nodemailer.createTransport({
  27. host: process.env.AUTH_EMAIL_SMTP_HOSTNAME,
  28. port: process.env.AUTH_EMAIL_SMTP_PORT,
  29. secure: true, // Use true for port 465, false for port 587
  30. auth: {
  31. user: process.env.AUTH_EMAIL_SMTP_USERNAME,
  32. pass: process.env.AUTH_EMAIL_SMTP_PASSWORD,
  33. },
  34. });
  35. export default class Auth {
  36. static addVerification(email) {
  37. const token = generateSecureRandomString()
  38. const code = generateSecureCode()
  39. authDB.prepare(`
  40. INSERT INTO verifications (email, token, code)
  41. VALUES (?, ?, ?)
  42. ON CONFLICT(email) DO UPDATE SET
  43. token = excluded.token,
  44. code = excluded.code;
  45. `).run(email, token, code)
  46. return { email, token, code }
  47. }
  48. static async sendVerification(email, subject, text, html) {
  49. return await transporter.sendMail({
  50. from: process.env.AUTH_EMAIL_SMTP_FROM,
  51. to: email,
  52. subject,
  53. text, // Plain-text version of the message
  54. html, // HTML version of the message
  55. });
  56. }
  57. static getVerification(token) {
  58. const statement = authDB.prepare(`
  59. SELECT *,
  60. ( CASE
  61. WHEN (strftime('%s', 'now') - strftime('%s', timestamp)) > $seconds THEN 0
  62. ELSE 1
  63. END
  64. ) AS valid
  65. FROM verifications
  66. WHERE token = $token;
  67. `)
  68. const result = statement.get({
  69. $seconds: process.env.EMAIL_VERIFICATION_TIMEOUT,
  70. $token: token
  71. });
  72. return result;
  73. }
  74. static removeVerification(token) {
  75. authDB.prepare(`
  76. DELETE
  77. FROM verifications
  78. WHERE token = ?;
  79. `).run(token)
  80. }
  81. static removeUserVerifications(email) {
  82. authDB.prepare(`
  83. DELETE
  84. FROM verifications
  85. WHERE email = ?;
  86. `).run(email)
  87. }
  88. static addUser(email) {
  89. const statement = authDB.prepare("INSERT INTO users (email) VALUES (?);")
  90. statement.run(
  91. email
  92. );
  93. return { email };
  94. }
  95. static getUser(email) {
  96. const statement = authDB.prepare("select * from users where email = ?;")
  97. const result = statement.get(
  98. email
  99. );
  100. return result;
  101. }
  102. static editUser(email, newName, newEmail) {
  103. const statement = authDB.prepare("select * from users where email = ?;")
  104. const user = statement.get(
  105. email
  106. );
  107. if (!user) return null
  108. const result = authDB.prepare(`
  109. update users
  110. set name = ?,
  111. email = ?
  112. where email = ?;
  113. `).run(newName, newEmail, email)
  114. Meteostanice.editOwnerOnOwned(email, newEmail)
  115. return result
  116. }
  117. static deleteUser(email) {
  118. Meteostanice.deleteOwned(email)
  119. this.removeUserSessions(email)
  120. this.removeUserVerifications(email)
  121. authDB.prepare(`
  122. DELETE
  123. FROM users
  124. WHERE email = ?;
  125. `).run(email)
  126. }
  127. static async createSession(email) {
  128. const id = generateSecureRandomString();
  129. const secret = generateSecureRandomString();
  130. const secretHash = await hashSecret(secret);
  131. const token = id + "." + secret;
  132. const statement = authDB.prepare("INSERT INTO sessions (id, secretHash, email) VALUES (?, ?, ?);")
  133. statement.run(
  134. id,
  135. secretHash,
  136. email
  137. );
  138. return { id, secretHash, token, email };
  139. }
  140. static async getSession(token) {
  141. if (!token) return null
  142. const [id, secret] = token.split(".")
  143. const secretHash = await hashSecret(secret)
  144. const statement = authDB.prepare(`
  145. SELECT *,
  146. ( CASE
  147. WHEN (strftime('%s', 'now') - strftime('%s', lastAccessed)) > $seconds THEN 0
  148. ELSE 1
  149. END
  150. ) AS valid
  151. FROM sessions
  152. WHERE id = $id AND secretHash = $secretHash;
  153. `)
  154. const result = statement.get({
  155. $seconds: process.env.SESSION_TIMEOUT,
  156. $id: id,
  157. $secretHash: secretHash
  158. })
  159. const updateStmt = authDB.prepare(`
  160. UPDATE sessions
  161. SET lastAccessed = CURRENT_TIMESTAMP
  162. WHERE id = $id AND secretHash = $secretHash
  163. `)
  164. updateStmt.run({
  165. $id: id,
  166. $secretHash: secretHash
  167. })
  168. return result
  169. }
  170. static removeSession(id) {
  171. authDB.prepare(`
  172. DELETE
  173. FROM sessions
  174. WHERE id = ?;
  175. `).run(id)
  176. }
  177. static removeUserSessions(email) {
  178. authDB.prepare(`
  179. DELETE
  180. FROM sessions
  181. WHERE email = ?;
  182. `).run(email)
  183. }
  184. }