@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.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,6 @@
1
+ "use client";
2
+
3
+ import { createAuthClient } from "better-auth/react";
4
+
5
+ export const authClient = createAuthClient();
6
+ export const { useSession, signIn, signUp, signOut } = authClient;
@@ -0,0 +1,3 @@
1
+ export { authClient, useSession, signIn, signUp, signOut } from "./auth-client";
2
+ export { createAuthMiddleware, authMiddlewareConfig } from "./middleware";
3
+ export type { AuthMiddlewareConfig } from "./middleware";
@@ -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";
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -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
+ }