Skip to content

feat: auth provider config with admin bootstrapping and SDK extraction#86

Merged
rorybyrne merged 1 commit into
mainfrom
085-feat-auth-provider
Mar 16, 2026
Merged

feat: auth provider config with admin bootstrapping and SDK extraction#86
rorybyrne merged 1 commit into
mainfrom
085-feat-auth-provider

Conversation

@rorybyrne

Copy link
Copy Markdown
Contributor

Summary

  • Auth provider config (feat: auth provider config with admin bootstrapping via ORCiD list #85): Restructure osa.yaml to promote name/domain to top-level, add provider-keyed auth config (auth.providers.orcid), and add auth.admins.orcid list for SUPERADMIN bootstrapping on first login. Operational config (database, logging, workers, indexes) stays env-var-only.
  • Admin bootstrapping: Users whose ORCiD ID appears in auth.admins.orcid are granted SUPERADMIN at user creation time. Non-retroactive — existing users are unaffected.
  • Server CLI removal: Delete server/osa/cli/ (cyclopts-based local dev CLI). Move OSAPaths to server/osa/util/paths.py. Remove cyclopts, rich, typer dependencies and [project.scripts] entry point.
  • Index config dormant: Remove indexes from config surface and simplify IndexProvider to return an empty registry. Vector search code stays in place but is no longer wired.
  • SDK extracted: The sdk/py/ directory is removed from this repo — it now lives in its own repository.

Test plan

  • Server unit tests pass (804 tests) with updated imports and no osa.cli references
  • New test_admin_bootstrap.py covers: admin ORCiD gets SUPERADMIN, non-admin gets base_role, existing users unaffected
  • New test_config.py covers: top-level name/domain, provider-keyed auth, ORCiD ID validation, ${VAR} interpolation
  • deploy/.env.example includes auth config section (ORCID_CLIENT_ID, ORCID_CLIENT_SECRET, JWT_SECRET)
  • Lint clean (ruff check osa/ tests/)

Closes #85

This commit adds ORCiD OAuth and JWT authentication configuration to the deployment environment example file, providing developers with the necessary environment variables to set up authentication for their OSA instances.

refactor: remove Python SDK from repository

This commit removes the entire Python SDK codebase from the repository as part of a restructuring effort. The SDK included CLI tools, runtime components, type definitions, testing utilities, and deployment functionality that are being relocated or redesigned.

refactor: remove CLI module and restructure config

Remove CLI commands, console utilities, and daemon management to focus
on server-only architecture. Restructure config by promoting server
fields to top-level and moving auth providers to nested structure.

feat: add admin ORCiD bootstrapping support

Add auth.admins.orcid config field with validation for automatic
SUPERADMIN role assignment on first login for specified ORCiD IDs.
@github-actions

Copy link
Copy Markdown

Code Coverage

Package Line Rate Complexity Health
. 76% 0
application 0% 0
application.api 100% 0
application.api.rest 0% 0
application.api.v1 82% 0
application.api.v1.routes 8% 0
application.event 100% 0
domain 100% 0
domain.auth 100% 0
domain.auth.command 90% 0
domain.auth.event 100% 0
domain.auth.model 93% 0
domain.auth.port 99% 0
domain.auth.query 93% 0
domain.auth.service 91% 0
domain.auth.util 100% 0
domain.auth.util.di 81% 0
domain.curation 100% 0
domain.curation.adapter 100% 0
domain.curation.command 100% 0
domain.curation.event 100% 0
domain.curation.handler 92% 0
domain.curation.model 100% 0
domain.curation.port 100% 0
domain.curation.query 100% 0
domain.curation.service 100% 0
domain.deposition 100% 0
domain.deposition.adapter 100% 0
domain.deposition.command 68% 0
domain.deposition.event 100% 0
domain.deposition.handler 100% 0
domain.deposition.model 98% 0
domain.deposition.port 100% 0
domain.deposition.query 60% 0
domain.deposition.service 97% 0
domain.discovery 100% 0
domain.discovery.model 100% 0
domain.discovery.port 100% 0
domain.discovery.query 100% 0
domain.discovery.service 94% 0
domain.discovery.util 100% 0
domain.discovery.util.di 0% 0
domain.export 100% 0
domain.export.adapter 100% 0
domain.export.command 100% 0
domain.export.event 100% 0
domain.export.model 100% 0
domain.export.port 100% 0
domain.export.query 100% 0
domain.export.service 100% 0
domain.feature 100% 0
domain.feature.event 100% 0
domain.feature.handler 100% 0
domain.feature.model 0% 0
domain.feature.port 100% 0
domain.feature.service 96% 0
domain.feature.util 100% 0
domain.feature.util.di 0% 0
domain.index 100% 0
domain.index.event 100% 0
domain.index.handler 75% 0
domain.index.model 84% 0
domain.index.service 100% 0
domain.record 100% 0
domain.record.adapter 100% 0
domain.record.command 100% 0
domain.record.event 100% 0
domain.record.handler 100% 0
domain.record.model 100% 0
domain.record.port 100% 0
domain.record.query 100% 0
domain.record.service 90% 0
domain.search 100% 0
domain.search.adapter 100% 0
domain.search.command 100% 0
domain.search.event 100% 0
domain.search.model 100% 0
domain.search.port 100% 0
domain.search.query 100% 0
domain.search.service 100% 0
domain.semantics 100% 0
domain.semantics.command 31% 0
domain.semantics.event 100% 0
domain.semantics.handler 100% 0
domain.semantics.model 100% 0
domain.semantics.port 100% 0
domain.semantics.query 0% 0
domain.semantics.service 100% 0
domain.semantics.util 100% 0
domain.semantics.util.di 0% 0
domain.shared 91% 0
domain.shared.authorization 87% 0
domain.shared.model 93% 0
domain.shared.port 100% 0
domain.source 100% 0
domain.source.event 100% 0
domain.source.handler 92% 0
domain.source.model 100% 0
domain.source.port 100% 0
domain.source.schedule 67% 0
domain.source.service 98% 0
domain.validation 100% 0
domain.validation.adapter 100% 0
domain.validation.command 0% 0
domain.validation.event 100% 0
domain.validation.handler 90% 0
domain.validation.model 85% 0
domain.validation.port 100% 0
domain.validation.query 100% 0
domain.validation.service 89% 0
infrastructure 100% 0
infrastructure.auth 0% 0
infrastructure.event 64% 0
infrastructure.http 36% 0
infrastructure.index 0% 0
infrastructure.index.vector 73% 0
infrastructure.messaging 100% 0
infrastructure.oci 52% 0
infrastructure.persistence 78% 0
infrastructure.persistence.adapter 59% 0
infrastructure.persistence.mappers 56% 0
infrastructure.persistence.repository 32% 0
infrastructure.source 0% 0
sdk 100% 0
sdk.index 100% 0
util 100% 0
util.di 25% 0
Summary 68% (4492 / 6576) 0

@greptile-apps

greptile-apps Bot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restructures the server config (osa.yaml) to promote name/domain to top-level fields, nests auth providers under auth.providers.orcid, and adds auth.admins.orcid for SUPERADMIN bootstrapping on first login. It also removes the server CLI (osa/cli/), extracts the SDK to its own repo, and makes index backends dormant. The code changes are well-structured with good test coverage (new test_config.py and test_admin_bootstrap.py).

  • Environment variable mismatch (breaking): server/.env.example still references OSA_AUTH__ORCID__CLIENT_ID but the config restructure means the correct env var is now OSA_AUTH__PROVIDERS__ORCID__CLIENT_ID. The old vars will be silently ignored, leaving ORCiD login disabled.
  • Deploy env vars not wired: deploy/.env.example defines ORCID_CLIENT_ID/ORCID_CLIENT_SECRET but docker-compose.yml never maps them to the server's expected OSA_AUTH__PROVIDERS__ORCID__* env vars.
  • Admin bootstrap not provider-scoped: The SUPERADMIN check in _find_or_create_user compares external_id against _admin_orcids without verifying the provider is "orcid" — low risk today but worth guarding for future providers.

Confidence Score: 3/5

  • The core code changes are solid, but the env var configuration mismatch will silently break ORCiD login for anyone following the .env.example files.
  • Score of 3 reflects that the application code and tests are well-written and correct, but the .env.example files (both server/ and deploy/) have not been updated to match the new auth.providers.orcid config path, which will cause ORCiD credentials to be silently ignored in deployments.
  • Pay close attention to server/.env.example (stale env var names) and deploy/.env.example + deploy/docker-compose.yml (missing ORCiD credential mapping).

Important Files Changed

Filename Overview
server/osa/config.py Restructured config: promoted name/domain to top-level, added auth.providers and auth.admins with ORCiD validation. Clean refactor with proper validation.
server/osa/domain/auth/service/auth.py Admin bootstrap logic grants SUPERADMIN to configured ORCiD IDs on first login. The external_id check is not scoped to the orcid provider.
deploy/.env.example Adds ORCID_CLIENT_ID/SECRET and JWT_SECRET vars, but these are not wired through docker-compose.yml to the server container's expected OSA_AUTH__PROVIDERS__ORCID__ env vars.
server/.env.example Not updated to reflect the config restructure — still uses OSA_AUTH__ORCID__CLIENT_ID instead of OSA_AUTH__PROVIDERS__ORCID__CLIENT_ID, so the env vars will be silently ignored.
server/osa/domain/auth/util/di/provider.py Correctly passes admin_orcids set from config to AuthService. Clean DI wiring.
server/osa/infrastructure/auth/di.py Updated to use config.auth.providers.orcid path. Correctly checks client_id before registering provider.
server/osa/infrastructure/index/di.py Simplified to return empty IndexRegistry, making index backends dormant. Clean simplification.
server/tests/unit/config/test_config.py Good test coverage for new config shape: top-level fields, provider-keyed auth, ORCiD validation, defaults.
server/tests/unit/domain/auth/test_admin_bootstrap.py Comprehensive tests covering admin SUPERADMIN grant, non-admin base_role, empty admins, existing user non-retroactivity, and single-role assignment.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[OAuth Callback] --> B["_find_or_create_user()"]
    B --> C{Linked account exists?}
    C -->|Yes| D[Return existing user]
    C -->|No| E[Create User + LinkedAccount]
    E --> F{external_id in admin_orcids?}
    F -->|Yes| G[effective_role = SUPERADMIN]
    F -->|No| H{base_role configured?}
    H -->|Yes| I[effective_role = base_role]
    H -->|No| J[No role assigned]
    G --> K[Save RoleAssignment]
    I --> K
    J --> L[Return new user]
    K --> L
Loading

Comments Outside Diff (1)

  1. server/.env.example, line 22-25 (link)

    Env vars point to the old config path

    The config was restructured so that ORCiD settings live under auth.providers.orcid instead of auth.orcid. With the OSA_ prefix and __ nested delimiter, these env vars should now be OSA_AUTH__PROVIDERS__ORCID__CLIENT_ID and OSA_AUTH__PROVIDERS__ORCID__CLIENT_SECRET. The current names (OSA_AUTH__ORCID__CLIENT_ID, OSA_AUTH__ORCID__CLIENT_SECRET) will silently be ignored because there is no longer an orcid field directly on AuthConfig — it moved to AuthConfig.providers.orcid.

    Similarly, OSA_AUTH__ORCID__SANDBOX on line 29 should become OSA_AUTH__PROVIDERS__ORCID__SANDBOX.

Last reviewed commit: 5932d57

Comment on lines +459 to +460
if identity_info.external_id in self._admin_orcids:
effective_role = Role.SUPERADMIN

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Admin check is not scoped to the ORCiD provider

This checks identity_info.external_id in self._admin_orcids without verifying that identity_info.provider == "orcid". While only ORCiD is supported today, the architecture already supports multiple providers (see ProviderRegistry). If a future provider happens to use an external_id that matches an ORCiD ID pattern, a non-ORCiD user would be silently granted SUPERADMIN. Consider adding a provider guard:

Suggested change
if identity_info.external_id in self._admin_orcids:
effective_role = Role.SUPERADMIN
if identity_info.provider == "orcid" and identity_info.external_id in self._admin_orcids:

Comment thread deploy/.env.example
Comment on lines +43 to +44
ORCID_CLIENT_ID=
ORCID_CLIENT_SECRET=

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ORCiD env vars are defined but never wired

ORCID_CLIENT_ID and ORCID_CLIENT_SECRET are defined here but deploy/docker-compose.yml does not map them to the OSA_AUTH__PROVIDERS__ORCID__CLIENT_ID / OSA_AUTH__PROVIDERS__ORCID__CLIENT_SECRET env vars the server expects. The JWT value is correctly mapped in docker-compose.yml, but the ORCiD credentials are missing. Without that mapping, deployers who fill in these values will find that ORCiD login is never enabled.

The server service's environment: section in docker-compose.yml needs corresponding entries to forward these values.

@rorybyrne rorybyrne merged commit 1c4a882 into main Mar 16, 2026
8 of 9 checks passed
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.

feat: auth provider config with admin bootstrapping via ORCiD list

1 participant