whs-authz-authn/browser-use-oauth
Watch
1
0
Fork
You've already forked browser-use-oauth
0
mirror of https://github.com/j93es/browser-use-oauth.git synced 2026-10-12 07:21:21 +09:00

feat: Prompt 구조 개선

This commit is contained in:
암냥 2025-06-27 20:28:12 +09:00
commit 70e8bdbbde
12 changed files with 130 additions and 66 deletions

View file

@ -116,23 +116,30 @@ uv run run.py 1 100 --skh
## 1. 파일 생성 ## 1. 파일 생성
`lib/llm/prompt` 폴더로 `lib/llm/prompt` 폴더에서 fallback 폴더를 복사하여
![](./docs/list.png) 원하는 프로바이더를 추가해줍니다. `ex) lib/llm/prompt/Google/`
fallback.py를 복사하여 ## 2. prompt.py 수정
원하는 프로바이더를 추가해줍니다. `ex) lib/llm/prompt/Google.py` Prompt에서 추가한 파일을 prompt.py에서 수정합니다.
## 2. __init__.py 수정 만약 로그인 정보를 넣고 싶다면 Sensitive
`Log into example.com as user x_username with password x_password`
![](./docs/guide.png) ## 3. model.py
Prompt에서 추가한 파일을 __init__.py에서 import합니다. 응답할 때 원하는 리턴 값을 `dict`로 받습니다.
## 3. 파일 수정 ## 4. \_\_init\_\_.py 수정
![alt text](./docs/guide_0.png)
생성한 파일에서 프롬프트를 수정합니다. 추가한 prompt에 따라 import합니다.
## 5. 사용 방법
```py
from lib.llm.prompt.fallback import prompt, model
```
# 참고하면 좋을만한 것 # 참고하면 좋을만한 것

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

BIN
docs/guide_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

View file

@ -15,7 +15,6 @@ from lib.utils import (
config, config,
) )
from lib.llm import CreateChatGoogleGenerativeAI, get_prompt from lib.llm import CreateChatGoogleGenerativeAI, get_prompt
import lib.browser_use.model as model
# Exponential backoff settings # Exponential backoff settings
INITIAL_BACKOFF = int(os.getenv("INITIAL_BACKOFF", "60")) # seconds INITIAL_BACKOFF = int(os.getenv("INITIAL_BACKOFF", "60")) # seconds
@ -77,6 +76,7 @@ async def extract_oauth_list(url: str):
"""첫 번째 Agent: 로그인 페이지를 찾고 OAuth 리스트만 추출""" """첫 번째 Agent: 로그인 페이지를 찾고 OAuth 리스트만 추출"""
target_url = url if url.startswith("http") else f"https://{url}" target_url = url if url.startswith("http") else f"https://{url}"
print(f"🔎 OAuth 리스트 추출 시작: {target_url}") print(f"🔎 OAuth 리스트 추출 시작: {target_url}")
prompt, model = get_prompt("auth")
agent_config = { agent_config = {
"url": target_url, "url": target_url,
@ -97,10 +97,10 @@ async def extract_oauth_list(url: str):
else None else None
), ),
"controller": Controller( "controller": Controller(
output_model=model.OAuthList, output_model=model if not isinstance(model, str) else None,
exclude_actions=["search_google", "unknown_action", "unkown"], exclude_actions=["search_google", "unknown_action", "unkown"],
), ),
"extend_planner_system_message": get_prompt("auth"), "extend_planner_system_message": prompt,
} }
} }
@ -117,7 +117,12 @@ async def extract_oauth_list(url: str):
try: try:
data = json.loads(final_result) data = json.loads(final_result)
oauth_providers = data.get("oauth_providers", []) oauth_providers = data.get("oauth_providers", [])
return [model.OAuth(provider=provider) for provider in oauth_providers] if not oauth_providers:
print("❌ OAuth 제공자가 없습니다.")
logger(f"❌ {url} - OAuth 제공자 없음: {final_result}")
return []
print(f"✅ OAuth 제공자 추출 완료: {oauth_providers}")
return oauth_providers
except (json.JSONDecodeError, KeyError) as e: except (json.JSONDecodeError, KeyError) as e:
print(f"❌ 결과 파싱 실패: {e}") print(f"❌ 결과 파싱 실패: {e}")
logger(f"❌ {url} 결과 파싱 실패: {final_result}") logger(f"❌ {url} 결과 파싱 실패: {final_result}")
@ -129,6 +134,8 @@ async def test_oauth_login(url: str, oauth_provider: str):
target_url = url if url.startswith("http") else f"https://{url}" target_url = url if url.startswith("http") else f"https://{url}"
print(f"🔐 {oauth_provider} 로그인 시작: {target_url}") print(f"🔐 {oauth_provider} 로그인 시작: {target_url}")
prompt, model = get_prompt(oauth_provider)
agent_config = { agent_config = {
"url": target_url, "url": target_url,
"log_context": f"{oauth_provider} 로그인", "log_context": f"{oauth_provider} 로그인",
@ -149,9 +156,10 @@ async def test_oauth_login(url: str, oauth_provider: str):
else None else None
), ),
"controller": Controller( "controller": Controller(
output_model=model if not isinstance(model, str) else None,
exclude_actions=["search_google", "unknown_action", "unkown"], exclude_actions=["search_google", "unknown_action", "unkown"],
), ),
"extend_planner_system_message": get_prompt(oauth_provider), "extend_planner_system_message": prompt,
} }
} }

