├── server ├── .gitignore ├── requirements.txt ├── database.py ├── openapi.json └── webserver.py ├── client ├── .eslintrc.json ├── app │ ├── globals.css │ ├── favicon.ico │ ├── oauth │ │ └── [slug] │ │ │ ├── .page.js.swp │ │ │ └── page.js │ ├── layout.js │ ├── login │ │ └── page.js │ └── dashboard │ │ └── page.js ├── jsconfig.json ├── postcss.config.js ├── next.config.js ├── tailwind.config.js ├── package.json ├── .gitignore └── public │ ├── google-logo.svg │ ├── logo.svg │ └── copy.svg └── README.md /server/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/* 2 | -------------------------------------------------------------------------------- /client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /client/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | -------------------------------------------------------------------------------- /client/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anil-matcha/GPT-Actions/main/client/app/favicon.ico -------------------------------------------------------------------------------- /client/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client/app/oauth/[slug]/.page.js.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anil-matcha/GPT-Actions/main/client/app/oauth/[slug]/.page.js.swp -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | rewrites: async () => { 5 | return [ 6 | { 7 | source: '/api/:path*', 8 | destination:'http://127.0.0.1:5000/:path*', 9 | }, 10 | ] 11 | }, 12 | } 13 | 14 | module.exports = nextConfig 15 | -------------------------------------------------------------------------------- /client/app/layout.js: -------------------------------------------------------------------------------- 1 | import { Inter } from 'next/font/google' 2 | import './globals.css' 3 | 4 | const inter = Inter({ subsets: ['latin'] }) 5 | 6 | export const metadata = { 7 | title: 'GPTAuth', 8 | description: 'Authenticate with your GPT', 9 | } 10 | 11 | export default function RootLayout({ children }) { 12 | return ( 13 | 14 | {children} 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 5 | './components/**/*.{js,ts,jsx,tsx,mdx}', 6 | './app/**/*.{js,ts,jsx,tsx,mdx}', 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 12 | 'gradient-conic': 13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | } 19 | -------------------------------------------------------------------------------- /server/requirements.txt: -------------------------------------------------------------------------------- 1 | alembic==1.13.0 2 | blinker==1.7.0 3 | certifi==2023.11.17 4 | charset-normalizer==3.3.2 5 | click==8.1.7 6 | Flask==3.0.0 7 | Flask-Login==0.6.3 8 | Flask-Migrate==4.0.5 9 | Flask-SQLAlchemy==3.1.1 10 | idna==3.6 11 | importlib-metadata==7.0.0 12 | importlib-resources==6.1.1 13 | itsdangerous==2.1.2 14 | Jinja2==3.1.2 15 | Mako==1.3.0 16 | MarkupSafe==2.1.3 17 | oauthlib==3.2.2 18 | requests==2.31.0 19 | requests-oauthlib==1.3.1 20 | SQLAlchemy==2.0.23 21 | typing_extensions==4.9.0 22 | urllib3==2.1.0 23 | Werkzeug==3.0.1 24 | zipp==3.17.0 25 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.6.2", 13 | "next": "14.0.3", 14 | "react": "^18", 15 | "react-dom": "^18" 16 | }, 17 | "devDependencies": { 18 | "autoprefixer": "^10.0.1", 19 | "eslint": "^8", 20 | "eslint-config-next": "14.0.3", 21 | "postcss": "^8", 22 | "tailwindcss": "^3.3.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /client/.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 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 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 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | package-lock.json 39 | -------------------------------------------------------------------------------- /client/public/google-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /client/app/login/page.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import Image from 'next/image' 3 | import { useState,useEffect } from "react" 4 | import axios from "axios" 5 | 6 | function Login() { 7 | useEffect(()=>{ 8 | axios.get("/api/login",{params:{id:localStorage.getItem('authID'), url:window.location.href}}).then((res)=>{ 9 | window.location.href = localStorage.getItem('redirectTo') 10 | }).catch((err)=>{ 11 | console.log(err) 12 | }) 13 | },[]) 14 | return ( 15 |
16 | 17 |
18 |

Logging you in ...

19 |
20 | 21 | 22 |
23 | ); 24 | } 25 | 26 | export default Login; 27 | -------------------------------------------------------------------------------- /client/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 15 | -------------------------------------------------------------------------------- /client/public/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | GPT Auth 3 |
4 |

Auth for your Custom GPT

