Skip to content

Commit

Permalink
feat(auth): allow URL ID and display_name to be edited
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheeg committed Aug 11, 2024
1 parent 14444f7 commit 619ac82
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 31 deletions.
10 changes: 6 additions & 4 deletions daos/UserDAO.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,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 to something more easily usable
ULID.ulid().toLowerCase(), // 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,
Expand Down Expand Up @@ -370,12 +370,14 @@ class UserDAO {
}

async updateProfile(user_id, update) {
await dbPool.query(
const result = await dbPool.query(
`UPDATE users
SET dob=$1, country_code=$2, city=$3, style=$4, interests=$5, timezone=$6
WHERE id=$7;
SET login=$1, display_name=$2, dob=$3, country_code=$4, city=$5, style=$6, interests=$7, timezone=$8
WHERE id=$9;
`,
[
update.login,
update.display_name,
update.dob,
update.country_code,
update.city,
Expand Down
18 changes: 5 additions & 13 deletions routes/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,7 @@ router.get('/twitch/callback', async (req, res) => {

req.session.save(() => {
console.log('Stored session user as', req.session.user);

if (req.session.auth_success_redirect) {
res.redirect(req.session.auth_success_redirect);
} else {
res.redirect('/');
}
res.redirect(req.session.auth_success_redirect || '/');
});
} catch (err) {
console.error(`Error when processing Twitch callback`);
Expand Down Expand Up @@ -224,12 +219,14 @@ router.get('/google/callback', async (req, res) => {
const payload = ticket.getPayload();

// mimic twitch shape
const login = ULID.ulid().toLowerCase();
const user_object = {
id: payload.sub,
secret: ULID.ulid(),
type: '',
description: '',
login: ULID.ulid(),
login,
display_name: login.slice(-10).toUpperCase(),
email: payload.email,
profile_image_url: payload.picture,
};
Expand Down Expand Up @@ -263,12 +260,7 @@ router.get('/google/callback', async (req, res) => {

req.session.save(() => {
console.log('Stored session user as', req.session.user);

if (req.session.auth_success_redirect) {
res.redirect(req.session.auth_success_redirect);
} else {
res.redirect('/');
}
res.redirect(req.session.auth_success_redirect || '/');
});
} catch (error) {
console.error('Error during authentication:', error);
Expand Down
35 changes: 30 additions & 5 deletions routes/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,32 @@ router.post('/update_profile', express.json(), async (req, res) => {
const errors = [];
const update = {};

if (req.body.login) {
if (/^[a-z0-9_]+$/.test(req.body.login.trim())) {
update.login = req.body.login.trim().toLowerCase();
} else {
errors.push('URL ID is not valid');
}
}

if (req.body.display_name) {
update.display_name = req.body.display_name;
}

if (/^(das|tap|roll|hybrid)$/i.test(req.body.style)) {
update.style = req.body.style.toLowerCase();
} else {
errors.push('Style is not valid');
}

const code = (req.body.country_code || '').toUpperCase();

// crude string test first, and then date test
if (
/^(19|20)\d{2}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/.test(req.body.dob)
) {
const age = getAge(req.body.dob);

// arbitrary age boundaries
if (age < 10 || age > 100) {
if (age < 5 || age > 120) {
update.dob = null;
// errors.push('Dob is not valid');
} else {
Expand All @@ -102,6 +112,8 @@ router.post('/update_profile', express.json(), async (req, res) => {
// errors.push('Dob is not valid');
}

const code = (req.body.country_code || '').toUpperCase();

if (code && countries.some(country => country.code === code)) {
update.country_code = code;
} else {
Expand Down Expand Up @@ -133,8 +145,21 @@ router.post('/update_profile', express.json(), async (req, res) => {
console.log({ errors });
res.status(400).json({ errors });
} else {
await UserDAO.updateProfile(req.session.user.id, update);
res.status(200).json({});
try {
await UserDAO.updateProfile(req.session.user.id, update);
res.status(200).json({});
} catch (err) {
if (/violates.*twitch_users_login_key/.test(err.message)) {
errors.push(
`URL ID "${req.body.login}" is already in use - pick another ID`
);
res.status(400).json({ errors });
} else {
res
.status(500)
.json({ errors: ['unexpected error - please try again later'] });
}
}
}
});

Expand Down
28 changes: 19 additions & 9 deletions views/settings.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,17 @@
}
.fieldset {
background-color: #fff;
border-radius: 6px;
box-shadow: 0 0.5em 1em -0.125em rgba(10,10,10,.1), 0 0 0 1px rgba(10,10,10,.02);
color: #4a4a4a;
display: block;
padding: 1.25rem;
border: 1px solid #ccc
}
.fieldset > legend {
color: #363636;
display: block;
font-size: 1rem;
font-weight: 700;
background-color: #fff;
padding: 0 5px;
width: max-content;
border: 0 none
Expand Down Expand Up @@ -65,16 +61,16 @@
<p>In competition layouts, profile information can be displayed on the board by a game host.</p>

<div class="field">
<label class="label">ID <em>(from <a href="https://www.twitch.tv/settings/profile" target="twitch">Twitch</a>)</em></label>
<label class="label">URL ID <em>(alphanumerical and underscore only)</em></label>
<div class="control">
<input class="input" type="text" value="<%= db_user.login %>" readonly="readonly" />
<input id="login" class="input" type="text" value="<%= db_user.login %>" />
</div>
</div>

<div class="field">
<label class="label">Display Name <em>(from <a href="https://www.twitch.tv/settings/profile" target="twitch">Twitch</a>)</em></label>
<label class="label">Display Name <em>(keep below 10 characters if possible)</em></label>
<div class="control">
<input class="input" type="text" value="<%= db_user.display_name %>" readonly="readonly" />
<input id="display_name" class="input" type="text" value="<%= db_user.display_name %>" />
</div>
</div>

Expand Down Expand Up @@ -231,6 +227,8 @@
</section>
<script type="text/javascript">
const profile_nodes = {
login: document.getElementById('login'),
display_name: document.getElementById('display_name'),
dob: document.getElementById('dob'),
country_code: document.getElementById('country_code'),
city: document.getElementById('city'),
Expand Down Expand Up @@ -267,10 +265,20 @@ document.getElementById('update_profile_btn').addEventListener('click', async ()
const data = {};
for (const [key, node] of Object.entries(profile_nodes)) {
data[key] = node.value;
data[key] = (node.value || '').trim();
}
// validate input client side
const errors = []
if (!/^[a-z0-9_]+$/.test(data.login)) {
errors.push('Invalid format for URL ID - use alphanumerical and underscrore only');
}
try {
if (errors.length) {
throw new Error('Unable to submit');
}
const response = await fetch('/settings/update_profile', {
method: 'POST',
cache: 'no-cache',
Expand Down Expand Up @@ -298,6 +306,8 @@ document.getElementById('update_profile_btn').addEventListener('click', async ()
console.error(err);
feedback.classList.add('is_danger');
feedback.textContent = `Profile could not be updated`;
if (errors.length) feedback.textContent += `: ${errors.join(', ')}`;
}
feedback.style.display = 'block';
Expand Down

0 comments on commit 619ac82

Please sign in to comment.