Working on auth...

This commit is contained in:
Daniel Flanagan 2022-10-07 17:09:13 -05:00
parent 7a5f83c633
commit f0755615ae
Signed by untrusted user: lytedev-divvy
GPG key ID: 6D69CEEE4ABBCD82
5 changed files with 93 additions and 20 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/config.json

View file

@ -9,10 +9,10 @@ interface TableSpec {
prepStatements?: string[];
}
const timestamps = [
"created_at timestamptz not null default now()",
"updated_at timestamptz not null default now()",
];
const createdAtTimestamp = "created_at timestamptz not null default now()";
const updatedAtTimestamp = "updated_at timestamptz not null default now()";
const timestamps = [createdAtTimestamp, updatedAtTimestamp];
const tables: Record<string, TableSpec> = {
"note": {
@ -26,15 +26,30 @@ const tables: Record<string, TableSpec> = {
columns: [
id,
"username text not null unique",
"hashed_password text not null",
"password_digest text not null",
"status user_status not null",
"display_name text",
...timestamps,
],
additionalTableStatements: [
"constraint valid_username check (username ~* '^[a-z\d\\-_]{2,38}$')",
],
},
"user_token": {
prepStatements: [
"drop type if exists user_token_type",
"create type user_token_type as enum ('session', 'reset')",
],
columns: [
id,
"token_digest bytea not null unique",
"type user_token_type not null",
"sent_to text not null",
createdAtTimestamp,
],
additionalStatements: [
"create index team_user_type on user_token (type)",
"create index team_user_sent_to on user_token (sent_to)",
],
},
"team": {
@ -94,8 +109,20 @@ create extension if not exists "uuid-ossp";
${createTables}
commit;
`;
const setupResult = await query(queryString);
console.debug(setupResult);
console.log(queryString);
const seedQuery = `
-- TODO: create reserved usernames?
with new_user as (
insert into "user" (username, hashed_password, status)
insert into "user" (username, password_digest, status)
values ('lytedev', '$2a$10$9fyDAOz6H4a393KHyjbvIe1WFxbhCJhq/CZmlXcEg4d1bE9Ey25WW', 'superadmin')
returning id as user_id
), new_team as (
@ -108,14 +135,11 @@ insert into "team_user" (user_id, team_id, status)
(select user_id from new_user),
(select team_id from new_team),
'owner'
);
) returning user_id;
commit;
`;
console.log(queryString);
const seedResult = await query(seedQuery);
const result = await query(queryString);
console.debug(result);
console.log(queryString);
console.debug(seedResult);
console.log(seedQuery);

39
mail/mod.ts Normal file
View file

@ -0,0 +1,39 @@
import Mailgun from "https://deno.land/x/mailgun@v1.1.1/index.ts";
import { type Message } from "https://deno.land/x/mailgun@v1.1.1/types.ts";
export type Email = Message;
const MAILGUN_API_KEY = Deno.env.get("MAILGUN_API_KEY");
const MAILGUN_DOMAIN = Deno.env.get("MAILGUN_DOMAIN");
if (!MAILGUN_API_KEY) {
console.error(
"MAILGUN_API_KEY not set. Emails will not be sent, only logged to the console.",
);
}
if (!MAILGUN_DOMAIN) {
console.error(
"MAILGUN_API_KEY not set. Emails will not be sent, only logged to the console.",
);
}
const mailgun = new Mailgun({
key: "YOUR_API_KEY",
region: "us",
domain: "YOUR_DOMAIN",
});
export async function send(email: Email) {
console.debug("Email:", email);
if (MAILGUN_API_KEY && MAILGUN_DOMAIN) {
return await mailgun.send(email);
} else {
return new Response(
"Email client is not properly configured. Email not sent.",
{
status: 401,
},
);
}
}

View file

@ -26,7 +26,7 @@ export const handler: Handlers<UserID | LoginError | null> = {
}
const result = await query<
{ id: string; username: string; hashed_password: string }
{ id: string; username: string; password_digest: string }
>(
`select * from "user" where username = $1`,
[username],
@ -34,8 +34,8 @@ export const handler: Handlers<UserID | LoginError | null> = {
if (result == null || result.rows.length < 1) {
return await invalidLogin(context);
}
const { rows: [{ id, hashed_password }] } = result;
if (await compare(password.toString(), hashed_password)) {
const { rows: [{ id, password_digest }] } = result;
if (await compare(password.toString(), password_digest)) {
return await context.render(id);
} else {
return await invalidLogin(context);

View file

@ -21,13 +21,13 @@ export const handler: Handlers<UserID | RegistrationError | null> = {
if (!password) {
return await context.render({ message: "no password provided" });
}
const hashed_password = await hash(password.toString());
const password_digest = await hash(password.toString());
try {
const result = await query<{ user_id: string }>(
`
with new_user as (
insert into "user" (username, hashed_password, status)
values ($username, $hashed_password, 'unverified')
insert into "user" (username, password_digest, status)
values ($username, $password_digest, 'unverified')
returning id as user_id
), new_team as (
insert into "team" (display_name)
@ -41,13 +41,14 @@ export const handler: Handlers<UserID | RegistrationError | null> = {
'owner'
) returning user_id
`,
{ username, hashed_password, team_name: `${username}'s First Team` },
{ username, password_digest, team_name: `${username}'s First Team` },
);
console.debug(result);
if (!result) throw "insert failed";
const { rows: [{ user_id: id }] } = result;
return await context.render(id);
} catch (err) {
console.log("Error fields:", { ...err });
if (
err instanceof PostgresError && err.fields.code == "23505" &&
err.fields.constraint == "user_username_key"
@ -55,6 +56,14 @@ export const handler: Handlers<UserID | RegistrationError | null> = {
return await context.render({
message: `A user with username '${username}' already exists`,
});
} else if (
err instanceof PostgresError && err.fields.code == "23514" &&
err.fields.constraint == "valid_username"
) {
return await context.render({
message:
`Username must ONLY be comprised of letters, number, dashes, and underscores`,
});
}
throw err;
}