Skip to content

Commit

Permalink
chore(auth): fix twitch login
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheeg committed Aug 11, 2024
1 parent 53b1bcb commit 58cafd9
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 82 deletions.
189 changes: 116 additions & 73 deletions daos/UserDAO.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class UserDAO {
async createUser(user_data, options) {
console.log('createUser', user_data, options);

// TODO: wrap all the queries here in a transaction

const identity = await dbPool.query(
`INSERT INTO user_identities
(provider, provider_user_id, login)
Expand All @@ -45,54 +47,71 @@ class UserDAO {
DO UPDATE SET login=$3, last_login_at=NOW()
RETURNING user_id
`,
[options.provider, user_data.id, user_data.id || null]
[options.provider, user_data.id, user_data.login || null]
);

let user_id = identity.rows?.[0]?.user_id;

if (user_id) {
// we already have a user mapping
// so we take the opportunity to update it with the lastest values - should we though? what if the data had been updated from another provider, or manually by the user?
if (options.provider === 'twitch') {
await dbPool.query(
`UPDATE users
SET type=$1, description=$2, display_name=$3, profile_image_url=$4, last_login_at=NOW();
`,
[
user_data.type,
user_data.description,
user_data.display_name,
user_data.profile_image_url,
]
);
} else if (options.provider === 'google') {
// TODO: including finding a login
}
} else {
console.log('creating user');
// user is not already mapped, we must create a new user now, and link it to the identity
if (options.provider === 'twitch') {
const res = await dbPool.query(
`INSERT INTO users
(login, secret, type, description, display_name, profile_image_url)
VALUES
($1, $2, $3, $4, $5, $6)
ON CONFLICT(login) DO NOTHING
RETURNING id
`,
[
user_data.login,
user_data.secret,
user_data.type,
user_data.description,
user_data.display_name,
user_data.profile_image_url,
]
);
do {
if (user_id) {
// we already have a user mapping
// so we take the opportunity to update it with the lastest values - should we though? what if the data had been updated from another provider, or manually by the user?
if (options.provider === 'twitch') {
await dbPool.query(
`UPDATE users
SET type=$1, description=$2, display_name=$3, profile_image_url=$4, last_login_at=NOW();
`,
[
user_data.type,
user_data.description,
user_data.display_name,
user_data.profile_image_url,
]
);
} else if (options.provider === 'google') {
// TODO: including finding a login
}
} else {
// no existing user mapped, which means we just created a new identity
// let's see if we can find a user that has the email that came in
if (user_data.email) {
console.log('trying to find user', {
email: user_data.email.toLowerCase(),
});

const res = await dbPool.query(
`SELECT user_id
FROM user_emails
WHERE email=$1
`,
[user_data.email.toLowerCase()]
);

console.log(res.rows);

if (res.rows.length === 1) {
console.log(`Found one matching user`);

user_id = res.rows[0].user_id;

user_id = res.rows?.[0]?.id;
// bingo, only one user has this email address, we link the identity to that user -- is this safe/desirable to do? 🤔
await dbPool.query(
`UPDATE user_identities
SET user_id=$1
WHERE provider=$2 AND provider_user_id=$3
`,
[user_id, options.provider, user_data.id]
);

if (!user_id) {
// TODO: verify one row was modified as expected

break; // everything is done! Identity, User, Email are all set
}
}

console.log('creating user');
// user is not already mapped, we must create a new user now, and link it to the identity
if (options.provider === 'twitch') {
const res = await dbPool.query(
`INSERT INTO users
(login, secret, type, description, display_name, profile_image_url)
Expand All @@ -102,7 +121,7 @@ class UserDAO {
RETURNING id
`,
[
ULID.ulid(), // this means the user cannot share his room via his twitch login 🥲. User should go update his login later.
user_data.login,
user_data.secret,
user_data.type,
user_data.description,
Expand All @@ -114,41 +133,65 @@ class UserDAO {
user_id = res.rows?.[0]?.id;

if (!user_id) {
throw new Error(`Unable to create twitch user ${user_data.login}`);
const res = await dbPool.query(
`INSERT INTO users
(login, secret, type, description, display_name, profile_image_url)
VALUES
($1, $2, $3, $4, $5, $6)
ON CONFLICT(login) DO NOTHING
RETURNING id
`,
[
ULID.ulid(), // this means the user cannot share his room via his twitch login 🥲. User should go update his login later to something more easily usable
user_data.secret,
user_data.type,
user_data.description,
user_data.display_name,
user_data.profile_image_url,
]
);

user_id = res.rows?.[0]?.id;

if (!user_id) {
throw new Error(
`Error: Unable to create twitch user ${user_data.login}`
);
}
}
} else if (provider === 'google') {
// TODO: including finding a login
}
} else if (provider === 'google') {
// TODO: including finding a login
}

console.log('updating user_identities', [
user_id,
options.provider,
user_data.id,
]);
console.log('updating user_identities', [
user_id,
options.provider,
user_data.id,
]);

await dbPool.query(
`UPDATE user_identities
SET user_id=$1
WHERE
provider=$2 AND provider_user_id=$3
`,
[user_id, options.provider, user_data.id]
);
}
await dbPool.query(
`UPDATE user_identities
SET user_id=$1
WHERE
provider=$2 AND provider_user_id=$3
`,
[user_id, options.provider, user_data.id]
);
}

// if email is supplied, we save it now too
if (user_data.email) {
await dbPool.query(
`INSERT INTO user_emails
(user_id, email)
VALUES
($1, $2)
ON CONFLICT DO NOTHING
`,
[user_id, user_data.email.toLowerCase()]
);
}
// if email is supplied, we save it now too
if (user_data.email) {
await dbPool.query(
`INSERT INTO user_emails
(user_id, email)
VALUES
($1, $2)
ON CONFLICT DO NOTHING
`,
[user_id, user_data.email.toLowerCase()]
);
}
} while (false);

// we force fetch to:
// 1) get the correct data shape
Expand Down
22 changes: 13 additions & 9 deletions setup/20240810.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ALTER TABLE users ALTER COLUMN login TYPE VARCHAR(40);
ALTER TABLE users RENAME COLUMN last_login TO last_login_at;
ALTER TABLE users ALTER COLUMN last_login_at SET DEFAULT NOW();

ALTER TABLE users RENAME COLUMN created_at TO created_at;
ALTER TABLE users RENAME COLUMN created_on TO created_at;
ALTER TABLE users ALTER COLUMN created_at SET DEFAULT NOW();

CREATE TYPE identity_provider AS ENUM ('google', 'twitch', 'github', 'discord', 'facebook', 'slack');
Expand Down Expand Up @@ -49,8 +49,8 @@ CREATE TABLE user_emails (
);

CREATE UNIQUE INDEX IDX_user_emails on user_emails (user_id, email);
CREATE UNIQUE INDEX IDX_user_emails_id on user_emails (user_id);
CREATE UNIQUE INDEX IDX_user_emails_email on user_emails (email);
CREATE INDEX IDX_user_emails_id on user_emails (user_id);
CREATE INDEX IDX_user_emails_email on user_emails (email);


-- the loop below
Expand All @@ -66,14 +66,18 @@ begin
order by created_at asc
loop
update users set id=new_user_id where id=f.id; -- doing this also updates ALL the scores!
insert into user_emails
(user_id, email)
values
(new_user_id, f.email);

if f.email is not null then
insert into user_emails
(user_id, email)
values
(new_user_id, lower(f.email));
end if;

insert into user_identities
(provider, provider_user_id, user_id, created_at, updated_at, last_login_at)
(provider, provider_user_id, user_id, login, created_at, updated_at, last_login_at)
values
('twitch', f.id, new_user_id, f.created_at, f.created_at, f.last_login_at);
('twitch', f.id, new_user_id, f.login, f.created_at, f.created_at, f.last_login_at);
new_user_id := new_user_id + 1;
end loop;
end;
Expand Down

0 comments on commit 58cafd9

Please sign in to comment.