Skip to content

feat(plugin-auth): port OIDC plugins + host LoginByExternal / authpro…#54

Open
osindex wants to merge 11 commits into
linaproai:mainfrom
osindex:feat/plugin-auth
Open

feat(plugin-auth): port OIDC plugins + host LoginByExternal / authpro…#54
osindex wants to merge 11 commits into
linaproai:mainfrom
osindex:feat/plugin-auth

Conversation

@osindex

@osindex osindex commented May 27, 2026

Copy link
Copy Markdown

…vider / PluginSettings

Brings the Google and Discord OIDC plugin suite from linglink into the linapro main tree. Includes the full host-side seams the plugins need (LoginByExternal, OAuthHandoffURL, authprovider capability, PluginSettings capability, workspace RouterMode, /auth/providers endpoint) plus the frontend oauth-handoff page and the plugin i18n resources.

Host (apps/lina-core):

  • internal/service/auth: LoginByExternal + ListProviders interface, auth_external.go impl, auth_provider.go impl, two new bizerr codes (AUTH_EXTERNAL_IDENTITY_INVALID, AUTH_EXTERNAL_USER_NOT_PROVISIONED)
  • internal/service/config: WorkspaceConfig.RouterMode + helpers + config.template.yaml documentation + cmd panic allowlist entry
  • internal/service/pluginsettings: new package writing namespaced . rows into sys_config so plugins drop private tables
  • internal/service/pluginhostservices: extended auth adapter (LoginByExternal, OAuthHandoffURL) + new pluginsettingsAdapter + directory/scope updates
  • pkg/plugin/capability/authprovider: new package (Provider registry, LoginEntry, kinds, ListViews)
  • pkg/plugin/capability/contract: extended auth.go + new pluginsettings.go
  • pkg/plugin/capability/capability.go: Services interface gains PluginSettings
  • api/auth/v1/auth_provider.go + internal/controller/auth/auth_v1_provider.go
    • cmd_http_routes binding: /auth/providers endpoint
  • testutil + runtime params test: stub PluginSettings accessor

Frontend (apps/lina-vben):

  • src/api/core/auth.ts: listAuthProvidersApi + ProviderEntity DTO
  • src/store/auth.ts: completeOAuthHandoff (single-tenant + multi-tenant)
  • src/router/routes/core.ts: /oauth-handoff route under AuthPageLayout
  • src/views/_core/authentication/oauth-handoff.vue: consumes host login outcome via vue-router query, dispatches to SPA landing or tenant picker
  • src/views/_core/authentication/login.vue: dynamic provider buttons from /auth/providers, append ?state= when backendRedirectEnabled
  • src/locales/langs/{zh-CN,en-US}/plugins.json: shared instruction strings for OIDC settings pages

Plugins (apps/lina-plugins):

  • linapro-oidc-google: full plugin (DTO, controller, OAuth client + state signing, settings service via PluginSettings, settings vue page, manifest/i18n with apidoc translations)
  • linapro-oidc-discord: same shape
  • lina-plugins.go + go.mod: register both plugins under blank import

Docs (docs/):

  • auth-provider-integration.md + zh-CN mirror (full integration guide)
  • README.md + zh-CN mirror

Behaviour summary:

  • OAuth callback path: /api/v1/auth//callback (rides existing /api/* proxy without extra reverse proxy config)
  • Two independent post-login paths:
    • SSO token delivery when enableBackendRedirect + state matches rule: 302 to rule URL with accessToken/refreshToken in query
    • SPA handoff otherwise: /oauth-handoff stores tokens then routes to defaultBackendRedirect (default /dashboard)
  • Client Secret semantics: empty input keeps stored secret, masked in GET
  • Redirect URI shown read-only with copy button (auto-derived from origin)

Verified locally in linapro:

  • google plugin: rtk go build ./... -> Success
  • discord plugin: rtk go build ./... -> Success
  • workspace: rtk go build ./... -> Success
  • linactl build needs pnpm install for web-antd which is a separate env setup step; the Go side is green

osindex added 4 commits May 27, 2026 21:16
…vider / PluginSettings

Brings the Google and Discord OIDC plugin suite from linglink into the
linapro main tree. Includes the full host-side seams the plugins need
(LoginByExternal, OAuthHandoffURL, authprovider capability, PluginSettings
capability, workspace RouterMode, /auth/providers endpoint) plus the
frontend oauth-handoff page and the plugin i18n resources.

