Skip to content

Commit

Permalink
chore: switch api to cookie based sessions - skip e2e (#2854)
Browse files Browse the repository at this point in the history
* chore: switch api to cookie based sessions - skip e2e

* chore: fix legacy http service to include api version in requests - skip e2e
  • Loading branch information
karolsojko committed Mar 5, 2024
1 parent f3f5c63 commit 1d0e8cf
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 13 deletions.
1 change: 1 addition & 0 deletions packages/api/src/Domain/Api/ApiVersion.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export enum ApiVersion {
v0 = '20200115',
v1 = '20240226',
}
4 changes: 2 additions & 2 deletions packages/api/src/Domain/Client/Auth/AuthApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
})

Expand All @@ -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,
})

Expand Down
4 changes: 2 additions & 2 deletions packages/api/src/Domain/Client/User/UserApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
})

Expand Down
17 changes: 17 additions & 0 deletions packages/api/src/Domain/Http/HttpService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -145,6 +147,10 @@ export class HttpService implements HttpServiceInterface {
await sleep(this.__latencySimulatorMs, true)
}

httpRequest.params = httpRequest.params
? this.params(httpRequest.params as Record<string | number | symbol, unknown>)
: undefined

const isRefreshRequest = httpRequest.url === joinPaths(this.host, Paths.v1.refreshSession)
if (this.inProgressRefreshSessionPromise && !isRefreshRequest) {
await this.inProgressRefreshSessionPromise
Expand Down Expand Up @@ -236,4 +242,15 @@ export class HttpService implements HttpServiceInterface {

return true
}

private params(inParams: Record<string | number | symbol, unknown>): HttpRequestParams {
const params = {
...inParams,
...{
[ApiEndpointParam.ApiVersion]: ApiVersion.v1,
},
}

return params
}
}
2 changes: 1 addition & 1 deletion packages/snjs/lib/Services/Api/ApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
21 changes: 16 additions & 5 deletions packages/snjs/mocha/session.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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.
Expand All @@ -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,
Expand Down

0 comments on commit 1d0e8cf

Please sign in to comment.