In terms of the user experience, you have it down:
- User requests invitation
- Someone or a process grants the invitation and an e-mail is sent to the user
- User clicks a link, is asked to enter sign-up details
For referral sign-ups:
- User requests invitation for another user
- Other user is automatically granted invitation and sent e-mail
- Other user clicks a link, is asked to enter sign-up details
In terms of implementation, however, you lost me at "reversible hash".
In short, I'd use a one-time token (arbitrary string) for the invitation validation, and have a table to store the invitations.
The table would look something like:
- InvitationId - identity
- Email - string
- Created - datetime
- Token - string
- Expiry - datetime
The two processes are actually quite similar, so I'll cover both pieces together.
1. Invitation request
User enters their e-mail, requesting an invitation, or as an existing user, enters an e-mail for another user.
In either case, this would make a new entry in the invitations table, populating the Email and Created fields.
In the case of an existing user sending an invitation, it would immediately invoke the next part:
2. Granting invitation
When someone or some process decides to grant the invitation, it would do two things:
- Generate a random token and Expiry date and update the table
- Send an e-mail containing a sign-up URL with the token
There are many ways to generate a random token, but something simple could be the the sha1 of a Guid, so you end up with something like
You'd then send the link as
The first thing to do, of course, is validate the received token: does it exist and hasn't yet expired?
If everything is good, then you allow the user to sign up, and upon completion, remove the invitation entry.
The token-based approach is much more secure, since it cannot be "cracked". Someone would have to use brute force to guess every possible token, and you could mitigate that by locking the e-mail address for the rest of the sign-up to the one in the invitation.
Keeping this separate from what will eventually be the "non-beta" signup process is also clean in terms of coming out of beta: you should be able to remove this table and related code fairly easily, in contrast to having extra fields stored with the regular user accounts. Think of the token more like a permission to create a new user entry.
I tend to not implement more than is necessary, and not knowing your use case it's hard to speculate on some other things you might want to implement:
You may want to keep invitations for some reason (reporting?), so you could use a status field or something similar, and/or date fields indicating the various actions
You may wish to re-send e-mails, and so it may be useful to store fields indicating when e-mails were sent (and how many)
You may wish to track sign-up source (eg, direct from a URL, or another user) and you could store that in an additional field when you create the invitation entry, and perhaps even transfer it to the account once they sign-up.