Skip to content

Commit 96c3ad5

Browse files
accept proof of invitation from a new user only if the username is unique among team members
1 parent 6ab7e9c commit 96c3ad5

File tree

3 files changed

+55
-3
lines changed

3 files changed

+55
-3
lines changed

packages/auth/src/team/Team.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,14 +554,29 @@ export class Team extends EventEmitter<TeamEvents> {
554554
return invitations.validate(proof, invitation)
555555
}
556556

557+
/** Check if username is not used by any other person within the team. */
558+
public validateUserName = (userName: string) => {
559+
const memberWithSameUserName = this.members().find(member =>
560+
member.userName.toLowerCase() == userName.toLowerCase()
561+
)
562+
if (memberWithSameUserName != undefined) {
563+
return invitations.fail('Username is not unique within the team.')
564+
}
565+
566+
return VALID;
567+
}
568+
557569
/** An existing team member calls this to admit a new member & their device to the team based on proof of invitation */
558570
public admitMember = (
559571
proof: ProofOfInvitation,
560572
memberKeys: Keyset | KeysetWithSecrets, // We accept KeysetWithSecrets here to simplify testing - in practice we'll only receive Keyset
561573
userName: string // The new member's desired user-facing name
562574
) => {
563-
const validation = this.validateInvitation(proof)
564-
if (!validation.isValid) throw validation.error
575+
const invitationValidation = this.validateInvitation(proof)
576+
if (!invitationValidation.isValid) throw invitationValidation.error
577+
578+
const userNameValidation = this.validateUserName(userName)
579+
if (!userNameValidation.isValid) throw userNameValidation.error
565580

566581
const { id } = proof
567582

packages/auth/src/team/test/invitations.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ describe('Team', () => {
8080
expect(bobsTeam.memberIsAdmin(bob.userId)).toBe(false)
8181

8282
// 👳🏽‍♂️ Charlie shows 👨🏻‍🦲 Bob his proof of invitation
83-
bobsTeam.admitMember(proofOfInvitation, charlie.user.keys, bob.user.userName)
83+
bobsTeam.admitMember(proofOfInvitation, charlie.user.keys, charlie.user.userName)
8484

8585
// 👍👳🏽‍♂️ Charlie is now on the team
8686
expect(bobsTeam.has(charlie.userId)).toBe(true)
@@ -260,6 +260,27 @@ describe('Team', () => {
260260
expect(submitBadProof).toThrow('Signature provided is not valid')
261261
})
262262

263+
it("won't accept proof of invitation with a username that is not unique", () => {
264+
const { alice, bob } = setup('alice', { user: 'bob', member: false })
265+
266+
// 👩🏾 Alice invites 👨🏻‍🦲 Bob by sending him a random secret key
267+
const { seed } = alice.team.inviteMember()
268+
269+
// 👨🏻‍🦲 Bob accepts the invitation
270+
const proofOfInvitation = generateProof(seed)
271+
272+
// 👨🏻‍🦲 Bob shows 👩🏾 Alice his proof of invitation, but uses Alice's username
273+
const tryToAdmitBob = () => {
274+
alice.team.admitMember(proofOfInvitation, bob.user.keys, alice.user.userName)
275+
}
276+
277+
// 👎 But the invitation is rejected because it the username is not unique
278+
expect(tryToAdmitBob).toThrowError('Username is not unique within the team.')
279+
280+
// ❌ 👨🏻‍🦲 Bob is not on the team
281+
expect(alice.team.has(bob.userId)).toBe(false)
282+
})
283+
263284
describe('devices', () => {
264285
it('creates and accepts an invitation for a device', () => {
265286
const { alice: aliceLaptop } = setup('alice')

packages/auth/src/team/validate.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,22 @@ const validators: TeamStateValidatorSet = {
112112
}
113113
return VALID
114114
},
115+
116+
/** Check if the username is not used by any other person within the team */
117+
mustBeUniqueUsername(...args) {
118+
const [previousState, link] = args
119+
if (link.body.type === 'ADMIT_MEMBER') {
120+
const { userName } = link.body.payload
121+
const memberWithSameUserName = previousState.members.find(member =>
122+
member.userName.toLowerCase() == userName.toLowerCase()
123+
)
124+
125+
if (memberWithSameUserName != undefined) {
126+
return fail('Username is not unique within the team.', ...args)
127+
}
128+
}
129+
return VALID
130+
},
115131
}
116132

117133
const fail = (message: string, previousState: TeamState, link: TeamLink) => {

0 commit comments

Comments
 (0)