diff --git a/packages/api/src/Domain/Api/ApiVersion.ts b/packages/api/src/Domain/Api/ApiVersion.ts index b69ba8f1aa2..f91972408f4 100644 --- a/packages/api/src/Domain/Api/ApiVersion.ts +++ b/packages/api/src/Domain/Api/ApiVersion.ts @@ -1,3 +1,4 @@ export enum ApiVersion { v0 = '20200115', + v1 = '20240226', } diff --git a/packages/api/src/Domain/Client/Auth/AuthApiService.ts b/packages/api/src/Domain/Client/Auth/AuthApiService.ts index 520d5bdb494..2c9fd86454e 100644 --- a/packages/api/src/Domain/Client/Auth/AuthApiService.ts +++ b/packages/api/src/Domain/Client/Auth/AuthApiService.ts @@ -50,7 +50,7 @@ export class AuthApiService implements AuthApiServiceInterface { try { const response = await this.authServer.recoveryKeyParams({ - api_version: ApiVersion.v0, + api_version: ApiVersion.v1, code_challenge: dto.codeChallenge, recovery_codes: dto.recoveryCodes, username: dto.username, @@ -78,7 +78,7 @@ export class AuthApiService implements AuthApiServiceInterface { try { const response = await this.authServer.signInWithRecoveryCodes({ - api_version: ApiVersion.v0, + api_version: ApiVersion.v1, code_verifier: dto.codeVerifier, password: dto.password, recovery_codes: dto.recoveryCodes, diff --git a/packages/api/src/Domain/Client/Subscription/SubscriptionApiService.ts b/packages/api/src/Domain/Client/Subscription/SubscriptionApiService.ts index 27bb1ccaceb..ff2debb1da9 100644 --- a/packages/api/src/Domain/Client/Subscription/SubscriptionApiService.ts +++ b/packages/api/src/Domain/Client/Subscription/SubscriptionApiService.ts @@ -36,7 +36,7 @@ export class SubscriptionApiService implements SubscriptionApiServiceInterface { try { const response = await this.subscriptionServer.listInvites({ - [ApiEndpointParam.ApiVersion]: ApiVersion.v0, + [ApiEndpointParam.ApiVersion]: ApiVersion.v1, }) return response @@ -56,7 +56,7 @@ export class SubscriptionApiService implements SubscriptionApiServiceInterface { try { const response = await this.subscriptionServer.cancelInvite({ - [ApiEndpointParam.ApiVersion]: ApiVersion.v0, + [ApiEndpointParam.ApiVersion]: ApiVersion.v1, inviteUuid, }) @@ -77,7 +77,7 @@ export class SubscriptionApiService implements SubscriptionApiServiceInterface { try { const response = await this.subscriptionServer.invite({ - [ApiEndpointParam.ApiVersion]: ApiVersion.v0, + [ApiEndpointParam.ApiVersion]: ApiVersion.v1, identifier: inviteeEmail, }) diff --git a/packages/api/src/Domain/Client/User/UserApiService.ts b/packages/api/src/Domain/Client/User/UserApiService.ts index c73f727fa04..a45b5920ad9 100644 --- a/packages/api/src/Domain/Client/User/UserApiService.ts +++ b/packages/api/src/Domain/Client/User/UserApiService.ts @@ -72,7 +72,7 @@ export class UserApiService implements UserApiServiceInterface { try { const response = await this.userServer.register({ - [ApiEndpointParam.ApiVersion]: ApiVersion.v0, + [ApiEndpointParam.ApiVersion]: ApiVersion.v1, password: registerDTO.serverPassword, email: registerDTO.email, ephemeral: registerDTO.ephemeral, @@ -92,7 +92,7 @@ export class UserApiService implements UserApiServiceInterface { try { const response = await this.userServer.update({ - [ApiEndpointParam.ApiVersion]: ApiVersion.v0, + [ApiEndpointParam.ApiVersion]: ApiVersion.v1, user_uuid: updateDTO.userUuid, }) diff --git a/packages/api/src/Domain/Http/HttpService.ts b/packages/api/src/Domain/Http/HttpService.ts index 68b6bc79bed..a33da5fcd53 100644 --- a/packages/api/src/Domain/Http/HttpService.ts +++ b/packages/api/src/Domain/Http/HttpService.ts @@ -9,9 +9,11 @@ import { HttpResponse, HttpResponseMeta, isErrorResponse, + ApiEndpointParam, } from '@standardnotes/responses' import { HttpServiceInterface } from './HttpServiceInterface' +import { ApiVersion } from '../Api' import { Paths } from '../Server/Auth/Paths' import { SessionRefreshResponseBody } from '../Response/Auth/SessionRefreshResponseBody' import { FetchRequestHandler } from './FetchRequestHandler' @@ -145,6 +147,10 @@ export class HttpService implements HttpServiceInterface { await sleep(this.__latencySimulatorMs, true) } + httpRequest.params = httpRequest.params + ? this.params(httpRequest.params as Record) + : undefined + const isRefreshRequest = httpRequest.url === joinPaths(this.host, Paths.v1.refreshSession) if (this.inProgressRefreshSessionPromise && !isRefreshRequest) { await this.inProgressRefreshSessionPromise @@ -236,4 +242,15 @@ export class HttpService implements HttpServiceInterface { return true } + + private params(inParams: Record): HttpRequestParams { + const params = { + ...inParams, + ...{ + [ApiEndpointParam.ApiVersion]: ApiVersion.v1, + }, + } + + return params + } } diff --git a/packages/snjs/lib/Services/Api/ApiService.ts b/packages/snjs/lib/Services/Api/ApiService.ts index 3ff24fb22ee..54848fac06e 100644 --- a/packages/snjs/lib/Services/Api/ApiService.ts +++ b/packages/snjs/lib/Services/Api/ApiService.ts @@ -81,7 +81,7 @@ import { Strings } from '@Lib/Strings' import { AnyFeatureDescription } from '@standardnotes/features' /** Legacy api version field to be specified in params when calling v0 APIs. */ -const V0_API_VERSION = '20200115' +const V0_API_VERSION = '20240226' type InvalidSessionObserver = (revoked: boolean) => void diff --git a/packages/snjs/mocha/session.test.js b/packages/snjs/mocha/session.test.js index cb21d467084..e15b8350375 100644 --- a/packages/snjs/mocha/session.test.js +++ b/packages/snjs/mocha/session.test.js @@ -96,8 +96,15 @@ describe('server session', function () { // After the above sync request is completed, we obtain the session information. const sessionAfterSync = application.legacyApi.getSession() - expect(sessionBeforeSync.accessToken.value).to.not.equal(sessionAfterSync.accessToken.value) - expect(sessionBeforeSync.refreshToken.value).to.not.equal(sessionAfterSync.refreshToken.value) + /** + * Access token and refresh token values in the new API version (20240226) represent the session uuid. + * So they should stay the same as they were since we are operating on the same session. + * + * The actual token values are stored in cookies indexed by the session uuid and are not accessible to the client. + */ + expect(sessionBeforeSync.accessToken.value).to.equal(sessionAfterSync.accessToken.value) + expect(sessionBeforeSync.refreshToken.value).to.equal(sessionAfterSync.refreshToken.value) + expect(sessionBeforeSync.accessToken.expiresAt).to.be.lessThan(sessionAfterSync.accessToken.expiresAt) // New token should expire in the future. expect(sessionAfterSync.accessToken.expiresAt).to.be.greaterThan(Date.now()) @@ -397,8 +404,8 @@ describe('server session', function () { const refreshSessionResponse = await application.legacyApi.refreshSession() expect(refreshSessionResponse.status).to.equal(400) - expect(refreshSessionResponse.data.error.tag).to.equal('expired-refresh-token') - expect(refreshSessionResponse.data.error.message).to.equal('The refresh token has expired.') + expect(refreshSessionResponse.data.error.tag).to.equal('invalid-parameters') + expect(refreshSessionResponse.data.error.message).to.equal('The provided parameters are not valid.') /* The access token and refresh token should be expired up to this point. @@ -411,7 +418,11 @@ describe('server session', function () { expect(syncResponse.data.error.message).to.equal('Invalid login credentials.') }).timeout(Factory.TwentySecondTimeout) - it('should fail when renewing a session with an invalid refresh token', async function () { + /** + * This test is skipped due to the fact that tokens reside now in cookies and are not accessible to the client. + * Thus it is not possible to tamper with the refresh token. + */ + it.skip('should fail when renewing a session with an invalid refresh token', async function () { await Factory.registerUserToApplication({ application: application, email: email,