@auth-ezz/nextjs 0.1.0 → 1.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/README.md CHANGED
@@ -24,7 +24,7 @@ npm install @ezz-auth/next
24
24
 
25
25
  ### 1. Environment Setup
26
26
 
27
- Add to your `.env.local`:
27
+ Add to your `.env`:
28
28
 
29
29
  ```bash
30
30
  # Core Auth System URL
@@ -38,7 +38,7 @@ AUTH_APP_SECRET=your_secret_key
38
38
  ### 2. Initialize Routes
39
39
 
40
40
  ```bash
41
- npx ezz-auth init
41
+ npx auth-ezz init
42
42
  ```
43
43
 
44
44
  This creates the necessary API routes for authentication callbacks.
package/bin/index.js CHANGED
@@ -1,8 +1,9 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import fs from "fs";
4
4
  import path from "path";
5
5
  import url from "url";
6
+ import { execSync } from "child_process";
6
7
 
7
8
  const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
8
9
  const projectRoot = process.cwd();
@@ -24,10 +25,7 @@ function findAppDir() {
24
25
 
25
26
  function copyTemplate(relativePath) {
26
27
  const source = path.join(__dirname, "..", "templates", relativePath);
27
- const target = path.join(
28
- findAppDir(),
29
- relativePath.replace(/^app\//, "")
30
- );
28
+ const target = path.join(findAppDir(), relativePath.replace(/^app\//, ""));
31
29
 
32
30
  fs.mkdirSync(path.dirname(target), { recursive: true });
33
31
 
@@ -40,17 +38,41 @@ function copyTemplate(relativePath) {
40
38
  console.log("✔ Created:", target);
41
39
  }
42
40
 
41
+ function runCommand(command) {
42
+ try {
43
+ execSync(command, {
44
+ stdio: "inherit",
45
+ cwd: projectRoot,
46
+ });
47
+ } catch (err) {
48
+ console.error(`❌ Failed to run: ${command}`);
49
+ process.exit(1);
50
+ }
51
+ }
52
+
53
+ function isShadcnInstalled() {
54
+ return fs.existsSync(path.join(projectRoot, "components.json"));
55
+ }
56
+
43
57
  /* ---------------- commands ---------------- */
44
58
 
45
59
  function runInit() {
46
60
  copyTemplate("app/api/auth/callback/route.ts");
47
61
  copyTemplate("app/api/auth/logout/route.ts");
48
62
 
63
+ if (!isShadcnInstalled()) {
64
+ console.log("\n📦 Installing shadcn/ui...\n");
65
+
66
+ runCommand("npx shadcn@latest init -y");
67
+ runCommand("npx shadcn@latest add --all");
68
+ } else {
69
+ console.log("⚠ shadcn/ui already installed, skipping");
70
+ }
71
+
72
+ console.log("\n✅ ezz-auth initialized successfully");
49
73
  console.log("\n✅ ezz-auth initialized");
50
- console.log("Next steps:");
51
- console.log("1. Set env variables");
52
- console.log("2. Redirect users to ezz-auth login");
53
- }
74
+
75
+ }
54
76
 
55
77
  /* ---------------- entry ---------------- */
56
78
 
@@ -59,9 +81,9 @@ if (command === "init") {
59
81
  } else {
60
82
  console.log(`
61
83
  Usage:
62
- npx ezz-auth init
84
+ npx auth-ezz init
63
85
 
64
86
  Description:
