feat(auth): wire login events into OAuth login helper
- Record success login event with risk scoring after OAuth token issuance - Import login-events repo + risk-scorer into oauth/routes - Best-effort recording — never blocks OAuth login flow
This commit is contained in:
parent
82d7f157d9
commit
0f4be0c325
@ -23,6 +23,8 @@ import { OAuthGoogleSchema, OAuthMicrosoftSchema, OAuthAppleSchema } from './typ
|
|||||||
import type { UserDoc, AuthProviderDoc } from '../types.js';
|
import type { UserDoc, AuthProviderDoc } from '../types.js';
|
||||||
import * as subscriptionRepo from '../../subscriptions/repository.js';
|
import * as subscriptionRepo from '../../subscriptions/repository.js';
|
||||||
import * as licenseRepo from '../../licenses/repository.js';
|
import * as licenseRepo from '../../licenses/repository.js';
|
||||||
|
import * as loginEventRepo from '../login-events/repository.js';
|
||||||
|
import { scoreLoginRisk } from '../login-events/risk-scorer.js';
|
||||||
|
|
||||||
export async function oauthRoutes(app: FastifyInstance) {
|
export async function oauthRoutes(app: FastifyInstance) {
|
||||||
// ── Shared OAuth login helper ─────────────────────────────
|
// ── Shared OAuth login helper ─────────────────────────────
|
||||||
@ -176,6 +178,37 @@ export async function oauthRoutes(app: FastifyInstance) {
|
|||||||
});
|
});
|
||||||
const refreshToken = await jwt.createRefreshToken({ sub: user.id, productId });
|
const refreshToken = await jwt.createRefreshToken({ sub: user.id, productId });
|
||||||
|
|
||||||
|
// Record OAuth login event (best-effort)
|
||||||
|
const ip = req.ip || 'unknown';
|
||||||
|
const method = `oauth_${provider}` as 'oauth_google' | 'oauth_microsoft' | 'oauth_apple';
|
||||||
|
try {
|
||||||
|
const recentFailures = await loginEventRepo.countRecentFailures(user.id, 15 * 60 * 1000);
|
||||||
|
const risk = scoreLoginRisk({
|
||||||
|
ip,
|
||||||
|
isNewIp: true,
|
||||||
|
isNewDevice: true,
|
||||||
|
isDeviceTrusted: false,
|
||||||
|
recentFailures,
|
||||||
|
method,
|
||||||
|
hourOfDay: new Date().getHours(),
|
||||||
|
});
|
||||||
|
await loginEventRepo.record({
|
||||||
|
id: `le_${crypto.randomUUID()}`,
|
||||||
|
userId: user.id,
|
||||||
|
productId,
|
||||||
|
result: 'success',
|
||||||
|
method,
|
||||||
|
riskLevel: risk.level,
|
||||||
|
riskScore: risk.score,
|
||||||
|
ip,
|
||||||
|
userAgent: req.headers['user-agent'] as string | undefined,
|
||||||
|
riskFlags: risk.flags,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
/* best-effort */
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessToken,
|
accessToken,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user