├── .editorconfig
├── .env
├── .gitignore
├── README.md
├── app
├── api
│ └── auth
│ │ └── [...nextauth]
│ │ └── route.ts
├── auth-provider.tsx
├── globals.css
├── layout.tsx
└── page.tsx
├── components.json
├── components
├── navigation
│ ├── auth-buttons.tsx
│ └── nav.tsx
├── theme
│ ├── theme-provider.tsx
│ └── theme-select.tsx
└── ui
│ ├── avatar.tsx
│ ├── button.tsx
│ ├── dropdown-menu.tsx
│ └── separator.tsx
├── lib
├── prisma.ts
└── utils.ts
├── next-auth.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── prisma
├── dev.db
├── migrations
│ ├── 20230808194533_init
│ │ └── migration.sql
│ └── migration_lock.toml
└── schema.prisma
├── process.d.ts
├── public
├── next.svg
└── vercel.svg
├── tailwind.config.js
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 | indent_style = tab
9 | indent_size = 4
10 |
11 | [*.{yml,md}]
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | NEXTAUTH_SECRET=2jOHkDR1p9zVVu9d//spFF6nyvi5o+0bbaFXZChJ0f0=
2 | NEXTAUTH_URL=https://9878-5-199-184-16.ngrok-free.app
3 | BOT_TOKEN=6624305292:AAF....
4 | BOT_USERNAME=monbot
5 | DATABASE_URL="file:./dev.db"
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | node_modules/
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 | lerna-debug.log*
10 | .yarn-integrity
11 | .npm
12 |
13 | .eslintcache
14 |
15 | *.tsbuildinfo
16 | next-env.d.ts
17 |
18 | .next
19 | .vercel
20 | .env*.local
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Telegram Authentication with NextAuth.js Example
2 |
3 | This example demonstrates how to set up Telegram authentication using NextAuth.js along with other technologies such as Next.js, PrismaJS, Tailwind CSS, and ShadcnUI.
4 |
5 | ## Getting Started
6 |
7 | Follow these steps to get started with the Telegram authentication example:
8 |
9 | 1. **Clone the Repository**
10 |
11 | ```sh
12 | git clone https://github.com/TeaByte/telegram-auth-nextjs.git
13 | cd telegram-auth-nextjs
14 | ```
15 |
16 | 2. **Install Dependencies**
17 |
18 | ```sh
19 | npm install
20 | ```
21 |
22 | 3. **Edit the `.env` File**
23 |
24 | Update the `BOT_TOKEN` and `BOT_USERNAME` values in the `.env` file with the data you obtained from [@BotFather](https://t.me/BotFather).
25 |
26 | 4. **Start the Development Server**
27 |
28 | ```sh
29 | npm run dev
30 | ```
31 |
32 | 5. **Expose Your Local Server with ngrok**
33 |
34 | If you want to test the authentication over the internet, you can use [ngrok](https://ngrok.com/) to expose your local server:
35 |
36 | ```sh
37 | ngrok http 3000
38 | ```
39 |
40 | Copy the ngrok URL generated and update the `NEXTAUTH_URL` in the `.env.local` file with it. Additionally, send the `/setdomain` command to [@BotFather](https://t.me/BotFather) with the ngrok URL to resolve the "Bot domain invalid" error.
41 |
42 | Remember to replace placeholders and follow the instructions in the example to set up Telegram authentication for your Next.js application. This example combines the power of NextAuth.js, PrismaJS, Tailwind CSS, and ShadcnUI to create a seamless authentication experience using Telegram.
43 |
44 | 
45 |
--------------------------------------------------------------------------------
/app/api/auth/[...nextauth]/route.ts:
--------------------------------------------------------------------------------
1 | import { createUserOrUpdate } from "@/lib/prisma";
2 | import NextAuth, { NextAuthOptions } from "next-auth";
3 | import CredentialsProvider from "next-auth/providers/credentials";
4 |
5 | import { objectToAuthDataMap, AuthDataValidator } from "@telegram-auth/server";
6 |
7 | declare module "next-auth" {
8 | interface Session {
9 | user: {
10 | id: string;
11 | name: string;
12 | image: string;
13 | email: string;
14 | };
15 | }
16 | }
17 |
18 | export const authOptions: NextAuthOptions = {
19 | providers: [
20 | CredentialsProvider({
21 | id: "telegram-login",
22 | name: "Telegram Login",
23 | credentials: {},
24 | async authorize(credentials, req) {
25 | const validator = new AuthDataValidator({
26 | botToken: `${process.env.BOT_TOKEN}`,
27 | });
28 |
29 | const data = objectToAuthDataMap(req.query || {});
30 | const user = await validator.validate(data);
31 |
32 | if (user.id && user.first_name) {
33 | const returned = {
34 | id: user.id.toString(),
35 | email: user.id.toString(),
36 | name: [user.first_name, user.last_name || ""].join(" "),
37 | image: user.photo_url,
38 | };
39 |
40 | try {
41 | await createUserOrUpdate(user);
42 | } catch {
43 | console.log(
44 | "Something went wrong while creating the user."
45 | );
46 | }
47 |
48 | return returned;
49 | }
50 | return null;
51 | },
52 | }),
53 | ],
54 | callbacks: {
55 | async session({ session, user, token }) {
56 | session.user.id = session.user.email;
57 | return session;
58 | },
59 | },
60 | pages: {
61 | signIn: "/auth/signin",
62 | error: "/auth/error",
63 | },
64 | };
65 |
66 | const handler = NextAuth(authOptions);
67 | export { handler as GET, handler as POST };
68 |
--------------------------------------------------------------------------------
/app/auth-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { SessionProvider } from 'next-auth/react';
4 |
5 | type Props = {
6 | children: React.ReactNode;
7 | };
8 |
9 | export default function AuthProvider({ children }: Props) {
10 | return {children};
11 | }
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 0 0% 3.9%;
9 |
10 | --card: 0 0% 100%;
11 | --card-foreground: 0 0% 3.9%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 0 0% 3.9%;
15 |
16 | --primary: 0 0% 9%;
17 | --primary-foreground: 0 0% 98%;
18 |
19 | --secondary: 0 0% 96.1%;
20 | --secondary-foreground: 0 0% 9%;
21 |
22 | --muted: 0 0% 96.1%;
23 | --muted-foreground: 0 0% 45.1%;
24 |
25 | --accent: 0 0% 96.1%;
26 | --accent-foreground: 0 0% 9%;
27 |
28 | --destructive: 0 84.2% 60.2%;
29 | --destructive-foreground: 0 0% 98%;
30 |
31 | --border: 0 0% 89.8%;
32 | --input: 0 0% 89.8%;
33 | --ring: 0 0% 3.9%;
34 |
35 | --radius: 0.5rem;
36 | }
37 |
38 | .dark {
39 | --background: 0 0% 3.9%;
40 | --foreground: 0 0% 98%;
41 |
42 | --card: 0 0% 3.9%;
43 | --card-foreground: 0 0% 98%;
44 |
45 | --popover: 0 0% 3.9%;
46 | --popover-foreground: 0 0% 98%;
47 |
48 | --primary: 0 0% 98%;
49 | --primary-foreground: 0 0% 9%;
50 |
51 | --secondary: 0 0% 14.9%;
52 | --secondary-foreground: 0 0% 98%;
53 |
54 | --muted: 0 0% 14.9%;
55 | --muted-foreground: 0 0% 63.9%;
56 |
57 | --accent: 0 0% 14.9%;
58 | --accent-foreground: 0 0% 98%;
59 |
60 | --destructive: 0 62.8% 30.6%;
61 | --destructive-foreground: 0 0% 98%;
62 |
63 | --border: 0 0% 14.9%;
64 | --input: 0 0% 14.9%;
65 | --ring: 0 0% 83.1%;
66 | }
67 | }
68 |
69 | @layer base {
70 | * {
71 | @apply border-border;
72 | }
73 | body {
74 | @apply bg-background text-foreground;
75 | }
76 | }
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import ThemeProvider from "@/components/theme/theme-provider";
2 | import Nav from "@/components/navigation/nav";
3 |
4 | import "./globals.css";
5 | import type { Metadata } from "next";
6 | import { Rubik } from "next/font/google";
7 |
8 | import AuthProvider from "./auth-provider";
9 |
10 | const font = Rubik({ subsets: ["cyrillic"] });
11 | export const metadata: Metadata = {
12 | title: "Telegram Auth",
13 | };
14 |
15 | export default function RootLayout({
16 | children,
17 | }: {
18 | children: React.ReactNode;
19 | }) {
20 | return (
21 |
22 |
23 |
24 |
29 |
30 | {children}
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { getServerSession } from "next-auth";
2 | import { authOptions } from "@/app/api/auth/[...nextauth]/route";
3 | import { prisma } from "@/lib/prisma";
4 |
5 | export default async function Home() {
6 | const session = await getServerSession(authOptions);
7 |
8 | if (!session) {
9 | return (
10 |
11 | Not logged in to see this
12 |
13 | );
14 | }
15 |
16 | const user = await prisma.user.findUnique({
17 | where: {
18 | id: session.user.id,
19 | },
20 | });
21 |
22 | return (
23 |
24 |
{user?.name}
25 | {user?.image}
26 | {user?.id}
27 | {user?.createdAt.getTime()}
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.js",
8 | "css": "app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true
11 | },
12 | "aliases": {
13 | "components": "@/components",
14 | "utils": "@/lib/utils"
15 | }
16 | }
--------------------------------------------------------------------------------
/components/navigation/auth-buttons.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | DropdownMenu,
5 | DropdownMenuContent,
6 | DropdownMenuItem,
7 | DropdownMenuLabel,
8 | DropdownMenuSeparator,
9 | DropdownMenuTrigger,
10 | } from "@/components/ui/dropdown-menu";
11 | import { LoginButton } from "@telegram-auth/react";
12 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
13 | import { Button } from "@/components/ui/button";
14 | import { ReloadIcon, ExitIcon } from "@radix-ui/react-icons";
15 |
16 | import { useSession, signIn, signOut } from "next-auth/react";
17 |
18 | export default function SignInButton({ botUsername }: { botUsername: string }) {
19 | const { data: session, status } = useSession();
20 |
21 | if (status === "loading") {
22 | return ;
23 | }
24 |
25 | if (status === "authenticated") {
26 | return (
27 |
28 |
29 |
30 |
31 |
35 |
36 | {session.user?.name}
37 |
38 |
39 |
40 |
41 |
42 | {session.user?.name}
43 |
44 | Test 1
45 | Test 2
46 |
47 | signOut()}>
48 |
49 | Sign out
50 |
51 |
52 |
53 | );
54 | }
55 |
56 | return (
57 | {
60 | signIn("telegram-login", { callbackUrl: "/" }, data as any);
61 | }}
62 | />
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/components/navigation/nav.tsx:
--------------------------------------------------------------------------------
1 | import { Separator } from "@/components/ui/separator";
2 | import ThemeToggle from "@/components/theme/theme-select";
3 | import SignInButton from "./auth-buttons";
4 |
5 | import Link from "next/link";
6 |
7 | export default function Nav() {
8 | return (
9 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/components/theme/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { ThemeProvider as NextThemesProvider } from "next-themes";
5 | import { type ThemeProviderProps } from "next-themes/dist/types";
6 |
7 | export default function ThemeProvider({
8 | children,
9 | ...props
10 | }: ThemeProviderProps) {
11 | return {children};
12 | }
13 |
--------------------------------------------------------------------------------
/components/theme/theme-select.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import { MoonIcon, SunIcon } from "@radix-ui/react-icons";
5 | import { useTheme } from "next-themes";
6 |
7 | import { Button } from "@/components/ui/button";
8 | import {
9 | DropdownMenu,
10 | DropdownMenuContent,
11 | DropdownMenuItem,
12 | DropdownMenuTrigger,
13 | } from "@/components/ui/dropdown-menu";
14 |
15 | export default function ThemeToggle() {
16 | const { setTheme } = useTheme();
17 |
18 | return (
19 |
20 |
21 |
26 |
27 |
28 | setTheme("light")}>
29 | Light
30 |
31 | setTheme("dark")}>
32 | Dark
33 |
34 | setTheme("system")}>
35 | System
36 |
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
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 | const Avatar = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 | ))
21 | Avatar.displayName = AvatarPrimitive.Root.displayName
22 |
23 | const AvatarImage = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => (
27 |
32 | ))
33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName
34 |
35 | const AvatarFallback = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 | ))
48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
49 |
50 | export { Avatar, AvatarImage, AvatarFallback }
51 |
--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------
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 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14 | destructive:
15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16 | outline:
17 | "border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
18 | secondary:
19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20 | ghost: "hover:bg-accent hover:text-accent-foreground",
21 | link: "text-primary underline-offset-4 hover:underline",
22 | },
23 | size: {
24 | default: "h-9 px-4 py-2",
25 | sm: "h-8 rounded-md px-3 text-xs",
26 | lg: "h-10 rounded-md px-8",
27 | icon: "h-9 w-9",
28 | },
29 | },
30 | defaultVariants: {
31 | variant: "default",
32 | size: "default",
33 | },
34 | }
35 | )
36 |
37 | export interface ButtonProps
38 | extends React.ButtonHTMLAttributes,
39 | VariantProps {
40 | asChild?: boolean
41 | }
42 |
43 | const Button = React.forwardRef(
44 | ({ className, variant, size, asChild = false, ...props }, ref) => {
45 | const Comp = asChild ? Slot : "button"
46 | return (
47 |
52 | )
53 | }
54 | )
55 | Button.displayName = "Button"
56 |
57 | export { Button, buttonVariants }
58 |
--------------------------------------------------------------------------------
/components/ui/dropdown-menu.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
5 | import {
6 | CheckIcon,
7 | ChevronRightIcon,
8 | DotFilledIcon,
9 | } from "@radix-ui/react-icons"
10 |
11 | import { cn } from "@/lib/utils"
12 |
13 | const DropdownMenu = DropdownMenuPrimitive.Root
14 |
15 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
16 |
17 | const DropdownMenuGroup = DropdownMenuPrimitive.Group
18 |
19 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal
20 |
21 | const DropdownMenuSub = DropdownMenuPrimitive.Sub
22 |
23 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
24 |
25 | const DropdownMenuSubTrigger = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef & {
28 | inset?: boolean
29 | }
30 | >(({ className, inset, children, ...props }, ref) => (
31 |
40 | {children}
41 |
42 |
43 | ))
44 | DropdownMenuSubTrigger.displayName =
45 | DropdownMenuPrimitive.SubTrigger.displayName
46 |
47 | const DropdownMenuSubContent = React.forwardRef<
48 | React.ElementRef,
49 | React.ComponentPropsWithoutRef
50 | >(({ className, ...props }, ref) => (
51 |
59 | ))
60 | DropdownMenuSubContent.displayName =
61 | DropdownMenuPrimitive.SubContent.displayName
62 |
63 | const DropdownMenuContent = React.forwardRef<
64 | React.ElementRef,
65 | React.ComponentPropsWithoutRef
66 | >(({ className, sideOffset = 4, ...props }, ref) => (
67 |
68 |
78 |
79 | ))
80 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
81 |
82 | const DropdownMenuItem = React.forwardRef<
83 | React.ElementRef,
84 | React.ComponentPropsWithoutRef & {
85 | inset?: boolean
86 | }
87 | >(({ className, inset, ...props }, ref) => (
88 |
97 | ))
98 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
99 |
100 | const DropdownMenuCheckboxItem = React.forwardRef<
101 | React.ElementRef,
102 | React.ComponentPropsWithoutRef
103 | >(({ className, children, checked, ...props }, ref) => (
104 |
113 |
114 |
115 |
116 |
117 |
118 | {children}
119 |
120 | ))
121 | DropdownMenuCheckboxItem.displayName =
122 | DropdownMenuPrimitive.CheckboxItem.displayName
123 |
124 | const DropdownMenuRadioItem = React.forwardRef<
125 | React.ElementRef,
126 | React.ComponentPropsWithoutRef
127 | >(({ className, children, ...props }, ref) => (
128 |
136 |
137 |
138 |
139 |
140 |
141 | {children}
142 |
143 | ))
144 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
145 |
146 | const DropdownMenuLabel = React.forwardRef<
147 | React.ElementRef,
148 | React.ComponentPropsWithoutRef & {
149 | inset?: boolean
150 | }
151 | >(({ className, inset, ...props }, ref) => (
152 |
161 | ))
162 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
163 |
164 | const DropdownMenuSeparator = React.forwardRef<
165 | React.ElementRef,
166 | React.ComponentPropsWithoutRef
167 | >(({ className, ...props }, ref) => (
168 |
173 | ))
174 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
175 |
176 | const DropdownMenuShortcut = ({
177 | className,
178 | ...props
179 | }: React.HTMLAttributes) => {
180 | return (
181 |
185 | )
186 | }
187 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
188 |
189 | export {
190 | DropdownMenu,
191 | DropdownMenuTrigger,
192 | DropdownMenuContent,
193 | DropdownMenuItem,
194 | DropdownMenuCheckboxItem,
195 | DropdownMenuRadioItem,
196 | DropdownMenuLabel,
197 | DropdownMenuSeparator,
198 | DropdownMenuShortcut,
199 | DropdownMenuGroup,
200 | DropdownMenuPortal,
201 | DropdownMenuSub,
202 | DropdownMenuSubContent,
203 | DropdownMenuSubTrigger,
204 | DropdownMenuRadioGroup,
205 | }
206 |
--------------------------------------------------------------------------------
/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = "horizontal", decorative = true, ...props },
14 | ref
15 | ) => (
16 |
27 | )
28 | )
29 | Separator.displayName = SeparatorPrimitive.Root.displayName
30 |
31 | export { Separator }
32 |
--------------------------------------------------------------------------------
/lib/prisma.ts:
--------------------------------------------------------------------------------
1 | import { TelegramUserData } from "@telegram-auth/server";
2 | import { PrismaClient } from "@prisma/client";
3 |
4 | export const prisma = new PrismaClient();
5 |
6 | export async function createUserOrUpdate(user: TelegramUserData) {
7 | return prisma.user.upsert({
8 | where: {
9 | id: user.id.toString(),
10 | },
11 | create: {
12 | id: user.id.toString(),
13 | name: user.first_name,
14 | image: user.photo_url,
15 | },
16 | update: {
17 | name: user.first_name,
18 | image: user.photo_url,
19 | },
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx"
2 | import { twMerge } from "tailwind-merge"
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs))
6 | }
7 |
--------------------------------------------------------------------------------
/next-auth.d.ts:
--------------------------------------------------------------------------------
1 | import "next-auth/jwt";
2 |
3 | declare module "next-auth/jwt" {
4 | interface JWT {
5 | /** The user's role. */
6 | userRole?: "admin";
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('next').NextConfig}
3 | */
4 | const config = {
5 | experimental: {
6 | externalDir: true,
7 | },
8 | images: {
9 | domains: ["t.me"],
10 | remotePatterns: [
11 | {
12 | protocol: "https",
13 | hostname: "t.me",
14 | port: "",
15 | pathname: "/u/**",
16 | },
17 | ],
18 | },
19 | };
20 |
21 | module.exports = config;
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "telegram-auth-nextjs",
3 | "private": true,
4 | "scripts": {
5 | "dev": "next dev",
6 | "build": "next build",
7 | "start": "next start",
8 | "clean": "rimraf .next .turbo",
9 | "typecheck": "tsc --noEmit"
10 | },
11 | "dependencies": {
12 | "@emotion/react": "^11",
13 | "@emotion/styled": "^11",
14 | "@prisma/client": "^5.1.1",
15 | "@radix-ui/react-avatar": "^1.0.3",
16 | "@radix-ui/react-dropdown-menu": "^2.0.5",
17 | "@radix-ui/react-icons": "^1.3.0",
18 | "@radix-ui/react-separator": "^1.0.3",
19 | "@radix-ui/react-slot": "^1.0.2",
20 | "@telegram-auth/react": "*",
21 | "@telegram-auth/server": "*",
22 | "class-variance-authority": "^0.7.0",
23 | "clsx": "^2.0.0",
24 | "framer-motion": "^6",
25 | "next": "14.1.1",
26 | "next-auth": "4.24.5",
27 | "next-themes": "^0.2.1",
28 | "nodemailer": "^6",
29 | "react": "^18.2.0",
30 | "react-dom": "^18.2.0",
31 | "tailwind-merge": "^1.14.0",
32 | "tailwindcss-animate": "^1.0.6"
33 | },
34 | "devDependencies": {
35 | "@types/node": "^17",
36 | "@types/react": "^18.0.15",
37 | "autoprefixer": "^10.4.14",
38 | "postcss": "^8.4.27",
39 | "prisma": "^5.1.1",
40 | "tailwindcss": "^3.3.3",
41 | "typescript": "^4"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/prisma/dev.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TeaByte/telegram-auth-nextjs/8e51b15858f96f83cb475c6d35f586d8c929d2da/prisma/dev.db
--------------------------------------------------------------------------------
/prisma/migrations/20230808194533_init/migration.sql:
--------------------------------------------------------------------------------
1 | -- CreateTable
2 | CREATE TABLE "User" (
3 | "id" TEXT NOT NULL PRIMARY KEY,
4 | "name" TEXT NOT NULL,
5 | "image" TEXT,
6 | "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
7 | "updatedAt" DATETIME NOT NULL
8 | );
9 |
--------------------------------------------------------------------------------
/prisma/migrations/migration_lock.toml:
--------------------------------------------------------------------------------
1 | # Please do not edit this file manually
2 | # It should be added in your version-control system (i.e. Git)
3 | provider = "sqlite"
--------------------------------------------------------------------------------
/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | generator client {
2 | provider = "prisma-client-js"
3 | }
4 |
5 | datasource db {
6 | provider = "sqlite"
7 | url = "file:./dev.db"
8 | }
9 |
10 | model User {
11 | id String @id
12 | name String
13 | image String?
14 |
15 | createdAt DateTime @default(now())
16 | updatedAt DateTime @updatedAt
17 | }
18 |
--------------------------------------------------------------------------------
/process.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace NodeJS {
2 | export interface ProcessEnv {
3 | NEXTAUTH_URL: string;
4 | NEXTAUTH_SECRET: string;
5 | BOT_TOKEN: string;
6 | BOT_USERNAME: string;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | './pages/**/*.{ts,tsx}',
6 | './components/**/*.{ts,tsx}',
7 | './app/**/*.{ts,tsx}',
8 | './src/**/*.{ts,tsx}',
9 | ],
10 | theme: {
11 | container: {
12 | center: true,
13 | padding: "2rem",
14 | screens: {
15 | "2xl": "1400px",
16 | },
17 | },
18 | extend: {
19 | colors: {
20 | border: "hsl(var(--border))",
21 | input: "hsl(var(--input))",
22 | ring: "hsl(var(--ring))",
23 | background: "hsl(var(--background))",
24 | foreground: "hsl(var(--foreground))",
25 | primary: {
26 | DEFAULT: "hsl(var(--primary))",
27 | foreground: "hsl(var(--primary-foreground))",
28 | },
29 | secondary: {
30 | DEFAULT: "hsl(var(--secondary))",
31 | foreground: "hsl(var(--secondary-foreground))",
32 | },
33 | destructive: {
34 | DEFAULT: "hsl(var(--destructive))",
35 | foreground: "hsl(var(--destructive-foreground))",
36 | },
37 | muted: {
38 | DEFAULT: "hsl(var(--muted))",
39 | foreground: "hsl(var(--muted-foreground))",
40 | },
41 | accent: {
42 | DEFAULT: "hsl(var(--accent))",
43 | foreground: "hsl(var(--accent-foreground))",
44 | },
45 | popover: {
46 | DEFAULT: "hsl(var(--popover))",
47 | foreground: "hsl(var(--popover-foreground))",
48 | },
49 | card: {
50 | DEFAULT: "hsl(var(--card))",
51 | foreground: "hsl(var(--card-foreground))",
52 | },
53 | },
54 | borderRadius: {
55 | lg: "var(--radius)",
56 | md: "calc(var(--radius) - 2px)",
57 | sm: "calc(var(--radius) - 4px)",
58 | },
59 | keyframes: {
60 | "accordion-down": {
61 | from: { height: 0 },
62 | to: { height: "var(--radix-accordion-content-height)" },
63 | },
64 | "accordion-up": {
65 | from: { height: "var(--radix-accordion-content-height)" },
66 | to: { height: 0 },
67 | },
68 | },
69 | animation: {
70 | "accordion-down": "accordion-down 0.2s ease-out",
71 | "accordion-up": "accordion-up 0.2s ease-out",
72 | },
73 | },
74 | },
75 | plugins: [require("tailwindcss-animate")],
76 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2019",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "paths": {
23 | "@/*": ["./*"]
24 | }
25 | },
26 | "include": [
27 | "next-env.d.ts",
28 | "**/*.ts",
29 | "**/*.tsx",
30 | ".next/types/**/*.ts",
31 | "process.d.ts",
32 | "next-env.d.ts",
33 | "next-auth.d.ts"
34 | ],
35 | "exclude": ["node_modules"]
36 | }
37 |
--------------------------------------------------------------------------------