View file

@ -1,18 +1,17 @@
# why this is isn't index from typing import Union, Type
# 이 파일을 __init__.py로 만든 이유는 from pydantic import BaseModel
# 굳이 이 짧은 코드를 파일을 하나 더 만드는게 코드의 가독성을 떨어뜨린다고 판단했기 때문입니다.
def get_prompt(type:str) -> str: def get_prompt(type: str) -> tuple[str, Type[BaseModel]] | str:
""" """
Prompt를 반환합니다. Prompt를 반환합니다.
:param type: 'auth' {Auth List} 또는 'google' {OAuth Provider}, 'meta' {OAuth Provider}을 지정합니다. :param type: 'auth' {Auth List} 또는 'google' {OAuth Provider}, 'meta' {OAuth Provider}을 지정합니다.
:return: 해당하는 프롬프트 문자열 :return: 해당하는 프롬프트 문자열 또는 (프롬프트, 모델) 튜플
""" """
if type.lower() == "auth": if type.lower() == "auth":
from lib.llm.prompt.auth_list import extract_oauth_list_prompt from lib.llm.prompt.get_oauth import prompt, model
return extract_oauth_list_prompt return prompt, model
else: else:
from lib.llm.prompt.fallback import extend_planner_system_message from lib.llm.prompt.fallback import model, prompt
return extend_planner_system_message return prompt, model

View file

@ -1,41 +0,0 @@
# @file purpose: This file contains the prompt for extracting a list of OAuth providers from a web page.
# OAuth 리스트 추출용 프롬프트 (클릭하지 않고 단순 식별만)
extract_oauth_list_prompt = f"""
🎯 목적: 주어진 초기 URL 내에서 **OAuth 로그인 Provider**를 찾아 아래 형식의 JSON으로 정리합니다.
📌 작업 목표:
- Google, GitHub, Discord, Facebook, Apple, Microsoft, Twitter, LinkedIn 등 **OAuth 인증을 사용하는 외부 로그인 링크**에서 Provider 이름만 모두 수집합니다.
- 로그인 버튼, 링크 클릭 등을 통해 탐색을 진행할 수 있습니다.
- **같은 provider가 여러 번 나와도 하나만 저장**합니다.
🛑 제한 사항:
- ❌ 로그인 입력창이나 이메일/비밀번호 입력 방식은 제외합니다.
- ❌ 검색 엔진, 사이트 외부 탐색은 금지합니다.
- ❌ URL 추측이나 직접 입력은 금지합니다.
- ❌ OAuth가 없는 경우 빈 배열 `[]`로 반환합니다.
- ❌ OAuth가 아닌 일반 로그인은 무시합니다.
🔍 탐색 방법:
1. 초기 URL에 접속하여 **클라이언트용 로그인 페이지**로 진입합니다.
2. 페이지가 정상적으로 로드되었다고 가정합니다.
3. 'Continue with X', 'Continue with Google'... 등의 버튼이나 링크를 식별합니다.
🧾 출력 형식 (예시):
```json
{{
"oauth_providers": [
"Google",
"GitHub",
"Discord"
]
}}
```
📌 주의:
결과가 없는 경우 빈 배열 `[]`로 반환합니다.
정확한 provider 이름을 포함해 주세요.
"""

