Skip to content
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

chore: switch api to cookie based sessions - skip e2e #2854

Merged
merged 2 commits into from Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/api/src/Domain/Api/ApiVersion.ts
@@ -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
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
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
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
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
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
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