├── .changeset ├── README.md └── config.json ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── changeset-version.yml │ └── pr-check.yml ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── config └── clean-package.json ├── examples ├── next-auth-app-router │ ├── .editorconfig │ ├── .example.env.local │ ├── .gitignore │ ├── README.md │ ├── app │ │ ├── api │ │ │ └── auth │ │ │ │ └── [...nextauth] │ │ │ │ └── route.ts │ │ ├── auth-provider.tsx │ │ ├── layout.tsx │ │ └── page.tsx │ ├── components │ │ └── info.tsx │ ├── next-auth.d.ts │ ├── next.config.js │ ├── package.json │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ └── tsconfig.json ├── next-auth │ ├── .editorconfig │ ├── .example.env.local │ ├── .gitignore │ ├── README.md │ ├── next-auth.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ │ ├── _app.tsx │ │ ├── api │ │ │ └── auth │ │ │ │ └── [...nextauth].ts │ │ ├── auth │ │ │ ├── error.tsx │ │ │ └── signin.tsx │ │ └── index.tsx │ ├── process.d.ts │ └── tsconfig.json └── remix-basic │ ├── .editorconfig │ ├── .example.env │ ├── .gitignore │ ├── README.md │ ├── app │ ├── entry.client.tsx │ ├── entry.server.tsx │ ├── root.tsx │ ├── routes │ │ ├── _index.tsx │ │ ├── auth.tsx │ │ └── login.tsx │ ├── sessions.ts │ └── style.css │ ├── package.json │ ├── public │ └── favicon.ico │ ├── remix.config.js │ ├── remix.env.d.ts │ └── tsconfig.json ├── internal ├── eslint-config │ ├── base.js │ ├── package.json │ ├── recommended.js │ ├── remix.js │ └── tests.js └── jest │ ├── jest.config.dom.js │ ├── jest.config.js │ ├── jest.config.miniflare.js │ ├── jest.config.node.js │ └── package.json ├── package.json ├── packages ├── react │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── .nojekyll │ │ ├── README.md │ │ └── interfaces │ │ │ ├── CreateScriptOptions.md │ │ │ ├── LoginButtonProps.md │ │ │ └── TelegramAuthData.md │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── LoginButton.tsx │ │ ├── createScript.ts │ │ ├── index.ts │ │ └── types.ts │ ├── tests │ │ ├── LoginButton.test.tsx │ │ └── createScript.test.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── typedoc.json └── server │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ ├── .nojekyll │ ├── README.md │ ├── classes │ │ └── AuthDataValidator.md │ └── interfaces │ │ ├── AuthDataValidatorOptions.md │ │ └── TelegramUserData.md │ ├── jest.config.js │ ├── package.json │ ├── src │ ├── AuthDataValidator.ts │ ├── index.ts │ └── utils │ │ ├── hexStringToArrayBuffer.ts │ │ ├── index.ts │ │ ├── objectToAuthDataMap.ts │ │ ├── searchParamsToAuthDataMap.ts │ │ ├── types.ts │ │ └── urlStrToAuthDataMap.ts │ ├── tests │ ├── AuthDataValidator.test.ts │ ├── hexStringToArrayBuffer.test.ts │ ├── objectToAuthDataMap.test.ts │ ├── searchParamsToAuthDataMap.test.ts │ ├── urlStrToAuthDataMap.test.ts │ └── utils.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── typedoc.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tsconfig.json ├── turbo.json └── typedoc.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json", 3 | "changelog": ["@changesets/changelog-github", { "repo": "manzoorwanijk/telegram-auth" }], 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "main", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | cjs/ 4 | coverage/ 5 | dist/ 6 | dts/ 7 | esm/ 8 | lib/ 9 | mjs/ 10 | umd/ 11 | *.d.ts 12 | .next 13 | +.turbo 14 | .cache 15 | **/pnpm-lock.yaml -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('eslint').Linter.Config} 3 | */ 4 | module.exports = { 5 | root: true, 6 | // This tells ESLint to load the config from the package `@telegram-auth/eslint-config` 7 | extends: ['@telegram-auth/eslint-config/recommended'], 8 | }; 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/changeset-version.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | if: github.repository == 'manzoorwanijk/telegram-auth' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout Repo 17 | # https://github.com/actions/checkout 18 | uses: actions/checkout@v4 19 | with: 20 | token: ${{ secrets.GH_TOKEN }} 21 | 22 | - name: Setup Node.js 23 | # https://github.com/actions/setup-node 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: 'lts/*' 27 | 28 | - name: Setup pnpm 29 | uses: pnpm/action-setup@v2 30 | with: 31 | version: 8 32 | run_install: false 33 | 34 | - name: Get pnpm store directory 35 | shell: bash 36 | run: | 37 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 38 | 39 | - uses: actions/cache@v3 40 | name: Setup pnpm cache 41 | with: 42 | path: ${{ env.STORE_PATH }} 43 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 44 | restore-keys: | 45 | ${{ runner.os }}-pnpm-store- 46 | 47 | - name: Install dependencies 48 | run: pnpm install 49 | 50 | - name: Create Release Pull Request or Publish to npm 51 | # https://github.com/changesets/action 52 | uses: changesets/action@v1 53 | with: 54 | # this expects you to have a script called release which does a build for your packages and calls changeset publish 55 | publish: pnpm release 56 | version: pnpm version-packages 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 59 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 60 | -------------------------------------------------------------------------------- /.github/workflows/pr-check.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request Checks 2 | 3 | on: 4 | pull_request: 5 | types: [opened, synchronize] 6 | 7 | jobs: 8 | build: 9 | name: Build and Test 10 | timeout-minutes: 15 11 | runs-on: ubuntu-latest 12 | env: 13 | TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} 14 | TURBO_TEAM: ${{ secrets.TURBO_TEAM }} 15 | 16 | steps: 17 | - name: Check out code 18 | uses: actions/checkout@v3 19 | 20 | - name: Install Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: lts/* 24 | 25 | - name: Setup pnpm 26 | uses: pnpm/action-setup@v2 27 | with: 28 | version: 8 29 | run_install: false 30 | 31 | - name: Get pnpm store directory 32 | shell: bash 33 | run: | 34 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV 35 | 36 | - uses: actions/cache@v3 37 | name: Setup pnpm cache 38 | with: 39 | path: ${{ env.STORE_PATH }} 40 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 41 | restore-keys: | 42 | ${{ runner.os }}-pnpm-store- 43 | 44 | - name: Install dependencies 45 | run: pnpm install 46 | 47 | - name: Lint 48 | run: pnpm lint 49 | 50 | - name: Build 51 | run: pnpm build 52 | 53 | - name: Test 54 | run: pnpm test 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules/ 5 | .pnp 6 | .pnp.js 7 | 8 | dist/ 9 | worker/ 10 | 11 | # testing 12 | coverage 13 | 14 | # next.js 15 | .next/ 16 | out/ 17 | build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # turbo 35 | .turbo 36 | .env 37 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": true, 4 | "printWidth": 120, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "es5", 8 | "useTabs": true 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Manzoor Wani 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram Auth 2 | 3 | This is a mono-repo for the `@telegram-auth/*` packages. It contains the following packages: 4 | 5 | - [`@telegram-auth/react`](./packages/react) - React component for the [Telegram Login Widget](https://core.telegram.org/widgets/login). 6 | - [`@telegram-auth/server`](./packages/server) - TS/JS class to validate the data received from Telegram Login Widget. 7 | -------------------------------------------------------------------------------- /config/clean-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "indent": "\t", 3 | "remove": ["devDependencies", "clean-package"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/.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 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/.example.env.local: -------------------------------------------------------------------------------- 1 | NEXTAUTH_SECRET=KNUYErmsociervyeruonUIIi7yevre 2 | NEXTAUTH_URL=https:https://something-here.ngrok.io 3 | 4 | BOT_TOKEN=xXxXxXxXxXxXxXxXxXxXxXxX 5 | BOT_USERNAME=SomeBotUsername -------------------------------------------------------------------------------- /examples/next-auth-app-router/.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 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/README.md: -------------------------------------------------------------------------------- 1 | # @telegram-auth next-auth App Router Example 2 | 3 | - [NextAuth.js](https://next-auth.js.org/) 4 | 5 | ## Get Started 6 | 7 | 1. Clone the repository 8 | 9 | ```sh 10 | git clone https://github.com/manzoorwanijk/telegram-auth.git 11 | cd telegram-auth 12 | ``` 13 | 14 | 2. Install and build dependencies 15 | 16 | ```sh 17 | pnpm install 18 | pnpm kick-off 19 | ``` 20 | 21 | 3. Go to the "examples/next-auth-app-router" example folder 22 | 23 | ```sh 24 | cd examples/next-auth-app-router 25 | ``` 26 | 27 | 4. Create a `.env.local` file by copying `.example.env.local` and update `BOT_TOKEN` and `BOT_USERNAME` with your bot's token and username that you got from [@BotFather](https://t.me/BotFather). 28 | 29 | 5. Start the dev server 30 | 31 | ```sh 32 | pnpm run dev 33 | ``` 34 | 35 | 6. You may want to use [ngrok](https://ngrok.com/) to expose your local server to the internet. 36 | 37 | ```sh 38 | ngrok http 3000 39 | ``` 40 | 41 | Copy the ngrok URL and update `NEXTAUTH_URL` in `.env.local` with it. 42 | 43 | Don't forget to send `/setdomain` command to [@BotFather](https://t.me/BotFather) with the ngrok URL to fix the "Bot domain invalid" error. 44 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import NextAuth, { NextAuthOptions } from 'next-auth'; 2 | import CredentialsProvider from 'next-auth/providers/credentials'; 3 | 4 | import { AuthDataValidator } from '@telegram-auth/server'; 5 | import { objectToAuthDataMap } from '@telegram-auth/server/utils'; 6 | 7 | export type User = { 8 | id: string; 9 | name: string; 10 | image: string; 11 | }; 12 | 13 | declare module 'next-auth' { 14 | interface Session { 15 | user: User; 16 | } 17 | } 18 | 19 | export const authOptions: NextAuthOptions = { 20 | providers: [ 21 | CredentialsProvider({ 22 | id: 'telegram-login', 23 | name: 'Telegram Login', 24 | credentials: {}, 25 | async authorize(credentials, req) { 26 | const validator = new AuthDataValidator({ 27 | botToken: `${process.env.BOT_TOKEN}`, 28 | }); 29 | 30 | const data = objectToAuthDataMap(req.query || {}); 31 | const user = await validator.validate(data); 32 | 33 | if (user.id && user.first_name) { 34 | return { 35 | id: user.id.toString(), 36 | name: [user.first_name, user.last_name || ''].join(' '), 37 | image: user.photo_url, 38 | }; 39 | } 40 | return null; 41 | }, 42 | }), 43 | ], 44 | }; 45 | 46 | const handler = NextAuth(authOptions); 47 | 48 | export const GET = handler; 49 | 50 | export const POST = handler; 51 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/app/auth-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { SessionProvider } from 'next-auth/react'; 4 | 5 | export function AuthProvider({ children }: { children: React.ReactNode }) { 6 | return {children}; 7 | } 8 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { ChakraProvider } from '@chakra-ui/react'; 2 | 3 | import type { Metadata } from 'next'; 4 | import { AuthProvider } from './auth-provider'; 5 | import { Info } from '../components/info'; 6 | 7 | export const metadata: Metadata = { 8 | title: 'Telegram Auth', 9 | }; 10 | 11 | export default function Layout({ children }: { children: React.ReactNode }) { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 |
{children}
19 |
20 | 21 | 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { getServerSession } from 'next-auth'; 2 | import { authOptions } from './api/auth/[...nextauth]/route'; 3 | import { Box, Heading } from '@chakra-ui/react'; 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 what is here 12 | 13 | ); 14 | } 15 | 16 | return ( 17 | 18 | You can see this because you are logged in. 19 |
{JSON.stringify(session.user, null, 2)}
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/components/info.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { Box, Button, Card, CardBody, CardFooter, Heading, Image, SkeletonText, Stack, Text } from '@chakra-ui/react'; 3 | import { LoginButton } from '@telegram-auth/react'; 4 | import { signIn, signOut, useSession } from 'next-auth/react'; 5 | import type { User } from '../app/api/auth/[...nextauth]/route'; 6 | 7 | function LoadingPlaceholder() { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export function Info({ botUsername }: { botUsername: string }) { 16 | const { data: session, status } = useSession(); 17 | 18 | if (status === 'loading') { 19 | return ; 20 | } 21 | 22 | const user = session?.user as User; 23 | 24 | if (status === 'authenticated') { 25 | return ( 26 | 27 | {user?.image ? ( 28 | 29 | ) : null} 30 | 31 | 32 | Hello 33 | You are signed in as  34 | {user.name} 35 | 36 | 37 | 47 | 48 | 49 | 50 | ); 51 | } 52 | 53 | return ( 54 | 55 | 56 | Hello 57 | 58 | 59 | You are not signed in 60 | 61 | 62 | 63 | { 66 | signIn('telegram-login', { callbackUrl: '/' }, data as any); 67 | }} 68 | /> 69 | 70 | 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/next-auth.d.ts: -------------------------------------------------------------------------------- 1 | import 'next-auth/jwt'; 2 | 3 | // Read more at: https://next-auth.js.org/getting-started/typescript#module-augmentation 4 | 5 | declare module 'next-auth/jwt' { 6 | interface JWT { 7 | /** The user's role. */ 8 | userRole?: 'admin'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/next.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('next').NextConfig} 3 | */ 4 | const config = { 5 | experimental: { 6 | externalDir: true, 7 | }, 8 | }; 9 | 10 | module.exports = config; 11 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@telegram-auth/example-next-auth-app-router", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next", 6 | "build": "next build", 7 | "start": "next start", 8 | "clean": "rimraf .next .turbo", 9 | "typecheck": "tsc --noEmit" 10 | }, 11 | "dependencies": { 12 | "@chakra-ui/react": "^2.8.2", 13 | "@emotion/react": "^11", 14 | "@emotion/styled": "^11", 15 | "@telegram-auth/react": "*", 16 | "@telegram-auth/server": "*", 17 | "framer-motion": "^11", 18 | "next": "^14.1.0", 19 | "next-auth": "^4.24.5", 20 | "nodemailer": "^6.9.8", 21 | "react": "^18.2.0", 22 | "react-dom": "^18.2.0" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^20.11.9", 26 | "@types/react": "^18.2.48", 27 | "typescript": "^5.3.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/next-auth-app-router/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["process.d.ts", "next-env.d.ts", "next-auth.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 3 | "compilerOptions": { 4 | "target": "ES2019", 5 | "lib": ["DOM", "DOM.Iterable", "ES2019"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "plugins": [ 19 | { 20 | "name": "next" 21 | } 22 | ] 23 | }, 24 | "exclude": ["node_modules", ".next"] 25 | } 26 | -------------------------------------------------------------------------------- /examples/next-auth/.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 | -------------------------------------------------------------------------------- /examples/next-auth/.example.env.local: -------------------------------------------------------------------------------- 1 | NEXTAUTH_URL=https:https://something-here.ngrok.io 2 | 3 | BOT_TOKEN=xXxXxXxXxXxXxXxXxXxXxXxX 4 | BOT_USERNAME=SomeBotUsername -------------------------------------------------------------------------------- /examples/next-auth/.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 | -------------------------------------------------------------------------------- /examples/next-auth/README.md: -------------------------------------------------------------------------------- 1 | # @telegram-auth next-auth Example 2 | 3 | - [NextAuth.js](https://next-auth.js.org/) 4 | 5 | ## Get Started 6 | 7 | 1. Clone the repository 8 | 9 | ```sh 10 | git clone https://github.com/manzoorwanijk/telegram-auth.git 11 | cd telegram-auth 12 | ``` 13 | 14 | 2. Install and build dependencies 15 | 16 | ```sh 17 | pnpm install 18 | pnpm kick-off 19 | ``` 20 | 21 | 3. Go to the next-auth example folder 22 | 23 | ```sh 24 | cd examples/next-auth 25 | ``` 26 | 27 | 4. Create a `.env.local` file by copying `.example.env.local` and update `BOT_TOKEN` and `BOT_USERNAME` with your bot's token and username that you got from [@BotFather](https://t.me/BotFather). 28 | 29 | 5. Start the dev server 30 | 31 | ```sh 32 | pnpm run dev 33 | ``` 34 | 35 | 6. You may want to use [ngrok](https://ngrok.com/) to expose your local server to the internet. 36 | 37 | ```sh 38 | ngrok http 3000 39 | ``` 40 | 41 | Copy the ngrok URL and update `NEXTAUTH_URL` in `.env.local` with it. 42 | 43 | Don't forget to send `/setdomain` command to [@BotFather](https://t.me/BotFather) with the ngrok URL to fix the "Bot domain invalid" error. 44 | -------------------------------------------------------------------------------- /examples/next-auth/next-auth.d.ts: -------------------------------------------------------------------------------- 1 | import 'next-auth/jwt'; 2 | 3 | // Read more at: https://next-auth.js.org/getting-started/typescript#module-augmentation 4 | 5 | declare module 'next-auth/jwt' { 6 | interface JWT { 7 | /** The user's role. */ 8 | userRole?: 'admin'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/next-auth/next.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('next').NextConfig} 3 | */ 4 | const config = { 5 | experimental: { 6 | externalDir: true, 7 | }, 8 | }; 9 | 10 | module.exports = config; 11 | -------------------------------------------------------------------------------- /examples/next-auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@telegram-auth/example-next-auth", 3 | "private": true, 4 | "scripts": { 5 | "dev": "next", 6 | "build": "next build", 7 | "start": "next start", 8 | "clean": "rimraf .next .turbo", 9 | "typecheck": "tsc --noEmit" 10 | }, 11 | "dependencies": { 12 | "@chakra-ui/react": "^2.8.2", 13 | "@emotion/react": "^11", 14 | "@emotion/styled": "^11", 15 | "@telegram-auth/react": "*", 16 | "@telegram-auth/server": "*", 17 | "framer-motion": "^11", 18 | "next": "^14.1.0", 19 | "next-auth": "^4.24.5", 20 | "nodemailer": "^6", 21 | "react": "^18.2.0", 22 | "react-dom": "^18.2.0" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^20.11.9", 26 | "@types/react": "^18.2.48", 27 | "typescript": "^5.3.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/next-auth/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { SessionProvider } from 'next-auth/react'; 2 | import { ChakraProvider } from '@chakra-ui/react'; 3 | 4 | import type { AppProps } from 'next/app'; 5 | import type { Session } from 'next-auth'; 6 | 7 | // Use of the is mandatory to allow components that call 8 | // `useSession()` anywhere in your application to access the `session` object. 9 | export default function App({ Component, pageProps: { session, ...pageProps } }: AppProps<{ session: Session }>) { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /examples/next-auth/pages/api/auth/[...nextauth].ts: -------------------------------------------------------------------------------- 1 | import NextAuth, { NextAuthOptions } from 'next-auth'; 2 | import CredentialsProvider from 'next-auth/providers/credentials'; 3 | import { AuthDataValidator } from '@telegram-auth/server'; 4 | import { objectToAuthDataMap } from '@telegram-auth/server/utils'; 5 | 6 | export const authOptions: NextAuthOptions = { 7 | providers: [ 8 | CredentialsProvider({ 9 | id: 'telegram-login', 10 | name: 'Telegram Login', 11 | credentials: {}, 12 | async authorize(credentials, req) { 13 | const validator = new AuthDataValidator({ botToken: `${process.env.BOT_TOKEN}` }); 14 | 15 | const data = objectToAuthDataMap(req.query || {}); 16 | 17 | const user = await validator.validate(data); 18 | 19 | if (user.id && user.first_name) { 20 | return { 21 | id: user.id.toString(), 22 | name: [user.first_name, user.last_name || ''].join(' '), 23 | image: user.photo_url, 24 | }; 25 | } 26 | 27 | return null; 28 | }, 29 | }), 30 | ], 31 | pages: { 32 | signIn: '/auth/signin', 33 | error: '/auth/error', 34 | }, 35 | }; 36 | 37 | export default NextAuth(authOptions); 38 | -------------------------------------------------------------------------------- /examples/next-auth/pages/auth/error.tsx: -------------------------------------------------------------------------------- 1 | import { Container, Center, Button, Alert, AlertIcon, AlertTitle, AlertDescription } from '@chakra-ui/react'; 2 | import { useRouter } from 'next/router'; 3 | 4 | const Error = () => { 5 | const router = useRouter(); 6 | 7 | return ( 8 | 9 |
10 | 19 | 20 | 21 | Error! 22 | 23 | {router.query.error} 24 | 25 | 35 |
36 |
37 | ); 38 | }; 39 | 40 | export default Error; 41 | -------------------------------------------------------------------------------- /examples/next-auth/pages/auth/signin.tsx: -------------------------------------------------------------------------------- 1 | import { GetServerSidePropsContext } from 'next'; 2 | import { signIn, getCsrfToken, getProviders } from 'next-auth/react'; 3 | import { LoginButton } from '@telegram-auth/react'; 4 | import { Container, Center, Button, VStack } from '@chakra-ui/react'; 5 | import { useRouter } from 'next/router'; 6 | 7 | const Signin = ({ 8 | providers, 9 | csrfToken, 10 | botUsername, 11 | }: { 12 | providers: Awaited>; 13 | csrfToken?: string; 14 | botUsername: string; 15 | }) => { 16 | const router = useRouter(); 17 | 18 | return ( 19 | 20 |
21 | 22 | {Object.values(providers || {}).map((provider) => { 23 | return ( 24 | 25 | {provider.id === 'telegram-login' ? ( 26 | <> 27 | { 30 | signIn(provider.id, { callbackUrl: '/' }, data as any); 31 | }} 32 | /> 33 | 34 | ) : ( 35 | <> 36 | {/* Render other providers */} 37 | 38 | 39 | )} 40 | 41 | ); 42 | })} 43 | 53 |
54 |
55 | ); 56 | }; 57 | 58 | export default Signin; 59 | 60 | export async function getServerSideProps(context: GetServerSidePropsContext) { 61 | const providers = await getProviders(); 62 | const csrfToken = await getCsrfToken(context); 63 | const botUsername = process.env.BOT_USERNAME; 64 | return { 65 | props: { 66 | providers, 67 | csrfToken, 68 | botUsername, 69 | }, 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /examples/next-auth/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Card, CardBody, CardFooter, Image, Stack, Heading, Text, Button, SkeletonText, Box } from '@chakra-ui/react'; 2 | import { signIn, signOut, useSession } from 'next-auth/react'; 3 | import { useRouter } from 'next/router'; 4 | 5 | function LoadingPlaceholder() { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | 13 | export default function IndexPage() { 14 | const { data: session, status } = useSession(); 15 | const loading = status === 'loading'; 16 | const router = useRouter(); 17 | 18 | if (loading) { 19 | return ; 20 | } 21 | 22 | const isLoggedIn = !!session?.user; 23 | 24 | return ( 25 | 26 | {loading ? null : ( 27 | <> 28 | {session?.user?.image ? ( 29 | 35 | ) : null} 36 | 37 | 38 | Hello 39 | 40 | 41 | {session?.user ? ( 42 | <> 43 | You are signed in as  44 | {session.user.email ?? session.user.name} 45 | 46 | ) : ( 47 | You are not signed in 48 | )} 49 | 50 | 51 | 52 | 69 | 70 | 71 | 72 | )} 73 | 74 | ); 75 | } 76 | -------------------------------------------------------------------------------- /examples/next-auth/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 | -------------------------------------------------------------------------------- /examples/next-auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["process.d.ts", "next-env.d.ts", "next-auth.d.ts", "**/*.ts", "**/*.tsx"], 3 | "compilerOptions": { 4 | "target": "ES2019", 5 | "lib": ["DOM", "DOM.Iterable", "ES2019"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "strict": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true 18 | }, 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /examples/remix-basic/.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 | -------------------------------------------------------------------------------- /examples/remix-basic/.example.env: -------------------------------------------------------------------------------- 1 | BOT_TOKEN=xXxXxXxXxXxXxXxXxXxXxXxX 2 | BOT_USERNAME=SomeBotUsername -------------------------------------------------------------------------------- /examples/remix-basic/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | /.cache 4 | /build 5 | /public/build 6 | .env 7 | -------------------------------------------------------------------------------- /examples/remix-basic/README.md: -------------------------------------------------------------------------------- 1 | # @telegram-auth remix-basic example 2 | 3 | - [Remix Docs](https://remix.run/docs) 4 | 5 | ## Get Started 6 | 7 | 1. Clone the repository 8 | 9 | ```sh 10 | git clone https://github.com/manzoorwanijk/telegram-auth.git 11 | cd telegram-auth 12 | ``` 13 | 14 | 2. Install and build dependencies 15 | 16 | ```sh 17 | pnpm install 18 | pnpm kick-off 19 | ``` 20 | 21 | 3. Go to the remix-basic example folder 22 | 23 | ```sh 24 | cd examples/remix-basic 25 | ``` 26 | 27 | 4. Create a `.env` file by copying `.example.env` and update `BOT_TOKEN` and `BOT_USERNAME` with your bot's token and username that you got from [@BotFather](https://t.me/BotFather). 28 | 29 | 5. Start the dev server 30 | 31 | ```sh 32 | pnpm run dev 33 | ``` 34 | 35 | 6. You may want to use [ngrok](https://ngrok.com/) to expose your local server to the internet. 36 | 37 | ```sh 38 | ngrok http 3000 39 | ``` 40 | 41 | Don't forget to send `/setdomain` command to [@BotFather](https://t.me/BotFather) with the ngrok URL to fix the "Bot domain invalid" error. 42 | -------------------------------------------------------------------------------- /examples/remix-basic/app/entry.client.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * By default, Remix will handle hydrating your app on the client for you. 3 | * You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ 4 | * For more information, see https://remix.run/file-conventions/entry.client 5 | */ 6 | 7 | import { RemixBrowser } from '@remix-run/react'; 8 | import { startTransition, StrictMode } from 'react'; 9 | import { hydrateRoot } from 'react-dom/client'; 10 | 11 | startTransition(() => { 12 | hydrateRoot( 13 | document, 14 | 15 | 16 | 17 | ); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/remix-basic/app/entry.server.tsx: -------------------------------------------------------------------------------- 1 | import { renderToString } from 'react-dom/server'; 2 | import type { EntryContext, HandleDataRequestFunction } from '@remix-run/node'; 3 | import { RemixServer } from '@remix-run/react'; 4 | 5 | export default function handleRequest( 6 | request: Request, 7 | responseStatusCode: number, 8 | responseHeaders: Headers, 9 | remixContext: EntryContext 10 | ) { 11 | const markup = renderToString(); 12 | 13 | responseHeaders.set('Content-Type', 'text/html'); 14 | 15 | return new Response('' + markup, { 16 | status: responseStatusCode, 17 | headers: responseHeaders, 18 | }); 19 | } 20 | 21 | export const handleDataRequest: HandleDataRequestFunction = (response: Response) => { 22 | response.headers.set('x-custom', 'yay!'); 23 | return response; 24 | }; 25 | -------------------------------------------------------------------------------- /examples/remix-basic/app/root.tsx: -------------------------------------------------------------------------------- 1 | import type { MetaFunction } from '@remix-run/node'; 2 | import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'; 3 | 4 | import styles from './style.css'; 5 | 6 | export default function App() { 7 | return ( 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | ); 23 | } 24 | 25 | export function links() { 26 | return [{ rel: 'stylesheet', href: styles }]; 27 | } 28 | -------------------------------------------------------------------------------- /examples/remix-basic/app/routes/_index.tsx: -------------------------------------------------------------------------------- 1 | import { json } from '@remix-run/node'; 2 | import { Link, Form } from '@remix-run/react'; 3 | import { useLoaderData } from '@remix-run/react'; 4 | import type { LoaderFunction } from '@remix-run/node'; 5 | import { getSession, commitSession } from '../sessions'; 6 | import type { TelegramAuthData } from '@telegram-auth/react'; 7 | 8 | export const loader: LoaderFunction = async ({ request }) => { 9 | const session = await getSession(request.headers.get('Cookie')); 10 | 11 | const user: TelegramAuthData = session.get('user'); 12 | 13 | const data = { user }; 14 | 15 | return json(data, { 16 | headers: { 17 | 'Set-Cookie': await commitSession(session), 18 | }, 19 | }); 20 | }; 21 | 22 | export default function Index() { 23 | const { user } = useLoaderData(); 24 | 25 | return ( 26 |
27 | {user ? ( 28 | <> 29 |

Hello {user.first_name}

30 |

You are logged in

31 |
32 | 33 |
34 | 35 | ) : ( 36 | <> 37 |

Hello

38 |

You are not logged in

39 |

40 | Go to Login page to login 41 |

42 | 43 | )} 44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /examples/remix-basic/app/routes/auth.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from '@remix-run/node'; 2 | import type { LoaderFunction } from '@remix-run/node'; 3 | import { AuthDataValidator } from '@telegram-auth/server'; 4 | import { urlStrToAuthDataMap } from '@telegram-auth/server/utils'; 5 | import { getSession, commitSession } from '../sessions'; 6 | 7 | export const loader: LoaderFunction = async ({ request }) => { 8 | const validator = new AuthDataValidator({ botToken: `${process.env.BOT_TOKEN}` }); 9 | 10 | const session = await getSession(request.headers.get('Cookie')); 11 | 12 | const data = urlStrToAuthDataMap(request.url); 13 | 14 | try { 15 | const user = await validator.validate(data); 16 | 17 | session.set('user', user); 18 | } catch (error: any) { 19 | session.flash('error', error.message); 20 | 21 | // Redirect back to the login page with errors. 22 | return redirect('/login', { 23 | headers: { 24 | 'Set-Cookie': await commitSession(session), 25 | }, 26 | }); 27 | } 28 | 29 | // Login succeeded, send them to the home page. 30 | return redirect('/', { 31 | headers: { 32 | 'Set-Cookie': await commitSession(session), 33 | }, 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /examples/remix-basic/app/routes/login.tsx: -------------------------------------------------------------------------------- 1 | import { json, redirect } from '@remix-run/node'; 2 | import { useLoaderData, Link } from '@remix-run/react'; 3 | import type { LoaderFunction, ActionFunction } from '@remix-run/node'; 4 | import { LoginButton } from '@telegram-auth/react'; 5 | 6 | import { getSession, commitSession, destroySession } from '../sessions'; 7 | 8 | export const loader: LoaderFunction = async ({ request }) => { 9 | const session = await getSession(request.headers.get('Cookie')); 10 | 11 | if (session.has('user')) { 12 | return redirect('/'); 13 | } 14 | 15 | const botUsername = process.env.BOT_USERNAME || ''; 16 | 17 | const data = { error: session.get('error'), botUsername }; 18 | 19 | return json(data, { 20 | headers: { 21 | 'Set-Cookie': await commitSession(session), 22 | }, 23 | }); 24 | }; 25 | 26 | export const action: ActionFunction = async ({ request }) => { 27 | const session = await getSession(request.headers.get('Cookie')); 28 | 29 | return redirect('/', { 30 | headers: { 31 | 'Set-Cookie': await destroySession(session), 32 | }, 33 | }); 34 | }; 35 | 36 | export default function Login() { 37 | const { botUsername, error } = useLoaderData(); 38 | 39 | return ( 40 |
41 | {error ?
{error}
: null} 42 |
43 |
44 | 45 |
46 |

47 | Go to Home page 48 |

49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /examples/remix-basic/app/sessions.ts: -------------------------------------------------------------------------------- 1 | import { createCookieSessionStorage } from '@remix-run/node'; 2 | 3 | const { getSession, commitSession, destroySession } = createCookieSessionStorage({ 4 | cookie: { 5 | name: '__session', 6 | secrets: ['s3cret'], 7 | secure: true, 8 | }, 9 | }); 10 | 11 | export { getSession, commitSession, destroySession }; 12 | -------------------------------------------------------------------------------- /examples/remix-basic/app/style.css: -------------------------------------------------------------------------------- 1 | body, 2 | html { 3 | height: 100%; 4 | } 5 | 6 | #root { 7 | height: 100%; 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | font-family: system-ui, sans-serif; 12 | line-height: 1.4; 13 | } 14 | 15 | .center { 16 | text-align: center; 17 | } 18 | 19 | .error { 20 | color: #ff0000; 21 | } 22 | -------------------------------------------------------------------------------- /examples/remix-basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@telegram-auth/example-remix-basic", 3 | "private": true, 4 | "sideEffects": false, 5 | "type": "module", 6 | "scripts": { 7 | "build": "remix build", 8 | "dev": "remix dev", 9 | "start": "remix-serve build", 10 | "clean": "rimraf .cache .turbo build", 11 | "typecheck": "tsc --noEmit" 12 | }, 13 | "dependencies": { 14 | "@remix-run/node": "^2.5.1", 15 | "@remix-run/react": "^2.5.1", 16 | "@remix-run/serve": "^2.5.1", 17 | "@telegram-auth/react": "*", 18 | "@telegram-auth/server": "*", 19 | "isbot": "^4.4.0", 20 | "react": "^18.2.0", 21 | "react-dom": "^18.2.0" 22 | }, 23 | "devDependencies": { 24 | "@remix-run/dev": "^2.5.1", 25 | "@types/react": "^18.2.48", 26 | "@types/react-dom": "^18.2.18", 27 | "typescript": "^5.3.3" 28 | }, 29 | "engines": { 30 | "node": ">=14" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/remix-basic/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manzoorwanijk/telegram-auth/f3cfc79c76e4ac28684231d77b8f84ed0c8169aa/examples/remix-basic/public/favicon.ico -------------------------------------------------------------------------------- /examples/remix-basic/remix.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('@remix-run/dev').AppConfig} */ 2 | export default { 3 | ignoredRouteFiles: ['**/.*'], 4 | // appDirectory: "app", 5 | // assetsBuildDirectory: "public/build", 6 | // publicPath: "/build/", 7 | // serverBuildPath: "build/index.js", 8 | }; 9 | -------------------------------------------------------------------------------- /examples/remix-basic/remix.env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/remix-basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], 3 | "compilerOptions": { 4 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 5 | "isolatedModules": true, 6 | "esModuleInterop": true, 7 | "jsx": "react-jsx", 8 | "moduleResolution": "node", 9 | "resolveJsonModule": true, 10 | "target": "ESNext", 11 | "strict": true, 12 | "allowJs": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "baseUrl": ".", 15 | "paths": { 16 | "~/*": ["./app/*"] 17 | }, 18 | "noEmit": true 19 | }, 20 | "exclude": ["**/node_modules", "**/build"] 21 | } 22 | -------------------------------------------------------------------------------- /internal/eslint-config/base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('eslint').Linter.Config} 3 | */ 4 | module.exports = { 5 | parser: '@typescript-eslint/parser', 6 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:prettier/recommended'], 7 | rules: { 8 | 'no-undef': 'off', 9 | 'no-unused-vars': 'off', 10 | 'no-console': 'warn', 11 | }, 12 | overrides: [ 13 | { 14 | files: ['*.json'], 15 | parser: 'eslint-plugin-json-es', 16 | extends: 'plugin:eslint-plugin-json-es/recommended', 17 | }, 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /internal/eslint-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@telegram-auth/eslint-config", 3 | "version": "1.0.0", 4 | "main": "recommended.js", 5 | "private": true, 6 | "license": "MIT", 7 | "files": [ 8 | "base.js", 9 | "recommended.js", 10 | "remix.js", 11 | "tests.js" 12 | ], 13 | "devDependencies": { 14 | "@typescript-eslint/eslint-plugin": "^6.9.0", 15 | "@typescript-eslint/parser": "^6.9.0", 16 | "@remix-run/eslint-config": "^2.1.0", 17 | "eslint": "^8.52.0", 18 | "eslint-config-prettier": "^9.0.0", 19 | "eslint-plugin-jest": "^27.6.0", 20 | "eslint-plugin-json-es": "^1.5.7", 21 | "eslint-plugin-prettier": "^5.0.1", 22 | "prettier": "^3.0.3", 23 | "react": "^18.2.0", 24 | "typescript": "^5.2.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /internal/eslint-config/recommended.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('eslint').Linter.Config} 3 | */ 4 | module.exports = { 5 | extends: ['./base', './tests'], 6 | }; 7 | -------------------------------------------------------------------------------- /internal/eslint-config/remix.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('eslint').Linter.Config} 3 | */ 4 | module.exports = { 5 | extends: ['./base', '@remix-run'], 6 | }; 7 | -------------------------------------------------------------------------------- /internal/eslint-config/tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('eslint').Linter.Config} 3 | */ 4 | module.exports = { 5 | extends: ['plugin:jest/recommended'], 6 | plugins: ['jest'], 7 | }; 8 | -------------------------------------------------------------------------------- /internal/jest/jest.config.dom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('ts-jest').JestConfigWithTsJest} 3 | */ 4 | const config = { 5 | ...require('./jest.config'), 6 | testEnvironment: 'jsdom', 7 | }; 8 | 9 | module.exports = config; 10 | -------------------------------------------------------------------------------- /internal/jest/jest.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('ts-jest').JestConfigWithTsJest} 3 | */ 4 | const config = { 5 | preset: 'ts-jest', 6 | testMatch: ['**/tests/**/*.test.[jt]s?(x)'], 7 | testPathIgnorePatterns: ['/build/', '/node_modules/'], 8 | }; 9 | 10 | module.exports = config; 11 | -------------------------------------------------------------------------------- /internal/jest/jest.config.miniflare.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('ts-jest').JestConfigWithTsJest} 3 | */ 4 | const config = { 5 | ...require('./jest.config'), 6 | // CloudFlare worker (miniflare) environment 7 | testEnvironment: 'miniflare', 8 | }; 9 | 10 | module.exports = config; 11 | -------------------------------------------------------------------------------- /internal/jest/jest.config.node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('ts-jest').JestConfigWithTsJest} 3 | */ 4 | const config = { 5 | ...require('./jest.config'), 6 | testEnvironment: 'node', 7 | }; 8 | 9 | module.exports = config; 10 | -------------------------------------------------------------------------------- /internal/jest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@telegram-auth/jest", 3 | "version": "1.0.0", 4 | "main": "jest.config.js", 5 | "private": true, 6 | "license": "MIT", 7 | "files": [ 8 | "jest.config.js", 9 | "jest.config.dom.js", 10 | "jest.config.miniflare.js", 11 | "jest.config.node.js" 12 | ], 13 | "bin": { 14 | "jest": "node_modules/jest/bin/jest.js" 15 | }, 16 | "devDependencies": { 17 | "@types/jest": "^29.5.6", 18 | "jest": "^29.7.0", 19 | "jest-environment-jsdom": "^29.7.0", 20 | "jest-environment-miniflare": "^2.14.1", 21 | "ts-jest": "^29.1.1", 22 | "typescript": "^5.2.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@telegram/auth", 3 | "private": true, 4 | "repository": { 5 | "type": "git", 6 | "url": "git+https://github.com/manzoorwanijk/telegram-auth.git" 7 | }, 8 | "bugs": { 9 | "url": "https://github.com/manzoorwanijk/telegram-auth/issues" 10 | }, 11 | "author": "Manzoor Wani ", 12 | "license": "MIT", 13 | "scripts": { 14 | "build": "turbo run build", 15 | "build:p": "turbo run build --filter=./packages/*", 16 | "build:docs": "turbo run build:docs", 17 | "clean": "turbo run clean", 18 | "deploy": "turbo run deploy", 19 | "dev": "turbo run dev --parallel", 20 | "kick-off": "pnpm clean && pnpm build:p", 21 | "lint": "eslint \"{packages,examples,internal}/**/*.{ts,tsx,js,jsx,json}\"", 22 | "lint:fix": "pnpm run lint --fix", 23 | "release": "changeset publish", 24 | "release:dev": "changeset publish --tag dev", 25 | "test": "turbo run test", 26 | "typecheck": "tsc --noEmit", 27 | "version-packages": "changeset version && pnpm -r run version", 28 | "version:dev": "changeset version --snapshot dev" 29 | }, 30 | "devDependencies": { 31 | "@changesets/changelog-github": "0.5.0", 32 | "@changesets/cli": "2.27.1", 33 | "@telegram-auth/eslint-config": "workspace:*", 34 | "eslint": "^8.56.0", 35 | "prettier": "^3.2.4", 36 | "rimraf": "^5.0.5", 37 | "turbo": "^1.11.3", 38 | "typescript": "^5.3.3" 39 | }, 40 | "workspaces": [ 41 | "examples/*", 42 | "internal/*", 43 | "packages/*" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /packages/react/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @telegram-auth/react Change Log 2 | 3 | ## 1.0.4 4 | 5 | ### Patch Changes 6 | 7 | - [#28](https://github.com/manzoorwanijk/telegram-auth/pull/28) [`d84bbd2`](https://github.com/manzoorwanijk/telegram-auth/commit/d84bbd25374d4a40c9b0166c23c16487f05d97de) Thanks [@manzoorwanijk](https://github.com/manzoorwanijk)! - Fixed conditional rendering of login button 8 | 9 | ## 1.0.3 10 | 11 | ### Patch Changes 12 | 13 | - [#22](https://github.com/manzoorwanijk/telegram-auth/pull/22) [`0444dc6`](https://github.com/manzoorwanijk/telegram-auth/commit/0444dc663acae11117a44cc4284a9f773a758328) Thanks [@manzoorwanijk](https://github.com/manzoorwanijk)! - Updated docs 14 | 15 | ## 1.0.2 16 | 17 | ### Patch Changes 18 | 19 | - [#17](https://github.com/manzoorwanijk/telegram-auth/pull/17) [`724b94f`](https://github.com/manzoorwanijk/telegram-auth/commit/724b94f889d18f46deaf54e76c3f8d038c9225bf) Thanks [@manzoorwanijk](https://github.com/manzoorwanijk)! - Fixed npm TS types for CJS 20 | 21 | ## 1.0.1 22 | 23 | ### Patch Changes 24 | 25 | - 57581b5: Fixed type exports 26 | 27 | ## 1.0.0 28 | 29 | ### Patch Changes 30 | 31 | - 7c92821: Initial release 32 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | # @telegram-auth/react 2 | 3 | [`@telegram-auth/react`](https://www.npmjs.com/package/@telegram-auth/react) exports a React component ([LoginButton](./docs#loginbutton)) to render a Telegram Login button using [Telegram Login Widget](https://core.telegram.org/widgets/login). 4 | 5 | ## Documentation 6 | 7 | - [Reference](./docs/README.md) 8 | 9 | ## Install 10 | 11 | ```sh 12 | # npm 13 | npm install @telegram-auth/react 14 | 15 | # yarn 16 | yarn add @telegram-auth/react 17 | 18 | # with pnpm 19 | pnpm add @telegram-auth/react 20 | ``` 21 | 22 | ## Usage 23 | 24 | ### with `authCallbackUrl` 25 | 26 | If you specify `authCallbackUrl`, the user will be redirected to the specified URL after the authentication process is completed. You must validate the data sent to the URL as query params before authorizing the user. 27 | 28 | 29 | ```tsx title=src/App.tsx 30 | import { LoginButton } from '@telegram-auth/react'; 31 | 32 | function App() { 33 | return ( 34 |
35 | 43 |
44 | ); 45 | } 46 | ``` 47 | 48 | ### with `onAuthCallback` 49 | 50 | 51 | ```jsx title=src/App.jsx 52 | import { LoginButton } from '@telegram-auth/react'; 53 | 54 | function App() { 55 | return ( 56 |
57 | { 60 | console.log(data); 61 | // call your backend here to validate the data and sign in the user 62 | }} 63 | /> 64 |
65 | ); 66 | } 67 | ``` 68 | 69 | ## Validation 70 | 71 | You can use [`@telegram-auth/server`](../server) server-side to validate the data. 72 | 73 | 74 | ```ts title=validate.ts 75 | import { AuthDataValidator } from '@telegram-auth/server'; 76 | import { urlStrToAuthDataMap } from '@telegram-auth/server/utils'; 77 | 78 | const validator = new AuthDataValidator({ botToken: process.env.BOT_TOKEN }); 79 | 80 | const data = urlStrToAuthDataMap(request.url); 81 | 82 | try { 83 | const user = await validator.validate(data); 84 | 85 | // The data is now valid and you can sign in the user. 86 | 87 | console.log(user); 88 | } catch (error) { 89 | console.error(error); 90 | } 91 | ``` 92 | -------------------------------------------------------------------------------- /packages/react/docs/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /packages/react/docs/README.md: -------------------------------------------------------------------------------- 1 | @telegram-auth/react 2 | 3 | # @telegram-auth/react 4 | 5 | ## Table of contents 6 | 7 | ### Interfaces 8 | 9 | - [TelegramAuthData](interfaces/TelegramAuthData.md) 10 | - [LoginButtonProps](interfaces/LoginButtonProps.md) 11 | - [CreateScriptOptions](interfaces/CreateScriptOptions.md) 12 | 13 | ### Functions 14 | 15 | - [LoginButton](README.md#loginbutton) 16 | - [createScript](README.md#createscript) 17 | 18 | ## Functions 19 | 20 | ### LoginButton 21 | 22 | ▸ **LoginButton**(`props`): `Element` 23 | 24 | A React component that renders a Telegram login button. 25 | 26 | #### Parameters 27 | 28 | | Name | Type | Description | 29 | | :------ | :------ | :------ | 30 | | `props` | [`LoginButtonProps`](interfaces/LoginButtonProps.md) | The props to pass to the component. | 31 | 32 | #### Returns 33 | 34 | `Element` 35 | 36 | A React component that renders the Telegram login button. 37 | 38 | **`See`** 39 | 40 | https://core.telegram.org/widgets/login 41 | 42 | #### Defined in 43 | 44 | [LoginButton.tsx:23](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/LoginButton.tsx#L23) 45 | 46 | ___ 47 | 48 | ### createScript 49 | 50 | ▸ **createScript**(`options`): [`HTMLScriptElement`]( https://developer.mozilla.org/docs/Web/API/HTMLScriptElement ) 51 | 52 | It creates a script tag with the right attributes to load the Telegram widget 53 | 54 | #### Parameters 55 | 56 | | Name | Type | Description | 57 | | :------ | :------ | :------ | 58 | | `options` | [`CreateScriptOptions`](interfaces/CreateScriptOptions.md) | The options to create the script. | 59 | 60 | #### Returns 61 | 62 | [`HTMLScriptElement`]( https://developer.mozilla.org/docs/Web/API/HTMLScriptElement ) 63 | 64 | A script element 65 | 66 | **`See`** 67 | 68 | https://core.telegram.org/widgets/login 69 | 70 | #### Defined in 71 | 72 | [createScript.ts:11](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/createScript.ts#L11) 73 | -------------------------------------------------------------------------------- /packages/react/docs/interfaces/CreateScriptOptions.md: -------------------------------------------------------------------------------- 1 | [@telegram-auth/react](../README.md) / CreateScriptOptions 2 | 3 | # Interface: CreateScriptOptions 4 | 5 | The options to create the script. 6 | 7 | ## Hierarchy 8 | 9 | - [`LoginButtonProps`](LoginButtonProps.md) 10 | 11 | ↳ **`CreateScriptOptions`** 12 | 13 | ## Table of contents 14 | 15 | ### Properties 16 | 17 | - [botUsername](CreateScriptOptions.md#botusername) 18 | - [authCallbackUrl](CreateScriptOptions.md#authcallbackurl) 19 | - [buttonSize](CreateScriptOptions.md#buttonsize) 20 | - [cornerRadius](CreateScriptOptions.md#cornerradius) 21 | - [lang](CreateScriptOptions.md#lang) 22 | - [onAuthCallback](CreateScriptOptions.md#onauthcallback) 23 | - [requestAccess](CreateScriptOptions.md#requestaccess) 24 | - [showAvatar](CreateScriptOptions.md#showavatar) 25 | - [widgetVersion](CreateScriptOptions.md#widgetversion) 26 | 27 | ## Properties 28 | 29 | ### botUsername 30 | 31 | • **botUsername**: `string` 32 | 33 | The username of the bot that will be used to authenticate the user. 34 | 35 | #### Inherited from 36 | 37 | [LoginButtonProps](LoginButtonProps.md).[botUsername](LoginButtonProps.md#botusername) 38 | 39 | #### Defined in 40 | 41 | [types.ts:25](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L25) 42 | 43 | ___ 44 | 45 | ### authCallbackUrl 46 | 47 | • `Optional` **authCallbackUrl**: `string` 48 | 49 | The URL where the auth data from Telegram will be sent. 50 | 51 | #### Inherited from 52 | 53 | [LoginButtonProps](LoginButtonProps.md).[authCallbackUrl](LoginButtonProps.md#authcallbackurl) 54 | 55 | #### Defined in 56 | 57 | [types.ts:20](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L20) 58 | 59 | ___ 60 | 61 | ### buttonSize 62 | 63 | • `Optional` **buttonSize**: ``"large"`` \| ``"medium"`` \| ``"small"`` 64 | 65 | The size of the button. 66 | 67 | **`Default`** 68 | 69 | ```ts 70 | 'large' 71 | ``` 72 | 73 | #### Inherited from 74 | 75 | [LoginButtonProps](LoginButtonProps.md).[buttonSize](LoginButtonProps.md#buttonsize) 76 | 77 | #### Defined in 78 | 79 | [types.ts:32](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L32) 80 | 81 | ___ 82 | 83 | ### cornerRadius 84 | 85 | • `Optional` **cornerRadius**: `number` 86 | 87 | The radius of the button corners. 88 | 89 | #### Inherited from 90 | 91 | [LoginButtonProps](LoginButtonProps.md).[cornerRadius](LoginButtonProps.md#cornerradius) 92 | 93 | #### Defined in 94 | 95 | [types.ts:37](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L37) 96 | 97 | ___ 98 | 99 | ### lang 100 | 101 | • `Optional` **lang**: `string` 102 | 103 | The language of the button. 104 | 105 | **`Default`** 106 | 107 | ```ts 108 | "en" 109 | ``` 110 | 111 | #### Inherited from 112 | 113 | [LoginButtonProps](LoginButtonProps.md).[lang](LoginButtonProps.md#lang) 114 | 115 | #### Defined in 116 | 117 | [types.ts:44](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L44) 118 | 119 | ___ 120 | 121 | ### onAuthCallback 122 | 123 | • `Optional` **onAuthCallback**: (`data`: [`TelegramAuthData`](TelegramAuthData.md)) => `void` 124 | 125 | The callback function that will be called when the user is authenticated. 126 | 127 | #### Type declaration 128 | 129 | ▸ (`data`): `void` 130 | 131 | ##### Parameters 132 | 133 | | Name | Type | 134 | | :------ | :------ | 135 | | `data` | [`TelegramAuthData`](TelegramAuthData.md) | 136 | 137 | ##### Returns 138 | 139 | `void` 140 | 141 | #### Inherited from 142 | 143 | [LoginButtonProps](LoginButtonProps.md).[onAuthCallback](LoginButtonProps.md#onauthcallback) 144 | 145 | #### Defined in 146 | 147 | [types.ts:49](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L49) 148 | 149 | ___ 150 | 151 | ### requestAccess 152 | 153 | • `Optional` **requestAccess**: ``null`` \| ``"write"`` 154 | 155 | The access level that the bot will request. 156 | 157 | **`Default`** 158 | 159 | ```ts 160 | "write" 161 | ``` 162 | 163 | #### Inherited from 164 | 165 | [LoginButtonProps](LoginButtonProps.md).[requestAccess](LoginButtonProps.md#requestaccess) 166 | 167 | #### Defined in 168 | 169 | [types.ts:56](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L56) 170 | 171 | ___ 172 | 173 | ### showAvatar 174 | 175 | • `Optional` **showAvatar**: `boolean` 176 | 177 | Whether to show the user's avatar. 178 | 179 | **`Default`** 180 | 181 | ```ts 182 | true 183 | ``` 184 | 185 | #### Inherited from 186 | 187 | [LoginButtonProps](LoginButtonProps.md).[showAvatar](LoginButtonProps.md#showavatar) 188 | 189 | #### Defined in 190 | 191 | [types.ts:63](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L63) 192 | 193 | ___ 194 | 195 | ### widgetVersion 196 | 197 | • `Optional` **widgetVersion**: `string` \| `number` 198 | 199 | The version of the Telegram widget to deal with browser caching. 200 | 201 | #### Inherited from 202 | 203 | [LoginButtonProps](LoginButtonProps.md).[widgetVersion](LoginButtonProps.md#widgetversion) 204 | 205 | #### Defined in 206 | 207 | [types.ts:68](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L68) 208 | -------------------------------------------------------------------------------- /packages/react/docs/interfaces/LoginButtonProps.md: -------------------------------------------------------------------------------- 1 | [@telegram-auth/react](../README.md) / LoginButtonProps 2 | 3 | # Interface: LoginButtonProps 4 | 5 | ## Hierarchy 6 | 7 | - **`LoginButtonProps`** 8 | 9 | ↳ [`CreateScriptOptions`](CreateScriptOptions.md) 10 | 11 | ## Table of contents 12 | 13 | ### Properties 14 | 15 | - [botUsername](LoginButtonProps.md#botusername) 16 | - [authCallbackUrl](LoginButtonProps.md#authcallbackurl) 17 | - [buttonSize](LoginButtonProps.md#buttonsize) 18 | - [cornerRadius](LoginButtonProps.md#cornerradius) 19 | - [lang](LoginButtonProps.md#lang) 20 | - [onAuthCallback](LoginButtonProps.md#onauthcallback) 21 | - [requestAccess](LoginButtonProps.md#requestaccess) 22 | - [showAvatar](LoginButtonProps.md#showavatar) 23 | - [widgetVersion](LoginButtonProps.md#widgetversion) 24 | 25 | ## Properties 26 | 27 | ### botUsername 28 | 29 | • **botUsername**: `string` 30 | 31 | The username of the bot that will be used to authenticate the user. 32 | 33 | #### Defined in 34 | 35 | [types.ts:25](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L25) 36 | 37 | ___ 38 | 39 | ### authCallbackUrl 40 | 41 | • `Optional` **authCallbackUrl**: `string` 42 | 43 | The URL where the auth data from Telegram will be sent. 44 | 45 | #### Defined in 46 | 47 | [types.ts:20](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L20) 48 | 49 | ___ 50 | 51 | ### buttonSize 52 | 53 | • `Optional` **buttonSize**: ``"large"`` \| ``"medium"`` \| ``"small"`` 54 | 55 | The size of the button. 56 | 57 | **`Default`** 58 | 59 | ```ts 60 | 'large' 61 | ``` 62 | 63 | #### Defined in 64 | 65 | [types.ts:32](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L32) 66 | 67 | ___ 68 | 69 | ### cornerRadius 70 | 71 | • `Optional` **cornerRadius**: `number` 72 | 73 | The radius of the button corners. 74 | 75 | #### Defined in 76 | 77 | [types.ts:37](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L37) 78 | 79 | ___ 80 | 81 | ### lang 82 | 83 | • `Optional` **lang**: `string` 84 | 85 | The language of the button. 86 | 87 | **`Default`** 88 | 89 | ```ts 90 | "en" 91 | ``` 92 | 93 | #### Defined in 94 | 95 | [types.ts:44](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L44) 96 | 97 | ___ 98 | 99 | ### onAuthCallback 100 | 101 | • `Optional` **onAuthCallback**: (`data`: [`TelegramAuthData`](TelegramAuthData.md)) => `void` 102 | 103 | The callback function that will be called when the user is authenticated. 104 | 105 | #### Type declaration 106 | 107 | ▸ (`data`): `void` 108 | 109 | ##### Parameters 110 | 111 | | Name | Type | 112 | | :------ | :------ | 113 | | `data` | [`TelegramAuthData`](TelegramAuthData.md) | 114 | 115 | ##### Returns 116 | 117 | `void` 118 | 119 | #### Defined in 120 | 121 | [types.ts:49](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L49) 122 | 123 | ___ 124 | 125 | ### requestAccess 126 | 127 | • `Optional` **requestAccess**: ``null`` \| ``"write"`` 128 | 129 | The access level that the bot will request. 130 | 131 | **`Default`** 132 | 133 | ```ts 134 | "write" 135 | ``` 136 | 137 | #### Defined in 138 | 139 | [types.ts:56](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L56) 140 | 141 | ___ 142 | 143 | ### showAvatar 144 | 145 | • `Optional` **showAvatar**: `boolean` 146 | 147 | Whether to show the user's avatar. 148 | 149 | **`Default`** 150 | 151 | ```ts 152 | true 153 | ``` 154 | 155 | #### Defined in 156 | 157 | [types.ts:63](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L63) 158 | 159 | ___ 160 | 161 | ### widgetVersion 162 | 163 | • `Optional` **widgetVersion**: `string` \| `number` 164 | 165 | The version of the Telegram widget to deal with browser caching. 166 | 167 | #### Defined in 168 | 169 | [types.ts:68](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L68) 170 | -------------------------------------------------------------------------------- /packages/react/docs/interfaces/TelegramAuthData.md: -------------------------------------------------------------------------------- 1 | [@telegram-auth/react](../README.md) / TelegramAuthData 2 | 3 | # Interface: TelegramAuthData 4 | 5 | The data received from Telegram when the user is authenticated. 6 | 7 | **`See`** 8 | 9 | https://core.telegram.org/widgets/login#receiving-authorization-data 10 | 11 | ## Table of contents 12 | 13 | ### Properties 14 | 15 | - [id](TelegramAuthData.md#id) 16 | - [first\_name](TelegramAuthData.md#first_name) 17 | - [auth\_date](TelegramAuthData.md#auth_date) 18 | - [hash](TelegramAuthData.md#hash) 19 | - [last\_name](TelegramAuthData.md#last_name) 20 | - [photo\_url](TelegramAuthData.md#photo_url) 21 | - [username](TelegramAuthData.md#username) 22 | 23 | ## Properties 24 | 25 | ### id 26 | 27 | • **id**: `number` 28 | 29 | #### Defined in 30 | 31 | [types.ts:7](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L7) 32 | 33 | ___ 34 | 35 | ### first\_name 36 | 37 | • **first\_name**: `string` 38 | 39 | #### Defined in 40 | 41 | [types.ts:8](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L8) 42 | 43 | ___ 44 | 45 | ### auth\_date 46 | 47 | • **auth\_date**: `number` 48 | 49 | #### Defined in 50 | 51 | [types.ts:9](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L9) 52 | 53 | ___ 54 | 55 | ### hash 56 | 57 | • **hash**: `string` 58 | 59 | #### Defined in 60 | 61 | [types.ts:10](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L10) 62 | 63 | ___ 64 | 65 | ### last\_name 66 | 67 | • `Optional` **last\_name**: `string` 68 | 69 | #### Defined in 70 | 71 | [types.ts:11](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L11) 72 | 73 | ___ 74 | 75 | ### photo\_url 76 | 77 | • `Optional` **photo\_url**: `string` 78 | 79 | #### Defined in 80 | 81 | [types.ts:12](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L12) 82 | 83 | ___ 84 | 85 | ### username 86 | 87 | • `Optional` **username**: `string` 88 | 89 | #### Defined in 90 | 91 | [types.ts:13](https://github.com/manzoorwanijk/telegram-auth/blob/d84bbd25374d4a40c9b0166c23c16487f05d97de/packages/react/src/types.ts#L13) 92 | -------------------------------------------------------------------------------- /packages/react/jest.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('@telegram-auth/jest/jest.config.dom'); 2 | 3 | /** 4 | * @type {import('ts-jest').JestConfigWithTsJest} 5 | */ 6 | const config = { 7 | ...baseConfig, 8 | }; 9 | 10 | module.exports = config; 11 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@telegram-auth/react", 3 | "version": "1.0.4", 4 | "description": "React component to display Telegram Login Widget", 5 | "keywords": [ 6 | "telegram", 7 | "bot", 8 | "login", 9 | "widget", 10 | "react" 11 | ], 12 | "author": "Manzoor Wani ", 13 | "homepage": "https://github.com/manzoorwanijk/telegram-auth/tree/main/packages/react", 14 | "license": "MIT", 15 | "main": "dist/cjs/index.js", 16 | "module": "dist/esm/index.js", 17 | "source": "src/index.ts", 18 | "exports": { 19 | "./package.json": "./package.json", 20 | ".": { 21 | "import": "./dist/esm/index.js", 22 | "require": "./dist/cjs/index.js" 23 | } 24 | }, 25 | "files": [ 26 | "dist", 27 | "src" 28 | ], 29 | "sideEffects": false, 30 | "publishConfig": { 31 | "access": "public" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/manzoorwanijk/telegram-auth.git", 36 | "directory": "packages/react" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/manzoorwanijk/telegram-auth/issues" 40 | }, 41 | "clean-package": "../../config/clean-package.json", 42 | "peerDependencies": { 43 | "react": ">=18" 44 | }, 45 | "scripts": { 46 | "build": "pnpm build:ts", 47 | "build:cjs": "tsc --project ./tsconfig.build.json --module commonjs --moduleResolution node --outDir dist/cjs", 48 | "build:docs": "typedoc", 49 | "build:esm": "tsc --project ./tsconfig.build.json", 50 | "build:ts": "pnpm build:esm && pnpm build:cjs", 51 | "clean": "rimraf dist .turbo", 52 | "dev": "tsc --watch", 53 | "postpack": "clean-package restore", 54 | "prepack": "clean-package", 55 | "prepublishOnly": "pnpm build", 56 | "test": "jest", 57 | "test:watch": "jest --watch", 58 | "typecheck": "tsc --noEmit", 59 | "version": "pnpm build:docs" 60 | }, 61 | "devDependencies": { 62 | "@telegram-auth/jest": "workspace:*", 63 | "@testing-library/dom": "^9.3.3", 64 | "@testing-library/jest-dom": "6.1.4", 65 | "@testing-library/react": "^14.0.0", 66 | "@types/jest": "^29.5.6", 67 | "@types/react": "^18.2.33", 68 | "@types/react-dom": "^18.2.14", 69 | "clean-package": "^2.2.0", 70 | "react": "^18.2.0", 71 | "react-dom": "^18.2.0", 72 | "typedoc": "^0.25.2", 73 | "typedoc-plugin-markdown": "^3.16.0", 74 | "typedoc-plugin-mdn-links": "^3.1.0", 75 | "typescript": "^5.2.2" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /packages/react/src/LoginButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | import { LoginButtonProps, TTelegramAuthLogin } from './types'; 3 | import { createScript } from './createScript'; 4 | 5 | /** 6 | * It takes an object with a bunch of properties and assigns it to the global variable 7 | * `TelegramAuthLogin` 8 | * 9 | * @param {TTelegramAuthLogin} options - The options to set on the global variable. 10 | */ 11 | function initTelegramAuthLogin(options: TTelegramAuthLogin) { 12 | window.TelegramAuthLogin = options; 13 | } 14 | 15 | /** 16 | * A React component that renders a Telegram login button. 17 | * 18 | * @see https://core.telegram.org/widgets/login 19 | * 20 | * @param {LoginButtonProps} props The props to pass to the component. 21 | * @returns A React component that renders the Telegram login button. 22 | */ 23 | export function LoginButton(props: LoginButtonProps) { 24 | const hiddenDivRef = useRef(null); 25 | const scriptRef = useRef(); 26 | 27 | useEffect(() => { 28 | // destroy the existing script element 29 | scriptRef.current?.remove(); 30 | 31 | // init the global variable 32 | initTelegramAuthLogin({ onAuthCallback: props.onAuthCallback }); 33 | 34 | // create a new script element and save it 35 | scriptRef.current = createScript(props); 36 | 37 | // add the script element to the DOM 38 | hiddenDivRef.current?.after(scriptRef.current); 39 | 40 | // Save siblings before unmount 41 | const siblings = hiddenDivRef.current?.parentElement?.children || []; 42 | 43 | return () => { 44 | // destroy the script element on unmount 45 | scriptRef.current?.remove(); 46 | 47 | // We also need to remove the rendered iframe 48 | for (const element of siblings) { 49 | if (element instanceof HTMLIFrameElement && element.src.includes('oauth.telegram.org')) { 50 | element.remove(); 51 | break; 52 | } 53 | } 54 | }; 55 | }, [props]); 56 | 57 | return