View file

@ -0,0 +1,2 @@
from lib.llm.prompt.fallback.prompt import prompt
from lib.llm.prompt.fallback.model import model

View file

@ -0,0 +1,6 @@
from pydantic import BaseModel
class model(BaseModel):
msg: str | None = None
status: str | None = None # "success", "mfa_required", "google_blocked", "sso_not_found", "login_page_not_found", "invalid_credentials"
final_url: str | None = None

View file

@ -0,0 +1,122 @@
# Extended planner prompt
prompt = f"""
🎯 목적: 웹 자동화를 위한 **SSO 로그인 리디렉션 URL 수집**
📌 주의사항 (전제 조건)
- ❌ **검색 엔진(Google, Bing 등) 사용 금지**
- ✅ **초기 제공된 URL 내에서만 탐색**
- ❌ 직접 이동하거나 추측한 링크 클릭 금지
- ⛔ 추측한 URL은 대답하거나 클릭하지 마세요
- OAuth가 아닌 일반 로그인은 무시
- OAuth가 없다면 **즉시 중단**하고 빈 배열 반환
---
## 🧩 Step 0: 페이지 차단(Block) 여부 확인
초기 URL의 로그인 페이지에 접근하여 다음 사항을 점검합니다:
- 🚫 페이지 차단됨 (Firewall, Access Denied 등) → 즉시 중단
- 🔒 CAPTCHA는 통과 가능 (해결하고 계속 진행)
- ❗ 로그인 UI가 정상적으로 로드되지 않으면 중단
📤 차단 시 즉시 종료
---
## 🔍 Step 1: 로그인 페이지 탐색
* 초기 URL에 접속하여 **클라이언트용 로그인 페이지**로 진입합니다.
* 쿠키 동의, 개인정보 안내 등 팝업은 무시하거나 닫고 계속 진행하세요.
* 페이지가 정상 로드되었다고 가정합니다.
---
## 👀 Step 2: SSO 로그인 버튼 식별
아래 **OAuth SSO 버튼들만** 유효합니다:
* ✅ Google, GitHub, Facebook, LinkedIn, Microsoft, Naver
**유효한 버튼 기준**:
* OAuth 인증 흐름을 실제로 트리거
* `window.location` 또는 `<a href=...>` 또는 JS로 redirect가 발생
**제외 버튼들 (클릭 금지)**:
* ❌ 일반 로그인, 패스키, 이메일/전화번호, 인증서 기반, 비밀번호 입력
---
## ✅ Step 3: 모든 SSO 버튼 클릭 및 로그인 시도
> 각 SSO 로그인 버튼을 클릭한 뒤 반드시 아래 절차를 **완전히 수행**해야 합니다.
각 SSO 버튼에 대해 다음을 수행:
1. 버튼 클릭
2. ✅ 로그인 진행:
- 로그인 페이지에서 OAuth 인증을 완료합니다.
- sign in with your username(email) x_username and password is x_password
- 버튼같은게 안눌리면 새로고침을 해봐
- **로그인 완료 후 authorize 등 버튼이 있으면 클릭**
- GitHub같은 경우 Authorize 버튼이 뜨는데 오래걸릴 수 있음, 기다려야 할 수도 있음
- 만약 버튼을 눌러도 반응이 없을 경우 새로고침을 한번 해주세요.
- **OAuth Flow가 완료되면 (callback URL 도달 또는 인증 완료) 즉시 작업 종료**
4. 로그인이 성공하면 모두 쿠키를 삭제하고 다음 SSO 버튼을 클릭합니다.
5. 다음 SSO 버튼으로 반복 진행
쿠키 삭제 방법:
chrome://settings/clearBrowserData에 들어가서 삭제해주세요.
🛑 절대 아래와 같이 해석하지 말 것:
- ❌ 버튼 클릭 후 페이지 로딩만 기다리고 돌아가기
- ❌ URL 저장 없이 go_back() 호출
---
### ✨ 추가 안전 장치: "뒤로가기(go_back) 호출 조건" 제한
```text
🛑 뒤로가기(go_back)은 다음 조건이 모두 충족될 때만 사용 => 다만 로그인 실패 시, 뒤로가기 수행:
- ✅ 로그인 흐름이 완료됨 (예: redirect back to app, or callback URL)
- ✅ 현재 리디렉션 URL이 수집됨
- ✅ 결과에 저장 후 다음 버튼 탐색을 위해 복귀 필요할 때
```
---
## 🚫 Step 4: 버튼 없음 또는 예외 발생 시
* 유효한 SSO 버튼이 **전혀 없을 경우**
* 예외, 오류 등 발생 시
-> 즉시 중단
---
최종 반환:
Return the result in the following format only:
```json
{{
"msg": "Google login completed",
"status": "success" | "already_logged_in" | "mfa_required" | "captcha_triggered" | "window_blocked" | "idpw_required" | "google_blocked" | "sso_not_found" | "login_page_not_found",
"final_url": "<url_after_login_redirect or empty string>"
}}
```
---
## 📎 중요 규칙 요약
* ✅ **모든 SSO 로그인은 반드시 실행** (가능한 버튼은 모두 클릭)
* 🔁 단계는 반드시 순서대로 진행
* 🔐 로그인은 쿠키/세션으로 유지된 상태에서 수행
* 👀 직접 OAuth Providor ID/PW를 입력하여도 됨 가지고 있다면
* ⛔ 추측한 URL은 접속하지 않음
---
"""