5 |
6 | 7 | 8 | 9 | **GPT Auth** provides a user-friendly solution to quickly setup oauth for your custom GPT and self-host it. 10 | 11 | ### Tutorial -> https://youtu.be/naJCyASboTk 12 | 13 | ## Key Features 🎯 14 | 15 | - **Robust Security**: Tailored for Custom GPTs, ensuring protection against unauthorized access. 16 | - **Access Control**: Effective monitoring and management of user access by GPT owners. 17 | - **Easy Integration**: User-friendly setup, comprehensive guide, and intuitive dashboard. 18 | - **Community & Support**: Access to a supportive community and dedicated developer support. 19 | - **Interactive Demo & Documentation**: Hands-on demo and extensive documentation available. 20 | 21 | ### Stack 22 | 23 | - Next.js 24 | - OpenAI 25 | - Tailwind 26 | - Flask 27 | - Sqlalchemy 28 | 29 | ### Run the project locally 30 | 31 | Minimum requirements to run the projects locally 32 | 33 | - Node.js v18 34 | - OpenAI API Key 35 | - Python3 36 | 37 | ```shell 38 | 39 | ## Client 40 | 41 | cd client 42 | 43 | npm install 44 | 45 | npm run build 46 | 47 | npm start 48 | 49 | ## Server 50 | 51 | cd ../server 52 | 53 | pip install -r requirements.txt 54 | 55 | python webserver.py 56 | ``` 57 | 58 | Use ngrok to test on a https endpoint 59 | 60 | ### Hosted version of GPT Auth 61 | 62 | If you don't want to setup locally and wish to use a hosted version, you can start from https://gpt-auth.thesamur.ai/ 63 | 64 | ### Demo 65 | 66 | Here is a demo chatgpt plugin built with GPT Auth to test the entire flow of the app https://chat.openai.com/g/g-xx7FJpizE-gpt-auth 67 | -------------------------------------------------------------------------------- /client/app/oauth/[slug]/page.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import Image from 'next/image' 3 | import { useState,useEffect } from "react" 4 | import axios from "axios" 5 | import { useSearchParams } from "next/navigation"; 6 | 7 | export default function Home({params:{slug}}) { 8 | const [authUrl,setAuthUrl] = useState("") 9 | const [user,setUser] = useState({email:'',password:''}) 10 | const searchParams = useSearchParams() 11 | useEffect(()=>{ 12 | localStorage.setItem('authID',slug) 13 | if(searchParams.get("myRedirect")!=null){ 14 | localStorage.setItem('redirectTo',searchParams.get("myRedirect")) 15 | }else{ 16 | var redirect_uri = searchParams.get('redirect_uri') 17 | redirect_uri += "?state="+searchParams.get('state')+"&code="+searchParams.get('client_id') 18 | localStorage.setItem('redirectTo',redirect_uri) 19 | } 20 | axios.get("/api/get_login_url",{params:{id:slug}}).then((res)=>{ 21 | setAuthUrl(res.data) 22 | }).catch((err)=>{ 23 | console.log(err) 24 | }) 25 | },[]) 26 | return ( 27 |
28 | 29 |
30 |

Welcome to GPTAuth