65
- Scaffold ezz-auth routes for Next.js App Router
87
+ Scaffold auth-ezz routes for Next.js App Router
66
88
  `);
67
89
  }
@@ -0,0 +1,53 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AvatarPrimitive from "@radix-ui/react-avatar"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ function Avatar({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
12
+ return (
13
+ <AvatarPrimitive.Root
14
+ data-slot="avatar"
15
+ className={cn(
16
+ "relative flex size-8 shrink-0 overflow-hidden rounded-full",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ )
22
+ }
23
+
24
+ function AvatarImage({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
28
+ return (
29
+ <AvatarPrimitive.Image
30
+ data-slot="avatar-image"
31
+ className={cn("aspect-square size-full", className)}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function AvatarFallback({
38
+ className,
39
+ ...props
40
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
41
+ return (
42
+ <AvatarPrimitive.Fallback
43
+ data-slot="avatar-fallback"
44
+ className={cn(
45
+ "bg-muted flex size-full items-center justify-center rounded-full",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+
53
+ export { Avatar, AvatarImage, AvatarFallback }
@@ -0,0 +1,62 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
15
+ outline:
16
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
+ ghost:
20
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
25
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
26
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
27
+ icon: "size-9",
28
+ "icon-sm": "size-8",
29
+ "icon-lg": "size-10",
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: "default",
34
+ size: "default",
35
+ },
36
+ }
37
+ )
38
+
39
+ function Button({
40
+ className,
41
+ variant = "default",
42
+ size = "default",
43
+ asChild = false,
44
+ ...props
45
+ }: React.ComponentProps<"button"> &
46
+ VariantProps<typeof buttonVariants> & {
47
+ asChild?: boolean
48
+ }) {
49
+ const Comp = asChild ? Slot : "button"
50
+
51
+ return (
52
+ <Comp
53
+ data-slot="button"
54
+ data-variant={variant}
55
+ data-size={size}
56
+ className={cn(buttonVariants({ variant, size, className }))}
57
+ {...props}
58
+ />
59
+ )
60
+ }
61
+
62
+ export { Button, buttonVariants }
@@ -0,0 +1,257 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
5
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
6
+
7
+ import { cn } from "@/lib/utils"
8
+
9
+ function DropdownMenu({
10
+ ...props
11
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
12
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
13
+ }
14
+
15
+ function DropdownMenuPortal({
16
+ ...props
17
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
18
+ return (
19
+ <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
20
+ )
21
+ }
22
+
23
+ function DropdownMenuTrigger({
24
+ ...props
25
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
26
+ return (
27
+ <DropdownMenuPrimitive.Trigger
28
+ data-slot="dropdown-menu-trigger"
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function DropdownMenuContent({
35
+ className,
36
+ sideOffset = 4,
37
+ ...props
38
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
39
+ return (
40
+ <DropdownMenuPrimitive.Portal>
41
+ <DropdownMenuPrimitive.Content
42
+ data-slot="dropdown-menu-content"
43
+ sideOffset={sideOffset}
44
+ className={cn(
45
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
46
+ className
47
+ )}
48
+ {...props}
49
+ />
50
+ </DropdownMenuPrimitive.Portal>
51
+ )
52
+ }
53
+
54
+ function DropdownMenuGroup({
55
+ ...props
56
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
57
+ return (
58
+ <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
59
+ )
60
+ }
61
+
62
+ function DropdownMenuItem({
63
+ className,
64
+ inset,
65
+ variant = "default",
66
+ ...props
67
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
68
+ inset?: boolean
69
+ variant?: "default" | "destructive"
70
+ }) {
71
+ return (
72
+ <DropdownMenuPrimitive.Item
73
+ data-slot="dropdown-menu-item"
74
+ data-inset={inset}
75
+ data-variant={variant}
76
+ className={cn(
77
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
78
+ className
79
+ )}
80
+ {...props}
81
+ />
82
+ )
83
+ }
84
+
85
+ function DropdownMenuCheckboxItem({
86
+ className,
87
+ children,
88
+ checked,
89
+ ...props
90
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
91
+ return (
92
+ <DropdownMenuPrimitive.CheckboxItem
93
+ data-slot="dropdown-menu-checkbox-item"
94
+ className={cn(
95
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
96
+ className
97
+ )}
98
+ checked={checked}
99
+ {...props}
100
+ >
101
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
102
+ <DropdownMenuPrimitive.ItemIndicator>
103
+ <CheckIcon className="size-4" />
104
+ </DropdownMenuPrimitive.ItemIndicator>
105
+ </span>
106
+ {children}
107
+ </DropdownMenuPrimitive.CheckboxItem>
108
+ )
109
+ }
110
+
111
+ function DropdownMenuRadioGroup({
112
+ ...props
113
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
114
+ return (
115
+ <DropdownMenuPrimitive.RadioGroup
116
+ data-slot="dropdown-menu-radio-group"
117
+ {...props}
118
+ />
119
+ )
120
+ }
121
+
122
+ function DropdownMenuRadioItem({
123
+ className,
124
+ children,
125
+ ...props
126
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
127
+ return (
128
+ <DropdownMenuPrimitive.RadioItem
129
+ data-slot="dropdown-menu-radio-item"
130
+ className={cn(
131
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
132
+ className
133
+ )}
134
+ {...props}
135
+ >
136
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
137
+ <DropdownMenuPrimitive.ItemIndicator>
138
+ <CircleIcon className="size-2 fill-current" />
139
+ </DropdownMenuPrimitive.ItemIndicator>
140
+ </span>
141
+ {children}
142
+ </DropdownMenuPrimitive.RadioItem>
143
+ )
144
+ }
145
+
146
+ function DropdownMenuLabel({
147
+ className,
148
+ inset,
149
+ ...props
150
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
151
+ inset?: boolean
152
+ }) {
153
+ return (
154
+ <DropdownMenuPrimitive.Label
155
+ data-slot="dropdown-menu-label"
156
+ data-inset={inset}
157
+ className={cn(
158
+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
159
+ className
160
+ )}
161
+ {...props}
162
+ />
163
+ )
164
+ }
165
+
166
+ function DropdownMenuSeparator({
167
+ className,
168
+ ...props
169
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
170
+ return (
171
+ <DropdownMenuPrimitive.Separator
172
+ data-slot="dropdown-menu-separator"
173
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
174
+ {...props}
175
+ />
176
+ )
177
+ }
178
+
179
+ function DropdownMenuShortcut({
180
+ className,
181
+ ...props
182
+ }: React.ComponentProps<"span">) {
183
+ return (
184
+ <span
185
+ data-slot="dropdown-menu-shortcut"
186
+ className={cn(
187
+ "text-muted-foreground ml-auto text-xs tracking-widest",
188
+ className
189
+ )}
190
+ {...props}
191
+ />
192
+ )
193
+ }
194
+
195
+ function DropdownMenuSub({
196
+ ...props
197
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
198
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
199
+ }
200
+
201
+ function DropdownMenuSubTrigger({
202
+ className,
203
+ inset,
204
+ children,
205
+ ...props
206
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
207
+ inset?: boolean
208
+ }) {
209
+ return (
210
+ <DropdownMenuPrimitive.SubTrigger
211
+ data-slot="dropdown-menu-sub-trigger"
212
+ data-inset={inset}
213
+ className={cn(
214
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
215
+ className
216
+ )}
217
+ {...props}
218
+ >
219
+ {children}
220
+ <ChevronRightIcon className="ml-auto size-4" />
221
+ </DropdownMenuPrimitive.SubTrigger>
222
+ )
223
+ }
224
+
225
+ function DropdownMenuSubContent({
226
+ className,
227
+ ...props
228
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
229
+ return (
230
+ <DropdownMenuPrimitive.SubContent
231
+ data-slot="dropdown-menu-sub-content"
232
+ className={cn(
233
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
234
+ className
235
+ )}
236
+ {...props}
237
+ />
238
+ )
239
+ }
240
+
241
+ export {
242
+ DropdownMenu,
243
+ DropdownMenuPortal,
244
+ DropdownMenuTrigger,
245
+ DropdownMenuContent,
246
+ DropdownMenuGroup,
247
+ DropdownMenuLabel,
248
+ DropdownMenuItem,
249
+ DropdownMenuCheckboxItem,
250
+ DropdownMenuRadioGroup,
251
+ DropdownMenuRadioItem,
252
+ DropdownMenuSeparator,
253
+ DropdownMenuShortcut,
254
+ DropdownMenuSub,
255
+ DropdownMenuSubTrigger,
256
+ DropdownMenuSubContent,
257
+ }
@@ -4,7 +4,7 @@ export function authMiddleware() {
4
4
  return function middleware(req) {
5
5
  const session = req.cookies.get("__auth_session")?.value;
6
6
  if (!session) {
7
- const loginUrl = new URL("/api/app/v1/authorize", process.env.NEXT_PUBLIC_AUTH_BASE_URL);
7
+ const loginUrl = new URL("/api/app/v1/authorize", process.env.AUTH_BASE_URL);
8
8
  loginUrl.searchParams.set("app_key", process.env.NEXT_PUBLIC_AUTH_PUBLIC_KEY);
9
9
  loginUrl.searchParams.set("redirect_uri", req.nextUrl.href);
10
10
  return NextResponse.redirect(loginUrl);
package/package.json CHANGED
@@ -1,31 +1,28 @@
1
1
  {
2
2
  "name": "@auth-ezz/nextjs",
3
- "version": "0.1.0",
4
3
  "type": "module",
5
- "types": "./index.d.ts",
6
4
  "bin": {
7
- "ezz-auth": "bin/index.js"
5
+ "auth-ezz": "./bin/index.js"
8
6
  },
9
- "publishConfig": {
10
- "access": "public"
11
- },
12
-
13
7
  "exports": {
14
8
  ".": {
15
- "types": "./index.d.ts",
16
- "default": "./client/index.js"
9
+ "react-server": "./index.js",
10
+ "import": "./index.js",
11
+ "default": "./index.js",
12
+ "types": "./index.d.ts"
17
13
  },
18
14
  "./client": {
19
- "types": "./client/index.d.ts",
20
- "default": "./client/index.js"
15
+ "browser": "./client/index.js",
16
+ "import": "./client/index.js",
17
+ "default": "./client/index.js",
18
+ "types": "./client/index.d.ts"
21
19
  },
22
20
  "./server": {
23
- "types": "./server/index.d.ts",
24
- "default": "./server/index.js"
25
- },
26
- "./middleware": {
27
- "types": "./middleware/authMiddleware.d.ts",
28
- "default": "./middleware/authMiddleware.js"
21
+ "react-server": "./server/index.js",
22
+ "import": "./server/index.js",
23
+ "default": "./server/index.js",
24
+ "types": "./server/index.d.ts"
29
25
  }
30
- }
26
+ },
27
+ "version": "1.1.0"
31
28
  }
@@ -9,7 +9,6 @@ export function handleAuthCallback(options = {}) {
9
9
  const code = searchParams.get("code");
10
10
  const rawState = searchParams.get("state");
11
11
 
12
- // ✅ SAFETY CHECK
13
12
  const state =
14
13
  rawState && rawState.startsWith("/") ? rawState : fallbackRedirect;
15
14
 
@@ -15,7 +15,7 @@ export async function requireAuth(options = {}) {
15
15
 
16
16
  const url = new URL(
17
17
  "/api/app/v1/authorize",
18
- process.env.NEXT_PUBLIC_AUTH_BASE_URL
18
+ process.env.AUTH_BASE_URL
19
19
  );
20
20
 
21
21
  url.searchParams.set(
@@ -1,4 +1,4 @@
1
- import { handleAuthCallback } from "@ezz-auth/next/server";
1
+ import { handleAuthCallback } from "@auth-ezz/nextjs/server";
2
2
 
3
3
  export const GET = handleAuthCallback({
4
4
  redirectTo: "/dashboard",
@@ -1,4 +1,4 @@
1
1
  // test-app/app/api/auth/logout/route.js
2
- import { handleLogout } from "@ezz-auth/next/server";
2
+ import { handleLogout } from "@auth-ezz/nextjs/server";
3
3
 
4
4
  export const POST = handleLogout();