-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: jwt signing #1967
feat: jwt signing #1967
Conversation
dac1175
to
f41e021
Compare
@@ -18,6 +18,7 @@ | |||
"devDependencies": { | |||
"@vue/cli-plugin-babel": "~5.0.8", | |||
"@vue/cli-service": "~5.0.8", | |||
"postcss": "^8.4.37", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems to be needed to start the example
f41e021
to
0711b7a
Compare
@davidyuk any feedback from your side? |
The motivation is unclear. Have you considered using typed data instead of JWT? Or it is not suitable for your use case? |
JWT is nice as its a widely used standard and supported by our curve, so we could expose it (and e.g. use it in superhero product login) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about motivation, seems that JWTs signed by a centralized provider are more useful/compatible that self-signed JWTs.
src/account/Base.ts
Outdated
message: object, | ||
options?: { | ||
aeppOrigin?: string; | ||
expireAt?: number; // unixtime ms |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would accept it as message.exp
src/utils/crypto.ts
Outdated
): Promise<string> { | ||
const header = { alg: 'EdDSA', typ: 'JWT' }; | ||
const payload = { ...message, exp: expireAt }; | ||
const body = `${base64url.encode(JSON.stringify(header))}.${base64url.encode(JSON.stringify(payload))}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
buffer.toString('base64url')
should work instead, it is available since node@16 nodejs/node@f8ab632
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, you can use canonicalize
(sdk already uses it in typed-data) instead JSON.stringify
to avoid fluctuations of JWT because of different key order
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice, didn't know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
buffer.toString('base64url')
doesn't seem to work once built in the browser
src/utils/crypto.ts
Outdated
@@ -142,6 +143,18 @@ export function sign(data: string | Uint8Array, privateKey: string | Uint8Array) | |||
return nacl.sign.detached(Buffer.from(data), Buffer.from(privateKey)); | |||
} | |||
|
|||
export async function signJWT( | |||
message: object, | |||
expireAt: number, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks to be an extra argument 🤷♀️
src/utils/crypto.ts
Outdated
message: object, | ||
expireAt: number, | ||
privateKey: Uint8Array, | ||
): Promise<string> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it also can return
Promise<`${string}.${string}.${string}`>
src/account/Rpc.ts
Outdated
override async signMessageJWT(message: object, options?: { | ||
expireAt?: number; | ||
}): Promise<string> { | ||
const { signature } = await this._rpcClient |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
technically it is a payload + signature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
renamed it
src/account/Base.ts
Outdated
@@ -54,6 +54,15 @@ export default abstract class AccountBase { | |||
}, | |||
): Promise<Uint8Array>; | |||
|
|||
abstract signMessageJWT( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
abstract signMessageJWT( | |
abstract signJWT( |
"Message" looks not necessary, because T already is "Token"
src/account/Memory.ts
Outdated
const secretKey = secretKeys.get(this); | ||
if (secretKey == null) throw new UnexpectedTsError(); | ||
const expireAt = options?.expireAt === undefined | ||
? new Date().getTime() + 30 * 60 * 1000 // default to 30min |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
? new Date().getTime() + 30 * 60 * 1000 // default to 30min | |
? Date.now() + 30 * 60 * 1000 // default to 30min |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you think its ok to keep the default expiry there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, there are different account types and I'm not sure each of them should define it. But here is not much logic to extract to "generate jwt" function. We can do nothing here and allow the wallet to handle jwt parameters (like not allowing signing a too general token).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed it :)
expireAt: number, | ||
privateKey: Uint8Array, | ||
): Promise<string> { | ||
const header = { alg: 'EdDSA', typ: 'JWT' }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EdDSA
is a correct value
nacl.sign
implements ed25519 https://www.npmjs.com/package/tweetnacl- ed25519 the same as EdDSA https://stackoverflow.com/a/66894047/6176994
const header = { alg: 'EdDSA', typ: 'JWT' }; | ||
const payload = { ...message, exp: expireAt }; | ||
const body = `${base64url.encode(JSON.stringify(header))}.${base64url.encode(JSON.stringify(payload))}`; | ||
const signature = sign(body, privateKey); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Insignificant concern is that all payloads we signed before have a special not overlapping prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we add sub_jwk
in the payload the risk should be even smaller
src/utils/crypto.ts
Outdated
privateKey: Uint8Array, | ||
): Promise<string> { | ||
const header = { alg: 'EdDSA', typ: 'JWT' }; | ||
const payload = { ...message, exp: expireAt }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to store signer address somehow in message or header. It can be
sub_jwk Public key used to check the signature of an ID Token
https://www.iana.org/assignments/jwt/jwt.xhtml
Or something own like ethereum/EIPs#1341 (Issuer field?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree!
be6de22
to
49d1fc3
Compare
49d1fc3
to
4ad056b
Compare
Currently based on the latest 13.2.2 release, thus the conflicts to try it easily with the current superhero wallet implementation.
related superhero wallet integration superhero-com/superhero-wallet#2875