Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4d5cd0a
feat: better auth
rebelchris Mar 6, 2026
500fb88
Merge branch 'main' of github.com:dailydotdev/daily-api into feat-bet…
rebelchris Mar 9, 2026
32178cf
fix: auth secrets
rebelchris Mar 9, 2026
4e35c46
fix: version 1.5
rebelchris Mar 9, 2026
692e3e5
fix: turnstile
rebelchris Mar 9, 2026
6137ec4
fix: security and tests
rebelchris Mar 10, 2026
5780cc8
fix: some more fixes for linking
rebelchris Mar 10, 2026
e222b9f
Merge branch 'main' of github.com:dailydotdev/daily-api into feat-bet…
rebelchris Mar 10, 2026
54336ff
fix: add mailing
rebelchris Mar 11, 2026
e7eb36f
Merge branch 'main' into feat-better-auth
rebelchris Mar 11, 2026
0a46d49
fix: address security scanner findings in betterAuth
github-actions[bot] Mar 11, 2026
d4fff85
Potential fix for code scanning alert no. 51: Missing rate limiting
rebelchris Mar 11, 2026
65009cf
fix: add rate limiting to social redirect handler
github-actions[bot] Mar 11, 2026
4b88500
fix: feedback PR
rebelchris Mar 11, 2026
2816519
fix: more cleanup
rebelchris Mar 11, 2026
c7bf89f
Merge branch 'main' into feat-better-auth
rebelchris Mar 11, 2026
bb47aac
fix: tests
rebelchris Mar 11, 2026
abdf09f
fix: feedback
rebelchris Mar 11, 2026
cdb7ba5
fix: feedback
rebelchris Mar 11, 2026
df9194e
fix: move to BA config
rebelchris Mar 12, 2026
7528cd8
Merge branch 'main' into feat-better-auth
rebelchris Mar 12, 2026
a7e6108
fix: refactor naming
rebelchris Mar 12, 2026
4a7482a
Merge remote-tracking branch 'origin/feat-better-auth' into feat-bett…
rebelchris Mar 12, 2026
ee131b5
fix: default
rebelchris Mar 12, 2026
c04e6e4
fix: lint issues
rebelchris Mar 12, 2026
0ead503
fix: auth rename and minor feedback
rebelchris Mar 12, 2026
c8eef45
Potential fix for code scanning alert no. 53: Missing rate limiting
rebelchris Mar 12, 2026
46b355d
fix: pr feedback
rebelchris Mar 12, 2026
d262dde
Merge remote-tracking branch 'origin/feat-better-auth' into feat-bett…
rebelchris Mar 12, 2026
7fefba3
fix: pr feedback
rebelchris Mar 12, 2026
0bdb04d
fix: add commentary
rebelchris Mar 13, 2026
c024a70
fix: include allocation client
rebelchris Mar 16, 2026
f5217c1
Merge branch 'main' into feat-better-auth
rebelchris Mar 16, 2026
5b73f5a
Merge branch 'main' into feat-better-auth
rebelchris Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ POST_SCRAPER_ORIGIN=http://localhost:8000
YGGDRASIL_SENTIMENT_ORIGIN=http://localhost:3002
HEIMDALL_ORIGIN=http://localhost:7000
KRATOS_ORIGIN=http://localhost:7000
BETTER_AUTH_REDIRECT_URL=https://sso.local.fylla.dev/api
MAGNI_ORIGIN=http://localhost:9000
MEILI_ORIGIN=http://localhost:7700/
SUBMIT_ARTICLE_THRESHOLD=250
Expand Down
21 changes: 21 additions & 0 deletions .infra/Pulumi.adhoc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,27 @@ config:
employmentAgreementBucketName: adhoc-daily-api
brokkrOrigin: http://brokkr-grpc:50051
mockExternalServices: true
betterAuthSecret:
Comment thread
rebelchris marked this conversation as resolved.
secure: v1:OMJzrWkQe5M2bCPT:zjmHZhC/lKVS9Wgm+m3kCthxwFwh+Ux9pp1E2g+NnQFpsLkVyCRjKEQY8JGdY0IcTaKmhVw9bjo/gMx9N1G6qsUegCIwnQ==
betterAuthBaseUrl: https://api.local.fylla.dev
betterAuthTrustedOrigins: https://app.local.fylla.dev:5002
betterAuthRedirectUrl: https://sso.local.fylla.dev/api
googleClientId:
secure: v1:vxiz7DL3y7wJC3Xb:U998JBQzsUpSrJUAwshlu68xUvWq5zm6hXZNQ6Jeqd82+fZU5/WJJ4t2ti9tlJgUcvrI0Nz7/0dweGmTLp4is58i1k7d37/ZcNjtTOhO39sqa/9hNjQPsQ==
googleClientSecret:
secure: v1:DyvUCX4JxvjrYwOv:eSsStUriEPV2u895v2k0RROIv6fecftf6ZGmQEZfALLp5q3TzVrQrg==
githubClientId:
secure: v1:nTT76ELlqKxR4Auh:BQSLiFpXOZjpJ/lZQxuRxC2RNlf3w4t3VvpMxznldd1lofbS
githubClientSecret:
secure: v1:GhL76XX0Y/yzoYG8:6kARUQHVOxbIP/WXxH+2IX91ADp650ZrEG7H+6FXuakJuzhDHTpk+q37jUq1hzHzp8xNbNFlud4=
appleClientId:
secure: v1:pK4ks8kCNPZO/KJc:REutw/UeJumYLJWA8cPy5iL83yaVgOOZh9azOyo7zcyj
appleClientSecret:
secure: v1:8TOrQ25yzgkRxr2C:n6FB51tbRNAl5bujHFjsn6a7kxEWlirmhX4wj/iWGM5BSc7n+8AxQkxhiLhe6xE0aXvfTPMUurfvbRMBHfkzbrJi7sZkMX7ZsseUHZLbyphA6+L3VxwsVvehArNx5iVJ8MzZW/LxsXZ50AtUjuXzLvaJKQtImdU9MLSPKy/EEOClTBSIn0leHJuEuDI4l7uFUN845qb+R6buTvy26jzaBNxTM//zLvf+2LN+iIbCKXsm77NdRT76x+Zr3rBQSnEWMZJjo4SE3eW5PJ30ZD0XJxung1CcErhWtVeHOkOQjGs4p6OGO/5S3NXK+RS65qQnMPoYiYPhkKhcmd/2FJqruzV7J93FGyw5+LUJ/AqPKpdCRNi1gMS5RPg+SG6Q5YiqvcMWiNbKPCsLStL3bZ2DKyvfBJx9XFWWSDcCFfft
facebookClientId:
secure: v1:HK6y83jsUMlifn1s:lVkv/LDrS8XKQDZtux2UgCCDMU+nAq1W2b1PNngYWQ==
facebookClientSecret:
secure: v1:a/9PRJS0TIBFdMcn:LfxVew6M8ANQHkmK8IAmpnvW/EZ6MKDmH1k2+qihluVS3lYJ740Ui4jvbTZoz55h
api:k8s:
namespace: local
api:temporal:
Expand Down
4 changes: 4 additions & 0 deletions .infra/crons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,8 @@ export const crons: Cron[] = [
name: 'agents-digest',
schedule: '17 4 * * *',
},
{
name: 'clean-expired-better-auth-sessions',
schedule: '0 3 * * *',
},
];
3 changes: 3 additions & 0 deletions __mocks__/better-auth-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const createAuthMiddleware = (
Comment thread
rebelchris marked this conversation as resolved.
fn: (ctx: Record<string, unknown>) => unknown,
) => fn;
14 changes: 14 additions & 0 deletions __mocks__/better-auth-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const fromNodeHeaders = (
headers: Record<string, string | string[] | undefined>,
): Headers => {
const h = new Headers();
for (const [key, value] of Object.entries(headers)) {
if (!value) continue;
if (Array.isArray(value)) {
value.forEach((v) => h.append(key, v));
} else {
h.set(key, value);
}
}
return h;
};
7 changes: 7 additions & 0 deletions __mocks__/better-auth-plugins-email-otp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const emailOTP = () => ({
id: 'email-otp',
endpoints: {},
hooks: { after: [] },
rateLimit: [],
options: {},
});
12 changes: 12 additions & 0 deletions __mocks__/better-auth-plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const captcha = () => ({
id: 'captcha',
options: {},
});

