From 44a2e348647bc80bdb58856f75bd79dc53d0ef1f Mon Sep 17 00:00:00 2001 From: LawyZheng Date: Wed, 15 Oct 2025 01:28:42 +0800 Subject: [PATCH] only keep totp endpoint (#3704) --- .../skyvern/parse-verification-code.j2 | 19 ---- skyvern/forge/sdk/db/id.py | 6 +- skyvern/forge/sdk/db/models.py | 4 +- skyvern/forge/sdk/routes/credentials.py | 94 ++++--------------- 4 files changed, 22 insertions(+), 101 deletions(-) delete mode 100644 skyvern/forge/prompts/skyvern/parse-verification-code.j2 diff --git a/skyvern/forge/prompts/skyvern/parse-verification-code.j2 b/skyvern/forge/prompts/skyvern/parse-verification-code.j2 deleted file mode 100644 index b94e52cb..00000000 --- a/skyvern/forge/prompts/skyvern/parse-verification-code.j2 +++ /dev/null @@ -1,19 +0,0 @@ -You receive either an email or a text message containing 2FA/MFA code or activation key. Your job is to parse the content, identify the code and return the code. There should be only one code in the content. The code must be from the content - -The most common form of code will be a series of digits, although sometimes it may contain letters. - -"Here is your code: 123456" or "Your code is: 123456" or "123456 is your code", "Here is your activation key: abcdefg", "Here is your key: 123asd" are some examples of the possible content. - -MAKE SURE YOU OUTPUT VALID JSON. No text before or after JSON, no trailing commas, no comments (//), no unnecessary quotes, etc. - -Reply in the following JSON format: -{ - "reasoning": str, // How you figure out what the code is or why the code is missing. Be precise here to explain the data source and the context that makes you believe where the correct code is - "code_found": bool, // true if the code is found. false if the code is not found - "code": str, // the 2FA/MFA verification code. If you cannot identify any code, do not come up with a code and return null -} - -Received Content containing 2FA/MFA code: -``` -{{ content }} -``` diff --git a/skyvern/forge/sdk/db/id.py b/skyvern/forge/sdk/db/id.py index e291b2e4..48a92f75 100644 --- a/skyvern/forge/sdk/db/id.py +++ b/skyvern/forge/sdk/db/id.py @@ -54,7 +54,7 @@ STEP_PREFIX = "stp" TASK_GENERATION_PREFIX = "tg" TASK_PREFIX = "tsk" TASK_RUN_PREFIX = "tr" -OTP_CODE_PREFIX = "otp" +TOTP_CODE_PREFIX = "totp" USER_PREFIX = "u" WORKFLOW_PARAMETER_PREFIX = "wp" WORKFLOW_PERMANENT_ID_PREFIX = "wpid" @@ -169,9 +169,9 @@ def generate_ai_suggestion_id() -> str: return f"{AI_SUGGESTION_PREFIX}_{int_id}" -def generate_otp_code_id() -> str: +def generate_totp_code_id() -> str: int_id = generate_id() - return f"{OTP_CODE_PREFIX}_{int_id}" + return f"{TOTP_CODE_PREFIX}_{int_id}" def generate_action_id() -> str: diff --git a/skyvern/forge/sdk/db/models.py b/skyvern/forge/sdk/db/models.py index 4d212fdb..7ab062d3 100644 --- a/skyvern/forge/sdk/db/models.py +++ b/skyvern/forge/sdk/db/models.py @@ -35,7 +35,6 @@ from skyvern.forge.sdk.db.id import ( generate_org_id, generate_organization_auth_token_id, generate_organization_bitwarden_collection_id, - generate_otp_code_id, generate_output_parameter_id, generate_persistent_browser_session_id, generate_script_block_id, @@ -48,6 +47,7 @@ from skyvern.forge.sdk.db.id import ( generate_task_run_id, generate_task_v2_id, generate_thought_id, + generate_totp_code_id, generate_workflow_id, generate_workflow_parameter_id, generate_workflow_permanent_id, @@ -584,7 +584,7 @@ class TOTPCodeModel(Base): Index("ix_totp_codes_otp_type", "organization_id", "otp_type"), ) - totp_code_id = Column(String, primary_key=True, default=generate_otp_code_id) + totp_code_id = Column(String, primary_key=True, default=generate_totp_code_id) totp_identifier = Column(String, nullable=False, index=True) organization_id = Column(String, ForeignKey("organizations.organization_id")) task_id = Column(String, ForeignKey("tasks.task_id")) diff --git a/skyvern/forge/sdk/routes/credentials.py b/skyvern/forge/sdk/routes/credentials.py index 4bb241c5..c4ea1d6a 100644 --- a/skyvern/forge/sdk/routes/credentials.py +++ b/skyvern/forge/sdk/routes/credentials.py @@ -3,7 +3,6 @@ from fastapi import BackgroundTasks, Body, Depends, HTTPException, Path, Query from skyvern.config import settings from skyvern.forge import app -from skyvern.forge.prompts import prompt_engine from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType from skyvern.forge.sdk.routes.code_samples import ( CREATE_CREDENTIAL_CODE_SAMPLE, @@ -51,36 +50,36 @@ async def fetch_credential_item_background(item_id: str) -> None: LOG.exception("Failed to fetch credential item from Bitwarden in background", item_id=item_id, error=str(e)) -async def parse_totp_code(content: str, organization_id: str) -> str | None: - prompt = prompt_engine.load_prompt("parse-verification-code", content=content) - code_resp = await app.SECONDARY_LLM_API_HANDLER( - prompt=prompt, prompt_name="parse-verification-code", organization_id=organization_id - ) - LOG.info("TOTP Code Parser Response", code_resp=code_resp) - return code_resp.get("code", None) - - -@legacy_base_router.post("/otp") -@legacy_base_router.post("/otp/", include_in_schema=False) +@legacy_base_router.post("/totp") +@legacy_base_router.post("/totp/", include_in_schema=False) @base_router.post( - "/credentials/otp", + "/credentials/totp", response_model=TOTPCode, - summary="Send OTP content", - description="Forward a OTP (TOTP, Magic Link) email or sms message containing otp login data to Skyvern. This endpoint stores the otp login data in database so that Skyvern can use it while running tasks/workflows.", + summary="Send TOTP code", + description="Forward a TOTP (2FA, MFA) email or sms message containing the code to Skyvern. This endpoint stores the code in database so that Skyvern can use it while running tasks/workflows.", tags=["Credentials"], openapi_extra={ - "x-fern-sdk-method-name": "send_otp_content", + "x-fern-sdk-method-name": "send_totp_code", + "x-fern-examples": [{"code-samples": [{"sdk": "python", "code": SEND_TOTP_CODE_CODE_SAMPLE}]}], }, ) @base_router.post( - "/credentials/otp/", + "/credentials/totp/", response_model=TOTPCode, include_in_schema=False, ) -async def send_otp_content( +async def send_totp_code( data: TOTPCodeCreate, curr_org: Organization = Depends(org_auth_service.get_current_org), ) -> TOTPCode: + LOG.info( + "Saving OTP code", + organization_id=curr_org.organization_id, + totp_identifier=data.totp_identifier, + task_id=data.task_id, + workflow_id=data.workflow_id, + workflow_run_id=data.workflow_run_id, + ) content = data.content.strip() otp_value: OTPValue | None = OTPValue(value=content, type=OTPType.TOTP) # We assume the user is sending the code directly when the length of code is less than or equal to 10 @@ -112,65 +111,6 @@ async def send_otp_content( ) -@legacy_base_router.post("/totp") -@legacy_base_router.post("/totp/", include_in_schema=False) -@base_router.post( - "/credentials/totp", - response_model=TOTPCode, - summary="Send TOTP code", - description="Forward a TOTP (2FA, MFA) email or sms message containing the code to Skyvern. This endpoint stores the code in database so that Skyvern can use it while running tasks/workflows.", - tags=["Credentials"], - openapi_extra={ - "x-fern-sdk-method-name": "send_totp_code", - "x-fern-examples": [{"code-samples": [{"sdk": "python", "code": SEND_TOTP_CODE_CODE_SAMPLE}]}], - }, -) -@base_router.post( - "/credentials/totp/", - response_model=TOTPCode, - include_in_schema=False, -) -async def send_totp_code( - data: TOTPCodeCreate, - curr_org: Organization = Depends(org_auth_service.get_current_org), -) -> TOTPCode: - LOG.info( - "Saving TOTP code", - organization_id=curr_org.organization_id, - totp_identifier=data.totp_identifier, - task_id=data.task_id, - workflow_id=data.workflow_id, - workflow_run_id=data.workflow_run_id, - ) - content = data.content.strip() - code: str | None = content - # We assume the user is sending the code directly when the length of code is less than or equal to 10 - if len(content) > 10: - code = await parse_totp_code(content, curr_org.organization_id) - if not code: - LOG.error( - "Failed to parse totp code", - totp_identifier=data.totp_identifier, - task_id=data.task_id, - workflow_id=data.workflow_id, - workflow_run_id=data.workflow_run_id, - content=data.content, - ) - raise HTTPException(status_code=400, detail="Failed to parse totp code") - return await app.DATABASE.create_otp_code( - organization_id=curr_org.organization_id, - totp_identifier=data.totp_identifier, - content=data.content, - code=code, - task_id=data.task_id, - workflow_id=data.workflow_id, - workflow_run_id=data.workflow_run_id, - source=data.source, - expired_at=data.expired_at, - otp_type=OTPType.TOTP, - ) - - @legacy_base_router.post("/credentials") @legacy_base_router.post("/credentials/", include_in_schema=False) @base_router.post(