Skip to content

Feat/seedless refresh token #15

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

Draft
wants to merge 6 commits into
base: feat/toprf-sdk-update
Choose a base branch
from

Conversation

tuna1207
Copy link
Member

Explanation

Add refresh token handling to SeedlessOnboardingController

  • store refresh token in vault
  • check for token expired in toprf call, refresh token and retry

References

Changelog

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

@tuna1207 tuna1207 force-pushed the feat/seedless-refresh-token branch from aaad9ed to 6887c51 Compare May 25, 2025 09:25
@@ -110,6 +115,10 @@ const seedlessOnboardingMetadata: StateMetadata<SeedlessOnboardingControllerStat
persist: true,
anonymous: true,
},
refreshToken: {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it be stored inside the vault as encrypted?

Copy link
Member Author

@tuna1207 tuna1207 May 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lwin-kyaw ah yes, it is stored inside the vault, this is only stored in state for convienience of usage in multiple places, it's not persisted.
It's only restored to state after unlockVaultAndGetBackupEncKey similar to other vault secret

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I saw, only nodeAuthTokens (and authPubKey?) is stored inside both vault and state. I think we should remove it from the vault, if we intent to use as plain text.

And for the refreshToken, I don't see any places that is used without needing to decrypt the vault. So, I think it should be only inside the vault.

}) {
return await this.#withControllerLock(async () => {
const doAuthenticate = async () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we refactor this inner function to class method, (private maybe), for the better readibility?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lwin-kyaw hmm i'm not sure about this, since the method is already authenticate and keeping the logic in other methods would actually make it harder to follow i think, what do you think ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that this is due to the skipLock param. May I know why it is needed?

@@ -196,8 +212,10 @@ export class SeedlessOnboardingController<EncryptionKey> extends BaseController<
userId: string;
groupedAuthConnectionId?: string;
socialLoginEmail?: string;
refreshToken: string;
skipLock?: boolean;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to skip lock?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lwin-kyaw this one is for executeWithRefreshToken to authenticate again when refreshing the token since executeWithRefreshToken should already be inside a lock

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this method, executeWithRefreshToken in the controller?

Anyway, I don't think we should skip the lock, the lock state represents the wallet access and also synced with other controllers.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we don't need to skip the lock. If the wallet is unlocked (user has logged in), the isLockUnlocked is already set to true.
And the refresh token can be retrieved from the vault (either with password or cachedEncryptionKey in state), and can proceed to the authenticateWithRefreshToken

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lwin-kyaw yes, by default we don't skip the lock, we skip only when it's already inside another lock

@@ -845,6 +900,7 @@ export class SeedlessOnboardingController<EncryptionKey> extends BaseController<
nodeAuthTokens: NodeAuthTokens;
toprfEncryptionKey: Uint8Array;
toprfAuthKeyPair: KeyPair;
refreshToken?: string;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I know what's the use for the refreshToken here? Seems like it's never used.

And refreshToken can be retrieved from the encrypted vault?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lwin-kyaw yes it's in the vault, this function decrypt the vault and return the refreshToken from the vault

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we should remove this from the method params?

@@ -1037,6 +1103,7 @@ export class SeedlessOnboardingController<EncryptionKey> extends BaseController<
authTokens: this.state.nodeAuthTokens,
toprfEncryptionKey,
toprfAuthKeyPair,
refreshToken: this.state.refreshToken,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be provided from the method params, so that we don't need to store it in the state.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lwin-kyaw we currently only receive refreshToken from authenticate method, and these methods are using refreshToken in #createNewVaultWithAuthData: createToprfKeyAndBackupSeedPhrase, fetchAllSeedPhrases, changePassword, syncLatestGlobalPassword
i'm not sure if it's a good idea to always redo #unlockVaultAndGetBackupEncKey in all of the above methods to pass refreshToken in again

*/
async refreshNodeAuthTokens(): Promise<void> {
this.#assertIsUnlocked();
const { refreshToken } = this.state;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be retrieved from the vault?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lwin-kyaw yes, in this case the vault is decrypted and refreshToken is set in temporal state

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants