@payez/next-mvp 3.7.1 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/better-auth.d.ts +81 -0
- package/dist/auth/better-auth.js +114 -0
- package/package.json +7 -1
- package/src/auth/better-auth.ts +132 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Better Auth Configuration (Phase 1 — parallel install)
|
|
3
|
+
*
|
|
4
|
+
* NOT wired to routes yet. Exists alongside auth-options.ts for testing.
|
|
5
|
+
* Wired in Phase 2 behind USE_BETTER_AUTH flag.
|
|
6
|
+
*
|
|
7
|
+
* Architecture: No database adapter — Better Auth runs in stateless mode
|
|
8
|
+
* with JWE cookie cache. User management stays on IDP, sessions on Redis.
|
|
9
|
+
*
|
|
10
|
+
* @see BETTER-AUTH-MIGRATION-SPEC.md
|
|
11
|
+
*/
|
|
12
|
+
import 'server-only';
|
|
13
|
+
import type { IDPClientConfig } from '../lib/idp-client-config';
|
|
14
|
+
/**
|
|
15
|
+
* Better Auth social provider config shape.
|
|
16
|
+
*/
|
|
17
|
+
export interface BetterAuthSocialProvider {
|
|
18
|
+
clientId: string;
|
|
19
|
+
clientSecret: string;
|
|
20
|
+
scope?: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build Better Auth social providers from IDP config.
|
|
24
|
+
* Replaces buildOAuthProviders() from providers/oauth.ts.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildBetterAuthProviders(config: IDPClientConfig): Record<string, BetterAuthSocialProvider>;
|
|
27
|
+
/**
|
|
28
|
+
* Create Better Auth instance from IDP config.
|
|
29
|
+
*
|
|
30
|
+
* No database — runs in stateless mode with JWE cookie cache.
|
|
31
|
+
* Call after getIDPClientConfig() resolves.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createBetterAuthInstance(idpConfig: IDPClientConfig): import("better-auth").Auth<{
|
|
34
|
+
secret: string;
|
|
35
|
+
socialProviders: Record<string, BetterAuthSocialProvider>;
|
|
36
|
+
session: {
|
|
37
|
+
cookieCache: {
|
|
38
|
+
enabled: true;
|
|
39
|
+
maxAge: number;
|
|
40
|
+
refreshCache: true;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
plugins: [{
|
|
44
|
+
id: "next-cookies";
|
|
45
|
+
hooks: {
|
|
46
|
+
before: {
|
|
47
|
+
matcher(ctx: import("better-auth").HookEndpointContext): boolean;
|
|
48
|
+
handler: (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>;
|
|
49
|
+
}[];
|
|
50
|
+
after: {
|
|
51
|
+
matcher(ctx: import("better-auth").HookEndpointContext): true;
|
|
52
|
+
handler: (inputContext: import("better-auth").MiddlewareInputContext<import("better-auth").MiddlewareOptions>) => Promise<void>;
|
|
53
|
+
}[];
|
|
54
|
+
};
|
|
55
|
+
}];
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Check if Better Auth is enabled via flag.
|
|
59
|
+
*/
|
|
60
|
+
export declare function isBetterAuthEnabled(): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Get flag-gated auth handler for Next.js route.
|
|
63
|
+
*
|
|
64
|
+
* When USE_BETTER_AUTH=true, returns Better Auth handlers.
|
|
65
|
+
* Otherwise returns null (caller uses NextAuth).
|
|
66
|
+
*
|
|
67
|
+
* Usage in host app route:
|
|
68
|
+
* ```ts
|
|
69
|
+
* import { getBetterAuthHandler } from '@payez/next-mvp/auth/better-auth';
|
|
70
|
+
*
|
|
71
|
+
* export async function GET(req: Request) {
|
|
72
|
+
* const ba = await getBetterAuthHandler();
|
|
73
|
+
* if (ba) return ba.GET(req);
|
|
74
|
+
* // ... existing NextAuth handler
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function getBetterAuthHandler(): Promise<{
|
|
79
|
+
GET: (req: Request) => Promise<Response>;
|
|
80
|
+
POST: (req: Request) => Promise<Response>;
|
|
81
|
+
} | null>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Better Auth Configuration (Phase 1 — parallel install)
|
|
4
|
+
*
|
|
5
|
+
* NOT wired to routes yet. Exists alongside auth-options.ts for testing.
|
|
6
|
+
* Wired in Phase 2 behind USE_BETTER_AUTH flag.
|
|
7
|
+
*
|
|
8
|
+
* Architecture: No database adapter — Better Auth runs in stateless mode
|
|
9
|
+
* with JWE cookie cache. User management stays on IDP, sessions on Redis.
|
|
10
|
+
*
|
|
11
|
+
* @see BETTER-AUTH-MIGRATION-SPEC.md
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.buildBetterAuthProviders = buildBetterAuthProviders;
|
|
15
|
+
exports.createBetterAuthInstance = createBetterAuthInstance;
|
|
16
|
+
exports.isBetterAuthEnabled = isBetterAuthEnabled;
|
|
17
|
+
exports.getBetterAuthHandler = getBetterAuthHandler;
|
|
18
|
+
require("server-only");
|
|
19
|
+
const better_auth_1 = require("better-auth");
|
|
20
|
+
const next_js_1 = require("better-auth/next-js");
|
|
21
|
+
const next_js_2 = require("better-auth/next-js");
|
|
22
|
+
const idp_client_config_1 = require("../lib/idp-client-config");
|
|
23
|
+
/**
|
|
24
|
+
* Build Better Auth social providers from IDP config.
|
|
25
|
+
* Replaces buildOAuthProviders() from providers/oauth.ts.
|
|
26
|
+
*/
|
|
27
|
+
function buildBetterAuthProviders(config) {
|
|
28
|
+
const providers = {};
|
|
29
|
+
for (const oauth of config.oauthProviders || []) {
|
|
30
|
+
if (!oauth.enabled)
|
|
31
|
+
continue;
|
|
32
|
+
const name = oauth.provider.toLowerCase();
|
|
33
|
+
providers[name] = {
|
|
34
|
+
clientId: oauth.clientId,
|
|
35
|
+
clientSecret: oauth.clientSecret,
|
|
36
|
+
scope: oauth.scopes?.split(' '),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return providers;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create Better Auth instance from IDP config.
|
|
43
|
+
*
|
|
44
|
+
* No database — runs in stateless mode with JWE cookie cache.
|
|
45
|
+
* Call after getIDPClientConfig() resolves.
|
|
46
|
+
*/
|
|
47
|
+
function createBetterAuthInstance(idpConfig) {
|
|
48
|
+
return (0, better_auth_1.betterAuth)({
|
|
49
|
+
secret: idpConfig.nextAuthSecret,
|
|
50
|
+
socialProviders: buildBetterAuthProviders(idpConfig),
|
|
51
|
+
// No database — stateless mode. Better Auth defaults to JWE cookie cache.
|
|
52
|
+
// Session cookie cache with refreshCache for DB-less setup.
|
|
53
|
+
session: {
|
|
54
|
+
cookieCache: {
|
|
55
|
+
enabled: true,
|
|
56
|
+
maxAge: 300,
|
|
57
|
+
refreshCache: true,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
plugins: [
|
|
61
|
+
(0, next_js_1.nextCookies)(),
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if Better Auth is enabled via flag.
|
|
67
|
+
*/
|
|
68
|
+
function isBetterAuthEnabled() {
|
|
69
|
+
return process.env.USE_BETTER_AUTH === 'true';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get Better Auth Next.js route handlers (GET, POST).
|
|
73
|
+
* Initializes Better Auth from IDP config on first call, caches the instance.
|
|
74
|
+
*/
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
let cachedInstance = null;
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
|
+
let initPromise = null;
|
|
79
|
+
async function getBetterAuthInstance() {
|
|
80
|
+
if (cachedInstance)
|
|
81
|
+
return cachedInstance;
|
|
82
|
+
if (!initPromise) {
|
|
83
|
+
initPromise = (0, idp_client_config_1.getIDPClientConfig)().then(config => {
|
|
84
|
+
const instance = createBetterAuthInstance(config);
|
|
85
|
+
cachedInstance = instance;
|
|
86
|
+
console.log('[BETTER_AUTH] Instance created for', config.clientSlug || config.clientId);
|
|
87
|
+
return instance;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return initPromise;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get flag-gated auth handler for Next.js route.
|
|
94
|
+
*
|
|
95
|
+
* When USE_BETTER_AUTH=true, returns Better Auth handlers.
|
|
96
|
+
* Otherwise returns null (caller uses NextAuth).
|
|
97
|
+
*
|
|
98
|
+
* Usage in host app route:
|
|
99
|
+
* ```ts
|
|
100
|
+
* import { getBetterAuthHandler } from '@payez/next-mvp/auth/better-auth';
|
|
101
|
+
*
|
|
102
|
+
* export async function GET(req: Request) {
|
|
103
|
+
* const ba = await getBetterAuthHandler();
|
|
104
|
+
* if (ba) return ba.GET(req);
|
|
105
|
+
* // ... existing NextAuth handler
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
async function getBetterAuthHandler() {
|
|
110
|
+
if (!isBetterAuthEnabled())
|
|
111
|
+
return null;
|
|
112
|
+
const auth = await getBetterAuthInstance();
|
|
113
|
+
return (0, next_js_2.toNextJsHandler)(auth);
|
|
114
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payez/next-mvp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
"require": "./dist/auth/auth-options.js",
|
|
48
48
|
"default": "./dist/auth/auth-options.js"
|
|
49
49
|
},
|
|
50
|
+
"./auth/better-auth": {
|
|
51
|
+
"types": "./dist/auth/better-auth.d.ts",
|
|
52
|
+
"require": "./dist/auth/better-auth.js",
|
|
53
|
+
"default": "./dist/auth/better-auth.js"
|
|
54
|
+
},
|
|
50
55
|
"./components/auth/FederatedAuthSection": {
|
|
51
56
|
"types": "./dist/components/auth/FederatedAuthSection.d.ts",
|
|
52
57
|
"require": "./dist/components/auth/FederatedAuthSection.js",
|
|
@@ -863,6 +868,7 @@
|
|
|
863
868
|
"@azure/identity": "^4.0.1",
|
|
864
869
|
"@azure/keyvault-secrets": "^4.7.0",
|
|
865
870
|
"@upstash/redis": "^1.35.6",
|
|
871
|
+
"better-auth": "^1.5.6",
|
|
866
872
|
"ioredis": "^5.3.2",
|
|
867
873
|
"jose": "^5.2.4",
|
|
868
874
|
"jsonwebtoken": "^9.0.2",
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Better Auth Configuration (Phase 1 — parallel install)
|
|
3
|
+
*
|
|
4
|
+
* NOT wired to routes yet. Exists alongside auth-options.ts for testing.
|
|
5
|
+
* Wired in Phase 2 behind USE_BETTER_AUTH flag.
|
|
6
|
+
*
|
|
7
|
+
* Architecture: No database adapter — Better Auth runs in stateless mode
|
|
8
|
+
* with JWE cookie cache. User management stays on IDP, sessions on Redis.
|
|
9
|
+
*
|
|
10
|
+
* @see BETTER-AUTH-MIGRATION-SPEC.md
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import 'server-only';
|
|
14
|
+
import { betterAuth } from 'better-auth';
|
|
15
|
+
import { nextCookies } from 'better-auth/next-js';
|
|
16
|
+
import { toNextJsHandler } from 'better-auth/next-js';
|
|
17
|
+
import type { IDPClientConfig } from '../lib/idp-client-config';
|
|
18
|
+
import { getIDPClientConfig } from '../lib/idp-client-config';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Better Auth social provider config shape.
|
|
22
|
+
*/
|
|
23
|
+
export interface BetterAuthSocialProvider {
|
|
24
|
+
clientId: string;
|
|
25
|
+
clientSecret: string;
|
|
26
|
+
scope?: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Build Better Auth social providers from IDP config.
|
|
31
|
+
* Replaces buildOAuthProviders() from providers/oauth.ts.
|
|
32
|
+
*/
|
|
33
|
+
export function buildBetterAuthProviders(
|
|
34
|
+
config: IDPClientConfig
|
|
35
|
+
): Record<string, BetterAuthSocialProvider> {
|
|
36
|
+
const providers: Record<string, BetterAuthSocialProvider> = {};
|
|
37
|
+
|
|
38
|
+
for (const oauth of config.oauthProviders || []) {
|
|
39
|
+
if (!oauth.enabled) continue;
|
|
40
|
+
const name = oauth.provider.toLowerCase();
|
|
41
|
+
providers[name] = {
|
|
42
|
+
clientId: oauth.clientId,
|
|
43
|
+
clientSecret: oauth.clientSecret,
|
|
44
|
+
scope: oauth.scopes?.split(' '),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return providers;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create Better Auth instance from IDP config.
|
|
53
|
+
*
|
|
54
|
+
* No database — runs in stateless mode with JWE cookie cache.
|
|
55
|
+
* Call after getIDPClientConfig() resolves.
|
|
56
|
+
*/
|
|
57
|
+
export function createBetterAuthInstance(idpConfig: IDPClientConfig) {
|
|
58
|
+
return betterAuth({
|
|
59
|
+
secret: idpConfig.nextAuthSecret as string,
|
|
60
|
+
|
|
61
|
+
socialProviders: buildBetterAuthProviders(idpConfig),
|
|
62
|
+
|
|
63
|
+
// No database — stateless mode. Better Auth defaults to JWE cookie cache.
|
|
64
|
+
// Session cookie cache with refreshCache for DB-less setup.
|
|
65
|
+
session: {
|
|
66
|
+
cookieCache: {
|
|
67
|
+
enabled: true,
|
|
68
|
+
maxAge: 300,
|
|
69
|
+
refreshCache: true,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
plugins: [
|
|
74
|
+
nextCookies(),
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if Better Auth is enabled via flag.
|
|
81
|
+
*/
|
|
82
|
+
export function isBetterAuthEnabled(): boolean {
|
|
83
|
+
return process.env.USE_BETTER_AUTH === 'true';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get Better Auth Next.js route handlers (GET, POST).
|
|
88
|
+
* Initializes Better Auth from IDP config on first call, caches the instance.
|
|
89
|
+
*/
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
let cachedInstance: any = null;
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
let initPromise: Promise<any> | null = null;
|
|
94
|
+
|
|
95
|
+
async function getBetterAuthInstance() {
|
|
96
|
+
if (cachedInstance) return cachedInstance;
|
|
97
|
+
|
|
98
|
+
if (!initPromise) {
|
|
99
|
+
initPromise = getIDPClientConfig().then(config => {
|
|
100
|
+
const instance = createBetterAuthInstance(config);
|
|
101
|
+
cachedInstance = instance;
|
|
102
|
+
console.log('[BETTER_AUTH] Instance created for', config.clientSlug || config.clientId);
|
|
103
|
+
return instance;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return initPromise;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get flag-gated auth handler for Next.js route.
|
|
112
|
+
*
|
|
113
|
+
* When USE_BETTER_AUTH=true, returns Better Auth handlers.
|
|
114
|
+
* Otherwise returns null (caller uses NextAuth).
|
|
115
|
+
*
|
|
116
|
+
* Usage in host app route:
|
|
117
|
+
* ```ts
|
|
118
|
+
* import { getBetterAuthHandler } from '@payez/next-mvp/auth/better-auth';
|
|
119
|
+
*
|
|
120
|
+
* export async function GET(req: Request) {
|
|
121
|
+
* const ba = await getBetterAuthHandler();
|
|
122
|
+
* if (ba) return ba.GET(req);
|
|
123
|
+
* // ... existing NextAuth handler
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export async function getBetterAuthHandler(): Promise<{ GET: (req: Request) => Promise<Response>; POST: (req: Request) => Promise<Response> } | null> {
|
|
128
|
+
if (!isBetterAuthEnabled()) return null;
|
|
129
|
+
|
|
130
|
+
const auth = await getBetterAuthInstance();
|
|
131
|
+
return toNextJsHandler(auth);
|
|
132
|
+
}
|