Skip to content

feat(todo): rewrite on Microsoft Graph with Service Principal, Each user signs in and n8n operations; fix secret leak in validation (1.0.0) - #4167

Open
ForeignKeyCN wants to merge 1 commit into
langgenius:mainfrom
ForeignKeyCN:feat/todo-per-user-signin
Open

ForeignKeyCN wants to merge 1 commit into
langgenius:mainfrom
ForeignKeyCN:feat/todo-per-user-signin

Conversation

@ForeignKeyCN

Copy link
Copy Markdown
Contributor

Summary

Brings the To Do plugin in line with Outlook (#4163), Teams (#4164), OneDrive (#4165) and Excel (#4166), and with n8n's Microsoft To Do node. The plugin was built on two third-party packages (pymstodo, dify-easy) and on requests-oauthlib; this PR replaces them with direct Microsoft Graph calls through a shared tools/graph_client.py.

Fixed: credential validation always failed and put secrets in its message. _validate_credentials ended with a debugging leftover that raised unconditionally with f"token: {token} ... credentials: {credentials}", i.e. the access and refresh tokens and the client secret, shown wherever the validation error is displayed. Validation now reads the task lists and reports a plain message; a test checks that no secret appears in it.

1. Base (existing workflows keep working)

  • The 8 tools keep their names, parameters and output ({"data": [...]} / the object in json, str() of it in text), including the historical quirks: get_tasks returns the task lists, and the task tools return only tasks that are not completed by default. Dates keep their previous text form.
  • OAuth authorizations made before 1.0.0 stored the whole token as JSON text under token; tools read it, the refresh accepts it and writes the new fields (access_token, refresh_token, graph_base_url). The refresh no longer sends scope. The OAuth scopes are unchanged (openid offline_access Tasks.ReadWrite, the ones pymstodo requested).
  • No more class-level state: the old OAuth code set ToDoConnection._redirect globally on every request.
  • IDs are path-segment encoded, paging only follows Graph's own links, and a stored Graph host other than a Microsoft Graph endpoint is refused.
  • OAuth tenant and national clouds: optional Tenant ID (default common, as before) and Microsoft Cloud; scopes are qualified outside the global cloud (openid / offline_access excepted).
  • Typed output variables on every tool.
  • YAML used zh_CN, which Dify does not show; now zh_Hans.

2. Auth modes (API Key credential)

  • Service Principal (n8n has it): application permission Tasks.ReadWrite.All; tools work on /users/{owner}/todo, the owner coming from the tool's Task Owner setting (form-only, never chosen by the LLM) or the credential's default. Saving with a default owner reads their lists to check the permission.
  • Each user signs in, same design as the other Microsoft plugins: authorization code + PKCE through a plugin endpoint (GET /todo/callback) that never holds the secret; per-user tokens Fernet-encrypted in plugin storage, keyed by a hash of client_id | Dify user ID; never in tool output or logs. Scopes: OAuth's plus User.Read (to name the signed-in account). Sign Out of To Do (todo_sign_out).

3. n8n parity (new tools carry a todo_ prefix: the new Agent rejects duplicate tool names across plugins)

n8n Tool
List: Get Many / Get get_lists / get_list_by_id (existing)
List: Create / Update / Delete todo_create_list / todo_update_list / todo_delete_list
Task: Get Many / Get get_tasks_by_list_id (new status_filter, limit) / get_task_by_id (existing)
Task: Create create_task, now with n8n's reminder_datetime, importance, status besides due_date and body_text
Task: Update / Delete todo_update_task / todo_delete_task
Linked Resource: Create / Get / Get Many / Update / Delete todo_create_linked_resource / todo_get_linked_resource / todo_list_linked_resources / todo_update_linked_resource / todo_delete_linked_resource

get_all_tasks and complete_task stay; get_all_tasks takes the same options and adds each task's list_id / list_name.

Version 0.0.8 → 1.0.0. README rewritten (the existing setup screenshots are kept; it now also lists the API permissions to add, which it did not before) and PRIVACY updated. Dependencies: pymstodo, dify-easy, requests-oauthlib, structlog, pydantic removed; cryptography added.

Release Notes

  • Fixed: saving an authorization always failed with an error that included the access token and client secret.
  • Each user signs in: a new authorization for chat agents; each person signs in once with their own Microsoft account and the tools work on their own tasks. Needs the new sign-in endpoint registered as a Web redirect URI on the app.
  • Service Principal authorization (app-only), working on a named user's tasks.
  • OAuth: optional Tenant ID and Microsoft Cloud (US Government, DoD, China/21Vianet). Existing authorizations keep working.
  • New tools matching n8n: create, rename and delete lists; update and delete tasks; create, read, list, update and delete linked resources. Create Task gains reminder, importance and status; the task lists can include completed tasks.
  • Every tool returns typed output variables; Chinese labels now show.

Change Type

  • Documentation / non-plugin change
  • Non-LLM plugin (tools, extensions, datasource, etc.)
  • LLM plugin

Screenshots / Videos

The setup screenshots in the README are the existing ones. The per-user flow is the same as in #4163–#4166 (screenshots there).

Testing

  • Unit tests: 77 passed (pytest; the plugin had none). They cover: the Graph context for OAuth (new and pre-1.0.0 token JSON credentials), Service Principal and per-user, national clouds and the Graph-host check; ID encoding; every tool's output variables on success and on a Graph error (a test checks every tool in the provider is covered); the pre-1.0.0 output shapes (task fields, date texts, get_tasks returning lists); the default "not completed" filter and the other filters; paging staying on the Graph host; the n8n task fields and their validation; linked resources (fields, URL check); OAuth URLs per tenant and cloud with the old scopes; refreshing a pre-1.0.0 credential; validation messages without secrets (and a working token now passing); the per-user sign-in flow and validation.
  • On a self-hosted Dify (Community 1.16.1): the plugin installs and upgrades, the sign-in endpoint answers, and an Each user signs in credential validates against Microsoft Entra ID (client ID and secret checked).
  • Not run end to end: the tools against a live To Do account (sign-in through a chat, reading and changing lists, tasks and linked resources), Service Principal against a real user, and an upgrade of a real pre-1.0.0 authorization. These are covered by the unit tests above.

Checklist

  • I have read the contribution guidelines
  • Version bumped in manifest.yaml (0.0.8 → 1.0.0)

…ser signs in, national clouds and n8n operations (1.0.0)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant