Adds the DingTalk credentials to every path that actually delivers
configuration: compose.release.yml (which has no env_file, so variables must
be listed explicitly), the Helm secret template and values, the k8s
deployment and its secret example. validate-release-config.sh gains DingTalk
in its provider loop, so a half-configured pair is rejected the same way.
Documents the three-stage strategy contract in the authentication design: a
table mapping each deviation -- authorize parameters, token exchange,
userinfo loading -- to its interface and current implementations, plus the
rule that a provider must never make account decisions itself.
Deployment notes and both FAQs now cover DingTalk, including the shared trap
with Feishu: their emails are admin-recorded and never confirmed, so
emailVerified is always false and an EMAIL_DOMAIN access policy would reject
every login through either provider.
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* feat(auth): let providers override OAuth userinfo loading
Some providers do not return a flat, standard userinfo payload, so
DefaultOAuth2UserService cannot read them. Add ProviderOAuth2UserService
so a provider can claim its own registration id and supply the loading
step, while everything after it stays shared.
The override runs inside the RemoteIdentityIoExecutor boundary added in
R1-A, so a provider's HTTP call does not hold the surrounding
transaction open. Registrations without an override keep using the
default user service unchanged.
Part of R1-A2 (public Provider adapters) per
openspec/changes/enterprise-identity-platform/rollout-plan.md.
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* feat(auth): add Feishu as a public login provider
Adds Feishu (Lark) as a public sign-in option: it authenticates a
SkillHub platform account and nothing more. No Organization membership,
no directory sync, no Namespace grants.
Feishu deviates from standard OAuth in two ways this handles:
its userinfo response is wrapped in a {code, msg, data} envelope, and it
reports errors with HTTP 200. FeishuOAuth2UserService unwraps that
envelope into flat attributes; FeishuClaimsExtractor maps them to the
shared OAuthClaims, so account decisions still run through the unified
identity core added in R1-A.
Subject and email semantics, which decide whether a login can reach an
existing account:
- open_id is the only subject. union_id stays in extra rather than
acting as a fallback: a subject that can change between logins would
split one person across two platform accounts. Promoting union_id
later needs an explicit alias migration.
- A blank or missing open_id fails the login instead of binding the
literal string "null".
- emailVerified is always false. Feishu emails are imported by an
organization admin and never confirmed with the user, so they carry no
verification signal and cannot be used to join an existing account.
Operational bounds: the userinfo call has connect and read timeouts so an
unresponsive Feishu endpoint cannot hold a login thread, and the
OAuth2Error description carries only the provider error code, because an
upstream message can quote the request URI and with it the access token.
Like the GitHub and GitLab extractors, the claims extractor logs nothing.
The login button follows the existing config-driven catalog: with no
client id configured, /api/v1/auth/methods does not list Feishu and no
button renders. No frontend code change is needed; the icon resolves by
provider name.
Adapted from the implementation in #696 by @yhd4711499, re-extracted onto
current main with the subject, logging and timeout changes above.
Part of R1-A2 (public Provider adapters) per
openspec/changes/enterprise-identity-platform/rollout-plan.md.
Co-authored-by: yhd4711499 <yhd4711499@users.noreply.github.com>
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* fix(auth): bound Feishu userinfo response and stop subject leaking into displayName
Three defects found reviewing this batch against the R1-A2 spec.
Response size limit. The spec's scope line asks for "远程 I/O 超时与响应大小
限制"; only the timeouts were implemented, so a misconfigured or hostile
OAUTH2_FEISHU_BASE_URI could stream an unbounded body into the parser.
Reads at most 64 KB before parsing, mirroring the 10 MB cap the shared
WebClientConfig already applies. Uses InputStream.readNBytes rather than
adding commons-io or guava, neither of which skillhub-auth declares.
Synthesized displayName. Falling back to "feishu-<open_id>" wrote the
external subject into UserAccount.displayName and into
UserActivatedEvent, carrying it somewhere event consumers may log it --
against the R1-A gate that logs must not contain the subject. Now stops
at name -> en_name like the GitHub and GitLab extractors.
Unused mobile attribute. A phone number was extracted into the principal
attributes and read by nothing. It is PII the spec did not ask for and it
widened the redaction surface for free.
Also drops a constructor overload that only passed List.of() through, and
a test that duplicated the blank-subject path.
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* docs(auth): document the provider adapter contract and Feishu operator setup
AGENTS.md and CONTRIBUTING.md both require docs updates when auth flows or
deployment config change; this batch changed both and touched no docs.
03-authentication-design.md described adding a provider as "branch on
registrationId inside CustomOAuth2UserService", which the
ProviderOAuth2UserService strategy supersedes. Rewrites that recipe:
register an OAuthClaimsExtractor bean per provider, add a
ProviderOAuth2UserService only when the userinfo response is non-standard,
and note that the login page needs no code change. Also records the
provider-side obligations that are easy to get wrong -- stable subject with
no fallback, emailVerified only on proven ownership, bounded remote calls,
no subject in logs -- and un-comments the config example, which still
listed GitLab as a future possibility.
faq.md told operators to delete "the github and gitlab blocks" to hide SSO
buttons. That advice was already incomplete and gets worse per provider, so
it now explains the config-driven mechanism: an empty client id keeps the
entry off the login page, no file edit needed.
09-deployment.md listed only the GitHub credentials. Adds GitLab and Feishu,
and flags a deployment trap: Feishu emails are admin-imported so
emailVerified is always false, and skillhub.access-policy.mode=EMAIL_DOMAIN
denies every unverified email, which would reject all Feishu logins.
Squares the Feishu logo viewBox. It was 407.87x324.19 while login-button
renders it in a square w-5 h-5 box, so the mark was distorted.
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* feat(deploy): wire Feishu credentials into the release surfaces
.env.release.example advertised OAUTH2_FEISHU_* knobs that no deployment
path could actually deliver. compose.release.yml has no env_file, so every
variable must be listed explicitly, and the Helm chart and k8s base only
mapped the GitHub secret keys. Setting the documented variables therefore
did nothing.
Adds Feishu to compose.release.yml, the Helm secret template and values,
the k8s deployment and its secret example. GitLab had the identical gap, so
it is wired at the same time rather than leaving the example file half true.
validate-release-config.sh only checked that GitHub's id and secret appear
together. A half-configured provider renders a login button whose exchange
then fails, so the check now loops over all three providers. Its test gained
both-directions cases per provider plus a fully configured pass; reverting
the loop to GitHub-only makes them fail.
Also adds the provider's only failure log. Nothing downstream records a
Feishu userinfo failure -- OAuth2LoginFailureHandler does not log either --
so the previous code was silent on error. Logs the exception class and
Feishu's own error code, never the upstream msg, which can quote the access
token; a test asserts the code is present and the token is not.
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* fix(auth): use JSON token exchange for Feishu OAuth
Made-with: Proma
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* fix(auth): preserve Feishu OAuth browser redirect
Add safe phase-level OAuth diagnostics and redact callback credentials from request logs.
Made-with: Proma
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* docs(deploy): clarify Feishu OAuth configuration and validation
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* fix(deploy): pass Feishu redirect URI through releases
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* fix(deploy): pass S3 chunked encoding setting
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* fix(deploy): preserve default Feishu callback derivation
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
---------
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Co-authored-by: yhd4711499 <yhd4711499@users.noreply.github.com>
Resolve open npm Dependabot alerts for the web app and docs site by refreshing dependency overrides and lockfiles.
Also sets the staging web forwarded-proto default so `make staging` can render the shared Nginx template when using the bare nginx image.
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* fix(api): tell callers why a request was forbidden
The scope filter already computes an exact reason ("Missing API token
scope: skill:delete", "API token cannot access endpoint: /x") and the
access-denied handler discarded it, returning a bare "Forbidden" for
every case: missing scope, endpoint closed to API tokens, and paths
that simply don't exist. Clients cannot tell those apart, so they
guess — the published CLI reports every 403 as "token may lack
required scope", which sent us debugging token scopes for an hour when
the real causes were a revoked token and a mistyped namespace path.
The reason now rides in the response via a new error.forbidden.detail
message (en + zh), and is logged alongside the exception type.
Signed-off-by: Gal Eyal <gal.e@popai.health>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(api): safely expose API token denial reasons
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
---------
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* docs(faq): add community-sourced deployment and operations Q&A
Adds entries collected from real user-support threads to the reference FAQ
(both zh and en):
- 502 on auth APIs while the page loads, traced to server startup failure
on the SKILLHUB_DOWNLOAD_ANON_COOKIE_SECRET placeholder guard
- config changes not taking effect (.env.release.example vs .env.release,
restart vs recreate)
- built-in skill sync failure in offline environments
- upgrade path with Flyway auto-migration and volume retention
- external dependencies and the lack of MySQL support
- granting SUPER_ADMIN to an OAuth account via the bootstrap admin
- telling CLI and server versions apart
- installing skills into a target directory on an intranet
Signed-off-by: FenjuFu <fufenjupku@gmail.com>
* docs(faq): move entries to the published docs source and fix inaccuracies
Move the new FAQ entries from document/ (a generated tree that the docs
build does not read) to docs/skillhub/, which is what make docs-build and
the Pages deploy actually publish.
Also address review feedback:
- drop the SKILLHUB_BUILTIN_SKILLS_ENABLED tip; compose.release.yml does
not pass that variable through, so setting it has no effect
- correct the dependency list: object storage defaults to local, S3 is
recommended for production
- soften the 502 wording, since upstream/DNS/network can also cause it
- state the 32-character minimum for the cookie secret
- give a real bulk-install example and qualify v0.2.12 as a server version
- drop entries already covered by existing upgrade/MySQL/version questions
Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
* docs(faq): correct deployment and admin guidance
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* docs(faq): fix remaining recreate guidance
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* docs(faq): clarify bulk install paths
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
---------
Signed-off-by: FenjuFu <fufenjupku@gmail.com>
Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Co-authored-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Also add Python CodeQL coverage in the security workflow so repository-level script regression checks stay green when Python source exists.
Signed-off-by: dongmucat <1127093059@qq.com>
Add more community-sourced questions (both Chinese and English):
- Troubleshooting CLI `skillhub publish` returning 400 (name conflict,
SKILL.md location/frontmatter, namespace membership, etc.)
- Required skill package structure (SKILL.md in root)
- "malformed input" on publish caused by non-UTF-8 / Chinese-path zips
- Per-package file-count limit and how to raise it
- Minimum server version for CLI features (v0.2.7+)
- PostgreSQL-only (no MySQL); plugins not distributable yet
- How to check server/CLI versions and customize via secondary dev
Signed-off-by: FenjuFu <fufenjupku@gmail.com>
Add questions frequently raised in the user community that were not yet
covered in the SkillHub FAQ, for both Chinese and English pages:
- Recommended deployment via the one-line script vs manual image pulls
- Redirected back to login page after deploying (manual deployment)
- Changing the admin password / why env changes need a restart
- Password change/reset requires email code (SMTP setup)
- Skill naming (English only; Chinese names error in OpenClaw)
- Whether unreviewed skills can be downloaded
- Hiding/removing GitHub & GitLab SSO login options
- Built-in Skill Scanner: iFLYTEK integration over Cisco's scanner (Apache-2.0)
- Which cisco-ai-skill-scanner version is used (unpinned in Dockerfile)
- Note that upgrades preserve registered skills; online docs link
Signed-off-by: FenjuFu <fufenjupku@gmail.com>