@greatapps/greatauth-ui 0.1.0
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/index.d.ts +1061 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
- package/src/auth/auth-client.ts +6 -0
- package/src/auth/index.ts +3 -0
- package/src/auth/middleware.ts +38 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-auth.ts +73 -0
- package/src/index.ts +13 -0
- package/src/lib/utils.ts +6 -0
- package/src/types/index.ts +42 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// src/auth/auth-client.ts
|
|
2
|
+
import { createAuthClient } from "better-auth/react";
|
|
3
|
+
var authClient = createAuthClient();
|
|
4
|
+
var { useSession, signIn, signUp, signOut } = authClient;
|
|
5
|
+
|
|
6
|
+
// src/auth/middleware.ts
|
|
7
|
+
import { NextResponse } from "next/server";
|
|
8
|
+
function createAuthMiddleware(config) {
|
|
9
|
+
const { publicPaths, preserveSearchParams = false } = config;
|
|
10
|
+
return function middleware(request) {
|
|
11
|
+
const { pathname, search } = request.nextUrl;
|
|
12
|
+
if (publicPaths.some((path) => pathname.startsWith(path))) {
|
|
13
|
+
return NextResponse.next();
|
|
14
|
+
}
|
|
15
|
+
const sessionToken = request.cookies.get("better-auth.session_token")?.value || request.cookies.get("__Secure-better-auth.session_token")?.value;
|
|
16
|
+
if (!sessionToken) {
|
|
17
|
+
const callbackUrl = preserveSearchParams ? pathname + search : pathname;
|
|
18
|
+
const loginUrl = new URL("/login", request.url);
|
|
19
|
+
loginUrl.searchParams.set("callbackUrl", callbackUrl);
|
|
20
|
+
return NextResponse.redirect(loginUrl);
|
|
21
|
+
}
|
|
22
|
+
return NextResponse.next();
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
var authMiddlewareConfig = {
|
|
26
|
+
matcher: ["/((?!_next/static|_next/image|favicon.ico|api/auth).*)"]
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/hooks/use-auth.ts
|
|
30
|
+
function decodeJwtPayload(token) {
|
|
31
|
+
try {
|
|
32
|
+
const parts = token.split(".");
|
|
33
|
+
if (parts.length !== 3) return null;
|
|
34
|
+
const payload = parts[1];
|
|
35
|
+
const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
36
|
+
return JSON.parse(decoded);
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function createUseAuth(config) {
|
|
42
|
+
return function useAuth() {
|
|
43
|
+
const { data: sessionData, isPending, error } = useSession();
|
|
44
|
+
const session = sessionData?.session ?? null;
|
|
45
|
+
const user = sessionData?.user ?? null;
|
|
46
|
+
const gauthToken = user?.gauthToken ?? null;
|
|
47
|
+
const idAccount = user?.idAccount ?? null;
|
|
48
|
+
const gauthProfile = user?.gauthProfile ?? null;
|
|
49
|
+
const result = {
|
|
50
|
+
session,
|
|
51
|
+
user,
|
|
52
|
+
gauthToken,
|
|
53
|
+
idAccount,
|
|
54
|
+
gauthProfile,
|
|
55
|
+
isLoading: isPending,
|
|
56
|
+
isAuthenticated: !!session && !!user,
|
|
57
|
+
error: error ?? null
|
|
58
|
+
};
|
|
59
|
+
if (config?.jwtClaims && gauthToken) {
|
|
60
|
+
const payload = decodeJwtPayload(gauthToken);
|
|
61
|
+
if (payload) {
|
|
62
|
+
for (const [jwtKey, fieldName] of Object.entries(config.jwtClaims)) {
|
|
63
|
+
result[fieldName] = payload[jwtKey] ?? null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (config?.accountIdOverride) {
|
|
68
|
+
const overriddenId = config.accountIdOverride(user);
|
|
69
|
+
if (overriddenId !== null) {
|
|
70
|
+
result.idAccount = overriddenId;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (config?.extraUserFields && user) {
|
|
74
|
+
for (const field of config.extraUserFields) {
|
|
75
|
+
result[field] = user[field] ?? null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/lib/utils.ts
|
|
83
|
+
import { clsx } from "clsx";
|
|
84
|
+
import { twMerge } from "tailwind-merge";
|
|
85
|
+
function cn(...inputs) {
|
|
86
|
+
return twMerge(clsx(inputs));
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
authClient,
|
|
90
|
+
authMiddlewareConfig,
|
|
91
|
+
cn,
|
|
92
|
+
createAuthMiddleware,
|
|
93
|
+
createUseAuth,
|
|
94
|
+
signIn,
|
|
95
|
+
signOut,
|
|
96
|
+
signUp,
|
|
97
|
+
useSession
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth/auth-client.ts","../src/auth/middleware.ts","../src/hooks/use-auth.ts","../src/lib/utils.ts"],"sourcesContent":["\"use client\";\n\nimport { createAuthClient } from \"better-auth/react\";\n\nexport const authClient = createAuthClient();\nexport const { useSession, signIn, signUp, signOut } = authClient;\n","import { NextResponse, type NextRequest } from \"next/server\";\n\nexport interface AuthMiddlewareConfig {\n publicPaths: string[];\n preserveSearchParams?: boolean;\n}\n\nexport function createAuthMiddleware(config: AuthMiddlewareConfig) {\n const { publicPaths, preserveSearchParams = false } = config;\n\n return function middleware(request: NextRequest) {\n const { pathname, search } = request.nextUrl;\n\n if (publicPaths.some((path) => pathname.startsWith(path))) {\n return NextResponse.next();\n }\n\n const sessionToken =\n request.cookies.get(\"better-auth.session_token\")?.value ||\n request.cookies.get(\"__Secure-better-auth.session_token\")?.value;\n\n if (!sessionToken) {\n const callbackUrl = preserveSearchParams\n ? pathname + search\n : pathname;\n\n const loginUrl = new URL(\"/login\", request.url);\n loginUrl.searchParams.set(\"callbackUrl\", callbackUrl);\n return NextResponse.redirect(loginUrl);\n }\n\n return NextResponse.next();\n };\n}\n\nexport const authMiddlewareConfig = {\n matcher: [\"/((?!_next/static|_next/image|favicon.ico|api/auth).*)\"],\n};\n","\"use client\";\n\nimport { useSession } from \"../auth/auth-client\";\n\ninterface JwtClaimsConfig {\n [jwtClaimName: string]: string;\n}\n\ninterface UseAuthConfig {\n jwtClaims?: JwtClaimsConfig;\n accountIdOverride?: (user: Record<string, unknown> | null) => string | null;\n extraUserFields?: string[];\n}\n\nfunction decodeJwtPayload(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3) return null;\n const payload = parts[1];\n const decoded = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n return JSON.parse(decoded);\n } catch {\n return null;\n }\n}\n\nexport function createUseAuth(config?: UseAuthConfig) {\n return function useAuth() {\n const { data: sessionData, isPending, error } = useSession();\n\n const session = sessionData?.session ?? null;\n const user = sessionData?.user ?? null;\n\n const gauthToken = (user as Record<string, unknown> | null)?.gauthToken as string | null ?? null;\n const idAccount = (user as Record<string, unknown> | null)?.idAccount as string | null ?? null;\n const gauthProfile = (user as Record<string, unknown> | null)?.gauthProfile as Record<string, unknown> | null ?? null;\n\n const result: Record<string, unknown> = {\n session,\n user,\n gauthToken,\n idAccount,\n gauthProfile,\n isLoading: isPending,\n isAuthenticated: !!session && !!user,\n error: error ?? null,\n };\n\n if (config?.jwtClaims && gauthToken) {\n const payload = decodeJwtPayload(gauthToken);\n if (payload) {\n for (const [jwtKey, fieldName] of Object.entries(config.jwtClaims)) {\n result[fieldName] = payload[jwtKey] ?? null;\n }\n }\n }\n\n if (config?.accountIdOverride) {\n const overriddenId = config.accountIdOverride(user as Record<string, unknown> | null);\n if (overriddenId !== null) {\n result.idAccount = overriddenId;\n }\n }\n\n if (config?.extraUserFields && user) {\n for (const field of config.extraUserFields) {\n result[field] = (user as Record<string, unknown>)[field] ?? null;\n }\n }\n\n return result;\n };\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";AAEA,SAAS,wBAAwB;AAE1B,IAAM,aAAa,iBAAiB;AACpC,IAAM,EAAE,YAAY,QAAQ,QAAQ,QAAQ,IAAI;;;ACLvD,SAAS,oBAAsC;AAOxC,SAAS,qBAAqB,QAA8B;AACjE,QAAM,EAAE,aAAa,uBAAuB,MAAM,IAAI;AAEtD,SAAO,SAAS,WAAW,SAAsB;AAC/C,UAAM,EAAE,UAAU,OAAO,IAAI,QAAQ;AAErC,QAAI,YAAY,KAAK,CAAC,SAAS,SAAS,WAAW,IAAI,CAAC,GAAG;AACzD,aAAO,aAAa,KAAK;AAAA,IAC3B;AAEA,UAAM,eACJ,QAAQ,QAAQ,IAAI,2BAA2B,GAAG,SAClD,QAAQ,QAAQ,IAAI,oCAAoC,GAAG;AAE7D,QAAI,CAAC,cAAc;AACjB,YAAM,cAAc,uBAChB,WAAW,SACX;AAEJ,YAAM,WAAW,IAAI,IAAI,UAAU,QAAQ,GAAG;AAC9C,eAAS,aAAa,IAAI,eAAe,WAAW;AACpD,aAAO,aAAa,SAAS,QAAQ;AAAA,IACvC;AAEA,WAAO,aAAa,KAAK;AAAA,EAC3B;AACF;AAEO,IAAM,uBAAuB;AAAA,EAClC,SAAS,CAAC,wDAAwD;AACpE;;;ACvBA,SAAS,iBAAiB,OAA+C;AACvE,MAAI;AACF,UAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,QAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,UAAM,UAAU,MAAM,CAAC;AACvB,UAAM,UAAU,KAAK,QAAQ,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG,CAAC;AAClE,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,QAAwB;AACpD,SAAO,SAAS,UAAU;AACxB,UAAM,EAAE,MAAM,aAAa,WAAW,MAAM,IAAI,WAAW;AAE3D,UAAM,UAAU,aAAa,WAAW;AACxC,UAAM,OAAO,aAAa,QAAQ;AAElC,UAAM,aAAc,MAAyC,cAA+B;AAC5F,UAAM,YAAa,MAAyC,aAA8B;AAC1F,UAAM,eAAgB,MAAyC,gBAAkD;AAEjH,UAAM,SAAkC;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX,iBAAiB,CAAC,CAAC,WAAW,CAAC,CAAC;AAAA,MAChC,OAAO,SAAS;AAAA,IAClB;AAEA,QAAI,QAAQ,aAAa,YAAY;AACnC,YAAM,UAAU,iBAAiB,UAAU;AAC3C,UAAI,SAAS;AACX,mBAAW,CAAC,QAAQ,SAAS,KAAK,OAAO,QAAQ,OAAO,SAAS,GAAG;AAClE,iBAAO,SAAS,IAAI,QAAQ,MAAM,KAAK;AAAA,QACzC;AAAA,MACF;AAAA,IACF;AAEA,QAAI,QAAQ,mBAAmB;AAC7B,YAAM,eAAe,OAAO,kBAAkB,IAAsC;AACpF,UAAI,iBAAiB,MAAM;AACzB,eAAO,YAAY;AAAA,MACrB;AAAA,IACF;AAEA,QAAI,QAAQ,mBAAmB,MAAM;AACnC,iBAAW,SAAS,OAAO,iBAAiB;AAC1C,eAAO,KAAK,IAAK,KAAiC,KAAK,KAAK;AAAA,MAC9D;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ACxEA,SAAS,YAA6B;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@greatapps/greatauth-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": ["dist", "src"],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"dev": "tsup --watch"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"react": "^19",
|
|
20
|
+
"react-dom": "^19",
|
|
21
|
+
"next": ">=15",
|
|
22
|
+
"lucide-react": "*",
|
|
23
|
+
"better-auth": "^1.4",
|
|
24
|
+
"next-themes": "*"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"clsx": "^2",
|
|
28
|
+
"tailwind-merge": "^3"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsup": "latest",
|
|
32
|
+
"typescript": "^5",
|
|
33
|
+
"react": "^19",
|
|
34
|
+
"react-dom": "^19",
|
|
35
|
+
"@types/react": "latest",
|
|
36
|
+
"@types/react-dom": "latest",
|
|
37
|
+
"next": "latest",
|
|
38
|
+
"better-auth": "latest",
|
|
39
|
+
"next-themes": "latest"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { NextResponse, type NextRequest } from "next/server";
|
|
2
|
+
|
|
3
|
+
export interface AuthMiddlewareConfig {
|
|
4
|
+
publicPaths: string[];
|
|
5
|
+
preserveSearchParams?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function createAuthMiddleware(config: AuthMiddlewareConfig) {
|
|
9
|
+
const { publicPaths, preserveSearchParams = false } = config;
|
|
10
|
+
|
|
11
|
+
return function middleware(request: NextRequest) {
|
|
12
|
+
const { pathname, search } = request.nextUrl;
|
|
13
|
+
|
|
14
|
+
if (publicPaths.some((path) => pathname.startsWith(path))) {
|
|
15
|
+
return NextResponse.next();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const sessionToken =
|
|
19
|
+
request.cookies.get("better-auth.session_token")?.value ||
|
|
20
|
+
request.cookies.get("__Secure-better-auth.session_token")?.value;
|
|
21
|
+
|
|
22
|
+
if (!sessionToken) {
|
|
23
|
+
const callbackUrl = preserveSearchParams
|
|
24
|
+
? pathname + search
|
|
25
|
+
: pathname;
|
|
26
|
+
|
|
27
|
+
const loginUrl = new URL("/login", request.url);
|
|
28
|
+
loginUrl.searchParams.set("callbackUrl", callbackUrl);
|
|
29
|
+
return NextResponse.redirect(loginUrl);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return NextResponse.next();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const authMiddlewareConfig = {
|
|
37
|
+
matcher: ["/((?!_next/static|_next/image|favicon.ico|api/auth).*)"],
|
|
38
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createUseAuth } from "./use-auth";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useSession } from "../auth/auth-client";
|
|
4
|
+
|
|
5
|
+
interface JwtClaimsConfig {
|
|
6
|
+
[jwtClaimName: string]: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface UseAuthConfig {
|
|
10
|
+
jwtClaims?: JwtClaimsConfig;
|
|
11
|
+
accountIdOverride?: (user: Record<string, unknown> | null) => string | null;
|
|
12
|
+
extraUserFields?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function decodeJwtPayload(token: string): Record<string, unknown> | null {
|
|
16
|
+
try {
|
|
17
|
+
const parts = token.split(".");
|
|
18
|
+
if (parts.length !== 3) return null;
|
|
19
|
+
const payload = parts[1];
|
|
20
|
+
const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
21
|
+
return JSON.parse(decoded);
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createUseAuth(config?: UseAuthConfig) {
|
|
28
|
+
return function useAuth() {
|
|
29
|
+
const { data: sessionData, isPending, error } = useSession();
|
|
30
|
+
|
|
31
|
+
const session = sessionData?.session ?? null;
|
|
32
|
+
const user = sessionData?.user ?? null;
|
|
33
|
+
|
|
34
|
+
const gauthToken = (user as Record<string, unknown> | null)?.gauthToken as string | null ?? null;
|
|
35
|
+
const idAccount = (user as Record<string, unknown> | null)?.idAccount as string | null ?? null;
|
|
36
|
+
const gauthProfile = (user as Record<string, unknown> | null)?.gauthProfile as Record<string, unknown> | null ?? null;
|
|
37
|
+
|
|
38
|
+
const result: Record<string, unknown> = {
|
|
39
|
+
session,
|
|
40
|
+
user,
|
|
41
|
+
gauthToken,
|
|
42
|
+
idAccount,
|
|
43
|
+
gauthProfile,
|
|
44
|
+
isLoading: isPending,
|
|
45
|
+
isAuthenticated: !!session && !!user,
|
|
46
|
+
error: error ?? null,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (config?.jwtClaims && gauthToken) {
|
|
50
|
+
const payload = decodeJwtPayload(gauthToken);
|
|
51
|
+
if (payload) {
|
|
52
|
+
for (const [jwtKey, fieldName] of Object.entries(config.jwtClaims)) {
|
|
53
|
+
result[fieldName] = payload[jwtKey] ?? null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (config?.accountIdOverride) {
|
|
59
|
+
const overriddenId = config.accountIdOverride(user as Record<string, unknown> | null);
|
|
60
|
+
if (overriddenId !== null) {
|
|
61
|
+
result.idAccount = overriddenId;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (config?.extraUserFields && user) {
|
|
66
|
+
for (const field of config.extraUserFields) {
|
|
67
|
+
result[field] = (user as Record<string, unknown>)[field] ?? null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
};
|
|
73
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Types
|
|
2
|
+
export type { AppShellConfig, MenuGroup, MenuItem, HeaderConfig, LoginFormConfig } from "./types";
|
|
3
|
+
|
|
4
|
+
// Auth
|
|
5
|
+
export { authClient, useSession, signIn, signUp, signOut } from "./auth";
|
|
6
|
+
export { createAuthMiddleware, authMiddlewareConfig } from "./auth";
|
|
7
|
+
export type { AuthMiddlewareConfig } from "./auth";
|
|
8
|
+
|
|
9
|
+
// Hooks
|
|
10
|
+
export { createUseAuth } from "./hooks";
|
|
11
|
+
|
|
12
|
+
// Utils
|
|
13
|
+
export { cn } from "./lib/utils";
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ReactNode, ComponentType } from "react";
|
|
2
|
+
|
|
3
|
+
export interface AppShellConfig {
|
|
4
|
+
appName: string;
|
|
5
|
+
appIcon: ReactNode;
|
|
6
|
+
appBadge?: string;
|
|
7
|
+
menuGroups: MenuGroup[];
|
|
8
|
+
routeLabels: Record<string, string>;
|
|
9
|
+
defaultBreadcrumb?: string;
|
|
10
|
+
userDisplayFields?: {
|
|
11
|
+
showLastName?: boolean;
|
|
12
|
+
};
|
|
13
|
+
footerExtra?: ReactNode;
|
|
14
|
+
onLogout?: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MenuGroup {
|
|
18
|
+
label: string;
|
|
19
|
+
items: MenuItem[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface MenuItem {
|
|
23
|
+
icon: ComponentType<{ className?: string }>;
|
|
24
|
+
label: string;
|
|
25
|
+
href: string;
|
|
26
|
+
children?: MenuItem[];
|
|
27
|
+
onClick?: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface HeaderConfig {
|
|
31
|
+
defaultBreadcrumb?: string;
|
|
32
|
+
routeLabels: Record<string, string>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LoginFormConfig {
|
|
36
|
+
icon: ReactNode;
|
|
37
|
+
appName: string;
|
|
38
|
+
appBadge?: { text: string; variant: "destructive" | "outline" };
|
|
39
|
+
description: string;
|
|
40
|
+
callbackUrlDefault: string;
|
|
41
|
+
onPostLoginSuccess?: (user: unknown, token?: string) => Promise<string | null>;
|
|
42
|
+
}
|