diff --git a/packages/services/src/Domain/Session/SessionsClientInterface.ts b/packages/services/src/Domain/Session/SessionsClientInterface.ts index ed70bc0a4..b7303fbdf 100644 --- a/packages/services/src/Domain/Session/SessionsClientInterface.ts +++ b/packages/services/src/Domain/Session/SessionsClientInterface.ts @@ -2,13 +2,12 @@ import { UserRegistrationResponseBody } from '@standardnotes/api' import { ProtocolVersion } from '@standardnotes/common' import { SNRootKey } from '@standardnotes/encryption' import { RootKeyInterface } from '@standardnotes/models' -import { ClientDisplayableError, SessionBody, SignInResponse, User, HttpResponse } from '@standardnotes/responses' +import { SessionBody, SignInResponse, User, HttpResponse } from '@standardnotes/responses' import { Base64String } from '@standardnotes/sncrypto-common' import { SessionManagerResponse } from './SessionManagerResponse' export interface SessionsClientInterface { - createDemoShareToken(): Promise populateSessionFromDemoShareToken(token: Base64String): Promise getUser(): User | undefined isCurrentSessionReadOnly(): boolean | undefined diff --git a/packages/snjs/lib/Services/Session/SessionManager.ts b/packages/snjs/lib/Services/Session/SessionManager.ts index 23c5cf071..8e5a9a546 100644 --- a/packages/snjs/lib/Services/Session/SessionManager.ts +++ b/packages/snjs/lib/Services/Session/SessionManager.ts @@ -45,7 +45,7 @@ import { } from '@standardnotes/responses' import { CopyPayloadWithContentOverride } from '@standardnotes/models' import { LegacySession, MapperInterface, Result, Session, SessionToken } from '@standardnotes/domain-core' -import { KeyParamsFromApiResponse, SNRootKeyParams, SNRootKey, CreateNewRootKey } from '@standardnotes/encryption' +import { KeyParamsFromApiResponse, SNRootKeyParams, SNRootKey } from '@standardnotes/encryption' import { Subscription } from '@standardnotes/security' import * as Common from '@standardnotes/common' @@ -640,32 +640,6 @@ export class SNSessionManager } } - public async createDemoShareToken(): Promise { - const session = this.getSession() - if (!session) { - return new ClientDisplayableError('Cannot generate share token without active session') - } - if (!(session instanceof Session)) { - return new ClientDisplayableError('Cannot generate share token with non-token session') - } - - const keyParams = (await this.protocolService.getRootKeyParams()) as SNRootKeyParams - - const payload: ShareToken = { - accessToken: session.accessToken.value, - refreshToken: session.refreshToken.value, - accessExpiration: session.accessToken.expiresAt, - refreshExpiration: session.refreshToken.expiresAt, - readonlyAccess: true, - masterKey: this.protocolService.getRootKey()?.masterKey as string, - keyParams: keyParams.content, - user: this.getSureUser(), - host: this.apiService.getHost(), - } - - return this.protocolService.crypto.base64Encode(JSON.stringify(payload)) - } - private decodeDemoShareToken(token: Base64String): ShareToken { const jsonString = this.protocolService.crypto.base64Decode(token) return JSON.parse(jsonString) @@ -674,28 +648,7 @@ export class SNSessionManager public async populateSessionFromDemoShareToken(token: Base64String): Promise { const sharePayload = this.decodeDemoShareToken(token) - const rootKey = CreateNewRootKey({ - masterKey: sharePayload.masterKey, - keyParams: sharePayload.keyParams, - version: sharePayload.keyParams.version, - }) - - const user = sharePayload.user - - const sessionOrError = this.createSession( - sharePayload.accessToken, - sharePayload.accessExpiration, - sharePayload.refreshToken, - sharePayload.refreshExpiration, - sharePayload.readonlyAccess, - ) - if (sessionOrError.isFailed()) { - console.error(sessionOrError.getError()) - - return - } - - await this.populateSession(rootKey, user, sessionOrError.getValue(), sharePayload.host) + await this.signIn(sharePayload.email, sharePayload.password, false, true) } private async populateSession( diff --git a/packages/snjs/lib/Services/Session/ShareToken.ts b/packages/snjs/lib/Services/Session/ShareToken.ts index 16a4932e6..1f4e93594 100644 --- a/packages/snjs/lib/Services/Session/ShareToken.ts +++ b/packages/snjs/lib/Services/Session/ShareToken.ts @@ -1,10 +1,6 @@ -import { User } from '@standardnotes/responses' -import { AnyKeyParamsContent } from '@standardnotes/common' import { RawSessionPayload } from './Sessions/Types' export type ShareToken = RawSessionPayload & { - masterKey: string - keyParams: AnyKeyParamsContent - user: User - host: string + email: string + password: string } diff --git a/packages/snjs/mocha/session-sharing.test.js b/packages/snjs/mocha/session-sharing.test.js deleted file mode 100644 index 65ef5fb6e..000000000 --- a/packages/snjs/mocha/session-sharing.test.js +++ /dev/null @@ -1,113 +0,0 @@ -/* eslint-disable no-undef */ -import * as Factory from './lib/factory.js' -chai.use(chaiAsPromised) -const expect = chai.expect - -describe('session sharing', function () { - this.timeout(Factory.TenSecondTimeout) - - beforeEach(async function () { - localStorage.clear() - - this.context = await Factory.createAppContext() - await this.context.launch() - - this.application = this.context.application - this.email = this.context.email - this.password = this.context.password - - await Factory.registerUserToApplication({ - application: this.application, - email: this.email, - password: this.password, - }) - }) - - afterEach(async function () { - await this.context.deinit() - this.context = undefined - this.application = undefined - localStorage.clear() - }) - - it('share token payloads should include neccessary params', async function () { - const token = await this.application.sessions.createDemoShareToken() - const payload = await this.application.sessions.decodeDemoShareToken(token) - - const expectedKeys = [ - 'accessToken', - 'refreshToken', - 'accessExpiration', - 'refreshExpiration', - 'readonlyAccess', - 'masterKey', - 'keyParams', - 'user', - 'host', - ] - - for (const key of expectedKeys) { - expect(payload[key]).to.not.be.undefined - } - }) - - it('populating session from share token should allow pulling in new items', async function () { - const token = await this.application.sessions.createDemoShareToken() - - await Factory.createSyncedNote(this.application, 'demo title', 'demo text') - - const otherContext = await Factory.createAppContext() - await otherContext.launch() - - const otherApplication = otherContext.application - - expect(otherApplication.items.getItems(ContentType.Note).length).to.equal(0) - - await otherApplication.sessions.populateSessionFromDemoShareToken(token) - - await otherApplication.sync.sync() - - const notes = otherApplication.items.getItems(ContentType.Note) - - expect(notes.length).to.equal(1) - - const note = notes[0] - - expect(note.title).to.equal('demo title') - expect(note.text).to.equal('demo text') - - await otherContext.deinit() - }) - - /** - * Demo session tokens can only be created manually via raw SQL entry on the DB side. - * There is no API to create share tokens. Therefore, the share token below is made from - * a copy of the master session, which is not readonly. - */ - it.skip('populating session from share token should not allow making changes', async function () { - const token = await this.application.sessions.createDemoShareToken() - - await Factory.createSyncedNote(this.application, 'demo title', 'demo text') - - const otherContext = await Factory.createAppContext() - await otherContext.launch() - - const otherApplication = otherContext.application - - await otherApplication.sessions.populateSessionFromDemoShareToken(token) - - await otherApplication.sync.sync() - - const note = otherApplication.items.getItems(ContentType.Note)[0] - - const syncResponse = otherContext.awaitNextSyncEvent(SyncEvent.SingleRoundTripSyncCompleted) - - await otherApplication.mutator.changeAndSaveItem(note, (mutator) => { - mutator.title = 'unauthorized change' - }) - - const result = await syncResponse - - expect(result.rawResponse.unsaved_items.length).to.equal(1) - }) -}) diff --git a/packages/snjs/mocha/test.html b/packages/snjs/mocha/test.html index 826cca630..715d4459a 100644 --- a/packages/snjs/mocha/test.html +++ b/packages/snjs/mocha/test.html @@ -84,7 +84,6 @@ - diff --git a/packages/web/src/javascripts/Application/Application.ts b/packages/web/src/javascripts/Application/Application.ts index c87b01f51..cf2cb23c8 100644 --- a/packages/web/src/javascripts/Application/Application.ts +++ b/packages/web/src/javascripts/Application/Application.ts @@ -426,6 +426,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter this.getViewControllerManager().accountMenuController.setShow(true) } + hideAccountMenu(): void { + this.getViewControllerManager().accountMenuController.setShow(false) + } + geDefaultEditorIdentifier(currentTag?: SNTag): EditorIdentifier { return ( currentTag?.preferences?.editorIdentifier || diff --git a/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx b/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx index 7155f3f7a..90699d3a8 100644 --- a/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx +++ b/packages/web/src/javascripts/Components/ApplicationView/ApplicationView.tsx @@ -90,7 +90,11 @@ const ApplicationView: FunctionComponent = ({ application, mainApplicatio return } - void application.user.populateSessionFromDemoShareToken(token) + const status = application.status.addMessage('Preparing demo...') + void application.user.populateSessionFromDemoShareToken(token).then(() => { + application.status.removeMessage(status) + application.hideAccountMenu() + }) }, [application]) const onAppLaunch = useCallback(() => {