You should probably use (and check) the iss
and aud
fields in the jwt to indicate which of your websites issued the token for which one (probably itself). While you only use this on one website, it doesn't matter much, but with multiple websites it gets more complicated, it's always a good idea to include in messages who sent it to whom.
Consider the max length that will fit in an url, a jwt base64-encoded (I guess) can get long (but I think the above scenario would work, so with this data I don't think it's too long already).
Also this allows a known-plaintext attack on the jwt secret as you will issue a new jwt and send it to any email address. Probably you could have some kind of rate limiting on the /register
endpoint to mitigate this. This might also have a denial of service aspect as signing jwts and sending emails can be fairly resource heavy.
And finally, the threat you mentioned in the last paragraph (a user sending his own link to somebody else so they register with the attacker's email address) is an excellent one. Only displaying the email address might not be enough mitigation, it totally depends on your scenario, even the visuals how it would be displayed or acknowledged by the user I guess. This is certainly one way to attack your users, one stronger mitigation is if they need to enter their email address again and you compare it to the one in the jwt - but that is in exchange for worse ux, the tradeoff is for you to decide. You could for example also explicitly have the user just confirm the email address if you think that's enough for your application, it very well can be.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…