View file

@ -0,0 +1,2 @@
from lib.llm.prompt.get_oauth.prompt import prompt
from lib.llm.prompt.get_oauth.model import model

View file

@ -0,0 +1,6 @@
from pydantic import BaseModel
class model(BaseModel):
msg: str | None = None
url: str | None = None
sso_list: list[str] = [] # List of SSO providers found on the login page

View file

@ -0,0 +1,61 @@
prompt = """
You are an expert in finding login pages.
Your task is to navigate to the login page of the given URL. Follow the steps below strictly and return results only in the specified format.
※ You are NOT allowed to navigate to URLs that are not directly discoverable within the initial domain. Do NOT use search engines or guess external login URLs.
0. INITIAL BLOCK CHECK
- If the browser is blocked when trying to access the page — due to firewall, CAPTCHA, regional restrictions, or other access denials — immediately terminate the process and return the following JSON:
```json
{
"msg": "Blocked",
"url": "",
"sso_list": []
}
```
- Do NOT proceed to further steps in this case.
1. LOGIN PAGE NAVIGATION
- Navigate only to a **client-side (non-enterprise)** login page within the provided domain.
- Do NOT rely on external tools, search engines, or links not directly found on the site.
- If a consent popup (e.g. for privacy/cookies) appears, you MUST dismiss or close it before proceeding.
- Since step 0 confirmed access, assume the page now loads properly.
2. SSO BUTTON IDENTIFICATION
- On the login page, look for the following social login (SSO) buttons:
- Google, GitHub, Facebook, LinkedIn, Microsoft, Naver, Slack, Etc.
- ✅ Proceed only if it is clearly an **actual SSO button**.
- ❌ Exclude the following:
- Passkey-related buttons
- Username/password fields
- Email-based login
- Non-OAuth methods such as certificate or phone verification
3. RETURN FORMAT
- If the login page is successfully found, return:
```json
{
"msg": "Login page found",
"url": "https://example.com/login",
"sso_list": ["Google", "GitHub"]
}
```
- If the login page cannot be found, return:
```json
{
"msg": "Login page not found",
"url": "",
"sso_list": []
}
```
- If blocked (as in step 0), return:
```json
{
"msg": "Blocked",
"url": "",
"sso_list": []
}
```
- Return ONLY the JSON object. Do NOT include any explanation, logging, or extra output.
"""