fix(server): disable Apple oauth on client version < 0.22.0 (#12984)

close AF-2705



#### PR Dependency Tree


* **PR #12984** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* The Apple OAuth provider is now available only for clients version
0.22.0 or higher.
* Client version detection has been improved by extracting version
information from request headers.

* **Bug Fixes**
* Ensured that the Apple OAuth provider is hidden for clients below
version 0.22.0.

* **Tests**
* Added comprehensive end-to-end and utility tests for OAuth provider
selection and client version extraction.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
fengmk2
2025-07-02 16:07:34 +08:00
committed by GitHub
parent bcd6a70b59
commit facf6ee28b
7 changed files with 255 additions and 5 deletions
@@ -1,7 +1,7 @@
import assert from 'node:assert';
import { gqlFetcherFactory } from '@affine/graphql';
import { INestApplication } from '@nestjs/common';
import { INestApplication, ModuleMetadata } from '@nestjs/common';
import { NestApplication } from '@nestjs/core';
import { Test, TestingModuleBuilder } from '@nestjs/testing';
import { PrismaClient } from '@prisma/client';
@@ -35,6 +35,7 @@ import { parseCookies, TEST_LOG_LEVEL } from '../utils';
interface TestingAppMetadata {
tapModule?(m: TestingModuleBuilder): void;
tapApp?(app: INestApplication): void;
imports?: ModuleMetadata['imports'];
}
export class TestingApp extends NestApplication {
@@ -203,7 +204,7 @@ export async function createApp(
const { tapModule, tapApp } = metadata;
const builder = Test.createTestingModule({
imports: [buildAppModule(globalThis.env)],
imports: [buildAppModule(globalThis.env), ...(metadata.imports ?? [])],
});
builder.overrideProvider(Mailer).useValue(new MockMailer());
@@ -0,0 +1,76 @@
# Snapshot report for `src/__tests__/e2e/oauth/resolver.spec.ts`
The actual snapshot is saved in `resolver.spec.ts.snap`.
Generated by [AVA](https://avajs.dev).
## should return apple oauth provider in version >= 0.22.0
> Snapshot 1
{
serverConfig: {
oauthProviders: [
'Google',
'Apple',
],
},
}
> Snapshot 2
{
serverConfig: {
oauthProviders: [
'Google',
'Apple',
],
},
}
> Snapshot 3
{
serverConfig: {
oauthProviders: [
'Google',
'Apple',
],
},
}
## should not return apple oauth provider when client version is not specified
> Snapshot 1
{
serverConfig: {
oauthProviders: [
'Google',
],
},
}
## should not return apple oauth provider in version < 0.22.0
> Snapshot 1
{
serverConfig: {
oauthProviders: [
'Google',
],
},
}
## should not return apple oauth provider when client version format is not correct
> Snapshot 1
{
serverConfig: {
oauthProviders: [
'Google',
],
},
}
@@ -0,0 +1,111 @@
import { oauthProvidersQuery } from '@affine/graphql';
import { ConfigModule } from '../../../base/config';
import { createApp, e2e, TestingApp } from '../test';
let app: TestingApp;
e2e.before(async () => {
app = await createApp({
imports: [
ConfigModule.override({
oauth: {
providers: {
apple: {
clientId: 'test',
clientSecret: 'test',
args: {
redirectUri: 'test',
},
},
google: {
clientId: 'test',
clientSecret: 'test',
args: {
redirectUri: 'test',
},
},
},
},
}),
],
});
});
e2e.after.always(async () => {
await app.close();
});
e2e('should return apple oauth provider in version >= 0.22.0', async t => {
const res = await app.gql({
query: oauthProvidersQuery,
context: {
headers: {
'x-affine-version': '0.22.0',
},
},
});
t.snapshot(res);
const res2 = await app.gql({
query: oauthProvidersQuery,
context: {
headers: {
'x-affine-version': '0.23.0-beta.1',
},
},
});
t.snapshot(res2);
const res3 = await app.gql({
query: oauthProvidersQuery,
context: {
headers: {
'x-affine-version': '2025.6.29-canary.93',
},
},
});
t.snapshot(res3);
});
e2e(
'should not return apple oauth provider when client version is not specified',
async t => {
const res = await app.gql({
query: oauthProvidersQuery,
});
t.snapshot(res);
}
);
e2e('should not return apple oauth provider in version < 0.22.0', async t => {
const res = await app.gql({
query: oauthProvidersQuery,
context: {
headers: {
'x-affine-version': '0.21.0',
},
},
});
t.snapshot(res);
});
e2e(
'should not return apple oauth provider when client version format is not correct',
async t => {
const res = await app.gql({
query: oauthProvidersQuery,
context: {
headers: {
'x-affine-version': 'mock-invalid-version',
},
},
});
t.snapshot(res);
}
);