31 | Login to continue 32 |
33 |
34 | 38 | 39 |
40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /server/database.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from sqlalchemy import DateTime, extract, ForeignKey, MetaData, cast, Table,create_engine 3 | from flask import Flask 4 | from flask_migrate import Migrate 5 | from flask_login import UserMixin, LoginManager 6 | 7 | convention = { 8 | "ix": 'ix_%(column_0_label)s', 9 | "uq": "uq_%(table_name)s_%(column_0_name)s", 10 | "ck": "ck_%(table_name)s_%(constraint_name)s", 11 | "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", 12 | "pk": "pk_%(table_name)s" 13 | } 14 | metadata = MetaData(naming_convention=convention) 15 | db = SQLAlchemy(metadata=metadata) 16 | app = Flask(__name__) 17 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///./test.db' 18 | 19 | login_manager = LoginManager(app) 20 | 21 | @login_manager.user_loader 22 | def load_user(user_id): 23 | getAdmin = EmbedAI_Admin.query.filter_by(id=user_id).first() 24 | return getAdmin 25 | 26 | db.init_app(app) 27 | db.app = app 28 | app.secret_key = "gptAuth" 29 | migrate = Migrate(app, db, compare_type=True, 30 | render_as_batch=True) 31 | 32 | 33 | 34 | class User(UserMixin,db.Model): 35 | __tablename__ = "user" 36 | id = db.Column(db.Integer, primary_key=True, autoincrement=True) 37 | token = db.Column(db.String(100)) 38 | name = db.Column(db.String(100)) 39 | email = db.Column(db.String(100), unique=True, index=True) 40 | google_id = db.Column(db.String(100)) 41 | profile_image = db.Column(db.String(100000)) 42 | admin_id = db.Column(db.String(100), ForeignKey("admin.id"), index=True) 43 | 44 | def get_id(self): 45 | return (self.id) 46 | 47 | class Admin(db.Model): 48 | __tablename__ = "admin" 49 | id = db.Column(db.String(100), primary_key=True) 50 | client_id = db.Column(db.String(100)) 51 | client_secret = db.Column(db.String(100)) 52 | google_client_id = db.Column(db.String(200)) 53 | google_client_secret = db.Column(db.String(200)) 54 | google_login_url = db.Column(db.String) 55 | users = db.relationship('User', backref='admin', 56 | cascade="all,delete", lazy='dynamic') 57 | -------------------------------------------------------------------------------- /server/openapi.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.1.0", 3 | "info": { 4 | "title": "Greeting API Integration", 5 | "version": "1.0.0", 6 | "description": "API integration for fetching random greetings." 7 | }, 8 | "servers": [ 9 | { 10 | "url": "http://localhost:5000" 11 | } 12 | ], 13 | "paths": { 14 | "/get_greeting": { 15 | "get": { 16 | "operationId": "getRandomGreeting", 17 | "summary": "Get a random greeting", 18 | "description": "Fetches a random greeting from the Greeting API.", 19 | "responses": { 20 | "200": { 21 | "description": "Successful response with a random greeting", 22 | "content": { 23 | "application/json": { 24 | "schema": { 25 | "type": "object", 26 | "properties": { 27 | "greeting": { 28 | "type": "string", 29 | "description": "The random greeting." 30 | } 31 | }, 32 | "required": ["greeting"] 33 | } 34 | } 35 | } 36 | }, 37 | "400":{ 38 | "description": "Error with Authentication", 39 | "content": { 40 | "application/json": { 41 | "schema": { 42 | "type": "object", 43 | "properties": { 44 | "error": { 45 | "type": "string", 46 | "description": "The error occurred." 47 | } 48 | }, 49 | "required": ["error"] 50 | } 51 | } 52 | } 53 | }, 54 | "default": { 55 | "description": "Unexpected error", 56 | "content": { 57 | "application/json": { 58 | "schema": { 59 | "$ref": "#/components/schemas/Error" 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | } 67 | }, 68 | "components": { 69 | "schemas": { 70 | "Error": { 71 | "type": "object", 72 | "properties": { 73 | "code": { 74 | "type": "integer", 75 | "format": "int32" 76 | }, 77 | "message": { 78 | "type": "string" 79 | } 80 | } 81 | } 82 | }, 83 | "securitySchemes": { 84 | "basicAuth": { 85 | "type": "http", 86 | "scheme": "basic" 87 | } 88 | } 89 | }, 90 | "security": [ 91 | { 92 | "basicAuth": [] 93 | } 94 | ] 95 | } -------------------------------------------------------------------------------- /server/webserver.py: -------------------------------------------------------------------------------- 1 | from flask import Flask,jsonify,request,session,render_template,redirect,url_for,make_response,send_from_directory,send_file 2 | from flask_login import login_required, login_user, current_user, logout_user 3 | from database import * 4 | import string, secrets 5 | import random 6 | from requests_oauthlib import OAuth2Session 7 | import urllib.parse 8 | import os 9 | from urllib.parse import urlparse, parse_qs 10 | os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' 11 | 12 | GOOGLE_AUTH_BASE_URL = "https://accounts.google.com/o/oauth2/v2/auth" 13 | 14 | GOOGLE_SCOPE = [ 15 | "https://www.googleapis.com/auth/userinfo.email", 16 | "https://www.googleapis.com/auth/userinfo.profile", 17 | "openid" 18 | ] 19 | 20 | DOMAIN_URL = "https://9979-122-171-21-142.ngrok-free.app" 21 | 22 | @app.route('/verify_token/', methods=['POST']) 23 | def verify_token(slug): 24 | alphabet = string.ascii_letters + string.digits 25 | token = ''.join(secrets.choice(alphabet) for i in range(8)) 26 | getUser = User.query.filter_by(admin_id=slug).order_by(User.id.desc()).first() 27 | getUser.token = token 28 | db.session.commit() 29 | return jsonify(access_token=token) 30 | 31 | @app.route('/get_greeting', methods=['GET']) 32 | def get_greeting(): 33 | token = request.headers['Authorization'].split(" ")[1] 34 | if User.query.filter_by(token=token).first()!=None: 35 | hello_list = ['Good morning','Good afternoon','How do you do?','What’s new?','Well, look who it is!','How have you been?','What’s up buttercup?','What’s up doc?','How are you?','What’s up','How are you going?','Yo, what’s up?','Where’ve you been?','Whassup?','Hey man','Hey dude','How are you guys?','Oi!','Mate!','Hey, what’s up','Cheers','You right?','How ya goin?','How are ya?','Howdy','What’s going on?','What do you know?','How’s this weather?'] 36 | return random.choice(hello_list),200 37 | else: 38 | return "Invalid credentials",400 39 | 40 | @app.route('/get_credentials', methods=['POST']) 41 | def get_credentials(): 42 | data = request.get_json() 43 | if "id" in data: 44 | id = data['id'] 45 | getAdmin = Admin.query.filter_by(id=id).first() 46 | redirect_uri = DOMAIN_URL + "/login" 47 | google = OAuth2Session( 48 | getAdmin.google_client_id, scope=GOOGLE_SCOPE, redirect_uri=redirect_uri) 49 | login_url, state = google.authorization_url(GOOGLE_AUTH_BASE_URL) 50 | getAdmin.google_login_url = login_url 51 | db.session.commit() 52 | else: 53 | adminId = secrets.token_urlsafe(12) 54 | alphabet = string.ascii_letters + string.digits 55 | client_id = ''.join(secrets.choice(alphabet) for i in range(8)) 56 | client_secret = ''.join(secrets.choice(alphabet) for i in range(8)) 57 | google_client_id = data["googleId"] 58 | google_client_secret = data["googleSecret"] 59 | url_host = urllib.parse.urlsplit(request.url).hostname 60 | redirect_uri = DOMAIN_URL + "/login" 61 | google = OAuth2Session( 62 | google_client_id, scope=GOOGLE_SCOPE, redirect_uri=redirect_uri) 63 | login_url, state = google.authorization_url(GOOGLE_AUTH_BASE_URL) 64 | getAdmin = Admin(id=adminId,client_id=client_id,client_secret=client_secret,google_login_url=login_url,google_client_id=google_client_id,google_client_secret=google_client_secret) 65 | db.session.add(getAdmin) 66 | db.session.commit() 67 | return jsonify(id=getAdmin.client_id,secret=getAdmin.client_secret,adminId=getAdmin.id) 68 | 69 | @app.route('/get_login_url', methods=['GET']) 70 | def get_login_url(): 71 | id = request.args.get('id') 72 | getAdmin = Admin.query.filter_by(id=id).first() 73 | return jsonify(getAdmin.google_login_url) 74 | 75 | @app.route('/oauth/', methods=['GET']) 76 | def oauth_slug(slug): 77 | redirect_uri = request.args.get('redirect_uri') 78 | redirect_uri += "?state="+request.args.get('state')+"&code="+request.args.get('client_id') 79 | url = DOMAIN_URL + "/oauth/"+slug+"?myRedirect="+redirect_uri 80 | return redirect(url) 81 | 82 | @app.route("/login", methods=['GET']) 83 | def login(): 84 | adminId = request.args.get("id") 85 | if request.args.get("url") != None: 86 | request_url = request.args.get("url") 87 | parsed_url = urlparse(request_url) 88 | query_params = parse_qs(parsed_url.query) 89 | code = query_params.get('code', [None])[0] 90 | else: 91 | request_url = request.url 92 | code = None 93 | url_host = urllib.parse.urlsplit(request.url).hostname 94 | redirect_uri = "https://"+url_host+"/google_callback" 95 | getAdmin = Admin.query.filter_by(id=adminId).first() 96 | redirect_uri = DOMAIN_URL + "/login" 97 | google = OAuth2Session( 98 | getAdmin.google_client_id, scope=GOOGLE_SCOPE, redirect_uri=redirect_uri) 99 | token_url = "https://www.googleapis.com/oauth2/v4/token" 100 | welcome = False 101 | google.fetch_token(token_url, client_secret=getAdmin.google_client_secret, 102 | code=code) 103 | response = google.get( 104 | 'https://www.googleapis.com/oauth2/v1/userinfo').json() 105 | email = response["email"].lower() 106 | googleId = str(response["id"]) 107 | name = response["name"] 108 | 109 | getUser = User.query.filter_by(email=email).first() 110 | if getUser == None: 111 | getUser = User(email=email,google_id=googleId, name=name, admin_id=getAdmin.id) 112 | db.session.add(getUser) 113 | db.session.commit() 114 | else: 115 | getUser.google_id = googleId 116 | db.session.commit() 117 | login_user(getUser, remember=True) 118 | return "Success",200 119 | 120 | if __name__ =='__main__': 121 | app.run(host="0.0.0.0", debug = True, port=5000) 122 | -------------------------------------------------------------------------------- /client/app/dashboard/page.js: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import Image from 'next/image' 3 | import { useState,useEffect } from "react" 4 | import axios from "axios" 5 | 6 | function Dashboard() { 7 | const [created,setCreated] = useState(false) 8 | const [credentials,setCredentials] = useState({adminId:'',id:'',secret:''}) 9 | const [URLs,setURLs] = useState({auth:'',token:''}) 10 | const [googleCreds,setGoogleCreds] = useState({id:'',secret:''}) 11 | const [fullUrl, setFullUrl] = useState(''); 12 | const createCredentials = () => { 13 | if(googleCreds.id==""||googleCreds.secret==""){ 14 | alert('Enter Google Credentials!') 15 | }else{ 16 | axios.post("/api/get_credentials",{googleId:googleCreds.id,googleSecret:googleCreds.secret}).then((res)=>{ 17 | setCredentials({adminId:res.data.adminId,id:res.data.id,secret:res.data.secret}) 18 | setCreated(true) 19 | localStorage.setItem('authID',res.data.adminId) 20 | }).catch((err)=>{ 21 | console.log(err) 22 | }) 23 | } 24 | } 25 | const copyCredential = (cred) => { 26 | navigator.clipboard.writeText(cred) 27 | } 28 | 29 | useEffect(() => { 30 | // This code runs on the client-side 31 | const url = window.location.protocol + "//" + window.location.host + "/login"; 32 | setFullUrl(url); 33 | }, []); 34 | 35 | const handleButtonClick = () => { 36 | // Ensure this function also safely accesses `window` 37 | if (typeof window !== 'undefined') { 38 | copyCredential(window.location.host + "/login"); 39 | } 40 | }; 41 | 42 | useEffect(()=>{ 43 | if(credentials.adminId!=""){ 44 | setURLs({auth:window.location.protocol + "//" + window.location.host + "/oauth/"+credentials.adminId,token:window.location.protocol + "//" + window.location.host + "/api/verify_token/"+credentials.adminId}) 45 | } 46 | },[credentials]) 47 | useEffect(()=>{ 48 | if(localStorage.getItem('authID')!=null){ 49 | axios.post("/api/get_credentials",{id:localStorage.getItem('authID')}).then((res)=>{ 50 | setCredentials({adminId:res.data.adminId,id:res.data.id,secret:res.data.secret}) 51 | setCreated(true) 52 | }).catch((err)=>{ 53 | console.log(err) 54 | }) 55 | } 56 | },[]) 57 | return ( 58 |
59 | 60 |
61 |

Welcome to GPTAuth

62 | Create your unique credentials to use in your GPT, using your Google Oauth app details 63 |
64 |
65 | 66 |
67 | 73 | 79 |
80 |
81 |
82 | {created==false? 83 | <> 84 |
85 | 86 |
87 | setGoogleCreds({id:e.target.value,secret:googleCreds.secret})}/> 88 |
89 |
90 |
91 | 92 |
93 | setGoogleCreds({secret:e.target.value,id:googleCreds.id})}/> 94 |
95 |
96 | 100 | 101 | : 102 | <> 103 |
104 | 105 |
106 | 107 | 110 |
111 |
112 |
113 | 114 |
115 | 116 | 119 |
120 |
121 |
122 | 123 |
124 | 125 | 128 |
129 |
130 |
131 | 132 |
133 | 134 | 137 |
138 |
139 | 140 | } 141 |
142 | ); 143 | } 144 | 145 | export default Dashboard; 146 | --------------------------------------------------------------------------------