export const emailOTP = () => ({
id: 'email-otp',
endpoints: {},
hooks: { after: [] },
rateLimit: [],
options: {},
});
8 changes: 8 additions & 0 deletions __mocks__/better-auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const betterAuth = () => ({
handler: async () => new Response('{}', { status: 200 }),
api: {
getSession: async () => null,
},
});

export type BetterAuthOptions = Record<string, unknown>;
87 changes: 87 additions & 0 deletions __tests__/cron/cleanExpiredBetterAuthSessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { cleanExpiredBetterAuthSessions as cron } from '../../src/cron/cleanExpiredBetterAuthSessions';
import { expectSuccessfulCron, saveFixtures } from '../helpers';
import { DataSource } from 'typeorm';
import createOrGetConnection from '../../src/db';
import { User } from '../../src/entity/user/User';
import { usersFixture } from '../fixture';
import { crons } from '../../src/cron/index';
import { sub, add } from 'date-fns';

let con: DataSource;

beforeAll(async () => {
con = await createOrGetConnection();
});

beforeEach(async () => {
jest.clearAllMocks();
await saveFixtures(
con,
User,
usersFixture.map((user) => ({
...user,
id: `${user.id}-bac`,
username: `${user.username}-bac`,
})),
);

const now = new Date();
await con.query(
`INSERT INTO ba_session (id, token, "userId", "expiresAt", "createdAt", "updatedAt")
VALUES ($1, $2, $3, $4, $5, $5),
($6, $7, $8, $9, $10, $10),
($11, $12, $13, $14, $15, $15)
ON CONFLICT (id) DO NOTHING`,
[
'expired-1',
'token-expired-1',
'1-bac',
sub(now, { days: 2 }),
sub(now, { days: 9 }),
'expired-2',
'token-expired-2',
'2-bac',
sub(now, { hours: 1 }),
sub(now, { days: 3 }),
'active-1',
'token-active-1',
'1-bac',
add(now, { days: 5 }),
now,
],
);
});

