mirror of
				https://github.com/khairul169/vaulterm.git
				synced 2025-10-31 19:59:38 +07:00 
			
		
		
		
	fix: native gitlab & github login
This commit is contained in:
		
							parent
							
								
									83d0ed380d
								
							
						
					
					
						commit
						95f9f76edd
					
				
							
								
								
									
										2
									
								
								frontend/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								frontend/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -22,3 +22,5 @@ expo-env.d.ts | ||||
| !.env.example | ||||
| *.apk | ||||
| *.aab | ||||
| android/ | ||||
| ios/ | ||||
|  | ||||
| @ -1,41 +1,42 @@ | ||||
| { | ||||
|     "name": "Vaulterm", | ||||
|     "slug": "vaulterm", | ||||
|     "version": "1.0.0", | ||||
|     "orientation": "portrait", | ||||
|     "icon": "./assets/images/icon.png", | ||||
|     "scheme": "vaulterm", | ||||
|     "userInterfaceStyle": "automatic", | ||||
|     "newArchEnabled": true, | ||||
|     "splash": { | ||||
|         "image": "./assets/images/splash.png", | ||||
|         "resizeMode": "contain", | ||||
|         "backgroundColor": "#ffffff" | ||||
|     }, | ||||
|     "ios": { | ||||
|         "supportsTablet": true | ||||
|     }, | ||||
|     "android": { | ||||
|         "package": "sh.rul.vaulterm", | ||||
|         "adaptiveIcon": { | ||||
|             "foregroundImage": "./assets/images/adaptive-icon.png", | ||||
|             "backgroundColor": "#ffffff" | ||||
|         } | ||||
|     }, | ||||
|     "web": { | ||||
|         "bundler": "metro", | ||||
|         "output": "single", | ||||
|         "favicon": "./assets/images/favicon.png" | ||||
|     }, | ||||
|     "plugins": [ | ||||
|         "expo-router" | ||||
|     ], | ||||
|     "experiments": { | ||||
|         "typedRoutes": true | ||||
|     }, | ||||
|     "extra": { | ||||
|         "eas": { | ||||
|             "projectId": "3e0112c1-f0ed-423c-b5cf-95633f23f6dc" | ||||
|         } | ||||
|   "name": "Vaulterm", | ||||
|   "slug": "vaulterm", | ||||
|   "version": "1.0.0", | ||||
|   "orientation": "portrait", | ||||
|   "icon": "./assets/images/icon.png", | ||||
|   "scheme": "vaulterm", | ||||
|   "userInterfaceStyle": "automatic", | ||||
|   "newArchEnabled": true, | ||||
|   "splash": { | ||||
|     "image": "./assets/images/splash.png", | ||||
|     "resizeMode": "contain", | ||||
|     "backgroundColor": "#ffffff" | ||||
|   }, | ||||
|   "ios": { | ||||
|     "supportsTablet": true, | ||||
|     "bundleIdentifier": "sh.rul.vaulterm" | ||||
|   }, | ||||
|   "android": { | ||||
|     "package": "sh.rul.vaulterm", | ||||
|     "adaptiveIcon": { | ||||
|       "foregroundImage": "./assets/images/adaptive-icon.png", | ||||
|       "backgroundColor": "#ffffff" | ||||
|     } | ||||
| } | ||||
|   }, | ||||
|   "web": { | ||||
|     "bundler": "metro", | ||||
|     "output": "single", | ||||
|     "favicon": "./assets/images/favicon.png" | ||||
|   }, | ||||
|   "plugins": [ | ||||
|     "expo-router" | ||||
|   ], | ||||
|   "experiments": { | ||||
|     "typedRoutes": true | ||||
|   }, | ||||
|   "extra": { | ||||
|     "eas": { | ||||
|       "projectId": "3e0112c1-f0ed-423c-b5cf-95633f23f6dc" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -27,8 +27,9 @@ const api = ofetch.create({ | ||||
|       throw new Error("Unauthorized"); | ||||
|     } | ||||
| 
 | ||||
|     if (error.response._data) { | ||||
|       const message = error.response._data.message; | ||||
|     const data = error.response._data; | ||||
|     if (data) { | ||||
|       const message = typeof data === "string" ? data : data?.message; | ||||
|       throw new Error(message || "Something went wrong"); | ||||
|     } | ||||
|   }, | ||||
|  | ||||
| @ -6,8 +6,9 @@ | ||||
|   "scripts": { | ||||
|     "start": "expo start", | ||||
|     "reset-project": "node ./scripts/reset-project.js", | ||||
|     "android": "expo start --android", | ||||
|     "ios": "expo start --ios", | ||||
|     "prebuild": "expo prebuild", | ||||
|     "android": "expo run:android", | ||||
|     "ios": "expo run:ios", | ||||
|     "web": "expo start --web", | ||||
|     "test": "jest --watchAll", | ||||
|     "lint": "expo lint", | ||||
|  | ||||
| @ -1,9 +1,9 @@ | ||||
| import React, { useEffect, useMemo } from "react"; | ||||
| import { makeRedirectUri, useAuthRequest } from "expo-auth-session"; | ||||
| import appConfig from "@/app.json"; | ||||
| import { Button } from "tamagui"; | ||||
| import { useOAuthCallback } from "../hooks"; | ||||
| import { useServerConfig } from "@/hooks/useServerConfig"; | ||||
| import { scheme } from "@/app.json"; | ||||
| 
 | ||||
| const LoginGithubButton = () => { | ||||
|   const { data: clientId } = useServerConfig("github_client_id"); | ||||
| @ -20,7 +20,7 @@ const LoginGithubButton = () => { | ||||
|     { | ||||
|       clientId: clientId, | ||||
|       scopes: ["identity"], | ||||
|       redirectUri: makeRedirectUri({ scheme: appConfig.scheme }), | ||||
|       redirectUri: makeRedirectUri({ scheme, path: "auth/login" }), | ||||
|     }, | ||||
|     discovery | ||||
|   ); | ||||
| @ -28,7 +28,7 @@ const LoginGithubButton = () => { | ||||
|   useEffect(() => { | ||||
|     if (response?.type === "success") { | ||||
|       const { code } = response.params; | ||||
|       oauth.mutate(code); | ||||
|       oauth.mutate({ code }); | ||||
|     } | ||||
|   }, [response]); | ||||
| 
 | ||||
|  | ||||
| @ -1,8 +1,9 @@ | ||||
| import React, { useEffect, useMemo } from "react"; | ||||
| import { useAuthRequest } from "expo-auth-session"; | ||||
| import { makeRedirectUri, useAuthRequest } from "expo-auth-session"; | ||||
| import { Button } from "tamagui"; | ||||
| import { useOAuthCallback } from "../hooks"; | ||||
| import { useServerConfig } from "@/hooks/useServerConfig"; | ||||
| import { scheme } from "@/app.json"; | ||||
| 
 | ||||
| const LoginGitlabButton = () => { | ||||
|   const { data: clientId } = useServerConfig("gitlab_client_id"); | ||||
| @ -19,8 +20,7 @@ const LoginGitlabButton = () => { | ||||
|     { | ||||
|       clientId, | ||||
|       scopes: ["read_user"], | ||||
|       // redirectUri: makeRedirectUri({ scheme: appConfig.scheme }),
 | ||||
|       redirectUri: "http://localhost:8081", | ||||
|       redirectUri: makeRedirectUri({ scheme, path: "auth/login" }), | ||||
|     }, | ||||
|     discovery | ||||
|   ); | ||||
|  | ||||
| @ -45,14 +45,17 @@ export const useRegisterMutation = () => { | ||||
| 
 | ||||
| export const useOAuthCallback = (type: string) => { | ||||
|   return useMutation({ | ||||
|     mutationFn: async (params: { code: string; verifier?: string }) => { | ||||
|       const res = await api(`/auth/oauth/${type}/callback`, { params }); | ||||
|     mutationFn: async (body: { code: string; verifier?: string }) => { | ||||
|       const res = await api(`/auth/${type}`, { body, method: "POST" }); | ||||
|       const { data } = loginResultSchema.safeParse(res); | ||||
|       if (!data) { | ||||
|         throw new Error("Invalid response!"); | ||||
|       } | ||||
|       return data; | ||||
|     }, | ||||
|     onError: (err) => { | ||||
|       console.log(err); | ||||
|     }, | ||||
|     onSuccess(data) { | ||||
|       authStore.setState({ token: data.sessionId }); | ||||
|       router.replace("/"); | ||||
|  | ||||
| @ -27,28 +27,26 @@ func getGithubConfig() *oauth2.Config { | ||||
| 		ClientID:     os.Getenv("GITHUB_CLIENT_ID"), | ||||
| 		ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"), | ||||
| 		Endpoint:     github.Endpoint, | ||||
| 		// RedirectURL:  "http://localhost:3000/auth/oauth/github/callback", | ||||
| 		RedirectURL: "http://localhost:8081", | ||||
| 		Scopes:      []string{"read:user"}, | ||||
| 		RedirectURL:  "vaulterm://auth/login", | ||||
| 		Scopes:       []string{"read:user"}, | ||||
| 	} | ||||
| 	return githubCfg | ||||
| } | ||||
| 
 | ||||
| func githubRedir(c *fiber.Ctx) error { | ||||
| 	// Redirect to GitHub login page | ||||
| 	url := getGithubConfig().AuthCodeURL("state", oauth2.AccessTypeOffline) | ||||
| 	return c.Redirect(url) | ||||
| } | ||||
| 
 | ||||
| func githubCallback(c *fiber.Ctx) error { | ||||
| 	code := c.Query("code") | ||||
| 	if code == "" { | ||||
| 	var body struct { | ||||
| 		Code string `json:"code"` | ||||
| 	} | ||||
| 	if err := c.BodyParser(&body); err != nil { | ||||
| 		return c.Status(fiber.StatusBadRequest).SendString("Failed to parse request body") | ||||
| 	} | ||||
| 	if body.Code == "" { | ||||
| 		return c.Status(fiber.StatusBadRequest).SendString("Missing code") | ||||
| 	} | ||||
| 
 | ||||
| 	// Exchange code for a token | ||||
| 	cfg := getGithubConfig() | ||||
| 	token, err := cfg.Exchange(c.Context(), code) | ||||
| 	token, err := cfg.Exchange(c.Context(), body.Code) | ||||
| 	if err != nil { | ||||
| 		return c.Status(fiber.StatusInternalServerError).SendString("Failed to exchange token") | ||||
| 	} | ||||
| @ -61,10 +59,10 @@ func githubCallback(c *fiber.Ctx) error { | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, _ := io.ReadAll(resp.Body) | ||||
| 	data, _ := io.ReadAll(resp.Body) | ||||
| 	if resp.StatusCode != 200 { | ||||
| 		return c.Status(fiber.StatusInternalServerError). | ||||
| 			SendString(fmt.Sprintf("GitHub API error: %s", string(body))) | ||||
| 			SendString(fmt.Sprintf("GitHub API error: %s", string(data))) | ||||
| 	} | ||||
| 
 | ||||
| 	// Parse user info | ||||
| @ -76,7 +74,7 @@ func githubCallback(c *fiber.Ctx) error { | ||||
| 		Email     string `json:"email"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := json.Unmarshal(body, &user); err != nil { | ||||
| 	if err := json.Unmarshal(data, &user); err != nil { | ||||
| 		return c.Status(fiber.StatusInternalServerError).SendString("Failed to parse user info") | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -5,7 +5,6 @@ import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 
 | ||||
| @ -17,61 +16,39 @@ import ( | ||||
| 	"rul.sh/vaulterm/server/utils" | ||||
| ) | ||||
| 
 | ||||
| type GitlabCfg struct { | ||||
| 	oauth2.Config | ||||
| 	verifier  string | ||||
| 	challenge string | ||||
| } | ||||
| var gitlabCfg *oauth2.Config | ||||
| 
 | ||||
| var gitlabCfg *GitlabCfg | ||||
| 
 | ||||
| func getGitlabConfig() *GitlabCfg { | ||||
| func getGitlabConfig() *oauth2.Config { | ||||
| 	if gitlabCfg != nil { | ||||
| 		return gitlabCfg | ||||
| 	} | ||||
| 
 | ||||
| 	oauthCfg := oauth2.Config{ | ||||
| 	gitlabCfg = &oauth2.Config{ | ||||
| 		ClientID:     os.Getenv("GITLAB_CLIENT_ID"), | ||||
| 		ClientSecret: os.Getenv("GITLAB_CLIENT_SECRET"), | ||||
| 		Endpoint:     gitlab.Endpoint, | ||||
| 		// RedirectURL:  "http://localhost:3000/auth/oauth/gitlab/callback", | ||||
| 		RedirectURL: "http://localhost:8081", | ||||
| 		Scopes:      []string{"read_user"}, | ||||
| 	} | ||||
| 	verifier := oauth2.GenerateVerifier() | ||||
| 	challenge := oauth2.S256ChallengeFromVerifier(verifier) | ||||
| 
 | ||||
| 	gitlabCfg = &GitlabCfg{ | ||||
| 		Config:    oauthCfg, | ||||
| 		verifier:  verifier, | ||||
| 		challenge: challenge, | ||||
| 		RedirectURL:  "vaulterm://auth/login", | ||||
| 		Scopes:       []string{"read_user"}, | ||||
| 	} | ||||
| 	return gitlabCfg | ||||
| } | ||||
| 
 | ||||
| func gitlabRedir(c *fiber.Ctx) error { | ||||
| 	// Redirect to Gitlab login page | ||||
| 	url := getGitlabConfig(). | ||||
| 		AuthCodeURL("login", oauth2.S256ChallengeOption(getGitlabConfig().verifier)) | ||||
| 	return c.Redirect(url) | ||||
| } | ||||
| 
 | ||||
| func gitlabCallback(c *fiber.Ctx) error { | ||||
| 	cfg := getGitlabConfig() | ||||
| 	code := c.Query("code") | ||||
| 	verifier := c.Query("verifier") | ||||
| 
 | ||||
| 	if code == "" { | ||||
| 		return c.Status(fiber.StatusBadRequest).SendString("Missing code") | ||||
| 	var body struct { | ||||
| 		Code     string `json:"code"` | ||||
| 		Verifier string `json:"verifier"` | ||||
| 	} | ||||
| 	if verifier == "" { | ||||
| 		verifier = cfg.verifier | ||||
| 	if err := c.BodyParser(&body); err != nil { | ||||
| 		return c.Status(fiber.StatusBadRequest).SendString("Failed to parse request body") | ||||
| 	} | ||||
| 	if body.Code == "" || body.Verifier == "" { | ||||
| 		return c.Status(fiber.StatusBadRequest).SendString("Missing code or verifier") | ||||
| 	} | ||||
| 
 | ||||
| 	// Exchange code for a token | ||||
| 	token, err := cfg.Exchange(c.Context(), code, oauth2.VerifierOption(verifier)) | ||||
| 	cfg := getGitlabConfig() | ||||
| 	token, err := cfg.Exchange(c.Context(), body.Code, oauth2.VerifierOption(body.Verifier)) | ||||
| 	if err != nil { | ||||
| 		log.Println(token, err) | ||||
| 		return c.Status(fiber.StatusInternalServerError).SendString("Failed to exchange token") | ||||
| 	} | ||||
| 
 | ||||
| @ -83,10 +60,10 @@ func gitlabCallback(c *fiber.Ctx) error { | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, _ := io.ReadAll(resp.Body) | ||||
| 	data, _ := io.ReadAll(resp.Body) | ||||
| 	if resp.StatusCode != 200 { | ||||
| 		return c.Status(fiber.StatusInternalServerError). | ||||
| 			SendString(fmt.Sprintf("Gitlab API error: %s", string(body))) | ||||
| 			SendString(fmt.Sprintf("Gitlab API error: %s", string(data))) | ||||
| 	} | ||||
| 
 | ||||
| 	// Parse user info | ||||
| @ -98,7 +75,7 @@ func gitlabCallback(c *fiber.Ctx) error { | ||||
| 		Email     string `json:"email"` | ||||
| 	} | ||||
| 
 | ||||
| 	if err := json.Unmarshal(body, &user); err != nil { | ||||
| 	if err := json.Unmarshal(data, &user); err != nil { | ||||
| 		return c.Status(fiber.StatusInternalServerError).SendString("Failed to parse user info") | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -16,11 +16,8 @@ func Router(app *fiber.App) { | ||||
| 	router.Post("/register", register) | ||||
| 	router.Post("/logout", middleware.Protected(), logout) | ||||
| 
 | ||||
| 	oauth := router.Group("/oauth") | ||||
| 	oauth.Get("/github", githubRedir) | ||||
| 	oauth.Get("/github/callback", githubCallback) | ||||
| 	oauth.Get("/gitlab", gitlabRedir) | ||||
| 	oauth.Get("/gitlab/callback", gitlabCallback) | ||||
| 	router.Post("/github", githubCallback) | ||||
| 	router.Post("/gitlab", gitlabCallback) | ||||
| } | ||||
| 
 | ||||
| func login(c *fiber.Ctx) error { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user