Host (apps/lina-core):
- internal/service/auth: LoginByExternal + ListProviders interface,
  auth_external.go impl, auth_provider.go impl, two new bizerr codes
  (AUTH_EXTERNAL_IDENTITY_INVALID, AUTH_EXTERNAL_USER_NOT_PROVISIONED)
- internal/service/config: WorkspaceConfig.RouterMode + helpers +
  config.template.yaml documentation + cmd panic allowlist entry
- internal/service/pluginsettings: new package writing namespaced
  <pluginID>.<key> rows into sys_config so plugins drop private tables
- internal/service/pluginhostservices: extended auth adapter (LoginByExternal,
  OAuthHandoffURL) + new pluginsettingsAdapter + directory/scope updates
- pkg/plugin/capability/authprovider: new package (Provider registry,
  LoginEntry, kinds, ListViews)
- pkg/plugin/capability/contract: extended auth.go + new pluginsettings.go
- pkg/plugin/capability/capability.go: Services interface gains PluginSettings
- api/auth/v1/auth_provider.go + internal/controller/auth/auth_v1_provider.go
  + cmd_http_routes binding: /auth/providers endpoint
- testutil + runtime params test: stub PluginSettings accessor

Frontend (apps/lina-vben):
- src/api/core/auth.ts: listAuthProvidersApi + ProviderEntity DTO
- src/store/auth.ts: completeOAuthHandoff (single-tenant + multi-tenant)
- src/router/routes/core.ts: /oauth-handoff route under AuthPageLayout
- src/views/_core/authentication/oauth-handoff.vue: consumes host login
  outcome via vue-router query, dispatches to SPA landing or tenant picker
- src/views/_core/authentication/login.vue: dynamic provider buttons from
  /auth/providers, append ?state=<id> when backendRedirectEnabled
- src/locales/langs/{zh-CN,en-US}/plugins.json: shared instruction strings
  for OIDC settings pages

Plugins (apps/lina-plugins):
- linapro-oidc-google: full plugin (DTO, controller, OAuth client + state
  signing, settings service via PluginSettings, settings vue page,
  manifest/i18n with apidoc translations)
- linapro-oidc-discord: same shape
- lina-plugins.go + go.mod: register both plugins under blank import

Docs (docs/):
- auth-provider-integration.md + zh-CN mirror (full integration guide)
- README.md + zh-CN mirror