describe('cleanExpiredBetterAuthSessions cron', () => {
it('should be registered', () => {
const registeredCron = crons.find((item) => item.name === cron.name);
expect(registeredCron).toBeDefined();
});

it('should delete expired sessions and keep active ones', async () => {
const before: { id: string }[] = await con.query(
`SELECT id FROM ba_session ORDER BY id`,
);
expect(before).toHaveLength(3);

await expectSuccessfulCron(cron);

const after: { id: string }[] = await con.query(
`SELECT id FROM ba_session ORDER BY id`,
);
expect(after).toEqual([{ id: 'active-1' }]);
});

it('should handle no expired sessions gracefully', async () => {
await con.query(
`DELETE FROM ba_session WHERE id IN ('expired-1', 'expired-2')`,
);

await expectSuccessfulCron(cron);

const after: { id: string }[] = await con.query(
`SELECT id FROM ba_session WHERE id = 'active-1'`,
);
expect(after).toHaveLength(1);
});
});
49 changes: 49 additions & 0 deletions __tests__/routes/betterAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import request from 'supertest';
import { FastifyInstance } from 'fastify';
import { DataSource } from 'typeorm';
import createOrGetConnection from '../../src/db';
import { saveFixtures } from '../helpers';
import { User } from '../../src/entity/user/User';
import { usersFixture } from '../fixture';
import { ioRedisPool } from '../../src/redis';

let app: FastifyInstance;
let con: DataSource;

beforeAll(async () => {
con = await createOrGetConnection();

process.env.BETTER_AUTH_SECRET = 'a]3Bv!k9Pz@mQ7wX#rL2sY&dN5fH8jT-test-only';
try {
const { initializeBetterAuth } = await import('../../src/betterAuth');
initializeBetterAuth();
} catch {
// BA initialization may fail in test with mocked module
}

const appFunc = (await import('../../src')).default;
app = await appFunc();
return app.ready();
});

afterAll(async () => {
if (app) {
await app.close();
}
});

beforeEach(async () => {
jest.clearAllMocks();
await ioRedisPool.execute((client) => client.flushall());
await saveFixtures(con, User, usersFixture);
});

describe('betterAuth routes', () => {
describe('native Better Auth routes', () => {
it('should return 200 for the health endpoint', async () => {
const res = await request(app.server).get('/auth/ok');

expect(res.status).toBe(200);
});
});
});
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,11 @@ module.exports = {
moduleNameMapper: {
'^file-type$': '<rootDir>/node_modules/file-type/index.js',
'^isomorphic-dompurify$': '<rootDir>/__mocks__/isomorphic-dompurify.ts',
'^better-auth$': '<rootDir>/__mocks__/better-auth.ts',
'^better-auth/api$': '<rootDir>/__mocks__/better-auth-api.ts',
'^better-auth/node$': '<rootDir>/__mocks__/better-auth-node.ts',
'^better-auth/plugins$': '<rootDir>/__mocks__/better-auth-plugins.ts',
'^better-auth/plugins/email-otp$':
'<rootDir>/__mocks__/better-auth-plugins-email-otp.ts',
},
};
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
"@types/uuid": "^10.0.0",
"apollo-server-errors": "^3.3.0",
"apollo-server-types": "^3.8.0",
"argon2": "^0.44.0",
"better-auth": "^1.5.0",
"close-with-grace": "^2.4.0",
"cloudinary": "^2.8.0",
"cockatiel": "^3.2.1",
Expand Down Expand Up @@ -166,6 +168,7 @@
"@types/markdown-it": "14.1.2",
"@types/node": "22.15.x",
"@types/node-fetch": "^2.6.13",
"@types/pg": "^8.15.6",
"@types/retry": "^0.12.5",
"@types/rss": "0.0.32",
"@types/supertest": "^6.0.3",
Expand Down
Loading
Loading