Behaviour summary:
- OAuth callback path: /api/v1/auth/<provider>/callback (rides existing
  /api/* proxy without extra reverse proxy config)
- Two independent post-login paths:
  - SSO token delivery when enableBackendRedirect + state matches rule:
    302 to rule URL with accessToken/refreshToken in query
  - SPA handoff otherwise: /oauth-handoff stores tokens then routes to
    defaultBackendRedirect (default /dashboard)
- Client Secret semantics: empty input keeps stored secret, masked in GET
- Redirect URI shown read-only with copy button (auto-derived from origin)

Verified locally in linapro:
- google plugin: rtk go build ./... -> Success
- discord plugin: rtk go build ./... -> Success
- workspace: rtk go build ./... -> Success
- linactl build needs pnpm install for web-antd which is a separate env
  setup step; the Go side is green
Introduces a stable host-level directory menu 'auth-provider' that
groups every third-party authentication provider plugin under a single
navigation entry. Localised as 授权管理 (zh-CN) and Authentication
Providers (en-US, source language).

Host changes:
- manifest/sql/013-auth-provider-management.sql: idempotent INSERT of
  the directory menu (parent_id=0, type='D', sort=11) so the entry is
  always present in role authorization and menu tree views even when
  no OIDC plugin is installed.
- manifest/i18n/{zh-CN,en-US}/menu.json: localized 'auth-provider'
  title for the workbench menu renderer.

Submodule bump (apps/lina-plugins):
- Brings in the matching parent_key change on Google and Discord OIDC
  plugins so their settings pages attach under auth-provider.
Plugins referencing parent_key=auth-provider failed at startup with 'plugin menu parent_key does not exist' because that parent was only created by SQL seed 013, which did not run at plugin menu-sync time.

The host now owns auth-provider as an on-demand managed catalog (menu_metadata.go registry). Menu sync materializes it from the host-owned definition when a plugin first mounts under it, and removes it once the last child plugin is uninstalled. Stable host catalogs and user menus are never auto-removed. Orphan parents without a host definition are still rejected. Removes the 013 seed; plugins only reference parent_key. Bumps official-plugins submodule.
GET /plugins ignored pagination: the request DTO had no pageNum/pageSize and the service returned the full filtered list every time, so pageNum=2 had no effect on /admin#/system/plugin. Add pageNum/pageSize to ListReq and ListInput, window the filtered result via paginatePluginItems, and keep total as the full filtered count. A non-positive page size disables paging so internal callers keep the full projection.
@gqcn gqcn self-requested a review May 28, 2026 06:21
@gqcn

gqcn commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

This PR still needs a few changes before it can be merged:

  • Critical apps/lina-plugins/linapro-oidc-google/backend/internal/service/provider/provider.go:65 and apps/lina-plugins/linapro-oidc-discord/backend/internal/service/provider/provider.go:58: the submodule providers still assign BackendRedirectEnabled, BackendRedirectDefault, and BackendRedirectRules, but the updated host authprovider.LoginEntry contract no longer has those fields. Because apps/lina-plugins/go.mod replaces lina-core with the sibling ../lina-core, the plugin workspace will not compile with this core change. Suggested fix: update both providers to return only the final public button metadata fields, or restore the contract intentionally if those fields are still required.
  • Needs change apps/lina-plugins/linapro-oidc-google/backend/internal/controller/oauth/oauth.go:67 and apps/lina-plugins/linapro-oidc-discord/backend/internal/controller/oauth/oauth.go:68: the login and callback paths still use the plugin-private settings.Enabled flag, while /auth/providers now shows buttons from host provider enablement. That leaves two enablement authorities, so an enabled plugin can show a login button but reject the click unless the hidden/private setting is also true. Suggested fix: gate OAuth start/callback on the host provider enablement state and remove the private enabled setting from the settings DTO, persistence, and pages.
  • Needs change apps/lina-plugins/linapro-oidc-google/frontend/pages/google-settings.vue:212 and apps/lina-plugins/linapro-oidc-discord/frontend/pages/discord-settings.vue:212: the OIDC settings pages still contain hard-coded visible labels, placeholders, button text, toast messages, and the private enable switch even though these plugins declare i18n.enabled: true. Suggested fix: move the remaining visible strings into the plugin/host locale resources, render them through $t, and align the page with the single host enablement source above.

I have not added the bot-approved label yet.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@gqcn

gqcn commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

This PR is closer, but a few issues still need to be fixed before it can be merged:

  • Critical apps/lina-core/internal/service/pluginsettings/pluginsettings.go:130 and apps/lina-core/internal/service/pluginsettings/pluginsettings.go:223: clearing a plugin setting uses the normal Delete() path on sys_config, which is soft-deleted because the table has deleted_at. Saving the same key again then hits the (tenant_id, key) unique index and updates the soft-deleted row without restoring visibility, so the setting remains invisible to GetString/List. This breaks flows like clearing backendRedirects or defaultBackendRedirect and later configuring them again. Suggested fix: make the plugin-settings clear path compatible with the unique key, for example by using a documented physical delete for these opaque plugin rows, and add a delete-then-set-then-read regression test.
  • Needs change apps/lina-core/api/auth/v1/auth_provider.go:13 and apps/lina-core/internal/service/auth/auth_code.go:63: the host adds new public auth API documentation strings and new external-login error message keys, but the host i18n resources are not updated. core-api-auth.json still has no ListProvidersReq, ListProvidersRes, or ProviderEntity translations, and error.auth.external.* is missing from the runtime error resources. Suggested fix: add the missing host apidoc and error translations for every configured locale, or remove the new source strings from the public/localized surface if they are not meant to be exposed.
  • Needs change openspec/changes/fix-plugin-auth-review-feedback/tasks.md:11: this active OpenSpec change records strict validation as unavailable and marks all feedback tasks complete, but the change only contains proposal.md and tasks.md; there is no specs/ delta to validate. Suggested fix: add the required spec delta and record openspec validate fix-plugin-auth-review-feedback --strict, or keep this as an internal review note outside openspec/changes/.

I have not added the bot-approved label yet.

osindex and others added 4 commits June 2, 2026 19:42
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
… on clear

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
…dback

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
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.

2 participants